Snapshot d0d351b2ef726ff1b76a3efb4aad91e4d1436f6a
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..e70e5f5
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,65 @@
+# Copyright (C) 2006 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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)
+
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+		libdex \
+		vm \
+		dalvikvm \
+		dexlist \
+		dexopt \
+		dexdump \
+		dvz \
+		dx \
+		libnativehelper \
+		tools \
+	))
+
+include $(subdirs)
+
+
+.PHONY: dex dex-debug
+ifeq ($(DONT_INSTALL_DEX_FILES),true)
+dex:
+	@echo "Forcing a remake with DONT_INSTALL_DEX_FILES=false"
+	$(hide) $(MAKE) DONT_INSTALL_DEX_FILES=false
+else
+# DONT_INSTALL_DEX_FILES is already false, so a normal make takes care of it.
+dex: $(DEFAULT_GOAL)
+endif
+
+d :=
+ifneq ($(GENERATE_DEX_DEBUG),)
+d := debug
+endif
+ifneq ($(DONT_INSTALL_DEX_FILES),true)
+d := $(d)-install
+endif
+ifneq ($(d),debug-install)
+# generate the debug .dex files, with a copy in ./dalvik/DEBUG-FILES.
+# We need to rebuild the .dex files for the debug output to be generated.
+# The "touch -c $(DX)" is a hack that we know will force
+# a rebuild of the .dex files.  If $(DX) doesn't exist yet,
+# we won't touch it (-c) and the normal build will create
+# the .dex files naturally.
+dex-debug:
+	@echo "Forcing an app rebuild with GENERATE_DEX_DEBUG=true"
+	@touch -c $(DX)
+	$(hide) $(MAKE) DONT_INSTALL_DEX_FILES=false GENERATE_DEX_DEBUG=true
+else
+# GENERATE_DEX_DEBUG and DONT_INSTALL_DEX_FILES are already set properly,
+# so a normal make takes care of it.
+dex-debug: $(DEFAULT_GOAL)
+endif
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..1bc6e95
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,54 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+#$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
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/dalvikvm/Android.mk b/dalvikvm/Android.mk
new file mode 100644
index 0000000..b7dda0f
--- /dev/null
+++ b/dalvikvm/Android.mk
@@ -0,0 +1,75 @@
+# 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.
+
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Common definitions.
+#
+
+dalvikvm_src_files := \
+    Main.c
+
+dalvikvm_c_includes := \
+    $(JNI_H_INCLUDE) \
+    dalvik/include
+
+
+#
+# Build for the target (device).
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(dalvikvm_src_files)
+LOCAL_C_INCLUDES := $(dalvikvm_c_includes)
+
+LOCAL_SHARED_LIBRARIES := \
+    libdvm \
+    libssl \
+    libz
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dalvikvm
+
+include $(BUILD_EXECUTABLE)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+    include $(CLEAR_VARS)
+    LOCAL_SRC_FILES := $(dalvikvm_src_files)
+    LOCAL_C_INCLUDES := $(dalvikvm_c_includes)
+
+    ifeq ($(HOST_OS)-$(HOST_ARCH),darwin-x86)
+        # OS X comes with all these libraries, so there is no need
+        # to build any of them. Note: OpenSSL consists of libssl
+        # and libcrypto.
+        LOCAL_LDLIBS := -lffi -lssl -lcrypto -lz -lsqlite3
+    else
+        LOCAL_LDLIBS += -ldl -lpthread
+        LOCAL_SHARED_LIBRARIES += libdvm libcrypto libicuuc libicui18n libssl
+    endif
+
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := dalvikvm
+
+    include $(BUILD_HOST_EXECUTABLE)
+
+endif
diff --git a/dalvikvm/Main.c b/dalvikvm/Main.c
new file mode 100644
index 0000000..666317c
--- /dev/null
+++ b/dalvikvm/Main.c
@@ -0,0 +1,301 @@
+/*
+ * 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 invocation of the Dalvik VM.
+ */
+#include "jni.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+
+
+/*
+ * We want failed write() calls to just return with an error.
+ */
+static void blockSigpipe()
+{
+    sigset_t mask;
+
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGPIPE);
+    if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
+        fprintf(stderr, "WARNING: SIGPIPE not blocked\n");
+}
+
+/*
+ * Create a String[] and populate it with the contents of argv.
+ */
+static jobjectArray createStringArray(JNIEnv* env, char* const argv[], int argc)
+{
+    jclass stringClass = NULL;
+    jobjectArray strArray = NULL;
+    jobjectArray result = NULL;
+    int i;
+
+    stringClass = (*env)->FindClass(env, "java/lang/String");
+    if ((*env)->ExceptionCheck(env)) {
+        fprintf(stderr, "Got exception while finding class String\n");
+        goto bail;
+    }
+    assert(stringClass != NULL);
+    strArray = (*env)->NewObjectArray(env, argc, stringClass, NULL);
+    if ((*env)->ExceptionCheck(env)) {
+        fprintf(stderr, "Got exception while creating String array\n");
+        goto bail;
+    }
+    assert(strArray != NULL);
+
+    for (i = 0; i < argc; i++) {
+        jstring argStr;
+
+        argStr = (*env)->NewStringUTF(env, argv[i]);
+        if ((*env)->ExceptionCheck(env)) {
+            fprintf(stderr, "Got exception while allocating Strings\n");
+            goto bail;
+        }
+        assert(argStr != NULL);
+        (*env)->SetObjectArrayElement(env, strArray, i, argStr);
+        (*env)->DeleteLocalRef(env, argStr);
+    }
+
+    /* return the array, and ensure we don't delete the local ref to it */
+    result = strArray;
+    strArray = NULL;
+
+bail:
+    (*env)->DeleteLocalRef(env, stringClass);
+    (*env)->DeleteLocalRef(env, strArray);
+    return result;
+}
+
+/*
+ * Determine whether or not the specified method is public.
+ *
+ * Returns JNI_TRUE on success, JNI_FALSE on failure.
+ */
+static int methodIsPublic(JNIEnv* env, jclass clazz, jmethodID methodId)
+{
+    static const int PUBLIC = 0x0001;   // java.lang.reflect.Modifiers.PUBLIC
+    jobject refMethod = NULL;
+    jclass methodClass = NULL;
+    jmethodID getModifiersId;
+    int modifiers;
+    int result = JNI_FALSE;
+
+    refMethod = (*env)->ToReflectedMethod(env, clazz, methodId, JNI_FALSE);
+    if (refMethod == NULL) {
+        fprintf(stderr, "Dalvik VM unable to get reflected method\n");
+        goto bail;
+    }
+
+    /*
+     * We now have a Method instance.  We need to call
+     * its getModifiers() method.
+     */
+    methodClass = (*env)->FindClass(env, "java/lang/reflect/Method");
+    if (methodClass == NULL) {
+        fprintf(stderr, "Dalvik VM unable to find class Method\n");
+        goto bail;
+    }
+    getModifiersId = (*env)->GetMethodID(env, methodClass,
+                        "getModifiers", "()I");
+    if (getModifiersId == NULL) {
+        fprintf(stderr, "Dalvik VM unable to find reflect.Method.getModifiers\n");
+        goto bail;
+    }
+
+    modifiers = (*env)->CallIntMethod(env, refMethod, getModifiersId);
+    if ((modifiers & PUBLIC) == 0) {
+        fprintf(stderr, "Dalvik VM: main() is not public\n");
+        goto bail;
+    }
+
+    result = JNI_TRUE;
+
+bail:
+    (*env)->DeleteLocalRef(env, refMethod);
+    (*env)->DeleteLocalRef(env, methodClass);
+    return result;
+}
+
+/*
+ * Parse arguments.  Most of it just gets passed through to the VM.  The
+ * JNI spec defines a handful of standard arguments.
+ */
+int main(int argc, char* const argv[])
+{
+    JavaVM* vm = NULL;
+    JNIEnv* env = NULL;
+    JavaVMInitArgs initArgs;
+    JavaVMOption* options = NULL;
+    char* slashClass = NULL;
+    int optionCount, curOpt, i, argIdx;
+    int needExtra = JNI_FALSE;
+    int result = 1;
+
+    setvbuf(stdout, NULL, _IONBF, 0);
+
+    /* ignore argv[0] */
+    argv++;
+    argc--;
+
+    /*
+     * If we're adding any additional stuff, e.g. function hook specifiers,
+     * add them to the count here.
+     *
+     * We're over-allocating, because this includes the options to the VM
+     * plus the options to the program.
+     */
+    optionCount = argc;
+
+    options = (JavaVMOption*) malloc(sizeof(JavaVMOption) * optionCount);
+    memset(options, 0, sizeof(JavaVMOption) * optionCount);
+
+    /*
+     * Copy options over.  Everything up to the name of the class starts
+     * with a '-' (the function hook stuff is strictly internal).
+     *
+     * [Do we need to catch & handle "-jar" here?]
+     */
+    for (curOpt = argIdx = 0; argIdx < argc; argIdx++) {
+        if (argv[argIdx][0] != '-' && !needExtra)
+            break;
+        options[curOpt++].optionString = strdup(argv[argIdx]);
+
+        /* some options require an additional arg */
+        needExtra = JNI_FALSE;
+        if (strcmp(argv[argIdx], "-classpath") == 0 ||
+            strcmp(argv[argIdx], "-cp") == 0)
+            /* others? */
+        {
+            needExtra = JNI_TRUE;
+        }
+    }
+
+    if (needExtra) {
+        fprintf(stderr, "Dalvik VM requires value after last option flag\n");
+        goto bail;
+    }
+
+    /* insert additional internal options here */
+
+    assert(curOpt <= optionCount);
+
+    initArgs.version = JNI_VERSION_1_4;
+    initArgs.options = options;
+    initArgs.nOptions = curOpt;
+    initArgs.ignoreUnrecognized = JNI_FALSE;
+
+    //printf("nOptions = %d\n", initArgs.nOptions);
+
+    blockSigpipe();
+
+    /*
+     * Start VM.  The current thread becomes the main thread of the VM.
+     */
+    if (JNI_CreateJavaVM(&vm, &env, &initArgs) < 0) {
+        fprintf(stderr, "Dalvik VM init failed (check log file)\n");
+        goto bail;
+    }
+
+    /*
+     * Make sure they provided a class name.  We do this after VM init
+     * so that things like "-Xrunjdwp:help" have the opportunity to emit
+     * a usage statement.
+     */
+    if (argIdx == argc) {
+        fprintf(stderr, "Dalvik VM requires a class name\n");
+        goto bail;
+    }
+
+    /*
+     * We want to call main() with a String array with our arguments in it.
+     * Create an array and populate it.  Note argv[0] is not included.
+     */
+    jobjectArray strArray;
+    strArray = createStringArray(env, &argv[argIdx+1], argc-argIdx-1);
+    if (strArray == NULL)
+        goto bail;
+
+    /*
+     * Find [class].main(String[]).
+     */
+    jclass startClass;
+    jmethodID startMeth;
+    char* cp;
+
+    /* convert "com.android.Blah" to "com/android/Blah" */
+    slashClass = strdup(argv[argIdx]);
+    for (cp = slashClass; *cp != '\0'; cp++)
+        if (*cp == '.')
+            *cp = '/';
+
+    startClass = (*env)->FindClass(env, slashClass);
+    if (startClass == NULL) {
+        fprintf(stderr, "Dalvik VM unable to locate class '%s'\n", slashClass);
+        goto bail;
+    }
+
+    startMeth = (*env)->GetStaticMethodID(env, startClass,
+                    "main", "([Ljava/lang/String;)V");
+    if (startMeth == NULL) {
+        fprintf(stderr, "Dalvik VM unable to find static main(String[]) in '%s'\n",
+            slashClass);
+        goto bail;
+    }
+
+    /*
+     * Make sure the method is public.  JNI doesn't prevent us from calling
+     * a private method, so we have to check it explicitly.
+     */
+    if (!methodIsPublic(env, startClass, startMeth))
+        goto bail;
+
+    /*
+     * Invoke main().
+     */
+    (*env)->CallStaticVoidMethod(env, startClass, startMeth, strArray);
+
+    if (!(*env)->ExceptionCheck(env))
+        result = 0;
+
+bail:
+    /*printf("Shutting down Dalvik VM\n");*/
+    if (vm != NULL) {
+        /*
+         * This allows join() and isAlive() on the main thread to work
+         * correctly, and also provides uncaught exception handling.
+         */
+        if ((*vm)->DetachCurrentThread(vm) != JNI_OK) {
+            fprintf(stderr, "Warning: unable to detach main thread\n");
+            result = 1;
+        }
+
+        if ((*vm)->DestroyJavaVM(vm) != 0)
+            fprintf(stderr, "Warning: Dalvik VM did not shut down cleanly\n");
+        /*printf("\nDalvik VM has exited\n");*/
+    }
+
+    for (i = 0; i < optionCount; i++)
+        free((char*) options[i].optionString);
+    free(options);
+    free(slashClass);
+    /*printf("--- VM is down, process exiting\n");*/
+    return result;
+}
diff --git a/dexdump/Android.mk b/dexdump/Android.mk
new file mode 100644
index 0000000..89749d6
--- /dev/null
+++ b/dexdump/Android.mk
@@ -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.
+
+#
+# dexdump, similar in purpose to objdump.
+#
+LOCAL_PATH:= $(call my-dir)
+
+dexdump_src_files := \
+		DexDump.c
+
+dexdump_c_includes := \
+		dalvik \
+		$(JNI_H_INCLUDE)
+
+dexdump_shared_libraries :=
+
+dexdump_static_libraries := \
+		libdex
+
+##
+##
+## Build the device command line tool dexdump
+##
+##
+ifneq ($(SDK_ONLY),true)  # SDK_only doesn't need device version
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexdump
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries) libz liblog
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries)
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_LDLIBS +=
+include $(BUILD_EXECUTABLE)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host command line tool dexdump
+##
+##
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexdump
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries) liblog
+
+ifneq ($(strip $(USE_MINGW)),)
+LOCAL_STATIC_LIBRARIES += libz
+else
+LOCAL_LDLIBS += -lpthread -lz
+endif
+
+include $(BUILD_HOST_EXECUTABLE)
+endif # !TARGET_SIMULATOR
diff --git a/dexdump/DexDump.c b/dexdump/DexDump.c
new file mode 100644
index 0000000..636408a
--- /dev/null
+++ b/dexdump/DexDump.c
@@ -0,0 +1,1891 @@
+/*
+ * 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 "dexdump" tool is intended to mimic "objdump".  When possible, use
+ * similar command-line arguments.
+ *
+ * TODO: rework the "plain" output format to be more regexp-friendly
+ *
+ * Differences between XML output and the "current.xml" file:
+ * - classes in same package are not all grouped together; generally speaking
+ *   nothing is sorted
+ * - no "deprecated" on fields and methods
+ * - no "value" on fields
+ * - no parameter names
+ * - no generic signatures on parameters, e.g. type="java.lang.Class&lt;?&gt;"
+ * - class shows declared fields and methods; does not show inherited fields
+ */
+#include "libdex/DexFile.h"
+#include "libdex/DexCatch.h"
+#include "libdex/DexClass.h"
+#include "libdex/DexProto.h"
+#include "libdex/InstrUtils.h"
+#include "libdex/OpCodeNames.h"
+#include "libdex/SysUtil.h"
+#include "libdex/CmdUtils.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+
+static const char* gProgName = "dexdump";
+
+static InstructionWidth* gInstrWidth;
+static InstructionFormat* gInstrFormat;
+
+typedef enum OutputFormat {
+    OUTPUT_PLAIN = 0,               /* default */
+    OUTPUT_XML,                     /* fancy */
+} OutputFormat;
+
+/* command-line options */
+struct {
+    bool checksumOnly;
+    bool disassemble;
+    bool showFileHeaders;
+    bool showSectionHeaders;
+    bool ignoreBadChecksum;
+    bool dumpRegisterMaps;
+    OutputFormat outputFormat;
+    const char* tempFileName;
+    bool exportsOnly;
+    bool verbose;
+} gOptions;
+
+/* basic info about a field or method */
+typedef struct FieldMethodInfo {
+    const char* classDescriptor;
+    const char* name;
+    const char* signature;
+} FieldMethodInfo;
+
+/*
+ * Get 2 little-endian bytes.
+ */
+static inline u2 get2LE(unsigned char const* pSrc)
+{
+    return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+static inline u4 get4LE(unsigned char const* pSrc)
+{
+    return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24);
+}
+
+/*
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+static const char* primitiveTypeLabel(char typeChar)
+{
+    switch (typeChar) {
+    case 'B':   return "byte";
+    case 'C':   return "char";
+    case 'D':   return "double";
+    case 'F':   return "float";
+    case 'I':   return "int";
+    case 'J':   return "long";
+    case 'S':   return "short";
+    case 'V':   return "void";
+    case 'Z':   return "boolean";
+    default:
+                return "UNKNOWN";
+    }
+}
+
+/*
+ * Converts a type descriptor to human-readable "dotted" form.  For
+ * example, "Ljava/lang/String;" becomes "java.lang.String", and
+ * "[I" becomes "int[]".  Also converts '$' to '.', which means this
+ * form can't be converted back to a descriptor.
+ */
+static char* descriptorToDot(const char* str)
+{
+    int targetLen = strlen(str);
+    int offset = 0;
+    int arrayDepth = 0;
+    char* newStr;
+
+    /* strip leading [s; will be added to end */
+    while (targetLen > 1 && str[offset] == '[') {
+        offset++;
+        targetLen--;
+    }
+    arrayDepth = offset;
+
+    if (targetLen == 1) {
+        /* primitive type */
+        str = primitiveTypeLabel(str[offset]);
+        offset = 0;
+        targetLen = strlen(str);
+    } else {
+        /* account for leading 'L' and trailing ';' */
+        if (targetLen >= 2 && str[offset] == 'L' &&
+            str[offset+targetLen-1] == ';')
+        {
+            targetLen -= 2;
+            offset++;
+        }
+    }
+
+    newStr = malloc(targetLen + arrayDepth * 2 +1);
+
+    /* copy class name over */
+    int i;
+    for (i = 0; i < targetLen; i++) {
+        char ch = str[offset + i];
+        newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
+    }
+
+    /* add the appropriate number of brackets for arrays */
+    while (arrayDepth-- > 0) {
+        newStr[i++] = '[';
+        newStr[i++] = ']';
+    }
+    newStr[i] = '\0';
+    assert(i == targetLen + arrayDepth * 2);
+
+    return newStr;
+}
+
+/*
+ * Converts the class name portion of a type descriptor to human-readable
+ * "dotted" form.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* descriptorClassToDot(const char* str)
+{
+    const char* lastSlash;
+    char* newStr;
+    char* cp;
+
+    /* reduce to just the class name, trimming trailing ';' */
+    lastSlash = strrchr(str, '/');
+    if (lastSlash == NULL)
+        lastSlash = str + 1;        /* start past 'L' */
+    else
+        lastSlash++;                /* start past '/' */
+
+    newStr = strdup(lastSlash);
+    newStr[strlen(lastSlash)-1] = '\0';
+    for (cp = newStr; *cp != '\0'; cp++) {
+        if (*cp == '$')
+            *cp = '.';
+    }
+
+    return newStr;
+}
+
+/*
+ * Returns a quoted string representing the boolean value.
+ */
+static const char* quotedBool(bool val)
+{
+    if (val)
+        return "\"true\"";
+    else
+        return "\"false\"";
+}
+
+static const char* quotedVisibility(u4 accessFlags)
+{
+    if ((accessFlags & ACC_PUBLIC) != 0)
+        return "\"public\"";
+    else if ((accessFlags & ACC_PROTECTED) != 0)
+        return "\"protected\"";
+    else if ((accessFlags & ACC_PRIVATE) != 0)
+        return "\"private\"";
+    else
+        return "\"package\"";
+}
+
+/*
+ * Count the number of '1' bits in a word.
+ */
+static int countOnes(u4 val)
+{
+    int count = 0;
+
+    val = val - ((val >> 1) & 0x55555555);
+    val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
+    count = (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
+
+    return count;
+}
+
+/*
+ * Flag for use with createAccessFlagStr().
+ */
+typedef enum AccessFor {
+    kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
+    kAccessForMAX
+} AccessFor;
+
+/*
+ * Create a new string with human-readable access flags.
+ *
+ * In the base language the access_flags fields are type u2; in Dalvik
+ * they're u4.
+ */
+static char* createAccessFlagStr(u4 flags, AccessFor forWhat)
+{
+#define NUM_FLAGS   18
+    static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = {
+        {
+            /* class, inner class */
+            "PUBLIC",           /* 0x0001 */
+            "PRIVATE",          /* 0x0002 */
+            "PROTECTED",        /* 0x0004 */
+            "STATIC",           /* 0x0008 */
+            "FINAL",            /* 0x0010 */
+            "?",                /* 0x0020 */
+            "?",                /* 0x0040 */
+            "?",                /* 0x0080 */
+            "?",                /* 0x0100 */
+            "INTERFACE",        /* 0x0200 */
+            "ABSTRACT",         /* 0x0400 */
+            "?",                /* 0x0800 */
+            "SYNTHETIC",        /* 0x1000 */
+            "ANNOTATION",       /* 0x2000 */
+            "ENUM",             /* 0x4000 */
+            "?",                /* 0x8000 */
+            "VERIFIED",         /* 0x10000 */
+            "OPTIMIZED",        /* 0x20000 */
+        },
+        {
+            /* method */
+            "PUBLIC",           /* 0x0001 */
+            "PRIVATE",          /* 0x0002 */
+            "PROTECTED",        /* 0x0004 */
+            "STATIC",           /* 0x0008 */
+            "FINAL",            /* 0x0010 */
+            "SYNCHRONIZED",     /* 0x0020 */
+            "BRIDGE",           /* 0x0040 */
+            "VARARGS",          /* 0x0080 */
+            "NATIVE",           /* 0x0100 */
+            "?",                /* 0x0200 */
+            "ABSTRACT",         /* 0x0400 */
+            "STRICT",           /* 0x0800 */
+            "SYNTHETIC",        /* 0x1000 */
+            "?",                /* 0x2000 */
+            "?",                /* 0x4000 */
+            "MIRANDA",          /* 0x8000 */
+            "CONSTRUCTOR",      /* 0x10000 */
+            "DECLARED_SYNCHRONIZED", /* 0x20000 */
+        },
+        {
+            /* field */
+            "PUBLIC",           /* 0x0001 */
+            "PRIVATE",          /* 0x0002 */
+            "PROTECTED",        /* 0x0004 */
+            "STATIC",           /* 0x0008 */
+            "FINAL",            /* 0x0010 */
+            "?",                /* 0x0020 */
+            "VOLATILE",         /* 0x0040 */
+            "TRANSIENT",        /* 0x0080 */
+            "?",                /* 0x0100 */
+            "?",                /* 0x0200 */
+            "?",                /* 0x0400 */
+            "?",                /* 0x0800 */
+            "SYNTHETIC",        /* 0x1000 */
+            "?",                /* 0x2000 */
+            "ENUM",             /* 0x4000 */
+            "?",                /* 0x8000 */
+            "?",                /* 0x10000 */
+            "?",                /* 0x20000 */
+        },
+    };
+    const int kLongest = 21;        /* strlen of longest string above */
+    int i, count;
+    char* str;
+    char* cp;
+
+    /*
+     * Allocate enough storage to hold the expected number of strings,
+     * plus a space between each.  We over-allocate, using the longest
+     * string above as the base metric.
+     */
+    count = countOnes(flags);
+    cp = str = (char*) malloc(count * (kLongest+1) +1);
+
+    for (i = 0; i < NUM_FLAGS; i++) {
+        if (flags & 0x01) {
+            const char* accessStr = kAccessStrings[forWhat][i];
+            int len = strlen(accessStr);
+            if (cp != str)
+                *cp++ = ' ';
+
+            memcpy(cp, accessStr, len);
+            cp += len;
+        }
+        flags >>= 1;
+    }
+    *cp = '\0';
+
+    return str;
+}
+
+
+/*
+ * Copy character data from "data" to "out", converting non-ASCII values
+ * to printf format chars or an ASCII filler ('.' or '?').
+ *
+ * The output buffer must be able to hold (2*len)+1 bytes.  The result is
+ * NUL-terminated.
+ */
+static void asciify(char* out, const unsigned char* data, size_t len)
+{
+    while (len--) {
+        if (*data < 0x20) {
+            /* could do more here, but we don't need them yet */
+            switch (*data) {
+            case '\0':
+                *out++ = '\\';
+                *out++ = '0';
+                break;
+            case '\n':
+                *out++ = '\\';
+                *out++ = 'n';
+                break;
+            default:
+                *out++ = '.';
+                break;
+            }
+        } else if (*data >= 0x80) {
+            *out++ = '?';
+        } else {
+            *out++ = *data;
+        }
+        data++;
+    }
+    *out = '\0';
+}
+
+/*
+ * Dump the file header.
+ */
+void dumpFileHeader(const DexFile* pDexFile)
+{
+    const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+    const DexHeader* pHeader = pDexFile->pHeader;
+    char sanitized[sizeof(pHeader->magic)*2 +1];
+
+    assert(sizeof(pHeader->magic) == sizeof(pOptHeader->magic));
+
+    if (pOptHeader != NULL) {
+        printf("Optimized DEX file header:\n");
+
+        asciify(sanitized, pOptHeader->magic, sizeof(pOptHeader->magic));
+        printf("magic               : '%s'\n", sanitized);
+        printf("dex_offset          : %d (0x%06x)\n",
+            pOptHeader->dexOffset, pOptHeader->dexOffset);
+        printf("dex_length          : %d\n", pOptHeader->dexLength);
+        printf("deps_offset         : %d (0x%06x)\n",
+            pOptHeader->depsOffset, pOptHeader->depsOffset);
+        printf("deps_length         : %d\n", pOptHeader->depsLength);
+        printf("opt_offset          : %d (0x%06x)\n",
+            pOptHeader->optOffset, pOptHeader->optOffset);
+        printf("opt_length          : %d\n", pOptHeader->optLength);
+        printf("flags               : %08x\n", pOptHeader->flags);
+        printf("checksum            : %08x\n", pOptHeader->checksum);
+        printf("\n");
+    }
+
+    printf("DEX file header:\n");
+    asciify(sanitized, pHeader->magic, sizeof(pHeader->magic));
+    printf("magic               : '%s'\n", sanitized);
+    printf("checksum            : %08x\n", pHeader->checksum);
+    printf("signature           : %02x%02x...%02x%02x\n",
+        pHeader->signature[0], pHeader->signature[1],
+        pHeader->signature[kSHA1DigestLen-2],
+        pHeader->signature[kSHA1DigestLen-1]);
+    printf("file_size           : %d\n", pHeader->fileSize);
+    printf("header_size         : %d\n", pHeader->headerSize);
+    printf("link_size           : %d\n", pHeader->linkSize);
+    printf("link_off            : %d (0x%06x)\n",
+        pHeader->linkOff, pHeader->linkOff);
+    printf("string_ids_size     : %d\n", pHeader->stringIdsSize);
+    printf("string_ids_off      : %d (0x%06x)\n",
+        pHeader->stringIdsOff, pHeader->stringIdsOff);
+    printf("type_ids_size       : %d\n", pHeader->typeIdsSize);
+    printf("type_ids_off        : %d (0x%06x)\n",
+        pHeader->typeIdsOff, pHeader->typeIdsOff);
+    printf("field_ids_size      : %d\n", pHeader->fieldIdsSize);
+    printf("field_ids_off       : %d (0x%06x)\n",
+        pHeader->fieldIdsOff, pHeader->fieldIdsOff);
+    printf("method_ids_size     : %d\n", pHeader->methodIdsSize);
+    printf("method_ids_off      : %d (0x%06x)\n",
+        pHeader->methodIdsOff, pHeader->methodIdsOff);
+    printf("class_defs_size     : %d\n", pHeader->classDefsSize);
+    printf("class_defs_off      : %d (0x%06x)\n",
+        pHeader->classDefsOff, pHeader->classDefsOff);
+    printf("data_size           : %d\n", pHeader->dataSize);
+    printf("data_off            : %d (0x%06x)\n",
+        pHeader->dataOff, pHeader->dataOff);
+    printf("\n");
+}
+
+/*
+ * Dump the "table of contents" for the opt area.
+ */
+void dumpOptDirectory(const DexFile* pDexFile)
+{
+    const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+    if (pOptHeader == NULL)
+        return;
+
+    printf("OPT section contents:\n");
+
+    const u4* pOpt = (const u4*) ((u1*) pOptHeader + pOptHeader->optOffset);
+
+    if (*pOpt == 0) {
+        printf("(1.0 format, only class lookup table is present)\n\n");
+        return;
+    }
+
+    /*
+     * The "opt" section is in "chunk" format: a 32-bit identifier, a 32-bit
+     * length, then the data.  Chunks start on 64-bit boundaries.
+     */
+    while (*pOpt != kDexChunkEnd) {
+        const char* verboseStr;
+
+        u4 size = *(pOpt+1);
+
+        switch (*pOpt) {
+        case kDexChunkClassLookup:
+            verboseStr = "class lookup hash table";
+            break;
+        case kDexChunkRegisterMaps:
+            verboseStr = "register maps";
+            break;
+        default:
+            verboseStr = "(unknown chunk type)";
+            break;
+        }
+
+        printf("Chunk %08x (%c%c%c%c) - %s (%d bytes)\n", *pOpt,
+            *pOpt >> 24, (char)(*pOpt >> 16), (char)(*pOpt >> 8), (char)*pOpt,
+            verboseStr, size);
+
+        size = (size + 8 + 7) & ~7;
+        pOpt += size / sizeof(u4);
+    }
+    printf("\n");
+}
+
+/*
+ * Dump a class_def_item.
+ */
+void dumpClassDef(DexFile* pDexFile, int idx)
+{
+    const DexClassDef* pClassDef;
+    const u1* pEncodedData;
+    DexClassData* pClassData;
+
+    pClassDef = dexGetClassDef(pDexFile, idx);
+    pEncodedData = dexGetClassData(pDexFile, pClassDef);
+    pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+    if (pClassData == NULL) {
+        fprintf(stderr, "Trouble reading class data\n");
+        return;
+    }
+
+    printf("Class #%d header:\n", idx);
+    printf("class_idx           : %d\n", pClassDef->classIdx);
+    printf("access_flags        : %d (0x%04x)\n",
+        pClassDef->accessFlags, pClassDef->accessFlags);
+    printf("superclass_idx      : %d\n", pClassDef->superclassIdx);
+    printf("interfaces_off      : %d (0x%06x)\n",
+        pClassDef->interfacesOff, pClassDef->interfacesOff);
+    printf("source_file_idx     : %d\n", pClassDef->sourceFileIdx);
+    printf("annotations_off     : %d (0x%06x)\n",
+        pClassDef->annotationsOff, pClassDef->annotationsOff);
+    printf("class_data_off      : %d (0x%06x)\n",
+        pClassDef->classDataOff, pClassDef->classDataOff);
+    printf("static_fields_size  : %d\n", pClassData->header.staticFieldsSize);
+    printf("instance_fields_size: %d\n",
+            pClassData->header.instanceFieldsSize);
+    printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);
+    printf("virtual_methods_size: %d\n",
+            pClassData->header.virtualMethodsSize);
+    printf("\n");
+
+    free(pClassData);
+}
+
+/*
+ * Dump an interface that a class declares to implement.
+ */
+void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
+    int i)
+{
+    const char* interfaceName =
+        dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("    #%d              : '%s'\n", i, interfaceName);
+    } else {
+        char* dotted = descriptorToDot(interfaceName);
+        printf("<implements name=\"%s\">\n</implements>\n", dotted);
+        free(dotted);
+    }
+}
+
+/*
+ * Dump the catches table associated with the code.
+ */
+void dumpCatches(DexFile* pDexFile, const DexCode* pCode)
+{
+    u4 triesSize = pCode->triesSize;
+
+    if (triesSize == 0) {
+        printf("      catches       : (none)\n");
+        return;
+    }
+
+    printf("      catches       : %d\n", triesSize);
+
+    const DexTry* pTries = dexGetTries(pCode);
+    u4 i;
+
+    for (i = 0; i < triesSize; i++) {
+        const DexTry* pTry = &pTries[i];
+        u4 start = pTry->startAddr;
+        u4 end = start + pTry->insnCount;
+        DexCatchIterator iterator;
+
+        printf("        0x%04x - 0x%04x\n", start, end);
+
+        dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
+
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+            const char* descriptor;
+
+            if (handler == NULL) {
+                break;
+            }
+
+            descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" :
+                dexStringByTypeIdx(pDexFile, handler->typeIdx);
+
+            printf("          %s -> 0x%04x\n", descriptor,
+                    handler->address);
+        }
+    }
+}
+
+static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum)
+{
+    printf("        0x%04x line=%d\n", address, lineNum);
+    return 0;
+}
+
+/*
+ * Dump the positions list.
+ */
+void dumpPositions(DexFile* pDexFile, const DexCode* pCode,
+        const DexMethod *pDexMethod)
+{
+    printf("      positions     : \n");
+    const DexMethodId *pMethodId
+            = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+    const char *classDescriptor
+            = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+    dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+            pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
+}
+
+static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress,
+        u4 endAddress, const char *name, const char *descriptor,
+        const char *signature)
+{
+    printf("        0x%04x - 0x%04x reg=%d %s %s %s\n",
+            startAddress, endAddress, reg, name, descriptor,
+            signature);
+}
+
+/*
+ * Dump the locals list.
+ */
+void dumpLocals(DexFile* pDexFile, const DexCode* pCode,
+        const DexMethod *pDexMethod)
+{
+    printf("      locals        : \n");
+
+    const DexMethodId *pMethodId
+            = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+    const char *classDescriptor
+            = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+    dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+            pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL);
+}
+
+/*
+ * Get information about a method.
+ */
+bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo)
+{
+    const DexMethodId* pMethodId;
+
+    if (methodIdx >= pDexFile->pHeader->methodIdsSize)
+        return false;
+
+    pMethodId = dexGetMethodId(pDexFile, methodIdx);
+    pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx);
+    pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+
+    pMethInfo->classDescriptor =
+            dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+    return true;
+}
+
+/*
+ * Get information about a field.
+ */
+bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo)
+{
+    const DexFieldId* pFieldId;
+
+    if (fieldIdx >= pDexFile->pHeader->fieldIdsSize)
+        return false;
+
+    pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+    pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx);
+    pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    pFieldInfo->classDescriptor =
+        dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+    return true;
+}
+
+
+/*
+ * Look up a class' descriptor.
+ */
+const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
+{
+    return dexStringByTypeIdx(pDexFile, classIdx);
+}
+
+/*
+ * Dump a single instruction.
+ */
+void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
+    int insnWidth, const DecodedInstruction* pDecInsn)
+{
+    const u2* insns = pCode->insns;
+    int i;
+
+    printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
+    for (i = 0; i < 8; i++) {
+        if (i < insnWidth) {
+            if (i == 7) {
+                printf(" ... ");
+            } else {
+                /* print 16-bit value in little-endian order */
+                const u1* bytePtr = (const u1*) &insns[insnIdx+i];
+                printf(" %02x%02x", bytePtr[0], bytePtr[1]);
+            }
+        } else {
+            fputs("     ", stdout);
+        }
+    }
+
+    if (pDecInsn->opCode == OP_NOP) {
+        u2 instr = get2LE((const u1*) &insns[insnIdx]);
+        if (instr == kPackedSwitchSignature) {
+            printf("|%04x: packed-switch-data (%d units)",
+                insnIdx, insnWidth);
+        } else if (instr == kSparseSwitchSignature) {
+            printf("|%04x: sparse-switch-data (%d units)",
+                insnIdx, insnWidth);
+        } else if (instr == kArrayDataSignature) {
+            printf("|%04x: array-data (%d units)",
+                insnIdx, insnWidth);
+        } else {
+            printf("|%04x: nop // spacer", insnIdx);
+        }
+    } else {
+        printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opCode));
+    }
+
+    switch (dexGetInstrFormat(gInstrFormat, pDecInsn->opCode)) {
+    case kFmt10x:        // op
+        break;
+    case kFmt12x:        // op vA, vB
+        printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+        break;
+    case kFmt11n:        // op vA, #+B
+        printf(" v%d, #int %d // #%x",
+            pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
+        break;
+    case kFmt11x:        // op vAA
+        printf(" v%d", pDecInsn->vA);
+        break;
+    case kFmt10t:        // op +AA
+    case kFmt20t:        // op +AAAA
+        {
+            s4 targ = (s4) pDecInsn->vA;
+            printf(" %04x // %c%04x",
+                insnIdx + targ,
+                (targ < 0) ? '-' : '+',
+                (targ < 0) ? -targ : targ);
+        }
+        break;
+    case kFmt22x:        // op vAA, vBBBB
+        printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+        break;
+    case kFmt21t:        // op vAA, +BBBB
+        {
+            s4 targ = (s4) pDecInsn->vB;
+            printf(" v%d, %04x // %c%04x", pDecInsn->vA,
+                insnIdx + targ,
+                (targ < 0) ? '-' : '+',
+                (targ < 0) ? -targ : targ);
+        }
+        break;
+    case kFmt21s:        // op vAA, #+BBBB
+        printf(" v%d, #int %d // #%x",
+            pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
+        break;
+    case kFmt21h:        // op vAA, #+BBBB0000[00000000]
+        // The printed format varies a bit based on the actual opcode.
+        if (pDecInsn->opCode == OP_CONST_HIGH16) {
+            s4 value = pDecInsn->vB << 16;
+            printf(" v%d, #int %d // #%x",
+                pDecInsn->vA, value, (u2)pDecInsn->vB);
+        } else {
+            s8 value = ((s8) pDecInsn->vB) << 48;
+            printf(" v%d, #long %lld // #%x",
+                pDecInsn->vA, value, (u2)pDecInsn->vB);
+        }
+        break;
+    case kFmt21c:        // op vAA, thing@BBBB
+        if (pDecInsn->opCode == OP_CONST_STRING) {
+            printf(" v%d, \"%s\" // string@%04x", pDecInsn->vA,
+                dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
+        } else if (pDecInsn->opCode == OP_CHECK_CAST ||
+                   pDecInsn->opCode == OP_NEW_INSTANCE ||
+                   pDecInsn->opCode == OP_CONST_CLASS)
+        {
+            printf(" v%d, %s // class@%04x", pDecInsn->vA,
+                getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
+        } else /* OP_SGET* */ {
+            FieldMethodInfo fieldInfo;
+            if (getFieldInfo(pDexFile, pDecInsn->vB, &fieldInfo)) {
+                printf(" v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
+                    fieldInfo.classDescriptor, fieldInfo.name,
+                    fieldInfo.signature, pDecInsn->vB);
+            } else {
+                printf(" v%d, ??? // field@%04x", pDecInsn->vA, pDecInsn->vB);
+            }
+        }
+        break;
+    case kFmt23x:        // op vAA, vBB, vCC
+        printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
+        break;
+    case kFmt22b:        // op vAA, vBB, #+CC
+        printf(" v%d, v%d, #int %d // #%02x",
+            pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
+        break;
+    case kFmt22t:        // op vA, vB, +CCCC
+        {
+            s4 targ = (s4) pDecInsn->vC;
+            printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
+                insnIdx + targ,
+                (targ < 0) ? '-' : '+',
+                (targ < 0) ? -targ : targ);
+        }
+        break;
+    case kFmt22s:        // op vA, vB, #+CCCC
+        printf(" v%d, v%d, #int %d // #%04x",
+            pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
+        break;
+    case kFmt22c:        // op vA, vB, thing@CCCC
+        if (pDecInsn->opCode == OP_INSTANCE_OF ||
+            pDecInsn->opCode == OP_NEW_ARRAY)
+        {
+            printf(" v%d, v%d, %s // class@%04x",
+                pDecInsn->vA, pDecInsn->vB,
+                getClassDescriptor(pDexFile, pDecInsn->vC), pDecInsn->vC);
+        } else {
+            /* iget* and iput*, including dexopt-generated -volatile */
+            FieldMethodInfo fieldInfo;
+            if (getFieldInfo(pDexFile, pDecInsn->vC, &fieldInfo)) {
+                printf(" v%d, v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
+                    pDecInsn->vB, fieldInfo.classDescriptor, fieldInfo.name,
+                    fieldInfo.signature, pDecInsn->vC);
+            } else {
+                printf(" v%d, v%d, ??? // field@%04x", pDecInsn->vA,
+                    pDecInsn->vB, pDecInsn->vC);
+            }
+        }
+        break;
+    case kFmt22cs:       // [opt] op vA, vB, field offset CCCC
+        printf(" v%d, v%d, [obj+%04x]",
+            pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
+        break;
+    case kFmt30t:
+        printf(" #%08x", pDecInsn->vA);
+        break;
+    case kFmt31i:        // op vAA, #+BBBBBBBB
+        {
+            /* this is often, but not always, a float */
+            union {
+                float f;
+                u4 i;
+            } conv;
+            conv.i = pDecInsn->vB;
+            printf(" v%d, #float %f // #%08x",
+                pDecInsn->vA, conv.f, pDecInsn->vB);
+        }
+        break;
+    case kFmt31c:        // op vAA, thing@BBBBBBBB
+        printf(" v%d, \"%s\" // string@%08x", pDecInsn->vA,
+            dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
+        break;
+    case kFmt31t:       // op vAA, offset +BBBBBBBB
+        printf(" v%d, %08x // +%08x",
+            pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
+        break;
+    case kFmt32x:        // op vAAAA, vBBBB
+        printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+        break;
+    case kFmt35c:        // op vB, {vD, vE, vF, vG, vA}, thing@CCCC
+        {
+            /* NOTE: decoding of 35c doesn't quite match spec */
+            fputs(" {", stdout);
+            for (i = 0; i < (int) pDecInsn->vA; i++) {
+                if (i == 0)
+                    printf("v%d", pDecInsn->arg[i]);
+                else
+                    printf(", v%d", pDecInsn->arg[i]);
+            }
+            if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY) {
+                printf("}, %s // class@%04x",
+                    getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
+            } else {
+                FieldMethodInfo methInfo;
+                if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
+                    printf("}, %s.%s:%s // method@%04x",
+                        methInfo.classDescriptor, methInfo.name,
+                        methInfo.signature, pDecInsn->vB);
+                } else {
+                    printf("}, ??? // method@%04x", pDecInsn->vB);
+                }
+            }
+        }
+        break;
+    case kFmt35ms:       // [opt] invoke-virtual+super
+    case kFmt35fs:       // [opt] invoke-interface
+        {
+            fputs(" {", stdout);
+            for (i = 0; i < (int) pDecInsn->vA; i++) {
+                if (i == 0)
+                    printf("v%d", pDecInsn->arg[i]);
+                else
+                    printf(", v%d", pDecInsn->arg[i]);
+            }
+            printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
+        }
+        break;
+    case kFmt3rc:        // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+        {
+            /*
+             * This doesn't match the "dx" output when some of the args are
+             * 64-bit values -- dx only shows the first register.
+             */
+            fputs(" {", stdout);
+            for (i = 0; i < (int) pDecInsn->vA; i++) {
+                if (i == 0)
+                    printf("v%d", pDecInsn->vC + i);
+                else
+                    printf(", v%d", pDecInsn->vC + i);
+            }
+            if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY_RANGE) {
+                printf("}, %s // class@%04x",
+                    getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
+            } else {
+                FieldMethodInfo methInfo;
+                if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
+                    printf("}, %s.%s:%s // method@%04x",
+                        methInfo.classDescriptor, methInfo.name,
+                        methInfo.signature, pDecInsn->vB);
+                } else {
+                    printf("}, ??? // method@%04x", pDecInsn->vB);
+                }
+            }
+        }
+        break;
+    case kFmt3rms:       // [opt] invoke-virtual+super/range
+    case kFmt3rfs:       // [opt] invoke-interface/range
+        {
+            /*
+             * This doesn't match the "dx" output when some of the args are
+             * 64-bit values -- dx only shows the first register.
+             */
+            fputs(" {", stdout);
+            for (i = 0; i < (int) pDecInsn->vA; i++) {
+                if (i == 0)
+                    printf("v%d", pDecInsn->vC + i);
+                else
+                    printf(", v%d", pDecInsn->vC + i);
+            }
+            printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
+        }
+        break;
+    case kFmt3rinline:   // [opt] execute-inline/range
+        {
+            fputs(" {", stdout);
+            for (i = 0; i < (int) pDecInsn->vA; i++) {
+                if (i == 0)
+                    printf("v%d", pDecInsn->vC + i);
+                else
+                    printf(", v%d", pDecInsn->vC + i);
+            }
+            printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
+        }
+        break;
+    case kFmt3inline:    // [opt] inline invoke
+        {
+#if 0
+            const InlineOperation* inlineOpsTable = dvmGetInlineOpsTable();
+            u4 tableLen = dvmGetInlineOpsTableLength();
+#endif
+
+            fputs(" {", stdout);
+            for (i = 0; i < (int) pDecInsn->vA; i++) {
+                if (i == 0)
+                    printf("v%d", pDecInsn->arg[i]);
+                else
+                    printf(", v%d", pDecInsn->arg[i]);
+            }
+#if 0
+            if (pDecInsn->vB < tableLen) {
+                printf("}, %s.%s:%s // inline #%04x",
+                    inlineOpsTable[pDecInsn->vB].classDescriptor,
+                    inlineOpsTable[pDecInsn->vB].methodName,
+                    inlineOpsTable[pDecInsn->vB].methodSignature,
+                    pDecInsn->vB);
+            } else {
+#endif
+                printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
+#if 0
+            }
+#endif
+        }
+        break;
+    case kFmt51l:        // op vAA, #+BBBBBBBBBBBBBBBB
+        {
+            /* this is often, but not always, a double */
+            union {
+                double d;
+                u8 j;
+            } conv;
+            conv.j = pDecInsn->vB_wide;
+            printf(" v%d, #double %f // #%016llx",
+                pDecInsn->vA, conv.d, pDecInsn->vB_wide);
+        }
+        break;
+    case kFmtUnknown:
+        break;
+    default:
+        printf(" ???");
+        break;
+    }
+
+
+    putchar('\n');
+
+}
+
+/*
+ * Dump a bytecode disassembly.
+ */
+void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
+{
+    const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
+    const u2* insns;
+    int insnIdx;
+    FieldMethodInfo methInfo;
+    int startAddr;
+    char* className = NULL;
+
+    assert(pCode->insnsSize > 0);
+    insns = pCode->insns;
+
+    getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
+    startAddr = ((u1*)pCode - pDexFile->baseAddr);
+    className = descriptorToDot(methInfo.classDescriptor);
+
+    printf("%06x:                                        |[%06x] %s.%s:%s\n",
+        startAddr, startAddr,
+        className, methInfo.name, methInfo.signature);
+
+    insnIdx = 0;
+    while (insnIdx < (int) pCode->insnsSize) {
+        int insnWidth;
+        OpCode opCode;
+        DecodedInstruction decInsn;
+        u2 instr;
+
+        instr = get2LE((const u1*)insns);
+        if (instr == kPackedSwitchSignature) {
+            insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
+        } else if (instr == kSparseSwitchSignature) {
+            insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
+        } else if (instr == kArrayDataSignature) {
+            int width = get2LE((const u1*)(insns+1));
+            int size = get2LE((const u1*)(insns+2)) |
+                       (get2LE((const u1*)(insns+3))<<16);
+            // The plus 1 is to round up for odd size and width
+            insnWidth = 4 + ((size * width) + 1) / 2;
+        } else {
+            opCode = instr & 0xff;
+            insnWidth = dexGetInstrWidthAbs(gInstrWidth, opCode);
+            if (insnWidth == 0) {
+                fprintf(stderr,
+                    "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
+                break;
+            }
+        }
+
+        dexDecodeInstruction(gInstrFormat, insns, &decInsn);
+        dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
+
+        insns += insnWidth;
+        insnIdx += insnWidth;
+    }
+
+    free(className);
+}
+
+/*
+ * Dump a "code" struct.
+ */
+void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
+{
+    const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
+
+    printf("      registers     : %d\n", pCode->registersSize);
+    printf("      ins           : %d\n", pCode->insSize);
+    printf("      outs          : %d\n", pCode->outsSize);
+    printf("      insns size    : %d 16-bit code units\n", pCode->insnsSize);
+
+    if (gOptions.disassemble)
+        dumpBytecodes(pDexFile, pDexMethod);
+
+    dumpCatches(pDexFile, pCode);
+    /* both of these are encoded in debug info */
+    dumpPositions(pDexFile, pCode, pDexMethod);
+    dumpLocals(pDexFile, pCode, pDexMethod);
+}
+
+/*
+ * Dump a method.
+ */
+void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
+{
+    const DexMethodId* pMethodId;
+    const char* backDescriptor;
+    const char* name;
+    char* typeDescriptor = NULL;
+    char* accessStr = NULL;
+
+    if (gOptions.exportsOnly &&
+        (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+    {
+        return;
+    }
+
+    pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+    name = dexStringById(pDexFile, pMethodId->nameIdx);
+    typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+
+    backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+    accessStr = createAccessFlagStr(pDexMethod->accessFlags,
+                    kAccessForMethod);
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("    #%d              : (in %s)\n", i, backDescriptor);
+        printf("      name          : '%s'\n", name);
+        printf("      type          : '%s'\n", typeDescriptor);
+        printf("      access        : 0x%04x (%s)\n",
+            pDexMethod->accessFlags, accessStr);
+
+        if (pDexMethod->codeOff == 0) {
+            printf("      code          : (none)\n");
+        } else {
+            printf("      code          -\n");
+            dumpCode(pDexFile, pDexMethod);
+        }
+
+        if (gOptions.disassemble)
+            putchar('\n');
+    } else if (gOptions.outputFormat == OUTPUT_XML) {
+        bool constructor = (name[0] == '<');
+
+        if (constructor) {
+            char* tmp;
+
+            tmp = descriptorClassToDot(backDescriptor);
+            printf("<constructor name=\"%s\"\n", tmp);
+            free(tmp);
+
+            tmp = descriptorToDot(backDescriptor);
+            printf(" type=\"%s\"\n", tmp);
+            free(tmp);
+        } else {
+            printf("<method name=\"%s\"\n", name);
+
+            const char* returnType = strrchr(typeDescriptor, ')');
+            if (returnType == NULL) {
+                fprintf(stderr, "bad method type descriptor '%s'\n",
+                    typeDescriptor);
+                goto bail;
+            }
+
+            char* tmp = descriptorToDot(returnType+1);
+            printf(" return=\"%s\"\n", tmp);
+            free(tmp);
+
+            printf(" abstract=%s\n",
+                quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
+            printf(" native=%s\n",
+                quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
+
+            bool isSync =
+                (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
+                (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+            printf(" synchronized=%s\n", quotedBool(isSync));
+        }
+
+        printf(" static=%s\n",
+            quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
+        printf(" final=%s\n",
+            quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
+        // "deprecated=" not knowable w/o parsing annotations
+        printf(" visibility=%s\n",
+            quotedVisibility(pDexMethod->accessFlags));
+
+        printf(">\n");
+
+        /*
+         * Parameters.
+         */
+        if (typeDescriptor[0] != '(') {
+            fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
+            goto bail;
+        }
+
+        char tmpBuf[strlen(typeDescriptor)+1];      /* more than big enough */
+        int argNum = 0;
+
+        const char* base = typeDescriptor+1;
+
+        while (*base != ')') {
+            char* cp = tmpBuf;
+
+            while (*base == '[')
+                *cp++ = *base++;
+
+            if (*base == 'L') {
+                /* copy through ';' */
+                do {
+                    *cp = *base++;
+                } while (*cp++ != ';');
+            } else {
+                /* primitive char, copy it */
+                if (strchr("ZBCSIFJD", *base) == NULL) {
+                    fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+                    goto bail;
+                }
+                *cp++ = *base++;
+            }
+
+            /* null terminate and display */
+            *cp++ = '\0';
+
+            char* tmp = descriptorToDot(tmpBuf);
+            printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
+                argNum++, tmp);
+            free(tmp);
+        }
+
+        if (constructor)
+            printf("</constructor>\n");
+        else
+            printf("</method>\n");
+    }
+
+bail:
+    free(typeDescriptor);
+    free(accessStr);
+}
+
+/*
+ * Dump a static (class) field.
+ */
+void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
+{
+    const DexFieldId* pFieldId;
+    const char* backDescriptor;
+    const char* name;
+    const char* typeDescriptor;
+    char* accessStr;
+
+    if (gOptions.exportsOnly &&
+        (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+    {
+        return;
+    }
+
+    pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
+    name = dexStringById(pDexFile, pFieldId->nameIdx);
+    typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+
+    accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("    #%d              : (in %s)\n", i, backDescriptor);
+        printf("      name          : '%s'\n", name);
+        printf("      type          : '%s'\n", typeDescriptor);
+        printf("      access        : 0x%04x (%s)\n",
+            pSField->accessFlags, accessStr);
+    } else if (gOptions.outputFormat == OUTPUT_XML) {
+        char* tmp;
+
+        printf("<field name=\"%s\"\n", name);
+
+        tmp = descriptorToDot(typeDescriptor);
+        printf(" type=\"%s\"\n", tmp);
+        free(tmp);
+
+        printf(" transient=%s\n",
+            quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
+        printf(" volatile=%s\n",
+            quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
+        // "value=" not knowable w/o parsing annotations
+        printf(" static=%s\n",
+            quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
+        printf(" final=%s\n",
+            quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
+        // "deprecated=" not knowable w/o parsing annotations
+        printf(" visibility=%s\n",
+            quotedVisibility(pSField->accessFlags));
+        printf(">\n</field>\n");
+    }
+
+    free(accessStr);
+}
+
+/*
+ * Dump an instance field.
+ */
+void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
+{
+    dumpSField(pDexFile, pIField, i);
+}
+
+/*
+ * Dump the class.
+ *
+ * Note "idx" is a DexClassDef index, not a DexTypeId index.
+ *
+ * If "*pLastPackage" is NULL or does not match the current class' package,
+ * the value will be replaced with a newly-allocated string.
+ */
+void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
+{
+    const DexTypeList* pInterfaces;
+    const DexClassDef* pClassDef;
+    DexClassData* pClassData = NULL;
+    const u1* pEncodedData;
+    const char* fileName;
+    const char* classDescriptor;
+    const char* superclassDescriptor;
+    char* accessStr = NULL;
+    int i;
+
+    pClassDef = dexGetClassDef(pDexFile, idx);
+
+    if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
+        //printf("<!-- omitting non-public class %s -->\n",
+        //    classDescriptor);
+        goto bail;
+    }
+
+    pEncodedData = dexGetClassData(pDexFile, pClassDef);
+    pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+    if (pClassData == NULL) {
+        printf("Trouble reading class data (#%d)\n", idx);
+        goto bail;
+    }
+
+    classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+    /*
+     * For the XML output, show the package name.  Ideally we'd gather
+     * up the classes, sort them, and dump them alphabetically so the
+     * package name wouldn't jump around, but that's not a great plan
+     * for something that needs to run on the device.
+     */
+    if (!(classDescriptor[0] == 'L' &&
+          classDescriptor[strlen(classDescriptor)-1] == ';'))
+    {
+        /* arrays and primitives should not be defined explicitly */
+        fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
+        /* keep going? */
+    } else if (gOptions.outputFormat == OUTPUT_XML) {
+        char* mangle;
+        char* lastSlash;
+        char* cp;
+
+        mangle = strdup(classDescriptor + 1);
+        mangle[strlen(mangle)-1] = '\0';
+
+        /* reduce to just the package name */
+        lastSlash = strrchr(mangle, '/');
+        if (lastSlash != NULL) {
+            *lastSlash = '\0';
+        } else {
+            *mangle = '\0';
+        }
+
+        for (cp = mangle; *cp != '\0'; cp++) {
+            if (*cp == '/')
+                *cp = '.';
+        }
+
+        if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
+            /* start of a new package */
+            if (*pLastPackage != NULL)
+                printf("</package>\n");
+            printf("<package name=\"%s\"\n>\n", mangle);
+            free(*pLastPackage);
+            *pLastPackage = mangle;
+        } else {
+            free(mangle);
+        }
+    }
+
+    accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
+
+    if (pClassDef->superclassIdx == kDexNoIndex) {
+        superclassDescriptor = NULL;
+    } else {
+        superclassDescriptor =
+            dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
+    }
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("Class #%d            -\n", idx);
+        printf("  Class descriptor  : '%s'\n", classDescriptor);
+        printf("  Access flags      : 0x%04x (%s)\n",
+            pClassDef->accessFlags, accessStr);
+
+        if (superclassDescriptor != NULL)
+            printf("  Superclass        : '%s'\n", superclassDescriptor);
+
+        printf("  Interfaces        -\n");
+    } else {
+        char* tmp;
+
+        tmp = descriptorClassToDot(classDescriptor);
+        printf("<class name=\"%s\"\n", tmp);
+        free(tmp);
+
+        if (superclassDescriptor != NULL) {
+            tmp = descriptorToDot(superclassDescriptor);
+            printf(" extends=\"%s\"\n", tmp);
+            free(tmp);
+        }
+        printf(" abstract=%s\n",
+            quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
+        printf(" static=%s\n",
+            quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
+        printf(" final=%s\n",
+            quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
+        // "deprecated=" not knowable w/o parsing annotations
+        printf(" visibility=%s\n",
+            quotedVisibility(pClassDef->accessFlags));
+        printf(">\n");
+    }
+    pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
+    if (pInterfaces != NULL) {
+        for (i = 0; i < (int) pInterfaces->size; i++)
+            dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
+    }
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN)
+        printf("  Static fields     -\n");
+    for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
+        dumpSField(pDexFile, &pClassData->staticFields[i], i);
+    }
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN)
+        printf("  Instance fields   -\n");
+    for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
+        dumpIField(pDexFile, &pClassData->instanceFields[i], i);
+    }
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN)
+        printf("  Direct methods    -\n");
+    for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+        dumpMethod(pDexFile, &pClassData->directMethods[i], i);
+    }
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN)
+        printf("  Virtual methods   -\n");
+    for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+        dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
+    }
+
+    // TODO: Annotations.
+
+    if (pClassDef->sourceFileIdx != kDexNoIndex)
+        fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
+    else
+        fileName = "unknown";
+
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("  source_file_idx   : %d (%s)\n",
+            pClassDef->sourceFileIdx, fileName);
+        printf("\n");
+    }
+
+    if (gOptions.outputFormat == OUTPUT_XML) {
+        printf("</class>\n");
+    }
+
+bail:
+    free(pClassData);
+    free(accessStr);
+}
+
+
+/*
+ * Advance "ptr" to ensure 32-bit alignment.
+ */
+static inline const u1* align32(const u1* ptr)
+{
+    return (u1*) (((int) ptr + 3) & ~0x03);
+}
+
+
+/*
+ * Dump a map in the "differential" format.
+ *
+ * TODO: show a hex dump of the compressed data.  (We can show the
+ * uncompressed data if we move the compression code to libdex; otherwise
+ * it's too complex to merit a fast & fragile implementation here.)
+ */
+void dumpDifferentialCompressedMap(const u1** pData)
+{
+    const u1* data = *pData;
+    const u1* dataStart = data -1;      // format byte already removed
+    u1 regWidth;
+    u2 numEntries;
+
+    /* standard header */
+    regWidth = *data++;
+    numEntries = *data++;
+    numEntries |= (*data++) << 8;
+
+    /* compressed data begins with the compressed data length */
+    int compressedLen = readUnsignedLeb128(&data);
+    int addrWidth = 1;
+    if ((*data & 0x80) != 0)
+        addrWidth++;
+
+    int origLen = 4 + (addrWidth + regWidth) * numEntries;
+    int compLen = (data - dataStart) + compressedLen;
+
+    printf("        (differential compression %d -> %d [%d -> %d])\n",
+        origLen, compLen,
+        (addrWidth + regWidth) * numEntries, compressedLen);
+
+    /* skip past end of entry */
+    data += compressedLen;
+
+    *pData = data;
+}
+
+/*
+ * Dump register map contents of the current method.
+ *
+ * "*pData" should point to the start of the register map data.  Advances
+ * "*pData" to the start of the next map.
+ */
+void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
+    const u1** pData)
+{
+    const u1* data = *pData;
+    const DexMethodId* pMethodId;
+    const char* name;
+    int offset = data - (u1*) pDexFile->pOptHeader;
+
+    pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+    name = dexStringById(pDexFile, pMethodId->nameIdx);
+    printf("      #%d: 0x%08x %s\n", idx, offset, name);
+
+    u1 format;
+    int addrWidth;
+
+    format = *data++;
+    if (format == 1) {              /* kRegMapFormatNone */
+        /* no map */
+        printf("        (no map)\n");
+        addrWidth = 0;
+    } else if (format == 2) {       /* kRegMapFormatCompact8 */
+        addrWidth = 1;
+    } else if (format == 3) {       /* kRegMapFormatCompact16 */
+        addrWidth = 2;
+    } else if (format == 4) {       /* kRegMapFormatDifferential */
+        dumpDifferentialCompressedMap(&data);
+        goto bail;
+    } else {
+        printf("        (unknown format %d!)\n", format);
+        /* don't know how to skip data; failure will cascade to end of class */
+        goto bail;
+    }
+
+    if (addrWidth > 0) {
+        u1 regWidth;
+        u2 numEntries;
+        int idx, addr, byte;
+
+        regWidth = *data++;
+        numEntries = *data++;
+        numEntries |= (*data++) << 8;
+
+        for (idx = 0; idx < numEntries; idx++) {
+            addr = *data++;
+            if (addrWidth > 1)
+                addr |= (*data++) << 8;
+
+            printf("        %4x:", addr);
+            for (byte = 0; byte < regWidth; byte++) {
+                printf(" %02x", *data++);
+            }
+            printf("\n");
+        }
+    }
+
+bail:
+    //if (addrWidth >= 0)
+    //    *pData = align32(data);
+    *pData = data;
+}
+
+/*
+ * Dump the contents of the register map area.
+ *
+ * These are only present in optimized DEX files, and the structure is
+ * not really exposed to other parts of the VM itself.  We're going to
+ * dig through them here, but this is pretty fragile.  DO NOT rely on
+ * this or derive other code from it.
+ */
+void dumpRegisterMaps(DexFile* pDexFile)
+{
+    const u1* pClassPool = pDexFile->pRegisterMapPool;
+    const u4* classOffsets;
+    const u1* ptr;
+    u4 numClasses;
+    int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
+    int idx;
+
+    if (pClassPool == NULL) {
+        printf("No register maps found\n");
+        return;
+    }
+
+    ptr = pClassPool;
+    numClasses = get4LE(ptr);
+    ptr += sizeof(u4);
+    classOffsets = (const u4*) ptr;
+
+    printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
+    printf("Maps for %d classes\n", numClasses);
+    for (idx = 0; idx < (int) numClasses; idx++) {
+        const DexClassDef* pClassDef;
+        const char* classDescriptor;
+
+        pClassDef = dexGetClassDef(pDexFile, idx);
+        classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
+            baseFileOffset + classOffsets[idx], classDescriptor);
+
+        if (classOffsets[idx] == 0)
+            continue;
+
+        /*
+         * What follows is a series of RegisterMap entries, one for every
+         * direct method, then one for every virtual method.
+         */
+        DexClassData* pClassData;
+        const u1* pEncodedData;
+        const u1* data = (u1*) pClassPool + classOffsets[idx];
+        u2 methodCount;
+        int i;
+
+        pEncodedData = dexGetClassData(pDexFile, pClassDef);
+        pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+        if (pClassData == NULL) {
+            fprintf(stderr, "Trouble reading class data\n");
+            continue;
+        }
+
+        methodCount = *data++;
+        methodCount |= (*data++) << 8;
+        data += 2;      /* two pad bytes follow methodCount */
+        if (methodCount != pClassData->header.directMethodsSize
+                            + pClassData->header.virtualMethodsSize)
+        {
+            printf("NOTE: method count discrepancy (%d != %d + %d)\n",
+                methodCount, pClassData->header.directMethodsSize,
+                pClassData->header.virtualMethodsSize);
+            /* this is bad, but keep going anyway */
+        }
+
+        printf("    direct methods: %d\n",
+            pClassData->header.directMethodsSize);
+        for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+            dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
+        }
+
+        printf("    virtual methods: %d\n",
+            pClassData->header.virtualMethodsSize);
+        for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+            dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
+        }
+
+        free(pClassData);
+    }
+}
+
+/*
+ * Dump the requested sections of the file.
+ */
+void processDexFile(const char* fileName, DexFile* pDexFile)
+{
+    char* package = NULL;
+    int i;
+
+    if (gOptions.verbose) {
+        printf("Opened '%s', DEX version '%.3s'\n", fileName,
+            pDexFile->pHeader->magic +4);
+    }
+
+    if (gOptions.dumpRegisterMaps) {
+        dumpRegisterMaps(pDexFile);
+        return;
+    }
+
+    if (gOptions.showFileHeaders) {
+        dumpFileHeader(pDexFile);
+        dumpOptDirectory(pDexFile);
+    }
+
+    if (gOptions.outputFormat == OUTPUT_XML)
+        printf("<api>\n");
+
+    for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
+        if (gOptions.showSectionHeaders)
+            dumpClassDef(pDexFile, i);
+
+        dumpClass(pDexFile, i, &package);
+    }
+
+    /* free the last one allocated */
+    if (package != NULL) {
+        printf("</package>\n");
+        free(package);
+    }
+
+    if (gOptions.outputFormat == OUTPUT_XML)
+        printf("</api>\n");
+}
+
+
+/*
+ * Process one file.
+ */
+int process(const char* fileName)
+{
+    DexFile* pDexFile = NULL;
+    MemMapping map;
+    bool mapped = false;
+    int result = -1;
+
+    if (gOptions.verbose)
+        printf("Processing '%s'...\n", fileName);
+
+    if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
+        goto bail;
+    mapped = true;
+
+    int flags = kDexParseVerifyChecksum;
+    if (gOptions.ignoreBadChecksum)
+        flags |= kDexParseContinueOnError;
+
+    pDexFile = dexFileParse(map.addr, map.length, flags);
+    if (pDexFile == NULL) {
+        fprintf(stderr, "ERROR: DEX parse failed\n");
+        goto bail;
+    }
+
+    if (gOptions.checksumOnly) {
+        printf("Checksum verified\n");
+    } else {
+        processDexFile(fileName, pDexFile);
+    }
+
+    result = 0;
+
+bail:
+    if (mapped)
+        sysReleaseShmem(&map);
+    if (pDexFile != NULL)
+        dexFileFree(pDexFile);
+    return result;
+}
+
+
+/*
+ * Show usage.
+ */
+void usage(void)
+{
+    fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
+    fprintf(stderr,
+        "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
+        gProgName);
+    fprintf(stderr, "\n");
+    fprintf(stderr, " -c : verify checksum and exit\n");
+    fprintf(stderr, " -d : disassemble code sections\n");
+    fprintf(stderr, " -f : display summary information from file header\n");
+    fprintf(stderr, " -h : display file header details\n");
+    fprintf(stderr, " -i : ignore checksum failures\n");
+    fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
+    fprintf(stderr, " -m : dump register maps (and nothing else)\n");
+    fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
+}
+
+/*
+ * Parse args.
+ *
+ * I'm not using getopt_long() because we may not have it in libc.
+ */
+int main(int argc, char* const argv[])
+{
+    bool wantUsage = false;
+    int ic;
+
+    memset(&gOptions, 0, sizeof(gOptions));
+    gOptions.verbose = true;
+
+    while (1) {
+        ic = getopt(argc, argv, "cdfhil:mt:");
+        if (ic < 0)
+            break;
+
+        switch (ic) {
+        case 'c':       // verify the checksum then exit
+            gOptions.checksumOnly = true;
+            break;
+        case 'd':       // disassemble Dalvik instructions
+            gOptions.disassemble = true;
+            break;
+        case 'f':       // dump outer file header
+            gOptions.showFileHeaders = true;
+            break;
+        case 'h':       // dump section headers, i.e. all meta-data
+            gOptions.showSectionHeaders = true;
+            break;
+        case 'i':       // continue even if checksum is bad
+            gOptions.ignoreBadChecksum = true;
+            break;
+        case 'l':       // layout
+            if (strcmp(optarg, "plain") == 0) {
+                gOptions.outputFormat = OUTPUT_PLAIN;
+            } else if (strcmp(optarg, "xml") == 0) {
+                gOptions.outputFormat = OUTPUT_XML;
+                gOptions.verbose = false;
+                gOptions.exportsOnly = true;
+            } else {
+                wantUsage = true;
+            }
+            break;
+        case 'm':       // dump register maps only
+            gOptions.dumpRegisterMaps = true;
+            break;
+        case 't':       // temp file, used when opening compressed Jar
+            gOptions.tempFileName = optarg;
+            break;
+        default:
+            wantUsage = true;
+            break;
+        }
+    }
+
+    if (optind == argc) {
+        fprintf(stderr, "%s: no file specified\n", gProgName);
+        wantUsage = true;
+    }
+
+    if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
+        fprintf(stderr, "Can't specify both -c and -i\n");
+        wantUsage = true;
+    }
+
+    /* initialize some VM tables */
+    gInstrWidth = dexCreateInstrWidthTable();
+    gInstrFormat = dexCreateInstrFormatTable();
+
+    if (wantUsage) {
+        usage();
+        return 2;
+    }
+
+    int result = 0;
+    while (optind < argc) {
+        result |= process(argv[optind++]);
+    }
+
+    free(gInstrWidth);
+    free(gInstrFormat);
+
+    return (result != 0);
+}
diff --git a/dexlist/Android.mk b/dexlist/Android.mk
new file mode 100644
index 0000000..aacdfb5
--- /dev/null
+++ b/dexlist/Android.mk
@@ -0,0 +1,54 @@
+# 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.
+
+#
+# dexlist -- list all concrete methods found in a DEX file
+#
+LOCAL_PATH:= $(call my-dir)
+
+dexdump_src_files := \
+		DexList.c
+
+dexdump_c_includes := \
+		dalvik \
+		$(JNI_H_INCLUDE)
+
+dexdump_shared_libraries :=
+
+dexdump_static_libraries := \
+		libdex
+
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexlist
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries) libcutils libz
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries)
+LOCAL_LDLIBS +=
+#include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexlist
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries) libcutils
+LOCAL_LDLIBS += -lpthread -lz
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # TARGET_SIMULATOR
diff --git a/dexlist/DexList.c b/dexlist/DexList.c
new file mode 100644
index 0000000..3552d2e
--- /dev/null
+++ b/dexlist/DexList.c
@@ -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.
+ */
+
+/*
+ * List all methods in all concrete classes in one or more DEX files.
+ */
+#include "libdex/DexFile.h"
+#include "libdex/DexClass.h"
+#include "libdex/DexProto.h"
+#include "libdex/SysUtil.h"
+#include "libdex/CmdUtils.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+
+static const char* gProgName = "dexlist";
+
+/* command-line args */
+static struct {
+    char*       argCopy;
+    const char* classToFind;
+    const char* methodToFind;
+} gParms;
+
+
+/*
+ * 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 '.'.
+ */
+static char* descriptorToDot(const char* str)
+{
+    size_t at = strlen(str);
+    char* newStr;
+
+    if (str[0] == 'L') {
+        assert(str[at - 1] == ';');
+        at -= 2; /* Two fewer chars to copy. */
+        str++; /* Skip the 'L'. */
+    }
+
+    newStr = malloc(at + 1); /* Add one for the '\0'. */
+    newStr[at] = '\0';
+
+    while (at > 0) {
+        at--;
+        newStr[at] = (str[at] == '/') ? '.' : str[at];
+    }
+
+    return newStr;
+}
+
+/*
+ * Position table callback; we just want to catch the number of the
+ * first line in the method, which *should* correspond to the first
+ * entry from the table.  (Could also use "min" here.)
+ */
+static int positionsCallback(void* cnxt, u4 address, u4 lineNum)
+{
+    int* pFirstLine = (int*) cnxt;
+    if (*pFirstLine == -1)
+        *pFirstLine = lineNum;
+    return 0;
+}
+
+
+/*
+ * Dump a method.
+ */
+void dumpMethod(DexFile* pDexFile, const char* fileName,
+    const DexMethod* pDexMethod, int i)
+{
+    const DexMethodId* pMethodId;
+    const DexCode* pCode;
+    const char* classDescriptor;
+    const char* methodName;
+    int firstLine;
+
+    /* abstract and native methods don't get listed */
+    if (pDexMethod->codeOff == 0)
+        return;
+
+    pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+    methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+
+    classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+    pCode = dexGetCode(pDexFile, pDexMethod);
+    assert(pCode != NULL);
+
+    /*
+     * If the filename is empty, then set it to something printable
+     * so that it is easier to parse.
+     *
+     * TODO: A method may override its class's default source file by
+     * specifying a different one in its debug info. This possibility
+     * should be handled here.
+     */
+    if (fileName == NULL || fileName[0] == 0) {
+        fileName = "(none)";
+    }
+
+    firstLine = -1;
+    dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+        pDexMethod->accessFlags, positionsCallback, NULL, &firstLine);
+
+    char* className = descriptorToDot(classDescriptor);
+    char* desc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+    u4 insnsOff = pDexMethod->codeOff + offsetof(DexCode, insns);
+
+    if (gParms.methodToFind != NULL &&
+        (strcmp(gParms.classToFind, className) != 0 ||
+         strcmp(gParms.methodToFind, methodName) != 0))
+    {
+        goto skip;
+    }
+
+    printf("0x%08x %d %s %s %s %s %d\n",
+        insnsOff, pCode->insnsSize * 2,
+        className, methodName, desc,
+        fileName, firstLine);
+
+skip:
+    free(desc);
+    free(className);
+}
+
+/*
+ * Run through all direct and virtual methods in the class.
+ */
+void dumpClass(DexFile* pDexFile, int idx)
+{
+    const DexClassDef* pClassDef;
+    DexClassData* pClassData;
+    const u1* pEncodedData;
+    const char* fileName;
+    int i;
+
+    pClassDef = dexGetClassDef(pDexFile, idx);
+    pEncodedData = dexGetClassData(pDexFile, pClassDef);
+    pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+    if (pClassData == NULL) {
+        fprintf(stderr, "Trouble reading class data\n");
+        return;
+    }
+
+    if (pClassDef->sourceFileIdx == 0xffffffff) {
+        fileName = NULL;
+    } else {
+        fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
+    }
+
+    /*
+     * TODO: Each class def points at a sourceFile, so maybe that
+     * should be printed out. However, this needs to be coordinated
+     * with the tools that parse this output.
+     */
+
+    for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+        dumpMethod(pDexFile, fileName, &pClassData->directMethods[i], i);
+    }
+
+    for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+        dumpMethod(pDexFile, fileName, &pClassData->virtualMethods[i], i);
+    }
+
+    free(pClassData);
+}
+
+/*
+ * Process a file.
+ *
+ * Returns 0 on success.
+ */
+int process(const char* fileName)
+{
+    DexFile* pDexFile = NULL;
+    MemMapping map;
+    bool mapped = false;
+    int result = -1;
+    UnzipToFileResult utfr;
+
+    utfr = dexOpenAndMap(fileName, NULL, &map, true);
+    if (utfr != kUTFRSuccess) {
+        if (utfr == kUTFRNoClassesDex) {
+            /* no classes.dex in the APK; pretend we succeeded */
+            result = 0;
+            goto bail;
+        }
+        fprintf(stderr, "Unable to process '%s'\n", fileName);
+        goto bail;
+    }
+    mapped = true;
+
+    pDexFile = dexFileParse(map.addr, map.length, kDexParseDefault);
+    if (pDexFile == NULL) {
+        fprintf(stderr, "Warning: DEX parse failed for '%s'\n", fileName);
+        goto bail;
+    }
+
+    printf("#%s\n", fileName);
+
+    int i;
+    for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
+        dumpClass(pDexFile, i);
+    }
+
+    result = 0;
+
+bail:
+    if (mapped)
+        sysReleaseShmem(&map);
+    if (pDexFile != NULL)
+        dexFileFree(pDexFile);
+    return result;
+}
+
+
+/*
+ * Show usage.
+ */
+void usage(void)
+{
+    fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
+    fprintf(stderr, "%s: dexfile [dexfile2 ...]\n", gProgName);
+    fprintf(stderr, "\n");
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+    int result = 0;
+    int i;
+
+    /*
+     * Find all instances of the fully-qualified method name.  This isn't
+     * really what dexlist is for, but it's easy to do it here.
+     */
+    if (argc > 3 && strcmp(argv[1], "--method") == 0) {
+        gParms.argCopy = strdup(argv[2]);
+        char* meth = strrchr(gParms.argCopy, '.');
+        if (meth == NULL) {
+            fprintf(stderr, "Expected package.Class.method\n");
+            free(gParms.argCopy);
+            return 2;
+        }
+        *meth = '\0';
+        gParms.classToFind = gParms.argCopy;
+        gParms.methodToFind = meth+1;
+        argv += 2;
+        argc -= 2;
+    }
+
+    if (argc < 2) {
+        fprintf(stderr, "%s: no file specified\n", gProgName);
+        usage();
+        return 2;
+    }
+
+    /*
+     * Run through the list of files.  If one of them fails we contine on,
+     * only returning a failure at the end.
+     */
+    for (i = 1; i < argc; i++)
+        result |= process(argv[i]);
+
+    free(gParms.argCopy);
+    return result;
+}
diff --git a/dexopt/Android.mk b/dexopt/Android.mk
new file mode 100644
index 0000000..3bb98a5
--- /dev/null
+++ b/dexopt/Android.mk
@@ -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.
+
+#
+# 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.c
+
+local_c_includes := \
+		dalvik \
+		dalvik/libdex \
+		dalvik/vm \
+		$(JNI_H_INCLUDE)
+
+local_shared_libraries := \
+		libssl \
+		libdvm \
+		libcrypto \
+		libicuuc \
+		libicui18n
+
+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 := $(local_shared_libraries) libcutils libexpat liblog libnativehelper libutils libz
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dexopt
+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 := $(local_shared_libraries)
+    LOCAL_STATIC_LIBRARIES :=  libcutils libexpat liblog libnativehelper libutils 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.c b/dexopt/OptMain.c
new file mode 100644
index 0000000..b8e5889
--- /dev/null
+++ b/dexopt/OptMain.c
@@ -0,0 +1,593 @@
+/*
+ * 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 "utils/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)
+{
+    ZipArchive zippy;
+    ZipEntry zipEntry;
+    size_t uncompLen;
+    long modWhen, crc32;
+    off_t dexOffset;
+    int err;
+    int result = -1;
+
+    memset(&zippy, 0, sizeof(zippy));
+
+    /* make sure we're still at the start of an empty file */
+    if (lseek(cacheFd, 0, SEEK_END) != 0) {
+        LOGE("DexOptZ: new cache file '%s' is not empty\n", 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 (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
+        LOGW("DexOptZ: unable to open zip archive '%s'\n", debugFileName);
+        goto bail;
+    }
+
+    zipEntry = dexZipFindEntry(&zippy, kClassesDex);
+    if (zipEntry == NULL) {
+        LOGW("DexOptZ: zip archive '%s' does not include %s\n",
+            debugFileName, kClassesDex);
+        goto bail;
+    }
+
+    /*
+     * Extract some info about the zip entry.
+     */
+    if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
+            &modWhen, &crc32) != 0)
+    {
+        LOGW("DexOptZ: zip archive GetEntryInfo failed on %s\n", debugFileName);
+        goto bail;
+    }
+
+    uncompLen = uncompLen;
+    modWhen = modWhen;
+    crc32 = crc32;
+
+    /*
+     * Extract the DEX data into the cache file at the current offset.
+     */
+    if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) {
+        LOGW("DexOptZ: extraction of %s from %s failed\n",
+            kClassesDex, debugFileName);
+        goto bail;
+    }
+
+    /* Parse the options. */
+
+    DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
+    DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
+    int dexoptFlags = 0;        /* bit flags, from enum DexoptFlags */
+
+    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;
+            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)
+    {
+        LOGE("DexOptZ: VM init failed\n");
+        goto bail;
+    }
+
+    //vmStarted = 1;
+
+    /* do the optimization */
+    if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
+            modWhen, crc32, isBootstrap))
+    {
+        LOGE("Optimization failed\n");
+        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)
+{
+    int result = -1;
+    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) {
+        LOGE("DexOptZ: BOOTCLASSPATH not set\n");
+        goto bail;
+    }
+
+    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--;
+        LOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d\n",
+            inputFileName, matchOffset);
+        bcpCopy = strdup(bcp);
+        bcpCopy[matchOffset] = '\0';
+
+        bcp = bcpCopy;
+        LOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'\n", bcp);
+        isBootstrap = true;
+    }
+
+    result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
+            bcp, dexoptFlags);
+
+bail:
+    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') {                                                \
+            LOGE("%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) {
+        LOGE("Wrong number of args for --zip (found %d)\n", 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);
+        goto bail;
+    }
+
+    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");
+        goto bail;
+    }
+
+    zipFd = open(zipName, O_RDONLY);
+    if (zipFd < 0) {
+        perror(argv[0]);
+        goto bail;
+    }
+
+    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;
+
+    if (argc < 10) {
+        /* don't have all mandatory args */
+        LOGE("Not enough arguments for --dex (found %d)\n", argc);
+        goto bail;
+    }
+
+    /* skip "--dex" */
+    argc--;
+    argv++;
+
+    /*
+     * Extract the args.
+     */
+    GET_ARG(vmBuildVersion, strtol, "bad vm build");
+    if (vmBuildVersion != DALVIK_VM_BUILD) {
+        LOGE("DexOpt: build rev does not match VM: %d vs %d\n",
+            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");
+
+    LOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=0x%x crc=0x%x flg=%d (argc=%d)\n",
+        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;
+            LOGV("DEP: '%s'\n", *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);
+    }
+    LOGV("  bootclasspath is '%s'\n", bootClassPath);
+
+    /* start the VM partway */
+    bool onlyOptVerifiedDex = false;
+    DexClassVerifyMode verifyMode;
+    DexOptimizerMode dexOptMode;
+
+    /* 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) {
+        LOGE("VM init failed\n");
+        goto bail;
+    }
+
+    vmStarted = true;
+
+    /* do the optimization */
+    if (!dvmContinueOptimization(fd, offset, length, debugFileName,
+            modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
+    {
+        LOGE("Optimization failed\n");
+        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) {
+        LOGI("DexOpt shutting down, result=%d\n", result);
+        dvmShutdown();
+    }
+#endif
+
+    //dvmLinearAllocDump(NULL);
+
+#if 0
+    {
+        extern int gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData,
+               gDvm__gcSimpleData;
+        LOGI("GC DATA: totinst=%d, gcinst=%d, gcdata=%d simpled=%d\n",
+            gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData, gDvm__gcSimpleData);
+    }
+#endif
+
+    free(bootClassPath);
+    LOGV("DexOpt command complete (result=%d)\n", 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/docs/dalvik-bytecode.css b/docs/dalvik-bytecode.css
new file mode 100644
index 0000000..e4a5caa
--- /dev/null
+++ b/docs/dalvik-bytecode.css
@@ -0,0 +1,165 @@
+h1 {
+    font-family: serif;
+    color: #222266;
+}
+
+h2 {
+    font-family: serif;
+    border-top-style: solid;
+    border-top-width: 2px;
+    border-color: #ccccdd;
+    padding-top: 12px;
+    margin-top: 48px;
+    margin-bottom: 2px;
+    color: #222266;
+}
+
+@media print {
+    table {
+        font-size: 8pt;
+    }
+}
+
+@media screen {
+    table {
+        font-size: 10pt;
+    }
+}
+
+
+/* general for all tables */
+
+table {
+    border-collapse: collapse;
+    margin-top: 12px;
+}
+
+table th {
+    font-family: sans-serif;
+    background: #aabbff;
+}
+
+table td {
+    font-family: sans-serif;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-width: 1px;
+    border-color: #aaaaff;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    padding-left: 4px;
+    padding-right: 6px;
+    background: #eeeeff;
+}
+
+table td p {
+    margin-top: 4pt;
+    margin-bottom: 0pt;
+}
+
+
+
+/* opcodes table */
+
+table.instruc {
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table.instruc td {
+    font-family: sans-serif;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-width: 1px;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    padding-left: 2px;
+    padding-right: 2px;
+}
+
+table.instruc td:first-child {
+    font-family: monospace;
+    font-size: 90%;
+    vertical-align: top;
+    width: 12%;
+}
+
+table.instruc td:first-child + td {
+    font-family: monospace;
+    font-size: 90%;
+    vertical-align: top;
+    width: 23%;
+}
+
+table.instruc td:first-child + td i {
+    font-family: sans-serif;
+    font-size: 90%;
+}
+
+table.instruc td:first-child + td + td {
+    vertical-align: top;
+    width: 28%;
+}
+
+table.instruc td:first-child + td + td + td {
+    vertical-align: top;
+    width: 37%;
+}
+
+
+/* supplemental opcode format table */
+
+table.supplement {
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table.supplement td:first-child {
+    font-family: monospace;
+    vertical-align: top;
+    width: 20%;
+}
+
+table.supplement td:first-child + td {
+    font-family: monospace;
+    vertical-align: top;
+    width: 20%;
+}
+
+table.supplement td:first-child + td + td {
+    font-family: sans-serif;
+    vertical-align: top;
+    width: 60%;
+}
+
+
+/* math details table */
+
+table.math {
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table.math td:first-child {
+    font-family: monospace;
+    vertical-align: top;
+    width: 10%;
+}
+
+table.math td:first-child + td {
+    font-family: monospace;
+    vertical-align: top;
+    width: 30%;
+}
+
+table.math td:first-child + td + td {
+    font-family: sans-serif;
+    vertical-align: top;
+    width: 60%;
+}
diff --git a/docs/dalvik-bytecode.html b/docs/dalvik-bytecode.html
new file mode 100644
index 0000000..35fa64b
--- /dev/null
+++ b/docs/dalvik-bytecode.html
@@ -0,0 +1,1503 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>Bytecode for the Dalvik VM</title>
+<link rel=stylesheet href="dalvik-bytecode.css">
+</head>
+
+<body>
+
+<h1>Bytecode for the Dalvik VM</h1>
+<p>Copyright &copy; 2007 The Android Open Source Project
+
+<h2>General Design</h2>
+
+<ul>
+<li>The machine model and calling conventions are meant to approximately
+  imitate common real architectures and C-style calling conventions:
+  <ul>
+  <li>The VM is register-based, and frames are fixed in size upon creation.
+    Each frame consists of a particular number of registers (specified by
+    the method) as well as any adjunct data needed to execute the method,
+    such as (but not limited to) the program counter and a reference to the
+    <code>.dex</code> file that contains the method.
+  </li>
+  <li>Registers are 32 bits wide. Adjacent register pairs are used for 64-bit
+    values.
+  </li>
+  <li>In terms of bitwise representation, <code>(Object) null == (int)
+    0</code>.
+  </li>
+  <li>The <i>N</i> arguments to a method land in the last <i>N</i> registers
+    of the method's invocation frame, in order. Wide arguments consume
+    two registers. Instance methods are passed a <code>this</code> reference
+    as their first argument.
+  </li>
+  </ul>
+<li>The storage unit in the instruction stream is a 16-bit unsigned quantity.
+  Some bits in some instructions are ignored / must-be-zero.
+</li>
+<li>Instructions aren't gratuitously limited to a particular type. For
+  example, instructions that move 32-bit register values without interpretation
+  don't have to specify whether they are moving ints or floats.
+</li>
+<li>There are separately enumerated and indexed constant pools for
+  references to strings, types, fields, and methods.
+</li>
+<li>Bitwise literal data is represented in-line in the instruction stream.</li>
+<li>Because, in practice, it is uncommon for a method to need more than
+  16 registers, and because needing more than eight registers <i>is</i>
+  reasonably common, many instructions are limited to only addressing
+  the first 16
+  registers. When reasonably possible, instructions allow references to
+  up to the first 256 registers. In cases where an instruction variant isn't
+  available to address a desired register, it is expected that the register
+  contents get moved from the original register to a low register (before the
+  operation) and/or moved from a low result register to a high register
+  (after the operation).
+</li>
+<li>There are several "pseudo-instructions" that are used to hold
+  variable-length data referred to by regular instructions (for example,
+  <code>fill-array-data</code>). Such instructions must never be
+  encountered during the normal flow of execution. In addition, the
+  instructions must be located on even-numbered bytecode offsets (that is,
+  4-byte aligned). In order to meet this requirement, dex generation tools
+  should emit an extra <code>nop</code> instruction as a spacer if such an
+  instruction would otherwise be unaligned. Finally, though not required,
+  it is expected that most tools will choose to emit these instructions at
+  the ends of methods, since otherwise it would likely be the case that
+  additional instructions would be needed to branch around them.
+</li>
+<li>When installed on a running system, some instructions may be altered,
+  changing their format, as an install-time static linking optimization.
+  This is to allow for faster execution once linkage is known.
+  See the associated
+  <a href="instruction-formats.html">instruction formats document</a>
+  for the suggested variants. The word "suggested" is used advisedly;
+  it is not mandatory to implement these.
+</li>
+<li>Human-syntax and mnemonics:
+  <ul>
+  <li>Dest-then-source ordering for arguments.</li>
+  <li>Some opcodes have a disambiguating suffix with respect to the type(s)
+    they operate on: Type-general 64-bit opcodes
+    are suffixed with <code>-wide</code>.
+    Type-specific opcodes are suffixed with their type (or a
+    straightforward abbreviation), one of: <code>-boolean</code>
+    <code>-byte</code> <code>-char</code> <code>-short</code>
+    <code>-int</code> <code>-long</code> <code>-float</code>
+    <code>-double</code> <code>-object</code> <code>-string</code>
+    <code>-class</code> <code>-void</code>. Type-general 32-bit opcodes
+    are unmarked.
+  </li>
+  <li>Some opcodes have a disambiguating suffix to distinguish
+    otherwise-identical operations that have different instruction layouts
+    or options. These suffixes are separated from the main names with a slash
+    ("<code>/</code>") and mainly exist at all to make there be a one-to-one
+    mapping with static constants in the code that generates and interprets
+    executables (that is, to reduce ambiguity for humans).
+  </li>
+  </ul>
+</li>
+<li>See the <a href="instruction-formats.html">instruction formats
+  document</a> for more details about the various instruction formats
+  (listed under "Op &amp; Format") as well as details about the opcode
+  syntax.
+</li>
+</ul>
+
+<h2>Summary of Instruction Set</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>00 10x</td>
+  <td>nop</td>
+  <td>&nbsp;</td>
+  <td>Waste cycles.</td>
+</tr>
+<tr>
+  <td>01 12x</td>
+  <td>move vA, vB</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> source register (4 bits)</td>
+  <td>Move the contents of one non-object register to another.</td>
+</tr>
+<tr>
+  <td>02 22x</td>
+  <td>move/from16 vAA, vBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> source register (16 bits)</td>
+  <td>Move the contents of one non-object register to another.</td>
+</tr>
+<tr>
+  <td>03 32x</td>
+  <td>move/16 vAAAA, vBBBB</td>
+  <td><code>A:</code> destination register (16 bits)<br/>
+    <code>B:</code> source register (16 bits)</td>
+  <td>Move the contents of one non-object register to another.</td>
+</tr>
+<tr>
+  <td>04 12x</td>
+  <td>move-wide vA, vB</td>
+  <td><code>A:</code> destination register pair (4 bits)<br/>
+    <code>B:</code> source register pair (4 bits)</td>
+  <td>Move the contents of one register-pair to another.
+    <p><b>Note:</b>
+    It is legal to move from <code>v<i>N</i></code> to either
+    <code>v<i>N-1</i></code> or <code>v<i>N+1</i></code>, so implementations
+    must arrange for both halves of a register pair to be read before
+    anything is written.</p>
+  </td>
+</tr>
+<tr>
+  <td>05 22x</td>
+  <td>move-wide/from16 vAA, vBBBB</td>
+  <td><code>A:</code> destination register pair (8 bits)<br/>
+    <code>B:</code> source register pair (16 bits)</td>
+  <td>Move the contents of one register-pair to another.
+    <p><b>Note:</b>
+    Implementation considerations are the same as <code>move-wide</code>,
+    above.</p>
+  </td>
+</tr>
+<tr>
+  <td>06 32x</td>
+  <td>move-wide/16 vAAAA, vBBBB</td>
+  <td><code>A:</code> destination register pair (16 bits)<br/>
+    <code>B:</code> source register pair (16 bits)</td>
+  <td>Move the contents of one register-pair to another.
+    <p><b>Note:</b>
+    Implementation considerations are the same as <code>move-wide</code>,
+    above.</p>
+  </td>
+</tr>
+<tr>
+  <td>07 12x</td>
+  <td>move-object vA, vB</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> source register (4 bits)</td>
+  <td>Move the contents of one object-bearing register to another.</td>
+</tr>
+<tr>
+  <td>08 22x</td>
+  <td>move-object/from16 vAA, vBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> source register (16 bits)</td>
+  <td>Move the contents of one object-bearing register to another.</td>
+</tr>
+<tr>
+  <td>09 32x</td>
+  <td>move-object/16 vAAAA, vBBBB</td>
+  <td><code>A:</code> destination register (16 bits)<br/>
+    <code>B:</code> source register (16 bits)</td>
+  <td>Move the contents of one object-bearing register to another.</td>
+</tr>
+<tr>
+  <td>0a 11x</td>
+  <td>move-result vAA</td>
+  <td><code>A:</code> destination register (8 bits)</td>
+  <td>Move the single-word non-object result of the most recent
+    <code>invoke-<i>kind</i></code> into the indicated register.
+    This must be done as the instruction immediately after an
+    <code>invoke-<i>kind</i></code> whose (single-word, non-object) result
+    is not to be ignored; anywhere else is invalid.</td>
+</tr>
+<tr>
+  <td>0b 11x</td>
+  <td>move-result-wide vAA</td>
+  <td><code>A:</code> destination register pair (8 bits)</td>
+  <td>Move the double-word result of the most recent
+    <code>invoke-<i>kind</i></code> into the indicated register pair.
+    This must be done as the instruction immediately after an
+    <code>invoke-<i>kind</i></code> whose (double-word) result
+    is not to be ignored; anywhere else is invalid.</td>
+</tr>
+<tr>
+  <td>0c 11x</td>
+  <td>move-result-object vAA</td>
+  <td><code>A:</code> destination register (8 bits)</td>
+  <td>Move the object result of the most recent <code>invoke-<i>kind</i></code>
+    into the indicated register. This must be done as the instruction
+    immediately after an <code>invoke-<i>kind</i></code> or
+    <code>filled-new-array</code>
+    whose (object) result is not to be ignored; anywhere else is invalid.</td>
+</tr>
+<tr>
+  <td>0d 11x</td>
+  <td>move-exception vAA</td>
+  <td><code>A:</code> destination register (8 bits)</td>
+  <td>Save a just-caught exception into the given register. This should
+    be the first instruction of any exception handler whose caught
+    exception is not to be ignored, and this instruction must <i>only</i>
+    ever occur as the first instruction of an exception handler; anywhere
+    else is invalid.</td>
+</tr>
+<tr>
+  <td>0e 10x</td>
+  <td>return-void</td>
+  <td>&nbsp;</td>
+  <td>Return from a <code>void</code> method.</td>
+</tr>
+<tr>
+  <td>0f 11x</td>
+  <td>return vAA</td>
+  <td><code>A:</code> return value register (8 bits)</td>
+  <td>Return from a single-width (32-bit) non-object value-returning
+    method.
+  </td>
+</tr>
+<tr>
+  <td>10 11x</td>
+  <td>return-wide vAA</td>
+  <td><code>A:</code> return value register-pair (8 bits)</td>
+  <td>Return from a double-width (64-bit) value-returning method.</td>
+</tr>
+<tr>
+  <td>11 11x</td>
+  <td>return-object vAA</td>
+  <td><code>A:</code> return value register (8 bits)</td>
+  <td>Return from an object-returning method.</td>
+</tr>
+<tr>
+  <td>12 11n</td>
+  <td>const/4 vA, #+B</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> signed int (4 bits)</td>
+  <td>Move the given literal value (sign-extended to 32 bits) into
+    the specified register.</td>
+</tr>
+<tr>
+  <td>13 21s</td>
+  <td>const/16 vAA, #+BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (16 bits)</td>
+  <td>Move the given literal value (sign-extended to 32 bits) into
+    the specified register.</td>
+</tr>
+<tr>
+  <td>14 31i</td>
+  <td>const vAA, #+BBBBBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> arbitrary 32-bit constant</td>
+  <td>Move the given literal value into the specified register.</td>
+</tr>
+<tr>
+  <td>15 21h</td>
+  <td>const/high16 vAA, #+BBBB0000</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (16 bits)</td>
+  <td>Move the given literal value (right-zero-extended to 32 bits) into
+    the specified register.</td>
+</tr>
+<tr>
+  <td>16 21s</td>
+  <td>const-wide/16 vAA, #+BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (16 bits)</td>
+  <td>Move the given literal value (sign-extended to 64 bits) into
+    the specified register-pair.</td>
+</tr>
+<tr>
+  <td>17 31i</td>
+  <td>const-wide/32 vAA, #+BBBBBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (32 bits)</td>
+  <td>Move the given literal value (sign-extended to 64 bits) into
+    the specified register-pair.</td>
+</tr>
+<tr>
+  <td>18 51l</td>
+  <td>const-wide vAA, #+BBBBBBBBBBBBBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> arbitrary double-width (64-bit) constant</td>
+  <td>Move the given literal value into
+    the specified register-pair.</td>
+</tr>
+<tr>
+  <td>19 21h</td>
+  <td>const-wide/high16 vAA, #+BBBB000000000000</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (16 bits)</td>
+  <td>Move the given literal value (right-zero-extended to 64 bits) into
+    the specified register-pair.</td>
+</tr>
+<tr>
+  <td>1a 21c</td>
+  <td>const-string vAA, string@BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> string index</td>
+  <td>Move a reference to the string specified by the given index into the
+    specified register.</td>
+</tr>
+<tr>
+  <td>1b 31c</td>
+  <td>const-string/jumbo vAA, string@BBBBBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> string index</td>
+  <td>Move a reference to the string specified by the given index into the
+    specified register.</td>
+</tr>
+<tr>
+  <td>1c 21c</td>
+  <td>const-class vAA, type@BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> type index</td>
+  <td>Move a reference to the class specified by the given index into the
+    specified register. In the case where the indicated type is primitive,
+    this will store a reference to the primitive type's degenerate
+    class.</td>
+</tr>
+<tr>
+  <td>1d 11x</td>
+  <td>monitor-enter vAA</td>
+  <td><code>A:</code> reference-bearing register (8 bits)</td>
+  <td>Acquire the monitor for the indicated object.</td>
+</tr>
+<tr>
+  <td>1e 11x</td>
+  <td>monitor-exit vAA</td>
+  <td><code>A:</code> reference-bearing register (8 bits)</td>
+  <td>Release the monitor for the indicated object.
+    <p><b>Note:</b>
+    If this instruction needs to throw an exception, it must do
+    so as if the pc has already advanced past the instruction.
+    It may be useful to think of this as the instruction successfully
+    executing (in a sense), and the exception getting thrown <i>after</i>
+    the instruction but <i>before</i> the next one gets a chance to
+    run. This definition makes it possible for a method to use
+    a monitor cleanup catch-all (e.g., <code>finally</code>) block as
+    the monitor cleanup for that block itself, as a way to handle the
+    arbitrary exceptions that might get thrown due to the historical
+    implementation of <code>Thread.stop()</code>, while still managing
+    to have proper monitor hygiene.</p>
+  </td>
+</tr>
+<tr>
+  <td>1f 21c</td>
+  <td>check-cast vAA, type@BBBB</td>
+  <td><code>A:</code> reference-bearing register (8 bits)<br/>
+    <code>B:</code> type index (16 bits)</td>
+  <td>Throw a <code>ClassCastException</code> if the reference in the
+    given register cannot be cast to the indicated type.
+    <p><b>Note:</b> Since <code>A</code> must always be a reference
+    (and not a primitive value), this will necessarily fail at runtime
+    (that is, it will throw an exception) if <code>B</code> refers to a
+    primitive type.</p>
+  </td>
+</tr>
+<tr>
+  <td>20 22c</td>
+  <td>instance-of vA, vB, type@CCCC</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> reference-bearing register (4 bits)<br/>
+    <code>C:</code> type index (16 bits)</td>
+  <td>Store in the given destination register <code>1</code>
+    if the indicated reference is an instance of the given type,
+    or <code>0</code> if not.
+    <p><b>Note:</b> Since <code>B</code> must always be a reference
+    (and not a primitive value), this will always result
+    in <code>0</code> being stored if <code>C</code> refers to a primitive
+    type.</td>
+</tr>
+<tr>
+  <td>21 12x</td>
+  <td>array-length vA, vB</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> array reference-bearing register (4 bits)</td>
+  <td>Store in the given destination register the length of the indicated
+    array, in entries</td>
+</tr>
+<tr>
+  <td>22 21c</td>
+  <td>new-instance vAA, type@BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> type index</td>
+  <td>Construct a new instance of the indicated type, storing a
+    reference to it in the destination. The type must refer to a
+    non-array class.</td>
+</tr>
+<tr>
+  <td>23 22c</td>
+  <td>new-array vA, vB, type@CCCC</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> size register<br/>
+    <code>C:</code> type index</td>
+  <td>Construct a new array of the indicated type and size. The type
+    must be an array type.</td>
+</tr>
+<tr>
+  <td>24 35c</td>
+  <td>filled-new-array {vD, vE, vF, vG, vA}, type@CCCC</td>
+  <td><code>B:</code> array size and argument word count (4 bits)<br/>
+    <code>C:</code> type index (16 bits)<br/>
+    <code>D..G, A:</code> argument registers (4 bits each)</td>
+  <td>Construct an array of the given type and size, filling it with the
+    supplied contents. The type must be an array type. The array's
+    contents must be single-word (that is,
+    no arrays of <code>long</code> or <code>double</code>, but reference
+    types are acceptable). The constructed
+    instance is stored as a "result" in the same way that the method invocation
+    instructions store their results, so the constructed instance must
+    be moved to a register with an immediately subsequent
+    <code>move-result-object</code> instruction (if it is to be used).</td>
+</tr>
+<tr>
+  <td>25 3rc</td>
+  <td>filled-new-array/range {vCCCC .. vNNNN}, type@BBBB</td>
+  <td><code>A:</code> array size and argument word count (8 bits)<br/>
+    <code>B:</code> type index (16 bits)<br/>
+    <code>C:</code> first argument register (16 bits)<br/>
+    <code>N = A + C - 1</code></td>
+  <td>Construct an array of the given type and size, filling it with
+    the supplied contents. Clarifications and restrictions are the same
+    as <code>filled-new-array</code>, described above.</td>
+</tr>
+<tr>
+  <td>26 31t</td>
+  <td>fill-array-data vAA, +BBBBBBBB <i>(with supplemental data as specified
+    below in "<code>fill-array-data</code> Format")</i></td>
+  <td><code>A:</code> array reference (8 bits)<br/>
+    <code>B:</code> signed "branch" offset to table data pseudo-instruction
+    (32 bits)
+  </td>
+  <td>Fill the given array with the indicated data. The reference must be
+    to an array of primitives, and the data table must match it in type and
+    must contain no more elements than will fit in the array. That is,
+    the array may be larger than the table, and if so, only the initial
+    elements of the array are set, leaving the remainder alone.
+  </td>
+</tr>
+<tr>
+  <td>27 11x</td>
+  <td>throw vAA</td>
+  <td><code>A:</code> exception-bearing register (8 bits)<br/></td>
+  <td>Throw the indicated exception.</td>
+</tr>
+<tr>
+  <td>28 10t</td>
+  <td>goto +AA</td>
+  <td><code>A:</code> signed branch offset (8 bits)</td>
+  <td>Unconditionally jump to the indicated instruction.
+    <p><b>Note:</b>
+    The branch offset must not be <code>0</code>. (A spin
+    loop may be legally constructed either with <code>goto/32</code> or
+    by including a <code>nop</code> as a target before the branch.)</p>
+  </td>
+</tr>
+<tr>
+  <td>29 20t</td>
+  <td>goto/16 +AAAA</td>
+  <td><code>A:</code> signed branch offset (16 bits)<br/></td>
+  <td>Unconditionally jump to the indicated instruction.
+    <p><b>Note:</b>
+    The branch offset must not be <code>0</code>. (A spin
+    loop may be legally constructed either with <code>goto/32</code> or
+    by including a <code>nop</code> as a target before the branch.)</p>
+  </td>
+</tr>
+<tr>
+  <td>2a 30t</td>
+  <td>goto/32 +AAAAAAAA</td>
+  <td><code>A:</code> signed branch offset (32 bits)<br/></td>
+  <td>Unconditionally jump to the indicated instruction.</td>
+</tr>
+<tr>
+  <td>2b 31t</td>
+  <td>packed-switch vAA, +BBBBBBBB <i>(with supplemental data as
+    specified below in "<code>packed-switch</code> Format")</i></td>
+  <td><code>A:</code> register to test<br/>
+    <code>B:</code> signed "branch" offset to table data pseudo-instruction
+    (32 bits)
+  </td>
+  <td>Jump to a new instruction based on the value in the
+    given register, using a table of offsets corresponding to each value
+    in a particular integral range, or fall through to the next
+    instruction if there is no match.
+  </td>
+</tr>
+<tr>
+  <td>2c 31t</td>
+  <td>sparse-switch vAA, +BBBBBBBB <i>(with supplemental data as
+    specified below in "<code>sparse-switch</code> Format")</i></td>
+  <td><code>A:</code> register to test<br/>
+    <code>B:</code> signed "branch" offset to table data pseudo-instruction
+    (32 bits)
+  </td>
+  <td>Jump to a new instruction based on the value in the given
+    register, using an ordered table of value-offset pairs, or fall
+    through to the next instruction if there is no match.
+  </td>
+</tr>
+<tr>
+  <td>2d..31 23x</td>
+  <td>cmp<i>kind</i> vAA, vBB, vCC<br/>
+    2d: cmpl-float <i>(lt bias)</i><br/>
+    2e: cmpg-float <i>(gt bias)</i><br/>
+    2f: cmpl-double <i>(lt bias)</i><br/>
+    30: cmpg-double <i>(gt bias)</i><br/>
+    31: cmp-long
+  </td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> first source register or pair<br/>
+    <code>C:</code> second source register or pair</td>
+  <td>Perform the indicated floating point or <code>long</code> comparison,
+    storing <code>0</code> if the two arguments are equal, <code>1</code>
+    if the second argument is larger, or <code>-1</code> if the first
+    argument is larger. The "bias" listed for the floating point operations
+    indicates how <code>NaN</code> comparisons are treated: "Gt bias"
+    instructions return <code>1</code> for <code>NaN</code> comparisons,
+    and "lt bias" instructions return
+    <code>-1</code>.
+    <p>For example, to check to see if floating point
+    <code>a &lt; b</code>, then it is advisable to use
+    <code>cmpg-float</code>; a result of <code>-1</code> indicates that
+    the test was true, and the other values indicate it was false either
+    due to a valid comparison or because one or the other values was
+    <code>NaN</code>.</p>
+  </td>
+</tr>
+<tr>
+  <td>32..37 22t</td>
+  <td>if-<i>test</i> vA, vB, +CCCC<br/>
+    32: if-eq<br/>
+    33: if-ne<br/>
+    34: if-lt<br/>
+    35: if-ge<br/>
+    36: if-gt<br/>
+    37: if-le<br/>
+  </td>
+  <td><code>A:</code> first register to test (4 bits)<br/>
+    <code>B:</code> second register to test (4 bits)<br/>
+    <code>C:</code> signed branch offset (16 bits)</td>
+  <td>Branch to the given destination if the given two registers' values
+    compare as specified.
+    <p><b>Note:</b>
+    The branch offset must not be <code>0</code>. (A spin
+    loop may be legally constructed either by branching around a
+    backward <code>goto</code> or by including a <code>nop</code> as
+    a target before the branch.)</p>
+  </td>
+</tr>
+<tr>
+  <td>38..3d 21t</td>
+  <td>if-<i>test</i>z vAA, +BBBB<br/>
+    38: if-eqz<br/>
+    39: if-nez<br/>
+    3a: if-ltz<br/>
+    3b: if-gez<br/>
+    3c: if-gtz<br/>
+    3d: if-lez<br/>
+  </td>
+  <td><code>A:</code> register to test (8 bits)<br/>
+    <code>B:</code> signed branch offset (16 bits)</td>
+  <td>Branch to the given destination if the given register's value compares
+    with 0 as specified.
+    <p><b>Note:</b>
+    The branch offset must not be <code>0</code>. (A spin
+    loop may be legally constructed either by branching around a
+    backward <code>goto</code> or by including a <code>nop</code> as
+    a target before the branch.)</p>
+  </td>
+</tr>
+<tr>
+  <td>3e..43 10x</td>
+  <td><i>(unused)</i></td>
+  <td>&nbsp;</td>
+  <td><i>(unused)</i></td>
+</tr>
+<tr>
+  <td>44..51 23x</td>
+  <td><i>arrayop</i> vAA, vBB, vCC<br/>
+    44: aget<br/>
+    45: aget-wide<br/>
+    46: aget-object<br/>
+    47: aget-boolean<br/>
+    48: aget-byte<br/>
+    49: aget-char<br/>
+    4a: aget-short<br/>
+    4b: aput<br/>
+    4c: aput-wide<br/>
+    4d: aput-object<br/>
+    4e: aput-boolean<br/>
+    4f: aput-byte<br/>
+    50: aput-char<br/>
+    51: aput-short
+  </td>
+  <td><code>A:</code> value register or pair; may be source or dest
+      (8 bits)<br/>
+    <code>B:</code> array register (8 bits)<br/>
+    <code>C:</code> index register (8 bits)</td>
+  <td>Perform the identified array operation at the identified index of
+    the given array, loading or storing into the value register.</td>
+</tr>
+<tr>
+  <td>52..5f 22c</td>
+  <td>i<i>instanceop</i> vA, vB, field@CCCC<br/>
+    52: iget<br/>
+    53: iget-wide<br/>
+    54: iget-object<br/>
+    55: iget-boolean<br/>
+    56: iget-byte<br/>
+    57: iget-char<br/>
+    58: iget-short<br/>
+    59: iput<br/>
+    5a: iput-wide<br/>
+    5b: iput-object<br/>
+    5c: iput-boolean<br/>
+    5d: iput-byte<br/>
+    5e: iput-char<br/>
+    5f: iput-short
+  </td>
+  <td><code>A:</code> value register or pair; may be source or dest
+      (4 bits)<br/>
+    <code>B:</code> object register (4 bits)<br/>
+    <code>C:</code> instance field reference index (16 bits)</td>
+  <td>Perform the identified object instance field operation with
+    the identified field, loading or storing into the value register.
+    <p><b>Note:</b> These opcodes are reasonable candidates for static linking,
+    altering the field argument to be a more direct offset.</p>
+  </td>
+</tr>
+<tr>
+  <td>60..6d 21c</td>
+  <td>s<i>staticop</i> vAA, field@BBBB<br/>
+    60: sget<br/>
+    61: sget-wide<br/>
+    62: sget-object<br/>
+    63: sget-boolean<br/>
+    64: sget-byte<br/>
+    65: sget-char<br/>
+    66: sget-short<br/>
+    67: sput<br/>
+    68: sput-wide<br/>
+    69: sput-object<br/>
+    6a: sput-boolean<br/>
+    6b: sput-byte<br/>
+    6c: sput-char<br/>
+    6d: sput-short
+  </td>
+  <td><code>A:</code> value register or pair; may be source or dest
+      (8 bits)<br/>
+    <code>B:</code> static field reference index (16 bits)</td>
+  <td>Perform the identified object static field operation with the identified
+    static field, loading or storing into the value register.
+    <p><b>Note:</b> These opcodes are reasonable candidates for static linking,
+    altering the field argument to be a more direct offset.</p>
+  </td>
+</tr>
+<tr>
+  <td>6e..72 35c</td>
+  <td>invoke-<i>kind</i> {vD, vE, vF, vG, vA}, meth@CCCC<br/>
+    6e: invoke-virtual<br/>
+    6f: invoke-super<br/>
+    70: invoke-direct<br/>
+    71: invoke-static<br/>
+    72: invoke-interface
+  </td>
+  <td><code>B:</code> argument word count (4 bits)<br/>
+    <code>C:</code> method index (16 bits)<br/>
+    <code>D..G, A:</code> argument registers (4 bits each)</td>
+  <td>Call the indicated method. The result (if any) may be stored
+    with an appropriate <code>move-result*</code> variant as the immediately
+    subsequent instruction.
+    <p><code>invoke-virtual</code> is used to invoke a normal virtual
+    method (a method that is not <code>private</code>, <code>static</code>,
+    or <code>final</code>, and is also not a constructor).</p>
+    <p><code>invoke-super</code> is used to invoke the closest superclass's
+    virtual method (as opposed to the one with the same <code>method_id</code>
+    in the calling class). The same method restrictions hold as for
+    <code>invoke-virtual</code>.</p>
+    <p><code>invoke-direct</code> is used to invoke a non-<code>static</code>
+    direct method (that is, an instance method that is by its nature
+    non-overridable, namely either a <code>private</code> instance method
+    or a constructor).</p>
+    <p><code>invoke-static</code> is used to invoke a <code>static</code>
+    method (which is always considered a direct method).</p>
+    <p><code>invoke-interface</code> is used to invoke an
+    <code>interface</code> method, that is, on an object whose concrete
+    class isn't known, using a <code>method_id</code> that refers to
+    an <code>interface</code>.</p>
+    <p><b>Note:</b> These opcodes are reasonable candidates for static linking,
+    altering the method argument to be a more direct offset
+    (or pair thereof).</p>
+  </td>
+</tr>
+<tr>
+  <td>73 10x</td>
+  <td><i>(unused)</i></td>
+  <td>&nbsp;</td>
+  <td><i>(unused)</i></td>
+</tr>
+<tr>
+  <td>74..78 3rc</td>
+  <td>invoke-<i>kind</i>/range {vCCCC .. vNNNN}, meth@BBBB<br/>
+    74: invoke-virtual/range<br/>
+    75: invoke-super/range<br/>
+    76: invoke-direct/range<br/>
+    77: invoke-static/range<br/>
+    78: invoke-interface/range
+  </td>
+  <td><code>A:</code> argument word count (8 bits)<br/>
+    <code>B:</code> method index (16 bits)<br/>
+    <code>C:</code> first argument register (16 bits)<br/>
+    <code>N = A + C - 1</code></td>
+  <td>Call the indicated method. See first <code>invoke-<i>kind</i></code>
+    description above for details, caveats, and suggestions.
+  </td>
+</tr>
+<tr>
+  <td>79..7a 10x</td>
+  <td><i>(unused)</i></td>
+  <td>&nbsp;</td>
+  <td><i>(unused)</i></td>
+</tr>
+<tr>
+  <td>7b..8f 12x</td>
+  <td><i>unop</i> vA, vB<br/>
+    7b: neg-int<br/>
+    7c: not-int<br/>
+    7d: neg-long<br/>
+    7e: not-long<br/>
+    7f: neg-float<br/>
+    80: neg-double<br/>
+    81: int-to-long<br/>
+    82: int-to-float<br/>
+    83: int-to-double<br/>
+    84: long-to-int<br/>
+    85: long-to-float<br/>
+    86: long-to-double<br/>
+    87: float-to-int<br/>
+    88: float-to-long<br/>
+    89: float-to-double<br/>
+    8a: double-to-int<br/>
+    8b: double-to-long<br/>
+    8c: double-to-float<br/>
+    8d: int-to-byte<br/>
+    8e: int-to-char<br/>
+    8f: int-to-short
+  </td>
+  <td><code>A:</code> destination register or pair (4 bits)<br/>
+    <code>B:</code> source register or pair (4 bits)</td>
+  <td>Perform the identified unary operation on the source register,
+    storing the result in the destination register.</td>
+</tr>
+
+<tr>
+  <td>90..af 23x</td>
+  <td><i>binop</i> vAA, vBB, vCC<br/>
+    90: add-int<br/>
+    91: sub-int<br/>
+    92: mul-int<br/>
+    93: div-int<br/>
+    94: rem-int<br/>
+    95: and-int<br/>
+    96: or-int<br/>
+    97: xor-int<br/>
+    98: shl-int<br/>
+    99: shr-int<br/>
+    9a: ushr-int<br/>
+    9b: add-long<br/>
+    9c: sub-long<br/>
+    9d: mul-long<br/>
+    9e: div-long<br/>
+    9f: rem-long<br/>
+    a0: and-long<br/>
+    a1: or-long<br/>
+    a2: xor-long<br/>
+    a3: shl-long<br/>
+    a4: shr-long<br/>
+    a5: ushr-long<br/>
+    a6: add-float<br/>
+    a7: sub-float<br/>
+    a8: mul-float<br/>
+    a9: div-float<br/>
+    aa: rem-float<br/>
+    ab: add-double<br/>
+    ac: sub-double<br/>
+    ad: mul-double<br/>
+    ae: div-double<br/>
+    af: rem-double
+  </td>
+  <td><code>A:</code> destination register or pair (8 bits)<br/>
+    <code>B:</code> first source register or pair (8 bits)<br/>
+    <code>C:</code> second source register or pair (8 bits)</td>
+  <td>Perform the identified binary operation on the two source registers,
+    storing the result in the first source register.</td>
+</tr>
+<tr>
+  <td>b0..cf 12x</td>
+  <td><i>binop</i>/2addr vA, vB<br/>
+    b0: add-int/2addr<br/>
+    b1: sub-int/2addr<br/>
+    b2: mul-int/2addr<br/>
+    b3: div-int/2addr<br/>
+    b4: rem-int/2addr<br/>
+    b5: and-int/2addr<br/>
+    b6: or-int/2addr<br/>
+    b7: xor-int/2addr<br/>
+    b8: shl-int/2addr<br/>
+    b9: shr-int/2addr<br/>
+    ba: ushr-int/2addr<br/>
+    bb: add-long/2addr<br/>
+    bc: sub-long/2addr<br/>
+    bd: mul-long/2addr<br/>
+    be: div-long/2addr<br/>
+    bf: rem-long/2addr<br/>
+    c0: and-long/2addr<br/>
+    c1: or-long/2addr<br/>
+    c2: xor-long/2addr<br/>
+    c3: shl-long/2addr<br/>
+    c4: shr-long/2addr<br/>
+    c5: ushr-long/2addr<br/>
+    c6: add-float/2addr<br/>
+    c7: sub-float/2addr<br/>
+    c8: mul-float/2addr<br/>
+    c9: div-float/2addr<br/>
+    ca: rem-float/2addr<br/>
+    cb: add-double/2addr<br/>
+    cc: sub-double/2addr<br/>
+    cd: mul-double/2addr<br/>
+    ce: div-double/2addr<br/>
+    cf: rem-double/2addr
+  </td>
+  <td><code>A:</code> destination and first source register or pair
+      (4 bits)<br/>
+    <code>B:</code> second source register or pair (4 bits)</td>
+  <td>Perform the identified binary operation on the two source registers,
+    storing the result in the first source register.</td>
+</tr>
+<tr>
+  <td>d0..d7 22s</td>
+  <td><i>binop</i>/lit16 vA, vB, #+CCCC<br/>
+    d0: add-int/lit16<br/>
+    d1: rsub-int (reverse subtract)<br/>
+    d2: mul-int/lit16<br/>
+    d3: div-int/lit16<br/>
+    d4: rem-int/lit16<br/>
+    d5: and-int/lit16<br/>
+    d6: or-int/lit16<br/>
+    d7: xor-int/lit16
+  </td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> source register (4 bits)<br/>
+    <code>C:</code> signed int constant (16 bits)</td>
+  <td>Perform the indicated binary op on the indicated register (first
+    argument) and literal value (second argument), storing the result in
+    the destination register.
+    <p><b>Note:</b>
+    <code>rsub-int</code> does not have a suffix since this version is the
+    main opcode of its family. Also, see below for details on its semantics.
+    </p>
+  </td>
+</tr>
+<tr>
+  <td>d8..e2 22b</td>
+  <td><i>binop</i>/lit8 vAA, vBB, #+CC<br/>
+    d8: add-int/lit8<br/>
+    d9: rsub-int/lit8<br/>
+    da: mul-int/lit8<br/>
+    db: div-int/lit8<br/>
+    dc: rem-int/lit8<br/>
+    dd: and-int/lit8<br/>
+    de: or-int/lit8<br/>
+    df: xor-int/lit8<br/>
+    e0: shl-int/lit8<br/>
+    e1: shr-int/lit8<br/>
+    e2: ushr-int/lit8
+  </td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> source register (8 bits)<br/>
+    <code>C:</code> signed int constant (8 bits)</td>
+  <td>Perform the indicated binary op on the indicated register (first
+    argument) and literal value (second argument), storing the result
+    in the destination register.
+    <p><b>Note:</b> See below for details on the semantics of
+    <code>rsub-int</code>.</p>
+  </td>
+</tr>
+<tr>
+  <td>e3..ff 10x</td>
+  <td><i>(unused)</i></td>
+  <td>&nbsp;</td>
+  <td><i>(unused)</i></td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>packed-switch</code> Format</h2>
+
+<table class="supplement">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>ident</td>
+  <td>ushort = 0x0100</td>
+  <td>identifying pseudo-opcode</td>
+</tr>
+<tr>
+  <td>size</td>
+  <td>ushort</td>
+  <td>number of entries in the table</td>
+</tr>
+<tr>
+  <td>first_key</td>
+  <td>int</td>
+  <td>first (and lowest) switch case value</td>
+</tr>
+<tr>
+  <td>targets</td>
+  <td>int[]</td>
+  <td>list of <code>size</code> relative branch targets. The targets are
+    relative to the address of the switch opcode, not of this table.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> The total number of code units for an instance of this
+table is <code>(size * 2) + 4</code>.</p>
+
+<h2><code>sparse-switch</code> Format</h2>
+
+<table class="supplement">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>ident</td>
+  <td>ushort = 0x0200</td>
+  <td>identifying pseudo-opcode</td>
+</tr>
+<tr>
+  <td>size</td>
+  <td>ushort</td>
+  <td>number of entries in the table</td>
+</tr>
+<tr>
+  <td>keys</td>
+  <td>int[]</td>
+  <td>list of <code>size</code> key values, sorted low-to-high</td>
+</tr>
+<tr>
+  <td>targets</td>
+  <td>int[]</td>
+  <td>list of <code>size</code> relative branch targets, each corresponding
+    to the key value at the same index. The targets are
+    relative to the address of the switch opcode, not of this table.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> The total number of code units for an instance of this
+table is <code>(size * 4) + 2</code>.</p>
+
+<h2><code>fill-array-data</code> Format</h2>
+
+<table class="supplement">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>ident</td>
+  <td>ushort = 0x0300</td>
+  <td>identifying pseudo-opcode</td>
+</tr>
+<tr>
+  <td>element_width</td>
+  <td>ushort</td>
+  <td>number of bytes in each element</td>
+</tr>
+<tr>
+  <td>size</td>
+  <td>uint</td>
+  <td>number of elements in the table</td>
+</tr>
+<tr>
+  <td>data</td>
+  <td>ubyte[]</td>
+  <td>data values</td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> The total number of code units for an instance of this
+table is <code>(size * element_width + 1) / 2 + 4</code>.</p>
+
+
+<h2>Mathematical Operation Details</h2>
+
+<p><b>Note:</b> Floating point operations must follow IEEE 754 rules, using
+round-to-nearest and gradual underflow, except where stated otherwise.</p>
+
+<table class="math">
+<thead>
+<tr>
+  <th>Opcode</th>
+  <th>C Semantics</th>
+  <th>Notes</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>neg-int</td>
+  <td>int32 a;<br/>
+    int32 result = -a;
+  </td>
+  <td>Unary twos-complement.</td>
+</tr>
+<tr>
+  <td>not-int</td>
+  <td>int32 a;<br/>
+    int32 result = ~a;
+  </td>
+  <td>Unary ones-complement.</td>
+</tr>
+<tr>
+  <td>neg-long</td>
+  <td>int64 a;<br/>
+    int64 result = -a;
+  </td>
+  <td>Unary twos-complement.</td>
+</tr>
+<tr>
+  <td>not-long</td>
+  <td>int64 a;<br/>
+    int64 result = ~a;
+  </td>
+  <td>Unary ones-complement.</td>
+</tr>
+<tr>
+  <td>neg-float</td>
+  <td>float a;<br/>
+    float result = -a;
+  </td>
+  <td>Floating point negation.</td>
+</tr>
+<tr>
+  <td>neg-double</td>
+  <td>double a;<br/>
+    double result = -a;
+  </td>
+  <td>Floating point negation.</td>
+</tr>
+<tr>
+  <td>int-to-long</td>
+  <td>int32 a;<br/>
+    int64 result = (int64) a;
+  </td>
+  <td>Sign extension of <code>int32</code> into <code>int64</code>.</td>
+</tr>
+<tr>
+  <td>int-to-float</td>
+  <td>int32 a;<br/>
+    float result = (float) a;
+  </td>
+  <td>Conversion of <code>int32</code> to <code>float</code>, using
+    round-to-nearest. This loses precision for some values.
+  </td>
+</tr>
+<tr>
+  <td>int-to-double</td>
+  <td>int32 a;<br/>
+    double result = (double) a;
+  </td>
+  <td>Conversion of <code>int32</code> to <code>double</code>.</td>
+</tr>
+<tr>
+  <td>long-to-int</td>
+  <td>int64 a;<br/>
+    int32 result = (int32) a;
+  </td>
+  <td>Truncation of <code>int64</code> into <code>int32</code>.</td>
+</tr>
+<tr>
+  <td>long-to-float</td>
+  <td>int64 a;<br/>
+    float result = (float) a;
+  </td>
+  <td>Conversion of <code>int64</code> to <code>float</code>, using
+    round-to-nearest. This loses precision for some values.
+  </td>
+</tr>
+<tr>
+  <td>long-to-double</td>
+  <td>int64 a;<br/>
+    double result = (double) a;
+  </td>
+  <td>Conversion of <code>int64</code> to <code>double</code>, using
+    round-to-nearest. This loses precision for some values.
+  </td>
+</tr>
+<tr>
+  <td>float-to-int</td>
+  <td>float a;<br/>
+    int32 result = (int32) a;
+  </td>
+  <td>Conversion of <code>float</code> to <code>int32</code>, using
+    round-toward-zero. <code>NaN</code> and <code>-0.0</code> (negative zero)
+    convert to the integer <code>0</code>. Infinities and values with
+    too large a magnitude to be represented get converted to either
+    <code>0x7fffffff</code> or <code>-0x80000000</code> depending on sign.
+  </td>
+</tr>
+<tr>
+  <td>float-to-long</td>
+  <td>float a;<br/>
+    int64 result = (int64) a;
+  </td>
+  <td>Conversion of <code>float</code> to <code>int64</code>, using
+    round-toward-zero. The same special case rules as for
+    <code>float-to-int</code> apply here, except that out-of-range values
+    get converted to either <code>0x7fffffffffffffff</code> or
+    <code>-0x8000000000000000</code> depending on sign.
+  </td>
+</tr>
+<tr>
+  <td>float-to-double</td>
+  <td>float a;<br/>
+    double result = (double) a;
+  </td>
+  <td>Conversion of <code>float</code> to <code>double</code>, preserving
+    the value exactly.
+  </td>
+</tr>
+<tr>
+  <td>double-to-int</td>
+  <td>double a;<br/>
+    int32 result = (int32) a;
+  </td>
+  <td>Conversion of <code>double</code> to <code>int32</code>, using
+    round-toward-zero. The same special case rules as for
+    <code>float-to-int</code> apply here.
+  </td>
+</tr>
+<tr>
+  <td>double-to-long</td>
+  <td>double a;<br/>
+    int64 result = (int64) a;
+  </td>
+  <td>Conversion of <code>double</code> to <code>int64</code>, using
+    round-toward-zero. The same special case rules as for
+    <code>float-to-long</code> apply here.
+  </td>
+</tr>
+<tr>
+  <td>double-to-float</td>
+  <td>double a;<br/>
+    float result = (float) a;
+  </td>
+  <td>Conversion of <code>double</code> to <code>float</code>, using
+    round-to-nearest. This loses precision for some values.
+  </td>
+</tr>
+<tr>
+  <td>int-to-byte</td>
+  <td>int32 a;<br/>
+    int32 result = (a &lt;&lt; 24) &gt;&gt; 24;
+  </td>
+  <td>Truncation of <code>int32</code> to <code>int8</code>, sign
+    extending the result.
+  </td>
+</tr>
+<tr>
+  <td>int-to-char</td>
+  <td>int32 a;<br/>
+    int32 result = a &amp; 0xffff;
+  </td>
+  <td>Truncation of <code>int32</code> to <code>uint16</code>, without
+    sign extension.
+  </td>
+</tr>
+<tr>
+  <td>int-to-short</td>
+  <td>int32 a;<br/>
+    int32 result = (a &lt;&lt; 16) &gt;&gt; 16;
+  </td>
+  <td>Truncation of <code>int32</code> to <code>int16</code>, sign
+    extending the result.
+  </td>
+</tr>
+<tr>
+  <td>add-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a + b;
+  </td>
+  <td>Twos-complement addition.</td>
+</tr>
+<tr>
+  <td>sub-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a - b;
+  </td>
+  <td>Twos-complement subtraction.</td>
+</tr>
+<tr>
+  <td>rsub-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = b - a;
+  </td>
+  <td>Twos-complement reverse subtraction.</td>
+</tr>
+<tr>
+  <td>mul-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a * b;
+  </td>
+  <td>Twos-complement multiplication.</td>
+</tr>
+<tr>
+  <td>div-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a / b;
+  </td>
+  <td>Twos-complement division, rounded towards zero (that is, truncated to
+    integer). This throws <code>ArithmeticException</code> if
+    <code>b == 0</code>.
+  </td>
+</tr>
+<tr>
+  <td>rem-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a % b;
+  </td>
+  <td>Twos-complement remainder after division. The sign of the result
+    is the same as that of <code>a</code>, and it is more precisely
+    defined as <code>result == a - (a / b) * b</code>. This throws
+    <code>ArithmeticException</code> if <code>b == 0</code>.
+  </td>
+</tr>
+<tr>
+  <td>and-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a &amp; b;
+  </td>
+  <td>Bitwise AND.</td>
+</tr>
+<tr>
+  <td>or-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a | b;
+  </td>
+  <td>Bitwise OR.</td>
+</tr>
+<tr>
+  <td>xor-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a ^ b;
+  </td>
+  <td>Bitwise XOR.</td>
+</tr>
+<tr>
+  <td>shl-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a &lt;&lt; (b &amp; 0x1f);
+  </td>
+  <td>Bitwise shift left (with masked argument).</td>
+</tr>
+<tr>
+  <td>shr-int</td>
+  <td>int32 a, b;<br/>
+    int32 result = a &gt;&gt; (b &amp; 0x1f);
+  </td>
+  <td>Bitwise signed shift right (with masked argument).</td>
+</tr>
+<tr>
+  <td>ushr-int</td>
+  <td>uint32 a, b;<br/>
+    int32 result = a &gt;&gt; (b &amp; 0x1f);
+  </td>
+  <td>Bitwise unsigned shift right (with masked argument).</td>
+</tr>
+<tr>
+  <td>add-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a + b;
+  </td>
+  <td>Twos-complement addition.</td>
+</tr>
+<tr>
+  <td>sub-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a - b;
+  </td>
+  <td>Twos-complement subtraction.</td>
+</tr>
+<tr>
+  <td>mul-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a * b;
+  </td>
+  <td>Twos-complement multiplication.</td>
+</tr>
+<tr>
+  <td>div-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a / b;
+  </td>
+  <td>Twos-complement division, rounded towards zero (that is, truncated to
+    integer). This throws <code>ArithmeticException</code> if
+    <code>b == 0</code>.
+  </td>
+</tr>
+<tr>
+  <td>rem-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a % b;
+  </td>
+  <td>Twos-complement remainder after division. The sign of the result
+    is the same as that of <code>a</code>, and it is more precisely
+    defined as <code>result == a - (a / b) * b</code>. This throws
+    <code>ArithmeticException</code> if <code>b == 0</code>.
+  </td>
+</tr>
+<tr>
+  <td>and-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a &amp; b;
+  </td>
+  <td>Bitwise AND.</td>
+</tr>
+<tr>
+  <td>or-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a | b;
+  </td>
+  <td>Bitwise OR.</td>
+</tr>
+<tr>
+  <td>xor-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a ^ b;
+  </td>
+  <td>Bitwise XOR.</td>
+</tr>
+<tr>
+  <td>shl-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a &lt;&lt; (b &amp; 0x3f);
+  </td>
+  <td>Bitwise shift left (with masked argument).</td>
+</tr>
+<tr>
+  <td>shr-long</td>
+  <td>int64 a, b;<br/>
+    int64 result = a &gt;&gt; (b &amp; 0x3f);
+  </td>
+  <td>Bitwise signed shift right (with masked argument).</td>
+</tr>
+<tr>
+  <td>ushr-long</td>
+  <td>uint64 a, b;<br/>
+    int64 result = a &gt;&gt; (b &amp; 0x3f);
+  </td>
+  <td>Bitwise unsigned shift right (with masked argument).</td>
+</tr>
+<tr>
+  <td>add-float</td>
+  <td>float a, b;<br/>
+    float result = a + b;
+  </td>
+  <td>Floating point addition.</td>
+</tr>
+<tr>
+  <td>sub-float</td>
+  <td>float a, b;<br/>
+    float result = a - b;
+  </td>
+  <td>Floating point subtraction.</td>
+</tr>
+<tr>
+  <td>mul-float</td>
+  <td>float a, b;<br/>
+    float result = a * b;
+  </td>
+  <td>Floating point multiplication.</td>
+</tr>
+<tr>
+  <td>div-float</td>
+  <td>float a, b;<br/>
+    float result = a / b;
+  </td>
+  <td>Floating point division.</td>
+</tr>
+<tr>
+  <td>rem-float</td>
+  <td>float a, b;<br/>
+    float result = a % b;
+  </td>
+  <td>Floating point remainder after division. This function is different
+    than IEEE 754 remainder and is defined as
+    <code>result == a - roundTowardZero(a / b) * b</code>.
+  </td>
+</tr>
+<tr>
+  <td>add-double</td>
+  <td>double a, b;<br/>
+    double result = a + b;
+  </td>
+  <td>Floating point addition.</td>
+</tr>
+<tr>
+  <td>sub-double</td>
+  <td>double a, b;<br/>
+    double result = a - b;
+  </td>
+  <td>Floating point subtraction.</td>
+</tr>
+<tr>
+  <td>mul-double</td>
+  <td>double a, b;<br/>
+    double result = a * b;
+  </td>
+  <td>Floating point multiplication.</td>
+</tr>
+<tr>
+  <td>div-double</td>
+  <td>double a, b;<br/>
+    double result = a / b;
+  </td>
+  <td>Floating point division.</td>
+</tr>
+<tr>
+  <td>rem-double</td>
+  <td>double a, b;<br/>
+    double result = a % b;
+  </td>
+  <td>Floating point remainder after division. This function is different
+    than IEEE 754 remainder and is defined as
+    <code>result == a - roundTowardZero(a / b) * b</code>.
+  </td>
+</tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/docs/dalvik-constraints.css b/docs/dalvik-constraints.css
new file mode 100644
index 0000000..a315a73
--- /dev/null
+++ b/docs/dalvik-constraints.css
@@ -0,0 +1,59 @@
+h1 {
+    font-family: serif;
+    color: #222266;
+}
+
+h2 {
+    font-family: serif;
+    border-top-style: solid;
+    border-top-width: 2px;
+    border-color: #ccccdd;
+    padding-top: 12px;
+    margin-top: 48px;
+    margin-bottom: 2px;
+    color: #222266;
+}
+
+@media print {
+    table {
+        font-size: 8pt;
+    }
+}
+
+@media screen {
+    table {
+        font-size: 10pt;
+    }
+}
+
+
+/* general for all tables */
+
+table {
+    border-collapse: collapse;
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table th {
+    font-family: sans-serif;
+    background: #aabbff;
+    text-align: left;
+}
+
+table td {
+    font-family: sans-serif;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-width: 1px;
+    border-color: #aaaaff;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    padding-left: 4px;
+    padding-right: 6px;
+    background: #eeeeff;
+    margin-top: 4pt;
+    margin-bottom: 0pt;
+}
diff --git a/docs/dalvik-constraints.html b/docs/dalvik-constraints.html
new file mode 100644
index 0000000..69abf3a
--- /dev/null
+++ b/docs/dalvik-constraints.html
@@ -0,0 +1,897 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+  <head>
+    <title>Dalvik bytecode constraints</title>
+    <link rel=stylesheet href="dalvik-constraints.css">
+  </head>
+
+  <body>
+
+    <h1>Dalvik bytecode constraints</h1>
+
+<!--
+    <h1>General integrity constraints</h1>
+
+    <table>
+      <tr>
+        <th>
+          Identifier
+        </th>
+
+        <th>
+          Description
+        </th>
+      </tr>
+
+      <tr>
+        <td>
+          A1
+        </td>
+
+        <td>
+          The magic number of the DEX file must be "dex\n035\0".
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A1
+        </td>
+
+        <td>
+          The checksum must be an Adler-32 checksum of the whole file contents
+          except magic and checksum field.
+        </td>
+      </tr>
+
+
+The signature must be a SHA-1 hash of the whole file contents except magic,
+checksum, and signature.
+
+The file_size must match the actual file size in bytes.
+
+The header_size must have the value 0x70.
+
+The endian_tag must have either the value ENDIAN_CONSTANT or
+REVERSE_ENDIAN_CONSTANT.
+
+For each of the link, string_ids, type_ids, proto_ids, field_ids, method_ids, class_defs
+and data sections, the offset and size fields must be either both zero or both
+non-zero. In the latter case, the offset must be four-byte-aligned.
+
+All offset fields in the header except map_off must be four-byte-aligned.
+
+The map_off field must be either zero or point into the data section. In the
+latter case, the data section must exist.
+
+None of the link, string_ids, type_ids, proto_ids, field_ids, method_ids, class_defs
+and data sections must overlap each other or the header.
+
+If a map exists, then each map entry must have a valid type. Each type may
+appear at most once.
+
+If a map exists, then each map entry must have a nonzero offset and size. The
+offset must point into the corresponding section of the file (i.e. a
+string_id_item must point into the string_ids section) and the explicit or
+implicit size of the item must match the actual contents and size of the
+section.
+
+If a map exists, then the offset of map entry n+1 must be greater or equal to
+the offset of map entry n plus then size of map entry n. This implies
+non-overlapping entries and low-to-high ordering.
+
+The following types of entries must have an offset that is
+four-byte-aligned: string_id_item, type_id_item, proto_id_item, field_id_item,
+method_id_item, class_def_item, type_list, code_item,
+annotations_directory_item.
+
+For each string_id_item, the string_data_off field must contain a valid
+reference into the data section. For the referenced string_data_item, the data
+field must contain a valid MUTF-8 string, and the utf16_size must match the
+decoded length of the string.
+
+For each type_id_item, the desciptor_idx field must contain a valid reference
+into the string_ids list. The referenced string must be a valid type descriptor.
+
+For each proto_id_item, the shorty_idx field must contain a valid reference
+into the string_ids list. The referenced string must be a valid shorty descriptor.
+Also, the return_type_idx field must be a valid index into the type_ids section,
+and the parameters_off field must be either zero or a valid offset pointing
+into the data section. If nonzero, the parameter list must not contain any void
+entries.
+
+For each field_id_item, both the class_idx and type_idx fields must be a valid
+ indices into the
+type_ids list. The entry referenced by class_idx must be a non-array reference type.
+In addition, the name_idx field must be a valid reference into the string_ids
+section, and the contents of the referenced entry must conform to the MemberName
+specification.
+
+For each method_id_item, the class_idx field must be a valid index into the
+type_ids section, and the
+referenced entry must be a non-array reference type. The proto_id field must
+be a valid reference into the proto_ids list. The name_idx field must be a
+valid reference into the string_ids
+section, and the contents of the referenced entry must conform to the MemberName
+specification.
+
+For each class_def_item, ...
+
+For each field_id_item, the class_idx field must be a valid index into the
+type_ids list. The referenced entry must be a non-array reference type.
+
+...
+
+-->
+
+    <h2>
+      Static constraints
+    </h2>
+
+    <p>
+    Static constraints are constraints on individual elements of the bytecode.
+    They usually can be checked without employing control or data-flow analysis
+    techniques.
+    </p>
+
+    <table>
+      <tr>
+        <th>
+          Identifier
+        </th>
+
+        <th>
+          Description
+        </th>
+
+        <th>
+          Spec equivalent
+        </th>
+      </tr>
+
+      <tr>
+        <td>
+          A1
+        </td>
+
+        <td>
+          The <code>insns</code> array must not be empty.
+        </td>
+
+        <td>
+          4.8.1.1
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A2
+        </td>
+
+        <td>
+          The first opcode in the <code>insns</code> array must have index zero.
+        </td>
+
+        <td>
+          4.8.1.3
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A3
+        </td>
+
+        <td>
+          The <code>insns</code> array must only contain valid Dalvik opcodes.
+        </td>
+
+        <td>
+          4.8.1.4
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A4
+        </td>
+
+        <td>
+          The index of instruction <code>n+1</code> must equal the index of
+          instruction <code>n</code> plus the length of instruction
+          <code>n</code>, taking into account possible operands.
+        </td>
+
+        <td>
+          4.8.1.5
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A5
+        </td>
+
+        <td>
+          The last instruction in the <code>insns</code> array must end at index
+          <code>insns_size-1</code>.
+        </td>
+
+        <td>
+          4.8.1.6
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A6
+        </td>
+
+        <td>
+          All <code>goto</code> and <code>if-&lt;kind&gt;</code> targets must
+          be opcodes within in the same method.
+        </td>
+
+        <td>
+          4.8.1.7
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A7
+        </td>
+
+        <td>
+          All targets of a <code>packed-switch</code> instruction must be
+          opcodes within in the same method. The size and the list of targets
+          must be consistent.
+        </td>
+
+        <td>
+          4.8.1.8
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A8
+        </td>
+
+        <td>
+          All targets of a <code>sparse-switch</code> instruction must be
+          opcodes within in the same method. The corresponding table must be
+          consistent and sorted low-to-high.
+        </td>
+
+        <td>
+          4.8.1.9
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A9
+        </td>
+
+        <td>
+          The <code>B</code> operand of the <code>const-string</code> and
+          <code>const-string/jumbo</code> instructions must be a valid index
+          into the string constant pool.
+        </td>
+
+        <td>
+          4.8.1.10
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A10
+        </td>
+
+        <td>
+          The <code>C</code> operand of the <code>iget&lt;kind&gt;</code> and
+          <code>iput&lt;kind&gt;</code> instructions must be a valid index into
+          the field constant pool. The referenced entry must represent an
+          instance field.
+        </td>
+
+        <td>
+          4.8.1.12
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A11
+        </td>
+
+        <td>
+          The <code>C</code> operand of the <code>sget&lt;kind&gt;</code> and
+          <code>sput&lt;kind&gt;</code> instructions must be a valid index into
+          the field constant pool. The referenced entry must represent a static
+          field.
+        </td>
+
+        <td>
+          4.8.1.12
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A12
+        </td>
+
+        <td>
+          The <code>C</code> operand of the <code>invoke-virtual</code>,
+          <code>invoke-super</code>, <code<invoke-direct</code> and
+          <code>invoke-static</code> instructions must be a valid index into the
+          method constant pool. In all cases, the referenced
+          <code>method_id</code> must belong to a class (not an interface).
+        </td>
+
+        <td>
+          4.8.1.13
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A13
+        </td>
+
+        <td>
+          The <code>B</code> operand of the <code>invoke-virtual/range</code>,
+          <code>invoke-super/range</code>, <code>invoke-direct/range</code>, and
+          <code>invoke-static/range</code> instructions must be a valid index
+          into the method constant pool. In all cases, the referenced
+          <code>method_id</code> must belong to a class (not an interface).
+        </td>
+
+        <td>
+          4.8.1.13
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A14
+        </td>
+
+        <td>
+          A method the name of which starts with a '<' must only be invoked
+          implicitly by the VM, not by code originating from a Dex file. The
+          only exception is the instance initializer, which may be invoked by
+          <code>invoke-direct</code>.
+        </td>
+
+        <td>
+          4.8.1.14
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A15
+        </td>
+
+        <td>
+          The <code>C</code> operand of the <code>invoke-interface</code>
+          instruction must be a valid index into the method constant pool. The
+          referenced <code>method_id</code> must belong to an interface (not a
+          class).
+        </td>
+
+        <td>
+          4.8.1.15
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A16
+        </td>
+
+        <td>
+          The <code>B</code> operand of the <code>invoke-interface/range</code>
+          instruction must be a valid index into the method constant pool.
+          The referenced <code>method_id</code> must belong to an interface (not
+          a class).
+        </td>
+
+        <td>
+          4.8.1.15
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A17
+        </td>
+
+        <td>
+          The <code>B</code> operand of the <code>const-class</code>,
+          <code>check-cast</code>, <code>new-instance</code>, and
+          <code>filled-new-array/range</code> instructions must be a valid index
+          into the type constant pool.
+        </td>
+
+        <td>
+          4.8.1.16
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A18
+        </td>
+
+        <td>
+          The <code>C</code> operand of the <code>instance-of</code>,
+          <code>new-array</code>, and <code>filled-new-array</code>
+          instructions must be a valid index into the type constant pool.
+        </td>
+
+        <td>
+          4.8.1.16
+        </td>
+      </tr>
+
+     <tr>
+        <td>
+          A19
+        </td>
+
+        <td>
+          The dimensions of an array created by a <code>new-array</code>
+          instruction must be less than <code>256</code>.
+        </td>
+
+        <td>
+          4.8.1.17
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A20
+        </td>
+
+        <td>
+          The <code>new</code> instruction must not refer to array classes,
+          interfaces, or abstract classes.
+        </td>
+
+        <td>
+          4.8.1.18
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A21
+        </td>
+
+        <td>
+          The type referred to by a <code>new-array</code> instruction must be
+          a valid, non-reference type.
+        </td>
+
+        <td>
+          4.8.1.20
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A22
+        </td>
+
+        <td>
+          All registers referred to by an instruction in a single-width
+          (non-pair) fashion must be valid for the current method. That is,
+          their indices must be non-negative and smaller than
+          <code>registers_size</code>.
+        </td>
+
+        <td>
+          4.8.1.21
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A23
+        </td>
+
+        <td>
+          All registers referred to by an instruction in a double-width (pair)
+          fashion must be valid for the current method. That is, their indices
+          must be non-negative and smaller than <code>registers_size-1</code>.
+        </td>
+
+        <td>
+          4.8.1.23
+        </td>
+      </tr>
+    </table>
+
+    <h2>
+      Structural constraints
+    </h2>
+
+    <p>
+    Structural constraints are constraints on relationships between several
+    elements of the bytecode. They usually can't be checked without employing
+    control or data-flow analysis techniques.
+    </p>
+
+    <table>
+      <tr>
+        <th>
+          Identifier
+        </th>
+
+        <th>
+          Description
+        </th>
+
+        <th>
+          Spec equivalent
+        </th>
+      </tr>
+
+      <tr>
+        <td>
+          B1
+        </td>
+
+        <td>
+          The number and types of arguments (registers and immediate values)
+          must always match the instruction.
+        </td>
+
+        <td>
+          4.8.2.1
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B2
+        </td>
+
+        <td>
+          Register pairs must never be broken up.
+        </td>
+
+        <td>
+          4.8.2.3
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B3
+        </td>
+
+        <td>
+          A register (or pair) has to be assigned first before it can be
+          read.
+        </td>
+
+        <td>
+          4.8.2.4
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B4
+        </td>
+
+        <td>
+          An <code>invoke-direct</code> instruction must only invoke an instance
+          initializer or a method in the current class or one of its
+          superclasses.
+        </td>
+
+        <td>
+          4.8.2.7
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B5
+        </td>
+
+        <td>
+          An instance initializer must only be invoked on an uninitialized
+          instance.
+        </td>
+
+        <td>
+          4.8.2.8
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B6
+        </td>
+
+        <td>
+          Instance methods may only be invoked on and instance fields may only
+          be accessed on already initialized instances.
+        </td>
+
+        <td>
+          4.8.2.9
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B7
+        </td>
+
+        <td>
+          A register which holds the result of a <code>new-instance</code>code>
+          instruction must not be used if the same
+          <code>new-instance</code>code> instruction is again executed before
+          the instance is initialized.
+        </td>
+
+        <td>
+          4.8.2.10
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B8
+        </td>
+
+        <td>
+           An instance initializer must call another instance initializer (same
+           class or superclass) before any instance members can be accessed.
+           Exceptions are non-inherited instance fields, which can be assigned
+           before calling another initializer, and the <code>Object</code> class
+           in general.
+        </td>
+
+        <td>
+          4.8.2.11
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B9
+        </td>
+
+        <td>
+           All actual method arguments must be assignment-compatible with their
+           respective formal arguments.
+        </td>
+
+        <td>
+          4.8.2.12
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B10
+        </td>
+
+        <td>
+           For each instance method invocation, the actual instance must be
+           assignment-compatible with the class or interface specified in the
+           instruction.
+        </td>
+
+        <td>
+          4.8.2.13
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B11
+        </td>
+
+        <td>
+           A <code>return&lt;kind&gt;</code> instruction must match its
+           method's return type.
+        </td>
+
+        <td>
+          4.8.2.14
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B12
+        </td>
+
+        <td>
+           When accessing protected members of a superclass, the actual type of
+           the instance being accessed must be either the current class or one
+           of its subclasses.
+        </td>
+
+        <td>
+          4.8.2.15
+        </td>
+      </tr>
+
+     <tr>
+        <td>
+          B13
+        </td>
+
+        <td>
+           The type of a value stored into a static field must be
+           assignment-compatible with or convertible to the field's type.
+        </td>
+
+        <td>
+          4.8.2.16
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B14
+        </td>
+
+        <td>
+           The type of a value stored into a field must be assignment-compatible
+           with or convertible to the field's type.
+        </td>
+
+        <td>
+          4.8.2.17
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B15
+        </td>
+
+        <td>
+           The type of every value stored into an array must be
+           assignment-compatible with the array's component type.
+        </td>
+
+        <td>
+          4.8.2.18
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B16
+        </td>
+
+        <td>
+           The <code>A</code> operand of a <code>throw</code> instruction must
+           be assignment-compatible with <code>java.lang.Throwable</code>.
+        </td>
+
+        <td>
+          4.8.2.19
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B17
+        </td>
+
+        <td>
+           The last reachable instruction of a method must either be a backwards
+           <code>goto</code> or branch, a <code>return</code>, or a
+           <code>throw</code> instruction. It must not be possible to leave the
+           <code>insns</code> array at the bottom.
+        </td>
+
+        <td>
+          4.8.2.20
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B18
+        </td>
+
+        <td>
+          The unassigned half of a former register pair may not be read (is
+          considered invalid) until it has been re-assigned by some other
+          instruction.
+        </td>
+
+        <td>
+          4.8.2.3, 4.8.2.4
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B19
+        </td>
+
+        <td>
+          A <code>move-result&lt;kind&gt;</code> instruction must be immediately
+          preceded (in the <code>insns</code> array) by an
+          <code>&lt;invoke-kind&gt;</code> instruction. The only exception is
+          the <code>move-result-object</code> instruction, which may also be
+          preceded by a <code>filled-new-array</code> instruction.
+        </td>
+
+        <td>
+          -
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B20
+        </td>
+
+        <td>
+          A <code>move-result&lt;kind&gt;</code> instruction must be immediately
+          preceded (in actual control flow) by a matching
+          <code>return-&lt;kind&gt;</code> instruction (it must not be jumped
+          to). The only exception is the <code>move-result-object</code>
+          instruction, which may also be preceded by a
+          <code>filled-new-array</code> instruction.
+        </td>
+
+        <td>
+          -
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B21
+        </td>
+
+        <td>
+          A <code>move-exception</code> instruction must only appear as the
+          first instruction in an exception handler.
+        </td>
+
+        <td>
+          -
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B22
+        </td>
+
+        <td>
+          The <code>packed-switch-data</code>, <code>sparse-switch-data</code>,
+          and <code>fill-array-data</code> pseudo-instructions must not be
+          reachable by control flow.
+        </td>
+
+        <td>
+          -
+        </td>
+      </tr>
+    </table>
+
+  </body>
+</html>
diff --git a/docs/debugger.html b/docs/debugger.html
new file mode 100644
index 0000000..1c47c7a
--- /dev/null
+++ b/docs/debugger.html
@@ -0,0 +1,243 @@
+<html>
+<head>
+<title>Dalvik Debugger Support</title>
+</head>
+
+<body>
+<h1>Dalvik Debugger Support</h1>
+
+<p>
+The Dalvik virtual machine supports source-level debugging with many popular
+development environments.  Any tool that allows remote debugging over JDWP
+(the
+<a href="http://java.sun.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html">
+Java Debug Wire Protocol</a>) is expected work.  Supported debuggers
+include jdb, Eclipse, IntelliJ, and JSwat.
+</p><p>
+The VM does not support tools based on JVMTI (Java Virtual
+Machine Tool Interface).  This is a relatively intrusive approach that
+relies on bytecode insertion, something the Dalvik VM does not currently
+support.
+</p><p>
+Dalvik's implementation of JDWP also includes hooks for supporting
+DDM (Dalvik Debug Monitor) features, notably as implemented by DDMS
+(Dalvik Debug Monitor Server) and the Eclipse ADT plugin.  The protocol
+and VM interaction is described in some detail
+<a href="debugmon.html">here</a>.
+</p><p>
+All of the debugger support in the VM lives in the <code>dalvik/vm/jdwp</code>
+directory, and is almost entirely isolated from the rest of the VM sources.
+<code>dalvik/vm/Debugger.c</code> bridges the gap.  The goal in doing so
+was to make it easier to re-use the JDWP code in other projects.
+</p><p>
+
+
+<h2>Implementation</h2>
+
+<p>
+Every VM that has debugging enabled starts a "JDWP" thread.  The thread
+typically sits idle until DDMS or a debugger connects.  The thread is
+only responsible for handling requests from the debugger; VM-initated
+communication, such as notifying the debugger when the VM has stopped at
+a breakpoint, are sent from the affected thread.
+</p><p>
+When the VM is started from the Android app framework, debugging is enabled
+for all applications when the system property <code>ro.debuggable</code>
+is set to </code>1</code> (use <code>adb shell getprop ro.debuggable</code>
+to check it).  If it's zero, debugging can be enabled via the application's
+manifest, which must include <code>android:debuggable="true"</code> in the
+<code>&lt;application&gt;</code> element.
+
+</p><p>
+The VM recognizes the difference between a connection from DDMS and a
+connection from a debugger (either directly or in concert with DDMS).
+A connection from DDMS alone doesn't result in a change in VM behavior,
+but when the VM sees debugger packets it allocates additional data
+structures and may switch to a different implementation of the interpreter.
+</p><p>
+Pre-Froyo implementations of the Dalvik VM used read-only memory mappings
+for all bytecode, which made it necessary to scan for breakpoints by
+comparing the program counter to a set of addresses.  In Froyo this was
+changed to allow insertion of breakpoint opcodes.  This allows the VM
+to execute code more quickly, and does away with the hardcoded limit
+of 20 breakpoints.  Even with this change, however, the debug-enabled
+interpreter is much slower than the regular interpreter (perhaps 5x).
+</p><p>
+The JDWP protocol is stateless, so the VM handles individual debugger
+requests as they arrive, and posts events to the debugger as they happen.
+</p><p>
+
+
+<h2>Debug Data</h2>
+<p> Source code debug data, which includes mappings of source code to
+bytecode and lists describing which registers are used to hold method
+arguments and local variables, are optionally emitted by the Java compiler.
+When <code>dx</code> converts Java bytecode to Dalvik bytecode, it must
+also convert this debug data.
+</p><p>
+<code>dx</code> must also ensure that it doesn't perform operations
+that confuse the debugger.  For example, re-using registers that hold
+method arguments and the "<code>this</code>" pointer is allowed in
+Dalvik bytecode if the values are never used or no longer needed.
+This can be very confusing for the debugger (and the programmer)
+since the values have method scope and aren't expected to disappear.  For
+this reason, <code>dx</code> generates sub-optimal code in some situations
+when debugging support is enabled.
+</p><p>
+Some of the debug data is used for other purposes; in particular, having
+filename and line number data is necessary for generating useful exception
+stack traces.  This data can be omitted by <code>dx</code> to make the DEX
+file smaller.
+</p><p>
+
+
+<h2>Usage</h2>
+
+<p>
+The Dalvik VM supports many of the same command-line flags that other popular
+desktop VMs do.  To start a VM with debugging enabled, you add a command-line
+flag with some basic options.  The basic incantation looks something
+like this:
+
+<pre>-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y</pre>
+or
+<pre>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=y</pre>
+
+</p><p>
+After the initial prefix, options are provided as name=value pairs.  The
+options currently supported by the Dalvik VM are:
+<dl>
+    <dt>transport (no default)</dt>
+    <dd>Communication transport mechanism to use.  Dalvik supports
+    TCP/IP sockets (<code>dt_socket</code>) and connection over USB
+    through ADB (<code>dt_android_adb</code>).
+    </dd>
+
+    <dt>server (default='n')</dt>
+    <dd>Determines whether the VM acts as a client or a server.  When
+    acting as a server, the VM waits for a debugger to connect to it.
+    When acting as a client, the VM attempts to connect to a waiting
+    debugger.
+    </dd>
+
+    <dt>suspend (default='n')</dt>
+    <dd>If set to 'y', the VM will wait for a debugger connection
+    before executing application code.  When the debugger connects (or
+    when the VM finishes connecting to the debugger), the VM tells the
+    debugger that it has suspended, and will not proceed until told
+    to resume.  If set to 'n', the VM just plows ahead.
+    </dd>
+
+    <dt>address (default="")</dt>
+    <dd>This must be <code>hostname:port</code> when <code>server=n</code>,
+    but can be just <code>port</code> when <code>server=y</code>.  This
+    specifies the IP address and port number to connect or listen to.
+    <br>
+    Listening on port 0 has a special meaning: try to
+    listen on port 8000; if that fails, try 8001, 8002, and so on.  (This
+    behavior is non-standard and may be removed from a future release.)
+    <br>This option has no meaning for <code>transport=dt_android_adb</code>.
+    </dd>
+
+    <dt>help (no arguments)</dt>
+    <dd>If this is the only option, a brief usage message is displayed.
+    </dd>
+
+    <dt>launch, onthrow, oncaught, timeout</dt>
+    <dd>These options are accepted but ignored.
+    </dd>
+</dl>
+
+</p><p>
+To debug a program on an Android device using DDMS over USB, you could
+use a command like this:
+<pre>% dalvikvm -agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y -cp /data/foo.jar Foo</pre>
+
+This tells the Dalvik VM to run the program with debugging enabled, listening
+for a connection from DDMS, and waiting for a debugger.  The program will show
+up with an app name of "?" in the process list, because it wasn't started
+from the Android application framework.  From here you would connect your
+debugger to the appropriate DDMS listen port (e.g.
+<code>jdb -attach localhost:8700</code> after selecting it in the app list).
+
+</p><p>
+To debug a program on an Android device using TCP/IP bridged across ADB,
+you would first need to set up forwarding:
+<pre>% adb forward tcp:8000 tcp:8000
+% adb shell dalvikvm -agentlib:jdwp=transport=dt_socket,address=8000,suspend=y,server=y -cp /data/foo.jar Foo</pre>
+and then <code>jdb -attach localhost:8000</code>.
+</p><p>
+(In the above examples, the VM will be suspended when you attach.  In jdb,
+type <code>cont</code> to continue.)
+</p><p>
+The DDMS integration makes the <code>dt_android_adb</code> transport much
+more convenient when debugging on an Android device, but when working with
+Dalvik on the desktop it makes sense to use the TCP/IP transport.
+</p><p>
+
+
+<h2>Known Issues and Limitations</h2>
+
+</p><p>
+Most of the optional features JDWP allows are not implemented.  These
+include field access watchpoints and better tracking of monitors.
+</p><p>
+Not all JDWP requests are implemented.  In particular, anything that
+never gets emitted by the debuggers we've used is not supported and will
+result in error messages being logged.  Support will be added when a
+use case is uncovered.
+</p><p>
+&nbsp;
+</p><p>
+The debugger and garbage collector are somewhat loosely
+integrated at present.  The VM currently guarantees that any object the
+debugger is aware of will not be garbage collected until after the
+debugger disconnects.  This can result in a build-up over time while the
+debugger is connected.  For example, if the debugger sees a running
+thread, the associated Thread object will not be collected, even after
+the thread terminates.
+</p><p>
+The only way to "unlock" the references is to detach and reattach the
+debugger.
+</p><p>
+&nbsp;
+</p><p>
+The translation from Java bytecode to Dalvik bytecode may result in
+identical sequences of instructions being combined.  This can make it
+look like the wrong bit of code is being executed.  For example:
+<pre>    int test(int i) {
+        if (i == 1) {
+            return 0;
+        }
+        return 1;
+    }</pre>
+The Dalvik bytecode uses a common <code>return</code> instruction for both
+<code>return</code> statements, so when <code>i</code> is 1 the debugger
+will single-step through <code>return 0</code> and then <code>return 1</code>.
+</p><p>
+&nbsp;
+</p><p>
+Dalvik handles synchronized methods differently from other VMs.
+Instead of marking a method as <code>synchronized</code> and expecting
+the VM to handle the locks, <code>dx</code> inserts a "lock"
+instruction at the top of the method and an "unlock" instruction in a
+synthetic <code>finally</code> block.  As a result, when single-stepping
+a <code>return</code> statement, the "current line" cursor may jump to
+the last line in the method.
+</p><p>
+This can also affect the way the debugger processes exceptions.  The
+debugger may decide to break on an
+exception based on whether that exception is "caught" or "uncaught".  To
+be considered uncaught, there must be no matching <code>catch</code> block
+or <code>finally</code> clause between the current point of execution and
+the top of the thread.  An exception thrown within or below a synchronized
+method will always be considered "caught", so the debugger won't stop
+until the exception is re-thrown from the synthetic <code>finally</code> block.
+</p><p>
+
+
+<address>Copyright &copy; 2009 The Android Open Source Project</address>
+</p>
+
+</body>
+</html>
diff --git a/docs/debugmon.html b/docs/debugmon.html
new file mode 100644
index 0000000..cf56ef5
--- /dev/null
+++ b/docs/debugmon.html
@@ -0,0 +1,736 @@
+<HTML>
+
+
+<head>
+  <title>Dalvik VM Debug Monitor</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  <link href="http://www.google.com/favicon.ico" type="image/x-icon"
+ rel="shortcut icon">
+  <link href="../android.css" type="text/css" rel="stylesheet">
+  <script language="JavaScript1.2" type="text/javascript">
+function highlight(name) {
+  if (document.getElementById) {
+    tags              = [ 'span', 'div', 'tr', 'td' ];
+    for (i in tags) {
+      elements        = document.getElementsByTagName(tags[i]);
+      if (elements) {
+        for (j = 0; j < elements.length; j++) {
+          elementName = elements[j].getAttribute("id");
+          if (elementName == name) {
+            elements[j].style.backgroundColor = "#C0F0C0";
+          } else if (elementName && elementName.indexOf("rev") == 0) {
+            elements[j].style.backgroundColor = "#FFFFFF";
+          }
+        }
+      }
+    }
+  }
+}
+  </script>
+</head>
+<body onload="prettyPrint()">
+
+<h1><a name="My_Project_"></a>Dalvik VM<br>Debug Monitor</h1>
+
+<!-- Status is one of: Draft, Current, Needs Update, Obsolete -->
+<p style="text-align:center"><strong>Status:</strong><em>Draft</em> &nbsp;
+<small>(as of March 6, 2007)</small></p>
+<address>
+[authors]
+<address>
+
+<!-- last modified date can be different to the "Status date." It automatically
+updates
+whenever the file is modified. -->
+<i>Modified:</i>
+ <!-- this script automatically sets the modified date,you don't need to modify
+it -->
+    <script type=text/javascript>
+        <!--
+        var lm = new Date(document.lastModified);
+        document.write(lm.toDateString());
+        //-->
+        </script>
+</address>
+
+<p><br>
+<HR>
+
+<h2>Introduction</h2>
+
+<p>It's extremely useful to be able to monitor the live state of the
+VM.  For Android, we need to monitor multiple VMs running on a device
+connected through USB or a wireless network connection.  This document
+describes a debug monitor server that interacts with multiple VMs, and
+an API that VMs and applications can use to provide information
+to the monitor.
+
+<p>Some things we can monitor with the Dalvik Debug Monitor ("DDM"):
+<ul>
+    <li> Thread states.  Track thread creation/exit, busy/idle status.
+    <li> Overall heap status, useful for a heap bitmap display or
+    fragmentation analysis.
+</ul>
+
+<p>It is possible for something other than a VM to act as a DDM client, but
+that is a secondary goal.  Examples include "logcat" log extraction
+and system monitors for virtual memory usage and load average.
+
+<p>It's also possible for the DDM server to be run on the device, with
+the information presented through the device UI.  However, the initial goal
+is to provide a display tool that takes advantage of desktop tools and
+screen real estate.
+
+<p>This work is necessary because we are unable to use standard JVMTI-based
+tools with Dalvik.  JVMTI relies on bytecode insertion, which is not
+currently possible because Dalvik doesn't support Java bytecode.
+
+<p>The DDM server is written in the Java programming language
+for portability.  It uses a desktop
+UI toolkit (SWT) for its interface.
+
+
+<h2>Protocol</h2>
+
+<p>To take advantage of existing infrastructure we are piggy-backing the
+DDM protocol on top of JDWP (the Java Debug Wire Protocol, normally spoken
+between a VM and a debugger).  To a
+non-DDM client, the DDM server just looks like a debugger.
+
+<p>The JDWP protocol is very close to what we want to use.  In particular:
+<ul>
+    <li>It explicitly allows for vendor-defined packets, so there is no
+    need to "bend" the JDWP spec.
+    <li>Events may be posted from the VM at arbitrary points.  Such
+    events do not elicit a response from the debugger, meaning the client
+    can post data and immediately resume work without worrying about the
+    eventual response.
+    <li>The basic protocol is stateless and asynchronous.  Request packets
+    from the debugger side include a serial number, which the VM includes
+    in the response packet.  This allows multiple simultaneous
+    conversations, which means the DDM traffic can be interleaved with
+    debugger traffic.
+</ul>
+
+<p>There are a few issues with using JDWP for our purposes:
+<ul>
+    <li>The VM only expects one connection from a debugger, so you couldn't
+    attach the monitor and a debugger at the same time.  This will be
+    worked around by connecting the debugger to the monitor and passing the
+    traffic through.  (We're already doing the pass-through with "jdwpspy";
+    requires some management of our request IDs though.)  This should
+    be more convenient than the current "guess the port
+    number" system when we're attached to a device.
+    <li>The VM behaves differently when a debugger is attached.  It will
+    run more slowly, and any objects passed to the monitor or debugger are
+    immune to GC.  We can work around this by not enabling the slow path
+    until non-DDM traffic is observed.  We also want to have a "debugger
+    has connected/disconnected" message that allows the VM to release
+    debugger-related resources without dropping the net connection.
+    <li>Non-DDM VMs should not freak out when DDM connects.  There are
+    no guarantees here for 3rd-party VMs (e.g. a certain mainstream VM,
+    which crashes instantly), but our older JamVM can be
+    configured to reject the "hello" packet.
+</ul>
+
+
+<h3>Connection Establishment</h3>
+
+<p>There are two basic approaches: have the server contact the VMs, and
+have the VMs contact the server.  The former is less "precise" than the
+latter, because you have to scan for the clients, but it has some
+advantages.
+
+<p>There are three interesting scenarios:
+<ol>
+    <li>The DDM server is started, then the USB-attached device is booted
+    or the simulator is launched.
+    <li>The device or simulator is already running when the DDM server
+    is started.
+    <li>The DDM server is running when an already-started device is
+    attached to USB.
+</ol>
+<p>If we have the VMs connect to the DDM server on startup, we only handle
+case #1.  If the DDM server scans for VMs when it starts, we only handle
+case #2.  Neither handles case #3, which is probably the most important
+of the bunch as the device matures.
+<p>The plan is to have a drop-down menu with two entries,
+"scan workstation" and "scan device".
+The former causes the DDM server to search for VMs on "localhost", the
+latter causes it to search for VMs on the other side of an ADB connection.
+The DDM server will scan for VMs every few seconds, either checking a
+range of known VM ports (e.g. 8000-8040) or interacting with some sort
+of process database on the device.  Changing modes causes all existing
+connections to be dropped.
+<p>When the DDM server first starts, it will try to execute "adb usb"
+to ensure that the ADB server is running.  (Note it will be necessary
+to launch the DDM server from a shell with "adb" in the path.)  If this
+fails, talking to the device will still be possible so long as the ADB
+daemon is already running.
+
+<h4>Connecting a Debugger</h4>
+
+<p>With the DDM server sitting on the JDWP port of all VMs, it will be
+necessary to connect the debugger through the DDM server.  Each VM being
+debugged will have a separate port being listened to by the DDM server,
+allowing you to connect a debugger to one or more VMs simultaneously.
+
+<p>In the common case, however, the developer will only want to debug
+a single VM.  One port (say 8700) will be listened to by the DDM server,
+and anything connecting to it will be connected to the "current VM"
+(selected in the UI).  This should allow developers to focus on a
+single application, which may otherwise shift around in the ordering, without
+having to adjust their IDE settings to a different port every time they
+restart the device.
+
+
+<h3>Packet Format</h3>
+
+<p>Information is sent in chunks.  Each chunk starts with:
+<pre>
+u4   type
+u4   length
+</pre>
+and contains a variable amount of type-specific data.
+Unrecognized types cause an empty response from the client and
+are quietly ignored by the server.  [Should probably return an error;
+need an "error" chunk type and a handler on the server side.]
+
+<p>The same chunk type may have different meanings when sent in different
+directions.  For example, the same type may be used for both a query and
+a response to the query.  For sanity the type must always be used in
+related transactions.
+
+<p>This is somewhat redundant with the JDWP framing, which includes a
+4-byte length and a two-byte type code ("command set" and "command"; a
+range of command set values is designated for "vendor-defined commands
+and extensions").  Using the chunk format allows us to remain independent
+of the underlying transport, avoids intrusive integration
+with JDWP client code, and provides a way to send multiple chunks in a
+single transmission unit.  [I'm taking the multi-chunk packets into
+account in the design, but do not plan to implement them unless the need
+arises.]
+
+<p>Because we may be sending data over a slow USB link, the chunks may be
+compressed.  Compressed chunks are written as a chunk type that
+indicates the compression, followed by the compressed length, followed
+by the original chunk type and the uncompressed length.  For zlib's deflate
+algorithm, the chunk type is "ZLIB".
+
+<p>Following the JDWP model, packets sent from the server to the client
+are always acknowledged, but packets sent from client to server never are.
+The JDWP error code field is always set to "no error"; failure responses
+from specific requests must be encoded into the DDM messages.
+
+<p>In what follows "u4" is an unsigned 32-bit value and "u1" is an
+unsigned 8-bit value.  Values are written in big-endian order to match
+JDWP.
+
+
+<h3>Initial Handshake</h3>
+
+<p>After the JDWP handshake, the server sends a HELO chunk to the client.
+If the client's JDWP layer rejects it, the server assumes that the client
+is not a DDM-aware VM, and does not send it any further DDM queries.
+<p>On the client side, upon seeing a HELO it can know that a DDM server
+is attached and prepare accordingly.  The VM should not assume that a
+debugger is attached until a non-DDM packet arrives.
+
+<h4>Chunk HELO (server --&gt; client)</h4>
+<p>Basic "hello" message.
+<pre>
+u4   DDM server protocol version
+</pre>
+
+
+<h4>Chunk HELO (client --&gt; server, reply only)</h4>
+Information about the client.  Must be sent in response to the HELO message.
+<pre>
+u4   DDM client protocol version
+u4   pid
+u4   VM ident string len (in 16-bit units)
+u4   application name len (in 16-bit units)
+var  VM ident string (UTF-16)
+var  application name (UTF-16)
+</pre>
+
+<p>If the client does not wish to speak to the DDM server, it should respond
+with a JDWP error packet.  This is the same behavior you'd get from a VM
+that doesn't support DDM.
+
+
+<h3>Debugger Management</h3>
+<p>VMs usually prepare for debugging when a JDWP connection is established,
+and release debugger-related resources when the connection drops.  We want
+to open the JDWP connection early and hold it open after the debugger
+disconnects.
+<p>The VM can tell when a debugger attaches, because it will start seeing
+non-DDM JDWP traffic, but it can't identify the disconnect.  For this reason,
+we need to send a packet to the client when the debugger disconnects.
+<p>If the DDM server is talking to a non-DDM-aware client, it will be
+necessary to drop and re-establish the connection when the debugger goes away.
+(This also works with DDM-aware clients; this packet is an optimization.)
+
+<h4>Chunk DBGD (server --&gt; client)</h4>
+<p>Debugger has disconnected.  The client responds with a DBGD to acknowledge
+receipt.  No data in request, no response required.
+
+
+<h3>VM Info</h3>
+<p>Update the server's info about the client.
+
+<h4>Chunk APNM (client --&gt; server)</h4>
+
+<p>If a VM's application name changes -- possible in our environment because
+of the "pre-initialized" app processes -- it must send up one of these.
+<pre>
+u4   application name len (in 16-bit chars)
+var  application name (UTF-16)
+</pre>
+
+<h4>Chunk WAIT (client --&gt; server)</h4>
+
+<p>This tells DDMS that one or more threads are waiting on an external
+event.  The simplest use is to tell DDMS that the VM is waiting for a
+debugger to attach.
+<pre>
+u1   reason  (0 = wait for debugger)
+</pre>
+If DDMS is attached, the client VM sends this up when waitForDebugger()
+is called.  If waitForDebugger() is called before DDMS attaches, the WAIT
+chunk will be sent up at about the same time as the HELO response.
+
+
+<h3>Thread Status</h3>
+
+<p>The client can send updates when their status changes, or periodically
+send thread state info, e.g. 2x per
+second to allow a "blinkenlights" display of thread activity.
+
+<h4>Chunk THEN (server --&gt; client)</h4>
+
+<p>Enable thread creation/death notification.
+<pre>
+u1   boolean (true=enable, false=disable)
+</pre>
+<p>The response is empty.  The client generates THCR packets for all
+known threads.  (Note the THCR packets may arrive before the THEN
+response.)
+
+<h4>Chunk THCR (client --&gt; server)</h4>
+<p>Thread Creation notification.
+<pre>
+u4   VM-local thread ID (usually a small int)
+u4   thread name len (in 16-bit chars)
+var  thread name (UTF-16)
+</pre>
+
+<h4>Chunk THDE (client --&gt; server)</h4>
+<p>Thread Death notification.
+<pre>
+u4   VM-local thread ID
+</pre>
+
+<h4>Chunk THST (server --&gt; client)</h4>
+
+<p>Enable periodic thread activity updates.
+Threads in THCR messages are assumed to be in the "initializing" state.  A
+THST message should follow closely on the heels of THCR.
+<pre>
+u4   interval, in msec
+</pre>
+<p>An interval of 0 disables the updates.  This is done periodically,
+rather than every time the thread state changes, to reduce the amount
+of data that must be sent for an actively running VM.
+
+<h4>Chunk THST (client --&gt; server)</h4>
+<p>Thread Status, describing the state of one or more threads.  This is
+most useful when creation/death notifications are enabled first.  The
+overall layout is:
+<pre>
+u4   count
+var  thread data
+</pre>
+Then, for every thread:
+<pre>
+u4   VM-local thread ID
+u1   thread state
+u1   suspended
+</pre>
+<p>"thread state" must be one of:
+<ul>    <!-- don't use ol, we may need (-1) or sparse -->
+    <li> 1 - running (now executing or ready to do so)
+    <li> 2 - sleeping (in Thread.sleep())
+    <li> 3 - monitor (blocked on a monitor lock)
+    <li> 4 - waiting (in Object.wait())
+    <li> 5 - initializing
+    <li> 6 - starting
+    <li> 7 - native (executing native code)
+    <li> 8 - vmwait (waiting on a VM resource)
+</ul>
+<p>"suspended" will be 0 if the thread is running, 1 if not.
+<p>[Any reason not to make "suspended" be the high bit of "thread state"?
+Do we need to differentiate suspend-by-GC from suspend-by-debugger?]
+<p>[We might be able to send the currently-executing method.  This is a
+little risky in a running VM, and increases the size of the messages
+considerably, but might be handy.]
+
+
+<h3>Heap Status</h3>
+
+<p>The client sends what amounts to a color-coded bitmap to the server,
+indicating which stretches of memory are free and which are in use.  For
+compactness the bitmap is run-length encoded, and based on multi-byte
+"allocation units" rather than byte counts.
+
+<p>In the future the server will be able to correlate the bitmap with more
+detailed object data, so enough information is provided to associate the
+bitmap data with virtual addresses.
+
+<p>Heaps may be broken into segments within the VM, and due to memory
+constraints it may be desirable to send the bitmap in smaller pieces,
+so the protocol allows the heap data to be sent in several chunks.
+To avoid ambiguity, the client is required
+to send explicit "start" and "end" messages during an update.
+
+<p>All messages include a "heap ID" that can be used to differentiate
+between multiple independent virtual heaps or perhaps a native heap.  The
+client is allowed to send information about different heaps simultaneously,
+so all heap-specific information is tagged with a "heap ID".
+
+<h4>Chunk HPIF (server --&gt; client)</h4>
+<p>Request heap info.
+<pre>
+u1   when to send
+</pre>
+<p>The "when" values are:
+<pre>
+0: never
+1: immediately
+2: at the next GC
+3: at every GC
+</pre>
+
+<h4>Chunk HPIF (client --&gt; server, reply only)</h4>
+<p>Heap Info.  General information about the heap, suitable for a summary
+display.
+<pre>
+u4   number of heaps
+</pre>
+For each heap:
+<pre>
+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
+</pre>
+<p>[We can get some of this from HPSG, more from HPSO.]
+<p>[Do we need a "heap overhead" stat here, indicating how much goes to
+waste?  e.g. (8 bytes per object * number of objects)]
+
+<h4>Chunk HPSG (server --&gt; client)</h4>
+<p>Request transmission of heap segment data.
+<pre>
+u1   when to send
+u1   what to send
+</pre>
+<p>The "when" to send will be zero to disable transmission, 1 to send
+during a GC.  Other values are currently undefined.  (Could use to pick
+which part of the GC to send it, or cause periodic transmissions.)
+<p>The "what" field is currently 0 for HPSG and 1 for HPSO.
+<p>No reply is expected.
+
+<h4>Chunk NHSG (server --&gt; client)</h4>
+<p>Request transmission of native heap segment data.
+<pre>
+u1   when to send
+u1   what to send
+</pre>
+<p>The "when" to send will be zero to disable transmission, 1 to send
+during a GC.  Other values are currently undefined.
+<p>The "what" field is currently ignored.
+<p>No reply is expected.
+
+<h4>Chunk HPST/NHST (client --&gt; server)</h4>
+<p>This is a Heap Start message.  It tells the server to discard any
+existing notion of what the client's heap looks like, and prepare for
+new information.  HPST indicates a virtual heap dump and must be followed
+by zero or more HPSG/HPSO messages and an HPEN.  NHST indicates a native
+heap dump and must be followed by zero or more NHSG messages and an NHEN.
+
+<p>The only data item is:
+<pre>
+u4   heap ID
+</pre>
+
+<h4>Chunk HPEN/NHEN (client --&gt; server)</h4>
+<p>Heap End, indicating that all information about the heap has been sent.
+A HPST will be paired with an HPEN and an NHST will be paired with an NHEN.
+
+<p>The only data item is:
+<pre>
+u4   heap ID
+</pre>
+
+<h4>Chunk HPSG (client --&gt; server)</h4>
+<p>Heap segment data.  Each chunk describes all or part of a contiguous
+stretch of heap memory.
+<pre>
+u4   heap ID
+u1   size of allocation unit, in bytes (e.g. 8 bytes)
+u4   virtual address of segment start
+u4   offset of this piece (relative to the virtual address)
+u4   length of piece, in allocation units
+var  usage data
+</pre>
+<p>The "usage data" indicates the status of each allocation unit.  The data
+is a stream of pairs of bytes, where the first byte indicates the state
+of the allocation unit, and the second byte indicates the number of
+consecutive allocation units with the same state.
+<p>The bits in the "state" byte have the following meaning:
+<pre>
++---------------------------------------+
+|  7 |  6 |  5 |  4 |  3 |  2 |  1 |  0 |
++---------------------------------------+
+|  P | U0 | K2 | K1 | K0 | S2 | S1 | S0 |
++---------------------------------------+
+</pre>
+<ul>
+    <li>'S': solidity
+    <ul>
+        <li>0=free
+        <li>1=has hard reference
+        <li>2=has soft reference
+        <li>3=has weak reference
+        <li>4=has phantom reference
+        <li>5=pending finalization
+        <li>6=marked, about to be swept
+    </ul>
+    <li>'K': kind
+    <ul>
+        <li>0=object
+        <li>1=class object
+        <li>2=array of byte/boolean
+        <li>3=array of char/short
+        <li>4=array of Object/int/float
+        <li>5=array of long/double
+    </ul>
+    <li>'P': partial flag (not used for HPSG)
+    <li>'U': unused, must be zero
+</ul>
+
+<p>The use of the various 'S' types depends on when the information is
+sent.  The current plan is to send it either immediately after a GC,
+or between the "mark" and "sweep" phases of the GC.  For a fancy generational
+collector, we may just want to send it up periodically.
+
+<p>The run-length byte indicates the number of allocation units minus one, so a
+length of 255 means there are 256 consecutive units with this state.  In
+some cases, e.g. arrays of bytes, the actual size of the data is rounded
+up the nearest allocation unit.
+<p>For HPSG, the runs do not end at object boundaries.  It is not possible
+to tell from this bitmap whether a run contains one or several objects.
+(But see HPSO, below.)
+<p>[If we find that we have many long runs, we can overload the 'P' flag
+or dedicate the 'U' flag to indicate that we have a 16-bit length instead
+of 8-bit.  We can also use a variable-width integer scheme for the length,
+encoding 1-128 in one byte, 1-16384 in two bytes, etc.]
+<p>[Alternate plan for 'K': array of byte, array of char, array of Object,
+array of miscellaneous primitive type]
+<p>To parse the data, the server runs through the usage data until either
+(a) the end of the chunk is reached, or (b) all allocation units have been
+accounted for.  (If these two things don't happen at the same time, the
+chunk is rejected.)
+<p>Example: suppose a VM has a heap at 0x10000 that is 0x2000 bytes long
+(with an 8-byte allocation unit size, that's 0x0400 units long).
+The client could send one chunk (allocSize=8, virtAddr=0x10000, offset=0,
+length=0x0400) or two (allocSize=8, virtAddr=0x10000, offset=0, length=0x300;
+then allocSize=8, virtAddr=0x10000, offset=0x300, length=0x100).
+<p>The client must encode the entire heap, including all free space at
+the end, or the server will not have an accurate impression of the amount
+of memory in the heap.  This refers to the current heap size, not the
+maximum heap size.
+
+<h4>Chunk HPSO (client --&gt; server)</h4>
+<p>This is essentially identical to HPSG, but the runs are terminated at
+object boundaries.  If an object is larger than 256 allocation units, the
+"partial" flag is set in all runs except the last.
+<p>The resulting unpacked bitmap is identical, but the object boundary
+information can be used to gain insights into heap layout.
+<p>[Do we want to have a separate message for this?  Maybe just include
+a "variant" flag in the HPST packet.  Another possible form of output
+would be one that indicates the age, in generations, of each block of
+memory.  That would provide a quick visual indication of "permanent vs.
+transient residents", perhaps with a 16-level grey scale.]
+
+<h4>Chunk NHSG (client --&gt; server)</h4>
+<p>Native heap segment data.  Each chunk describes all or part of a
+contiguous stretch of native heap memory.  The format is the same as
+for HPSG, except that only solidity values 0 (= free) and 1 (= hard
+reference) are used, and the kind value is always 0 for free chunks
+and 7 for allocated chunks, indicating a non-VM object.
+<pre>
+u4   heap ID
+u1   size of allocation unit, in bytes (e.g. 8 bytes)
+u4   virtual address of segment start
+u4   offset of this piece (relative to the virtual address)
+u4   length of piece, in allocation units
+var  usage data
+</pre>
+
+<h3>Generic Replies</h3>
+
+The client-side chunk handlers need a common way to report simple success
+or failure.  By convention, an empty reply packet indicates success.
+
+<h4>Chunk FAIL (client --&gt; server, reply only)</h4>
+<p>The chunk includes a machine-readable error code and a
+human-readable error message.  Server code can associate the failure
+with the original request by comparing the JDWP packet ID.
+<p>This allows a standard way of, for example, rejecting badly-formed
+request packets.
+<pre>
+u4   error code
+u4   error message len (in 16-bit chars)
+var  error message (UTF-16)
+</pre>
+
+<h3>Miscellaneous</h3>
+
+<h4>Chunk EXIT (server --&gt; client)</h4>
+<p>Cause the client to exit with the specified status, using System.exit().
+Useful for certain kinds of testing.
+<pre>
+u4   exit status
+</pre>
+
+<h4>Chunk DTRC (server --&gt; client)</h4>
+<p>[TBD] start/stop dmtrace; can send the results back over the wire.  For
+size reasons we probably need "sending", "data", "key", "finished" as
+4 separate chunks/packets rather than one glob.
+
+
+<h2>Client API</h2>
+
+<p>The API is written in the Java programming language
+for convenience.  The code is free to call native methods if appropriate.
+
+<h3>Chunk Handler API</h3>
+
+<p>The basic idea is that arbitrary code can register handlers for
+specific chunk types.  When a DDM chunk with that type arrives, the
+appropriate handler is invoked.  The handler's return value provides the
+response to the server.
+
+<p>There are two packages.  android.ddm lives in the "framework" library,
+and has all of the chunk handlers and registration code.  It can freely
+use Android classes.  org.apache.harmony.dalvik.ddmc lives in the "core"
+library, and has
+some base classes and features that interact with the VM.  Nothing should
+need to modify the org.apache.harmony.dalvik.ddmc classes.
+
+<p>The DDM classes pass chunks of data around with a simple class:
+
+<pre class=prettyprint>
+class Chunk {
+    int type;
+    byte[] data;
+    int offset, length;
+};
+</pre>
+
+<p>The chunk handlers accept and return them:
+<pre class=prettyprint>
+public Chunk handleChunk(Chunk request)
+</pre>
+<p>The code is free to parse the chunk and generate a response in any
+way it chooses.  Big-endian byte ordering is recommended but not mandatory.
+<p>Chunk handlers will be notified when a DDM server connects or disconnects,
+so that they can perform setup and cleanup operations:
+<pre class=prettyprint>
+public void connected()
+public void disconnected()
+</pre>
+
+<p>The method processes the request, formulates a response, and returns it.
+If the method returns null, an empty JDWP success message will be returned.
+<p>The request/response interaction is essentially asynchronous in the
+protocol.  The packets are linked together with the JDWP message ID.
+<p>[We could use ByteBuffer here instead of byte[], but it doesn't gain
+us much.  Wrapping a ByteBuffer around an array is easy.  We don't want
+to pass the full packet in because we could have multiple chunks in one
+request packet.  The DDM code needs to collect and aggregate the responses
+to all chunks into a single JDWP response packet.  Parties wanting to
+write multiple chunks in response to a single chunk should send a null
+response back and use "sendChunk()" to send the data independently.]
+
+<h3>Unsolicited event API</h3>
+
+<p>If a piece of code wants to send a chunk of data to the server at some
+arbitrary time, it may do so with a method provided by
+org.apache.harmony.dalvik.DdmServer:
+
+<pre class=prettyprint>
+public static void sendChunk(Chunk chunk)
+</pre>
+
+<p>There is no response or status code.  No exceptions are thrown.
+
+
+<h2>Server API</h2>
+
+<p>This is similar to the client side in many ways, but makes extensive
+use of ByteBuffer in a perhaps misguided attempt to use java.nio.channels
+and avoid excessive thread creation and unnecessary data copying.
+
+<p>Upon receipt of a packet, the server will identify it as one of:
+<ol>
+    <li>Message to be passed through to the debugger
+    <li>Response to an earlier request
+    <li>Unsolicited event packet
+</ol>
+<p>To handle (2), when messages are sent from the server to the client,
+the message must be paired with a callback method.  The response might be
+delayed for a while -- or might never arrive -- so the server can't block
+waiting for responses from the client.
+<p>The chunk handlers look like this:
+<pre class=prettyprint>
+public void handleChunk(Client client, int type,
+    ByteBuffer data, boolean isReply, int msgId)
+</pre>
+<p>The arguments are:
+<dl>
+    <dt>client
+    <dd>An object representing the client VM that send us the packet.
+    <dt>type
+    <dd>The 32-bit chunk type.
+    <dt>data
+    <dd>The data.  The data's length can be determined by calling data.limit().
+    <dt>isReply
+    <dd>Set to "true" if this was a reply to a message we sent earlier,
+    "false" if the client sent this unsolicited.
+    <dt>msgId
+    <dd>The JDWP message ID.  Useful for connecting replies with requests.
+</dl>
+<p>If a handler doesn't like the contents of a packet, it should log an
+error message and return.  If the handler doesn't recognize the packet at
+all, it can call the superclass' handleUnknownChunk() method.
+
+<p>As with the client, the server code can be notified when clients
+connect or disconnect.  This allows the handler to send initialization
+code immediately after a connect, or clean up after a disconnect.
+<p>Data associated with a client can be stored in a ClientData object,
+which acts as a general per-client dumping around for VM and UI state.
+
+
+<P><BR>
+
+<HR>
+
+<address>Copyright &copy; 2007 The Android Open Source Project</address>
+
+</body>
+</HTML>
diff --git a/docs/dex-format.css b/docs/dex-format.css
new file mode 100644
index 0000000..153dd4e
--- /dev/null
+++ b/docs/dex-format.css
@@ -0,0 +1,387 @@
+h1 {
+    font-family: serif;
+    border-top-style: solid;
+    border-top-width: 5px;
+    padding-top: 9pt;
+    margin-top: 40pt;
+    color: #222266;
+}
+
+h1.title {
+    border: none;
+}
+
+h2 {
+    font-family: serif;
+    border-top-style: solid;
+    border-top-width: 2px;
+    border-color: #ccccdd;
+    padding-top: 9pt;
+    margin-top: 40pt;
+    margin-bottom: 2pt;
+    color: #222266;
+}
+
+h3 {
+    font-family: serif;
+    font-style: bold;
+    margin-top: 20pt;
+    margin-bottom: 2pt;
+    color: #222266;
+}
+
+h4 {
+    font-family: serif;
+    font-style: italic;
+    margin-top: 2pt;
+    margin-bottom: 2pt;
+    color: #666688;
+}
+
+@media print {
+    table {
+        font-size: 8pt;
+    }
+}
+
+@media screen {
+    table {
+        font-size: 10pt;
+    }
+}
+
+pre {
+    background: #eeeeff;
+    border-color: #aaaaff;
+    border-style: solid;
+    border-width: 1px;
+    margin-left: 40pt;
+    margin-right: 40pt;
+    padding: 6pt;
+}
+
+table {
+    border-collapse: collapse;
+    margin-top: 10pt;
+    margin-left: 40pt;
+    margin-right: 40pt;
+}
+
+table th {
+    font-family: sans-serif;
+    background: #aabbff;
+}
+
+table td {
+    font-family: sans-serif;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-width: 1px;
+    border-color: #aaaaff;
+    padding-top: 3pt;
+    padding-bottom: 3pt;
+    padding-left: 3pt;
+    padding-right: 4pt;
+    background: #eeeeff;
+}
+
+table p {
+    margin-bottom: 0pt;
+}
+
+/* for the bnf syntax sections */
+
+table.bnf {
+    background: #eeeeff;
+    border-color: #aaaaff;
+    border-style: solid;
+    border-width: 1px;
+    margin-top: 3pt;
+    margin-bottom: 3pt;
+    padding-top: 2pt;
+    padding-bottom: 6pt;
+    padding-left: 6pt;
+    padding-right: 6pt;
+}
+
+table.bnf td {
+    border: none;
+    padding-left: 6pt;
+    padding-right: 6pt;
+    padding-top: 1pt;
+    padding-bottom: 1pt;
+}
+
+table.bnf td:first-child {
+    padding-right: 0pt;
+    width: 8pt;
+}
+
+table.bnf td:first-child td {
+    padding-left: 0pt;
+}
+
+table.bnf td.def {
+    padding-top: 6pt;
+}
+
+table.bnf td.bar {
+    padding-left: 15pt;
+}
+
+table.bnf code {
+    font-weight: bold;
+}
+
+
+/* for the type name guide */
+
+table.guide {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.guide td:first-child {
+    font-family: monospace;
+    width: 15%;
+}
+
+table.guide td:first-child + td {
+    font-family: sans-serif;
+    width: 85%;
+}
+
+
+/* for the LEB128 example tables */
+
+table.leb128Bits {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.leb128Bits td {
+    border-left: solid #aaaaff 1px;
+    border-right: solid #aaaaff 1px;
+}
+
+table.leb128Bits td.start1 {
+    border-left: none;
+}
+
+table.leb128Bits td.start2 {
+    border-left: solid #000 2px;
+}
+
+table.leb128Bits td.end2 {
+    border-right: none;
+}
+
+table.leb128 {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.leb128 td:first-child {
+    font-family: monospace;
+    text-align: center;
+    width: 31%;
+}
+
+table.leb128 td:first-child + td {
+    font-family: monospace;
+    text-align: center;
+    width: 23%;
+}
+
+table.leb128 td:first-child + td + td {
+    font-family: monospace;
+    text-align: center;
+    width: 23%;
+}
+
+table.leb128 td:first-child + td + td + td {
+    font-family: monospace;
+    text-align: center;
+    width: 23%;
+}
+
+
+/* for the general format tables */
+
+table.format {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.format td:first-child {
+    font-family: monospace;
+    width: 20%;
+}
+
+table.format td:first-child + td {
+    font-family: monospace;
+    width: 20%;
+}
+
+table.format td:first-child + td + td {
+    width: 60%;
+}
+
+table.format td i {
+    font-family: sans-serif;
+}
+
+
+/* for the type code table */
+
+table.typeCodes {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.typeCodes td:first-child {
+    font-family: monospace;
+    width: 30%;
+}
+
+table.typeCodes td:first-child + td {
+    font-family: monospace;
+    width: 30%;
+}
+
+table.typeCodes td:first-child + td + td {
+    font-family: monospace;
+    width: 10%;
+}
+
+table.typeCodes td:first-child + td + td + td {
+    font-family: monospace;
+    width: 30%;
+}
+
+table.typeCodes td i {
+    font-family: sans-serif;
+}
+
+
+/* for the access flags table */
+
+table.accessFlags {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.accessFlags td:first-child {
+    font-family: monospace;
+    width: 10%;
+}
+
+table.accessFlags td:first-child + td {
+    font-family: monospace;
+    width: 6%;
+}
+
+table.accessFlags td:first-child + td + td {
+    width: 28%;
+}
+
+table.accessFlags td:first-child + td + td + td {
+    width: 28%;
+}
+
+table.accessFlags td:first-child + td + td + td + td {
+    width: 28%;
+}
+
+table.accessFlags i {
+    font-family: sans-serif;
+}
+
+
+/* for the descriptor table */
+
+table.descriptor {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.descriptor td:first-child {
+    font-family: monospace;
+    width: 25%;
+}
+
+table.descriptor td:first-child + td {
+    font-family: sans-serif;
+    width: 75%;
+}
+
+
+/* for the debug bytecode table */
+
+table.debugByteCode {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.debugByteCode td:first-child {
+    font-family: monospace;
+    width: 20%;
+}
+
+table.debugByteCode td:first-child + td {
+    font-family: monospace;
+    width: 5%;
+}
+
+table.debugByteCode td:first-child + td + td{
+    font-family: monospace;
+    width: 15%;
+}
+
+table.debugByteCode td:first-child + td + td + td {
+    width: 25%;
+}
+
+table.debugByteCode td:first-child + td + td + td + td {
+    width: 35%;
+}
+
+table.debugByteCode i {
+    font-family: sans-serif;
+}
+
+
+/* for the encoded value table */
+
+table.encodedValue {
+    margin-top: 20pt;
+    margin-bottom: 20pt;
+}
+
+table.encodedValue td:first-child {
+    font-family: monospace;
+    width: 12%;
+}
+
+table.encodedValue td:first-child + td {
+    font-family: monospace;
+    width: 10%;
+}
+
+table.encodedValue td:first-child + td + td {
+    font-family: monospace;
+    width: 15%;
+}
+
+table.encodedValue td:first-child + td + td + td {
+    font-family: monospace;
+    width: 15%;
+}
+
+table.encodedValue td:first-child + td + td + td + td {
+    width: 48%;
+}
+
+table.encodedValue td i {
+    font-family: sans-serif;
+}
diff --git a/docs/dex-format.html b/docs/dex-format.html
new file mode 100644
index 0000000..cab9d4c
--- /dev/null
+++ b/docs/dex-format.html
@@ -0,0 +1,3043 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>.dex &mdash; Dalvik Executable Format</title>
+<link rel=stylesheet href="dex-format.css">
+</head>
+
+<body>
+
+<h1 class="title"><code>.dex</code> &mdash; Dalvik Executable Format</h1>
+<p>Copyright &copy; 2007 The Android Open Source Project
+
+<p>This document describes the layout and contents of <code>.dex</code>
+files, which are used to hold a set of class definitions and their associated
+adjunct data.</p>
+
+<h1>Guide To Types</h1>
+
+<table class="guide">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>byte</td>
+  <td>8-bit signed int</td>
+</tr>
+<tr>
+  <td>ubyte</td>
+  <td>8-bit unsigned int</td>
+</tr>
+<tr>
+  <td>short</td>
+  <td>16-bit signed int, little-endian</td>
+</tr>
+<tr>
+  <td>ushort</td>
+  <td>16-bit unsigned int, little-endian</td>
+</tr>
+<tr>
+  <td>int</td>
+  <td>32-bit signed int, little-endian</td>
+</tr>
+<tr>
+  <td>uint</td>
+  <td>32-bit unsigned int, little-endian</td>
+</tr>
+<tr>
+  <td>long</td>
+  <td>64-bit signed int, little-endian</td>
+</tr>
+<tr>
+  <td>ulong</td>
+  <td>64-bit unsigned int, little-endian</td>
+</tr>
+<tr>
+  <td>sleb128</td>
+  <td>signed LEB128, variable-length (see below)</td>
+</tr>
+<tr>
+  <td>uleb128</td>
+  <td>unsigned LEB128, variable-length (see below)</td>
+</tr>
+<tr>
+  <td>uleb128p1</td>
+  <td>unsigned LEB128 plus <code>1</code>, variable-length (see below)</td>
+</tr>
+</tbody>
+</table>
+
+<h3>LEB128</h3>
+
+<p>LEB128 ("<b>L</b>ittle-<b>E</b>ndian <b>B</b>ase <b>128</b>") is a
+variable-length encoding for
+arbitrary signed or unsigned integer quantities. The format was
+borrowed from the <a href="http://dwarfstd.org/Dwarf3Std.php">DWARF3</a>
+specification. In a <code>.dex</code> file, LEB128 is only ever used to
+encode 32-bit quantities.</p>
+
+<p>Each LEB128 encoded value consists of one to five
+bytes, which together represent a single 32-bit value. Each
+byte has its most significant bit set except for the final byte in the
+sequence, which has its most significant bit clear. The remaining
+seven bits of each byte are payload, with the least significant seven
+bits of the quantity in the first byte, the next seven in the second
+byte and so on. In the case of a signed LEB128 (<code>sleb128</code>),
+the most significant payload bit of the final byte in the sequence is
+sign-extended to produce the final value. In the unsigned case
+(<code>uleb128</code>), any bits not explicitly represented are
+interpreted as <code>0</code>.
+
+<table class="leb128Bits">
+<thead>
+<tr><th colspan="16">Bitwise diagram of a two-byte LEB128 value</th></tr>
+<tr>
+  <th colspan="8">First byte</td>
+  <th colspan="8">Second byte</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td class="start1"><code>1</code></td>
+  <td>bit<sub>6</sub></td>
+  <td>bit<sub>5</sub></td>
+  <td>bit<sub>4</sub></td>
+  <td>bit<sub>3</sub></td>
+  <td>bit<sub>2</sub></td>
+  <td>bit<sub>1</sub></td>
+  <td>bit<sub>0</sub></td>
+  <td class="start2"><code>0</code></td>
+  <td>bit<sub>13</sub></td>
+  <td>bit<sub>12</sub></td>
+  <td>bit<sub>11</sub></td>
+  <td>bit<sub>10</sub></td>
+  <td>bit<sub>9</sub></td>
+  <td>bit<sub>8</sub></td>
+  <td class="end2">bit<sub>7</sub></td>
+</tr>
+</tbody>
+</table>
+
+<p>The variant <code>uleb128p1</code> is used to represent a signed
+value, where the representation is of the value <i>plus one</i> encoded
+as a <code>uleb128</code>. This makes the encoding of <code>-1</code>
+(alternatively thought of as the unsigned value <code>0xffffffff</code>)
+&mdash; but no other negative number &mdash; a single byte, and is
+useful in exactly those cases where the represented number must either
+be non-negative or <code>-1</code> (or <code>0xffffffff</code>),
+and where no other negative values are allowed (or where large unsigned
+values are unlikely to be needed).</p>
+
+<p>Here are some examples of the formats:</p>
+
+<table class="leb128">
+<thead>
+<tr>
+  <th>Encoded Sequence</th>
+  <th>As <code>sleb128</code></th>
+  <th>As <code>uleb128</code></th>
+  <th>As <code>uleb128p1</code></th>
+</tr>
+</thead>
+<tbody>
+  <tr><td>00</td><td>0</td><td>0</td><td>-1</td></tr>
+  <tr><td>01</td><td>1</td><td>1</td><td>0</td></tr>
+  <tr><td>7f</td><td>-1</td><td>127</td><td>126</td></tr>
+  <tr><td>80 7f</td><td>-128</td><td>16256</td><td>16255</td></tr>
+</tbody>
+</table>
+
+<h1>Overall File Layout</h1>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>header</td>
+  <td>header_item</td>
+  <td>the header</td>
+</tr>
+<tr>
+  <td>string_ids</td>
+  <td>string_id_item[]</td>
+  <td>string identifiers list. These are identifiers for all the strings
+    used by this file, either for internal naming (e.g., type descriptors)
+    or as constant objects referred to by code. This list must be sorted
+    by string contents, using UTF-16 code point values (not in a
+    locale-sensitive manner).
+  </td>
+</tr>
+<tr>
+  <td>type_ids</td>
+  <td>type_id_item[]</td>
+  <td>type identifiers list. These are identifiers for all types (classes,
+    arrays, or primitive types) referred to by this file, whether defined
+    in the file or not. This list must be sorted by <code>string_id</code>
+    index.
+  </td>
+</tr>
+<tr>
+  <td>proto_ids</td>
+  <td>proto_id_item[]</td>
+  <td>method prototype identifiers list. These are identifiers for all
+    prototypes referred to by this file. This list must be sorted in
+    return-type (by <code>type_id</code> index) major order, and then
+    by arguments (also by <code>type_id</code> index).
+  </td>
+</tr>
+<tr>
+  <td>field_ids</td>
+  <td>field_id_item[]</td>
+  <td>field identifiers list. These are identifiers for all fields
+    referred to by this file, whether defined in the file or not. This
+    list must be sorted, where the defining type (by <code>type_id</code>
+    index) is the major order, field name (by <code>string_id</code> index)
+    is the intermediate order, and type (by <code>type_id</code> index)
+    is the minor order.
+  </td>
+</tr>
+<tr>
+  <td>method_ids</td>
+  <td>method_id_item[]</td>
+  <td>method identifiers list. These are identifiers for all methods
+    referred to by this file, whether defined in the file or not. This
+    list must be sorted, where the defining type (by <code>type_id</code>
+    index) is the major order, method name (by <code>string_id</code>
+    index) is the intermediate order, and method
+    prototype (by <code>proto_id</code> index) is the minor order.
+  </td>
+</tr>
+<tr>
+  <td>class_defs</td>
+  <td>class_def_item[]</td>
+  <td>class definitions list. The classes must be ordered such that a given
+    class's superclass and implemented interfaces appear in the
+    list earlier than the referring class.
+  </td>
+</tr>
+<tr>
+  <td>data</td>
+  <td>ubyte[]</td>
+  <td>data area, containing all the support data for the tables listed above.
+    Different items have different alignment requirements, and
+    padding bytes are inserted before each item if necessary to achieve
+    proper alignment.
+  </td>
+</tr>
+<tr>
+  <td>link_data</td>
+  <td>ubyte[]</td>
+  <td>data used in statically linked files. The format of the data in
+    this section is left unspecified by this document;
+    this section is empty in unlinked files, and runtime implementations
+    may use it as they see fit.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h1>Bitfield, String, and Constant Definitions</h1>
+
+<h2><code>DEX_FILE_MAGIC</code></h2>
+<h4>embedded in <code>header_item</code></h4>
+
+<p>The constant array/string <code>DEX_FILE_MAGIC</code> is the list of
+bytes that must appear at the beginning of a <code>.dex</code> file
+in order for it to be recognized as such. The value intentionally
+contains a newline (<code>"\n"</code> or <code>0x0a</code>) and a
+null byte (<code>"\0"</code> or <code>0x00</code>) in order to help
+in the detection of certain forms of corruption. The value also
+encodes a format version number as three decimal digits, which is
+expected to increase monotonically over time as the format evolves.</p>
+
+<pre>
+ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 }
+                        = "dex\n035\0"
+</pre>
+
+<p><b>Note:</b> At least a couple earlier versions of the format have
+been used in widely-available public software releases. For example,
+version <code>009</code> was used for the M3 releases of the
+Android platform (November-December 2007),
+and version <code>013</code> was used for the M5 releases of the Android
+platform (February-March 2008). In several respects, these earlier versions
+of the format differ significantly from the version described in this
+document.</p>
+
+<h2><code>ENDIAN_CONSTANT</code> and <code>REVERSE_ENDIAN_CONSTANT</code></h2>
+<h4>embedded in <code>header_item</code></h4>
+
+<p>The constant <code>ENDIAN_CONSTANT</code> is used to indicate the
+endianness of the file in which it is found. Although the standard
+<code>.dex</code> format is little-endian, implementations may choose
+to perform byte-swapping. Should an implementation come across a
+header whose <code>endian_tag</code> is <code>REVERSE_ENDIAN_CONSTANT</code>
+instead of <code>ENDIAN_CONSTANT</code>, it would know that the file
+has been byte-swapped from the expected form.</p>
+
+<pre>
+uint ENDIAN_CONSTANT = 0x12345678;
+uint REVERSE_ENDIAN_CONSTANT = 0x78563412;
+</pre>
+
+<h2><code>NO_INDEX</code></h2>
+<h4>embedded in <code>class_def_item</code> and
+<code>debug_info_item</code></h4>
+
+<p>The constant <code>NO_INDEX</code> is used to indicate that
+an index value is absent.</p>
+
+<p><b>Note:</b> This value isn't defined to be
+<code>0</code>, because that is in fact typically a valid index.</p>
+
+<p><b>Also Note:</b> The chosen value for <code>NO_INDEX</code> is
+representable as a single byte in the <code>uleb128p1</code> encoding.</p>
+
+<pre>
+uint NO_INDEX = 0xffffffff;    // == -1 if treated as a signed int
+</pre>
+
+<h2><code>access_flags</code> Definitions</h2>
+<h4>embedded in <code>class_def_item</code>,
+<code>field_item</code>, <code>method_item</code>, and
+<code>InnerClass</code></h4>
+
+<p>Bitfields of these flags are used to indicate the accessibility and
+overall properties of classes and class members.</p>
+
+<table class="accessFlags">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Value</th>
+  <th>For Classes (and <code>InnerClass</code> annotations)</th>
+  <th>For Fields</th>
+  <th>For Methods</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>ACC_PUBLIC</td>
+  <td>0x1</td>
+  <td><code>public</code>: visible everywhere</td>
+  <td><code>public</code>: visible everywhere</td>
+  <td><code>public</code>: visible everywhere</td>
+</tr>
+<tr>
+  <td>ACC_PRIVATE</td>
+  <td>0x2</td>
+  <td><super>*</super>
+    <code>private</code>: only visible to defining class
+  </td>
+  <td><code>private</code>: only visible to defining class</td>
+  <td><code>private</code>: only visible to defining class</td>
+</tr>
+<tr>
+  <td>ACC_PROTECTED</td>
+  <td>0x4</td>
+  <td><super>*</super>
+    <code>protected</code>: visible to package and subclasses
+  </td>
+  <td><code>protected</code>: visible to package and subclasses</td>
+  <td><code>protected</code>: visible to package and subclasses</td>
+</tr>
+<tr>
+  <td>ACC_STATIC</td>
+  <td>0x8</td>
+  <td><super>*</super>
+    <code>static</code>: is not constructed with an outer
+    <code>this</code> reference</td>
+  <td><code>static</code>: global to defining class</td>
+  <td><code>static</code>: does not take a <code>this</code> argument</td>
+</tr>
+<tr>
+  <td>ACC_FINAL</td>
+  <td>0x10</td>
+  <td><code>final</code>: not subclassable</td>
+  <td><code>final</code>: immutable after construction</td>
+  <td><code>final</code>: not overridable</td>
+</tr>
+<tr>
+  <td>ACC_SYNCHRONIZED</td>
+  <td>0x20</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td><code>synchronized</code>: associated lock automatically acquired
+    around call to this method. <b>Note:</b> This is only valid to set when
+    <code>ACC_NATIVE</code> is also set.</td>
+</tr>
+<tr>
+  <td>ACC_VOLATILE</td>
+  <td>0x40</td>
+  <td>&nbsp;</td>
+  <td><code>volatile</code>: special access rules to help with thread
+    safety</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>ACC_BRIDGE</td>
+  <td>0x40</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>bridge method, added automatically by compiler as a type-safe
+    bridge</td>
+</tr>
+<tr>
+  <td>ACC_TRANSIENT</td>
+  <td>0x80</td>
+  <td>&nbsp;</td>
+  <td><code>transient</code>: not to be saved by default serialization</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>ACC_VARARGS</td>
+  <td>0x80</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>last argument should be treated as a "rest" argument by compiler</td>
+</tr>
+<tr>
+  <td>ACC_NATIVE</td>
+  <td>0x100</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td><code>native</code>: implemented in native code</td>
+</tr>
+<tr>
+  <td>ACC_INTERFACE</td>
+  <td>0x200</td>
+  <td><code>interface</code>: multiply-implementable abstract class</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>ACC_ABSTRACT</td>
+  <td>0x400</td>
+  <td><code>abstract</code>: not directly instantiable</td>
+  <td>&nbsp;</td>
+  <td><code>abstract</code>: unimplemented by this class</td>
+</tr>
+<tr>
+  <td>ACC_STRICT</td>
+  <td>0x800</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td><code>strictfp</code>: strict rules for floating-point arithmetic</td>
+</tr>
+<tr>
+  <td>ACC_SYNTHETIC</td>
+  <td>0x1000</td>
+  <td>not directly defined in source code</td>
+  <td>not directly defined in source code</td>
+  <td>not directly defined in source code</td>
+</tr>
+<tr>
+  <td>ACC_ANNOTATION</td>
+  <td>0x2000</td>
+  <td>declared as an annotation class</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>ACC_ENUM</td>
+  <td>0x4000</td>
+  <td>declared as an enumerated type</td>
+  <td>declared as an enumerated value</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td><i>(unused)</i></td>
+  <td>0x8000</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>ACC_CONSTRUCTOR</td>
+  <td>0x10000</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>constructor method (class or instance initializer)</td>
+</tr>
+<tr>
+  <td>ACC_DECLARED_<br/>SYNCHRONIZED</td>
+  <td>0x20000</td>
+  <td>&nbsp;</td>
+  <td>&nbsp;</td>
+  <td>declared <code>synchronized</code>. <b>Note:</b> This has no effect on
+    execution (other than in reflection of this flag, per se).
+  </td>
+</tr>
+</tbody>
+</table>
+
+<p><super>*</super> Only allowed on for <code>InnerClass</code> annotations,
+and must not ever be on in a <code>class_def_item</code>.</p>
+
+<h2>MUTF-8 (Modified UTF-8) Encoding</h2>
+
+<p>As a concession to easier legacy support, the <code>.dex</code> format
+encodes its string data in a de facto standard modified UTF-8 form, hereafter
+referred to as MUTF-8. This form is identical to standard UTF-8, except:</p>
+
+<ul>
+  <li>Only the one-, two-, and three-byte encodings are used.</li>
+  <li>Code points in the range <code>U+10000</code> &hellip;
+    <code>U+10ffff</code> are encoded as a surrogate pair, each of
+    which is represented as a three-byte encoded value.</li>
+  <li>The code point <code>U+0000</code> is encoded in two-byte form.</li>
+  <li>A plain null byte (value <code>0</code>) indicates the end of
+    a string, as is the standard C language interpretation.</li>
+</ul>
+
+<p>The first two items above can be summarized as: MUTF-8
+is an encoding format for UTF-16, instead of being a more direct
+encoding format for Unicode characters.</p>
+
+<p>The final two items above make it simultaneously possible to include
+the code point <code>U+0000</code> in a string <i>and</i> still manipulate
+it as a C-style null-terminated string.</p>
+
+<p>However, the special encoding of <code>U+0000</code> means that, unlike
+normal UTF-8, the result of calling the standard C function
+<code>strcmp()</code> on a pair of MUTF-8 strings does not always
+indicate the properly signed result of comparison of <i>unequal</i> strings.
+When ordering (not just equality) is a concern, the most straightforward
+way to compare MUTF-8 strings is to decode them character by character,
+and compare the decoded values. (However, more clever implementations are
+also possible.)</p>
+
+<p>Please refer to <a href="http://unicode.org">The Unicode
+Standard</a> for further information about character encoding.
+MUTF-8 is actually closer to the (relatively less well-known) encoding
+<a href="http://www.unicode.org/reports/tr26/">CESU-8</a> than to UTF-8
+per se.</p>
+
+<h2><code>encoded_value</code> Encoding</h2>
+<h4>embedded in <code>annotation_element</code> and
+<code>encoded_array_item</code></h4>
+
+<p>An <code>encoded_value</code> is an encoded piece of (nearly)
+arbitrary hierarchically structured data. The encoding is meant to
+be both compact and straightforward to parse.</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>(value_arg &lt;&lt; 5) | value_type</td>
+  <td>ubyte</td>
+  <td>byte indicating the type of the immediately subsequent
+    <code>value</code> along
+    with an optional clarifying argument in the high-order three bits.
+    See below for the various <code>value</code> definitions.
+    In most cases, <code>value_arg</code> encodes the length of
+    the immediately-subsequent <code>value</code> in bytes, as
+    <code>(size - 1)</code>, e.g., <code>0</code> means that
+    the value requires one byte, and <code>7</code> means it requires
+    eight bytes; however, there are exceptions as noted below.
+  </td>
+</tr>
+<tr>
+  <td>value</td>
+  <td>ubyte[]</td>
+  <td>bytes representing the value, variable in length and interpreted
+    differently for different <code>value_type</code> bytes, though
+    always little-endian. See the various value definitions below for
+    details.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3>Value Formats</h3>
+
+<table class="encodedValue">
+<thead>
+<tr>
+  <th>Type Name</th>
+  <th><code>value_type</code></th>
+  <th><code>value_arg</code> Format</th>
+  <th><code>value</code> Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>VALUE_BYTE</td>
+  <td>0x00</td>
+  <td><i>(none; must be <code>0</code>)</i></td>
+  <td>ubyte[1]</td>
+  <td>signed one-byte integer value</td>
+</tr>
+<tr>
+  <td>VALUE_SHORT</td>
+  <td>0x02</td>
+  <td>size - 1 (0&hellip;1)</td>
+  <td>ubyte[size]</td>
+  <td>signed two-byte integer value, sign-extended</td>
+</tr>
+<tr>
+  <td>VALUE_CHAR</td>
+  <td>0x03</td>
+  <td>size - 1 (0&hellip;1)</td>
+  <td>ubyte[size]</td>
+  <td>unsigned two-byte integer value, zero-extended</td>
+</tr>
+<tr>
+  <td>VALUE_INT</td>
+  <td>0x04</td>
+  <td>size - 1 (0&hellip;3)</td>
+  <td>ubyte[size]</td>
+  <td>signed four-byte integer value, sign-extended</td>
+</tr>
+<tr>
+  <td>VALUE_LONG</td>
+  <td>0x06</td>
+  <td>size - 1 (0&hellip;7)</td>
+  <td>ubyte[size]</td>
+  <td>signed eight-byte integer value, sign-extended</td>
+</tr>
+<tr>
+  <td>VALUE_FLOAT</td>
+  <td>0x10</td>
+  <td>size - 1 (0&hellip;3)</td>
+  <td>ubyte[size]</td>
+  <td>four-byte bit pattern, zero-extended <i>to the right</i>, and
+    interpreted as an IEEE754 32-bit floating point value
+  </td>
+</tr>
+<tr>
+  <td>VALUE_DOUBLE</td>
+  <td>0x11</td>
+  <td>size - 1 (0&hellip;7)</td>
+  <td>ubyte[size]</td>
+  <td>eight-byte bit pattern, zero-extended <i>to the right</i>, and
+    interpreted as an IEEE754 64-bit floating point value
+  </td>
+</tr>
+<tr>
+  <td>VALUE_STRING</td>
+  <td>0x17</td>
+  <td>size - 1 (0&hellip;3)</td>
+  <td>ubyte[size]</td>
+  <td>unsigned (zero-extended) four-byte integer value,
+    interpreted as an index into
+    the <code>string_ids</code> section and representing a string value
+  </td>
+</tr>
+<tr>
+  <td>VALUE_TYPE</td>
+  <td>0x18</td>
+  <td>size - 1 (0&hellip;3)</td>
+  <td>ubyte[size]</td>
+  <td>unsigned (zero-extended) four-byte integer value,
+    interpreted as an index into
+    the <code>type_ids</code> section and representing a reflective
+    type/class value
+  </td>
+</tr>
+<tr>
+  <td>VALUE_FIELD</td>
+  <td>0x19</td>
+  <td>size - 1 (0&hellip;3)</td>
+  <td>ubyte[size]</td>
+  <td>unsigned (zero-extended) four-byte integer value,
+    interpreted as an index into
+    the <code>field_ids</code> section and representing a reflective
+    field value
+  </td>
+</tr>
+<tr>
+  <td>VALUE_METHOD</td>
+  <td>0x1a</td>
+  <td>size - 1 (0&hellip;3)</td>
+  <td>ubyte[size]</td>
+  <td>unsigned (zero-extended) four-byte integer value,
+    interpreted as an index into
+    the <code>method_ids</code> section and representing a reflective
+    method value
+  </td>
+</tr>
+<tr>
+  <td>VALUE_ENUM</td>
+  <td>0x1b</td>
+  <td>size - 1 (0&hellip;3)</td>
+  <td>ubyte[size]</td>
+  <td>unsigned (zero-extended) four-byte integer value,
+    interpreted as an index into
+    the <code>field_ids</code> section and representing the value of
+    an enumerated type constant
+  </td>
+</tr>
+<tr>
+  <td>VALUE_ARRAY</td>
+  <td>0x1c</td>
+  <td><i>(none; must be <code>0</code>)</i></td>
+  <td>encoded_array</td>
+  <td>an array of values, in the format specified by
+    "<code>encoded_array</code> Format" below. The size
+    of the <code>value</code> is implicit in the encoding.
+  </td>
+</tr>
+<tr>
+  <td>VALUE_ANNOTATION</td>
+  <td>0x1d</td>
+  <td><i>(none; must be <code>0</code>)</i></td>
+  <td>encoded_annotation</td>
+  <td>a sub-annotation, in the format specified by
+    "<code>encoded_annotation</code> Format" below. The size
+    of the <code>value</code> is implicit in the encoding.
+  </td>
+</tr>
+<tr>
+  <td>VALUE_NULL</td>
+  <td>0x1e</td>
+  <td><i>(none; must be <code>0</code>)</i></td>
+  <td><i>(none)</i></td>
+  <td><code>null</code> reference value</td>
+</tr>
+<tr>
+  <td>VALUE_BOOLEAN</td>
+  <td>0x1f</td>
+  <td>boolean (0&hellip;1)</td>
+  <td><i>(none)</i></td>
+  <td>one-bit value; <code>0</code> for <code>false</code> and
+    <code>1</code> for <code>true</code>. The bit is represented in the
+    <code>value_arg</code>.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_array</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>size</td>
+  <td>uleb128</td>
+  <td>number of elements in the array</td>
+</tr>
+<tr>
+  <td>values</td>
+  <td>encoded_value[size]</td>
+  <td>a series of <code>size</code> <code>encoded_value</code> byte
+    sequences in the format specified by this section, concatenated
+    sequentially.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_annotation</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>type_idx</td>
+  <td>uleb128</td>
+  <td>type of the annotation. This must be a class (not array or primitive)
+    type.
+  </td>
+</tr>
+<tr>
+  <td>size</td>
+  <td>uleb128</td>
+  <td>number of name-value mappings in this annotation</td>
+</tr>
+<tr>
+  <td>elements</td>
+  <td>annotation_element[size]</td>
+  <td>elements of the annotataion, represented directly in-line (not as
+    offsets). Elements must be sorted in increasing order by
+    <code>string_id</code> index.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>annotation_element</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>name_idx</td>
+  <td>uleb128</td>
+  <td>element name, represented as an index into the
+    <code>string_ids</code> section. The string must conform to the
+    syntax for <i>MemberName</i>, defined above.
+  </td>
+</tr>
+<tr>
+  <td>value</td>
+  <td>encoded_value</td>
+  <td>element value</td>
+</tr>
+</tbody>
+</table>
+
+<h2>String Syntax</h2>
+
+<p>There are several kinds of item in a <code>.dex</code> file which
+ultimately refer to a string. The following BNF-style definitions
+indicate the acceptable syntax for these strings.</p>
+
+<h3><i>SimpleName</i></h3>
+
+<p>A <i>SimpleName</i> is the basis for the syntax of the names of other
+things. The <code>.dex</code> format allows a fair amount of latitude
+here (much more than most common source languages). In brief, a simple
+name may consist of any low-ASCII alphabetic character or digit, a few
+specific low-ASCII symbols, and most non-ASCII code points that are not
+control, space, or special characters. Note that surrogate code points
+(in the range <code>U+d800</code> &hellip; <code>U+dfff</code>) are not
+considered valid name characters, per se, but Unicode supplemental
+characters <i>are</i> valid (which are represented by the final
+alternative of the rule for <i>SimpleNameChar</i>), and they should be
+represented in a file as pairs of surrogate code points in the MUTF-8
+encoding.</p>
+
+<table class="bnf">
+  <tr><td colspan="2" class="def"><i>SimpleName</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><i>SimpleNameChar</i> (<i>SimpleNameChar</i>)*</td>
+  </tr>
+
+  <tr><td colspan="2" class="def"><i>SimpleNameChar</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><code>'A'</code> &hellip; <code>'Z'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'a'</code> &hellip; <code>'z'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'0'</code> &hellip; <code>'9'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'$'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'-'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'_'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>U+00a1</code> &hellip; <code>U+1fff</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>U+2010</code> &hellip; <code>U+2027</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>U+2030</code> &hellip; <code>U+d7ff</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>U+e000</code> &hellip; <code>U+ffef</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>U+10000</code> &hellip; <code>U+10ffff</code></td>
+  </tr>
+</table>
+
+<h3><i>MemberName</i></h3>
+<h4>used by <code>field_id_item</code> and <code>method_id_item</code></h4>
+
+<p>A <i>MemberName</i> is the name of a member of a class, members being
+fields, methods, and inner classes.</p>
+
+<table class="bnf">
+  <tr><td colspan="2" class="def"><i>MemberName</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><i>SimpleName</i></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'&lt;'</code> <i>SimpleName</i> <code>'&gt;'</code></td>
+  </tr>
+</table>
+
+<h3><i>FullClassName</i></h3>
+
+<p>A <i>FullClassName</i> is a fully-qualified class name, including an
+optional package specifier followed by a required name.</p>
+
+<table class="bnf">
+  <tr><td colspan="2" class="def"><i>FullClassName</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><i>OptionalPackagePrefix</i> <i>SimpleName</i></td>
+  </tr>
+
+  <tr><td colspan="2" class="def"><i>OptionalPackagePrefix</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td>(<i>SimpleName</i> <code>'/'</code>)*</td>
+  </tr>
+</table>
+
+<h3><i>TypeDescriptor</i></h3>
+<h4>used by <code>type_id_item</code></h4>
+
+<p>A <i>TypeDescriptor</i> is the representation of any type, including
+primitives, classes, arrays, and <code>void</code>. See below for
+the meaning of the various versions.</p>
+
+<table class="bnf">
+  <tr><td colspan="2" class="def"><i>TypeDescriptor</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><code>'V'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><i>FieldTypeDescriptor</i></td>
+  </tr>
+
+  <tr><td colspan="2" class="def"><i>FieldTypeDescriptor</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><i>NonArrayFieldTypeDescriptor</i></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td>(<code>'['</code> * 1&hellip;255)
+      <i>NonArrayFieldTypeDescriptor</i></td>
+  </tr>
+
+  <tr>
+    <td colspan="2" class="def"><i>NonArrayFieldTypeDescriptor</i>&rarr;</td>
+  </tr>
+  <tr>
+    <td/>
+    <td><code>'Z'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'B'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'S'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'C'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'I'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'J'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'F'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'D'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'L'</code> <i>FullClassName</i> <code>';'</code></td>
+  </tr>
+</table>
+
+<h3><i>ShortyDescriptor</i></h3>
+<h4>used by <code>proto_id_item</code></h4>
+
+<p>A <i>ShortyDescriptor</i> is the short form representation of a method
+prototype, including return and parameter types, except that there is
+no distinction between various reference (class or array) types. Instead,
+all reference types are represented by a single <code>'L'</code> character.</p>
+
+<table class="bnf">
+  <tr><td colspan="2" class="def"><i>ShortyDescriptor</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><i>ShortyReturnType</i> (<i>ShortyFieldType</i>)*</td>
+  </tr>
+
+  <tr><td colspan="2" class="def"><i>ShortyReturnType</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><code>'V'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><i>ShortyFieldType</i></td>
+  </tr>
+
+  <tr><td colspan="2" class="def"><i>ShortyFieldType</i> &rarr;</td></tr>
+  <tr>
+    <td/>
+    <td><code>'Z'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'B'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'S'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'C'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'I'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'J'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'F'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'D'</code></td>
+  </tr>
+  <tr>
+    <td class="bar">|</td>
+    <td><code>'L'</code></td>
+  </tr>
+</table>
+
+<h2><i>TypeDescriptor</i> Semantics</h2>
+
+<p>This is the meaning of each of the variants of <i>TypeDescriptor</i>.</p>
+
+<table class="descriptor">
+<thead>
+<tr>
+  <th>Syntax</th>
+  <th>Meaning</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>V</td>
+  <td><code>void</code>; only valid for return types</td>
+</tr>
+<tr>
+  <td>Z</td>
+  <td><code>boolean</code></td>
+</tr>
+<tr>
+  <td>B</td>
+  <td><code>byte</code></td>
+</tr>
+<tr>
+  <td>S</td>
+  <td><code>short</code></td>
+</tr>
+<tr>
+  <td>C</td>
+  <td><code>char</code></td>
+</tr>
+<tr>
+  <td>I</td>
+  <td><code>int</code></td>
+</tr>
+<tr>
+  <td>J</td>
+  <td><code>long</code></td>
+</tr>
+<tr>
+  <td>F</td>
+  <td><code>float</code></td>
+</tr>
+<tr>
+  <td>D</td>
+  <td><code>double</code></td>
+</tr>
+<tr>
+  <td>L<i>fully/qualified/Name</i>;</td>
+  <td>the class <code><i>fully.qualified.Name</i></code></td>
+</tr>
+<tr>
+  <td>[<i>descriptor</i></td>
+  <td>array of <code><i>descriptor</i></code>, usable recursively for
+    arrays-of-arrays, though it is invalid to have more than 255
+    dimensions.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h1>Items and Related Structures</h1>
+
+<p>This section includes definitions for each of the top-level items that
+may appear in a <code>.dex</code> file.
+
+<h2><code>header_item</code></h2>
+<h4>appears in the <code>header</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>magic</td>
+  <td>ubyte[8] = DEX_FILE_MAGIC</td>
+  <td>magic value. See discussion above under "<code>DEX_FILE_MAGIC</code>"
+    for more details.
+  </td>
+</tr>
+<tr>
+  <td>checksum</td>
+  <td>uint</td>
+  <td>adler32 checksum of the rest of the file (everything but
+    <code>magic</code> and this field); used to detect file corruption
+  </td>
+</tr>
+<tr>
+  <td>signature</td>
+  <td>ubyte[20]</td>
+  <td>SHA-1 signature (hash) of the rest of the file (everything but
+    <code>magic</code>, <code>checksum</code>, and this field); used
+    to uniquely identify files
+  </td>
+</tr>
+<tr>
+  <td>file_size</td>
+  <td>uint</td>
+  <td>size of the entire file (including the header), in bytes
+</tr>
+<tr>
+  <td>header_size</td>
+  <td>uint = 0x70</td>
+  <td>size of the header (this entire section), in bytes. This allows for at
+    least a limited amount of backwards/forwards compatibility without
+    invalidating the format.
+  </td>
+</tr>
+<tr>
+  <td>endian_tag</td>
+  <td>uint = ENDIAN_CONSTANT</td>
+  <td>endianness tag. See discussion above under "<code>ENDIAN_CONSTANT</code>
+    and <code>REVERSE_ENDIAN_CONSTANT</code>" for more details.
+  </td>
+</tr>
+<tr>
+  <td>link_size</td>
+  <td>uint</td>
+  <td>size of the link section, or <code>0</code> if this file isn't
+    statically linked</td>
+</tr>
+<tr>
+  <td>link_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the link section, or
+    <code>0</code> if <code>link_size == 0</code>. The offset, if non-zero,
+    should be to an offset into the <code>link_data</code> section. The
+    format of the data pointed at is left unspecified by this document;
+    this header field (and the previous) are left as hooks for use by
+    runtime implementations.
+  </td>
+</tr>
+<tr>
+  <td>map_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the map item, or
+    <code>0</code> if this file has no map. The offset, if non-zero,
+    should be to an offset into the <code>data</code> section,
+    and the data should be in the format specified by "<code>map_list</code>"
+    below.
+  </td>
+</tr>
+<tr>
+  <td>string_ids_size</td>
+  <td>uint</td>
+  <td>count of strings in the string identifiers list</td>
+</tr>
+<tr>
+  <td>string_ids_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the string identifiers list, or
+    <code>0</code> if <code>string_ids_size == 0</code> (admittedly a
+    strange edge case). The offset, if non-zero,
+    should be to the start of the <code>string_ids</code> section.
+  </td>
+</tr>
+<tr>
+  <td>type_ids_size</td>
+  <td>uint</td>
+  <td>count of elements in the type identifiers list</td>
+</tr>
+<tr>
+  <td>type_ids_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the type identifiers list, or
+    <code>0</code> if <code>type_ids_size == 0</code> (admittedly a
+    strange edge case). The offset, if non-zero,
+    should be to the start of the <code>type_ids</code>
+    section.
+  </td>
+</tr>
+<tr>
+  <td>proto_ids_size</td>
+  <td>uint</td>
+  <td>count of elements in the prototype identifiers list</td>
+</tr>
+<tr>
+  <td>proto_ids_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the prototype identifiers list, or
+    <code>0</code> if <code>proto_ids_size == 0</code> (admittedly a
+    strange edge case). The offset, if non-zero,
+    should be to the start of the <code>proto_ids</code>
+    section.
+  </td>
+</tr>
+<tr>
+  <td>field_ids_size</td>
+  <td>uint</td>
+  <td>count of elements in the field identifiers list</td>
+</tr>
+<tr>
+  <td>field_ids_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the field identifiers list, or
+    <code>0</code> if <code>field_ids_size == 0</code>. The offset, if
+    non-zero, should be to the start of the <code>field_ids</code>
+    section.</td>
+</td>
+</tr>
+<tr>
+  <td>method_ids_size</td>
+  <td>uint</td>
+  <td>count of elements in the method identifiers list</td>
+</tr>
+<tr>
+  <td>method_ids_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the method identifiers list, or
+    <code>0</code> if <code>method_ids_size == 0</code>. The offset, if
+    non-zero, should be to the start of the <code>method_ids</code>
+    section.</td>
+</tr>
+<tr>
+  <td>class_defs_size</td>
+  <td>uint</td>
+  <td>count of elements in the class definitions list</td>
+</tr>
+<tr>
+  <td>class_defs_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the class definitions list, or
+    <code>0</code> if <code>class_defs_size == 0</code> (admittedly a
+    strange edge case). The offset, if non-zero,
+    should be to the start of the <code>class_defs</code> section.
+  </td>
+</tr>
+<tr>
+  <td>data_size</td>
+  <td>uint</td>
+  <td>Size of <code>data</code> section in bytes. Must be an even
+    multiple of sizeof(uint).</td>
+</tr>
+<tr>
+  <td>data_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the start of the
+   <code>data</code> section.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>map_list</code></h2>
+<h4>appears in the <code>data</code> section</h4>
+<h4>referenced from <code>header_item</code></h4>
+<h4>alignment: 4 bytes</h4>
+
+<p>This is a list of the entire contents of a file, in order. It
+contains some redundancy with respect to the <code>header_item</code>
+but is intended to be an easy form to use to iterate over an entire
+file. A given type may appear at most once in a map, but there is no
+restriction on what order types may appear in, other than the
+restrictions implied by the rest of the format (e.g., a
+<code>header</code> section must appear first, followed by a
+<code>string_ids</code> section, etc.). Additionally, the map entries must
+be ordered by initial offset and must not overlap.</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>size</td>
+  <td>uint</td>
+  <td>size of the list, in entries</td>
+</tr>
+<tr>
+  <td>list</td>
+  <td>map_item[size]</td>
+  <td>elements of the list</td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>map_item</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>type</td>
+  <td>ushort</td>
+  <td>type of the items; see table below</td>
+</tr>
+<tr>
+  <td>unused</td>
+  <td>ushort</td>
+  <td><i>(unused)</i></td>
+</tr>
+<tr>
+  <td>size</td>
+  <td>uint</td>
+  <td>count of the number of items to be found at the indicated offset</td>
+</tr>
+<tr>
+  <td>offset</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the items in question</td>
+</tr>
+</tbody>
+</table>
+
+
+<h3>Type Codes</h3>
+
+<table class="typeCodes">
+<thead>
+<tr>
+  <th>Item Type</th>
+  <th>Constant</th>
+  <th>Value</th>
+  <th>Item Size In Bytes</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>header_item</td>
+  <td>TYPE_HEADER_ITEM</td>
+  <td>0x0000</td>
+  <td>0x70</td>
+</tr>
+<tr>
+  <td>string_id_item</td>
+  <td>TYPE_STRING_ID_ITEM</td>
+  <td>0x0001</td>
+  <td>0x04</td>
+</tr>
+<tr>
+  <td>type_id_item</td>
+  <td>TYPE_TYPE_ID_ITEM</td>
+  <td>0x0002</td>
+  <td>0x04</td>
+</tr>
+<tr>
+  <td>proto_id_item</td>
+  <td>TYPE_PROTO_ID_ITEM</td>
+  <td>0x0003</td>
+  <td>0x0c</td>
+</tr>
+<tr>
+  <td>field_id_item</td>
+  <td>TYPE_FIELD_ID_ITEM</td>
+  <td>0x0004</td>
+  <td>0x08</td>
+</tr>
+<tr>
+  <td>method_id_item</td>
+  <td>TYPE_METHOD_ID_ITEM</td>
+  <td>0x0005</td>
+  <td>0x08</td>
+</tr>
+<tr>
+  <td>class_def_item</td>
+  <td>TYPE_CLASS_DEF_ITEM</td>
+  <td>0x0006</td>
+  <td>0x20</td>
+</tr>
+<tr>
+  <td>map_list</td>
+  <td>TYPE_MAP_LIST</td>
+  <td>0x1000</td>
+  <td>4 + (item.size * 12)</td>
+</tr>
+<tr>
+  <td>type_list</td>
+  <td>TYPE_TYPE_LIST</td>
+  <td>0x1001</td>
+  <td>4 + (item.size * 2)</td>
+</tr>
+<tr>
+  <td>annotation_set_ref_list</td>
+  <td>TYPE_ANNOTATION_SET_REF_LIST</td>
+  <td>0x1002</td>
+  <td>4 + (item.size * 4)</td>
+</tr>
+<tr>
+  <td>annotation_set_item</td>
+  <td>TYPE_ANNOTATION_SET_ITEM</td>
+  <td>0x1003</td>
+  <td>4 + (item.size * 4)</td>
+</tr>
+<tr>
+  <td>class_data_item</td>
+  <td>TYPE_CLASS_DATA_ITEM</td>
+  <td>0x2000</td>
+  <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+  <td>code_item</td>
+  <td>TYPE_CODE_ITEM</td>
+  <td>0x2001</td>
+  <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+  <td>string_data_item</td>
+  <td>TYPE_STRING_DATA_ITEM</td>
+  <td>0x2002</td>
+  <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+  <td>debug_info_item</td>
+  <td>TYPE_DEBUG_INFO_ITEM</td>
+  <td>0x2003</td>
+  <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+  <td>annotation_item</td>
+  <td>TYPE_ANNOTATION_ITEM</td>
+  <td>0x2004</td>
+  <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+  <td>encoded_array_item</td>
+  <td>TYPE_ENCODED_ARRAY_ITEM</td>
+  <td>0x2005</td>
+  <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+  <td>annotations_directory_item</td>
+  <td>TYPE_ANNOTATIONS_DIRECTORY_ITEM</td>
+  <td>0x2006</td>
+  <td><i>implicit; must parse</i></td>
+</tr>
+</tbody>
+</table>
+
+
+<h2><code>string_id_item</code></h2>
+<h4>appears in the <code>string_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>string_data_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the string data for this
+    item. The offset should be to a location
+    in the <code>data</code> section, and the data should be in the
+    format specified by "<code>string_data_item</code>" below.
+    There is no alignment requirement for the offset.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>string_data_item</code></h2>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>utf16_size</td>
+  <td>uleb128</td>
+  <td>size of this string, in UTF-16 code units (which is the "string
+    length" in many systems). That is, this is the decoded length of
+    the string. (The encoded length is implied by the position of
+    the <code>0</code> byte.)</td>
+</tr>
+<tr>
+  <td>data</td>
+  <td>ubyte[]</td>
+  <td>a series of MUTF-8 code units (a.k.a. octets, a.k.a. bytes)
+    followed by a byte of value <code>0</code>. See
+    "MUTF-8 (Modified UTF-8) Encoding" above for details and
+    discussion about the data format.
+    <p><b>Note:</b> It is acceptable to have a string which includes
+    (the encoded form of) UTF-16 surrogate code units (that is,
+    <code>U+d800</code> &hellip; <code>U+dfff</code>)
+    either in isolation or out-of-order with respect to the usual
+    encoding of Unicode into UTF-16. It is up to higher-level uses of
+    strings to reject such invalid encodings, if appropriate.</p>
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>type_id_item</code></h2>
+<h4>appears in the <code>type_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>descriptor_idx</td>
+  <td>uint</td>
+  <td>index into the <code>string_ids</code> list for the descriptor
+    string of this type. The string must conform to the syntax for
+    <i>TypeDescriptor</i>, defined above.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>proto_id_item</code></h2>
+<h4>appears in the <code>proto_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>shorty_idx</td>
+  <td>uint</td>
+  <td>index into the <code>string_ids</code> list for the short-form
+    descriptor string of this prototype. The string must conform to the
+    syntax for <i>ShortyDescriptor</i>, defined above, and must correspond
+    to the return type and parameters of this item.
+  </td>
+</tr>
+<tr>
+  <td>return_type_idx</td>
+  <td>uint</td>
+  <td>index into the <code>type_ids</code> list for the return type
+    of this prototype
+  </td>
+</tr>
+<tr>
+  <td>parameters_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the list of parameter types
+    for this prototype, or <code>0</code> if this prototype has no
+    parameters. This offset, if non-zero, should be in the
+    <code>data</code> section, and the data there should be in the
+    format specified by <code>"type_list"</code> below. Additionally, there
+    should be no reference to the type <code>void</code> in the list.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>field_id_item</code></h2>
+<h4>appears in the <code>field_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>class_idx</td>
+  <td>ushort</td>
+  <td>index into the <code>type_ids</code> list for the definer of this
+    field. This must be a class type, and not an array or primitive type.
+  </td>
+</tr>
+<tr>
+  <td>type_idx</td>
+  <td>ushort</td>
+  <td>index into the <code>type_ids</code> list for the type of
+    this field
+  </td>
+</tr>
+<tr>
+  <td>name_idx</td>
+  <td>uint</td>
+  <td>index into the <code>string_ids</code> list for the name of this
+    field. The string must conform to the syntax for <i>MemberName</i>,
+    defined above.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>method_id_item</code></h2>
+<h4>appears in the <code>method_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>class_idx</td>
+  <td>ushort</td>
+  <td>index into the <code>type_ids</code> list for the definer of this
+    method. This must be a class or array type, and not a primitive type.
+  </td>
+</tr>
+<tr>
+  <td>proto_idx</td>
+  <td>ushort</td>
+  <td>index into the <code>proto_ids</code> list for the prototype of
+    this method
+  </td>
+</tr>
+<tr>
+  <td>name_idx</td>
+  <td>uint</td>
+  <td>index into the <code>string_ids</code> list for the name of this
+    method. The string must conform to the syntax for <i>MemberName</i>,
+    defined above.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>class_def_item</code></h2>
+<h4>appears in the <code>class_defs</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>class_idx</td>
+  <td>uint</td>
+  <td>index into the <code>type_ids</code> list for this class.
+    This must be a class type, and not an array or primitive type.
+  </td>
+</tr>
+<tr>
+  <td>access_flags</td>
+  <td>uint</td>
+  <td>access flags for the class (<code>public</code>, <code>final</code>,
+    etc.). See "<code>access_flags</code> Definitions" for details.
+  </td>
+</tr>
+<tr>
+  <td>superclass_idx</td>
+  <td>uint</td>
+  <td>index into the <code>type_ids</code> list for the superclass, or
+    the constant value <code>NO_INDEX</code> if this class has no
+    superclass (i.e., it is a root class such as <code>Object</code>).
+    If present, this must be a class type, and not an array or primitive type.
+  </td>
+</tr>
+<tr>
+  <td>interfaces_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the list of interfaces, or
+    <code>0</code> if there are none. This offset
+    should be in the <code>data</code> section, and the data
+    there should be in the format specified by
+    "<code>type_list</code>" below. Each of the elements of the list
+    must be a class type (not an array or primitive type), and there
+    must not be any duplicates.
+  </td>
+</tr>
+<tr>
+  <td>source_file_idx</td>
+  <td>uint</td>
+  <td>index into the <code>string_ids</code> list for the name of the
+    file containing the original source for (at least most of) this class,
+    or the special value <code>NO_INDEX</code> to represent a lack of
+    this information. The <code>debug_info_item</code> of any given method
+    may override this source file, but the expectation is that most classes
+    will only come from one source file.
+  </td>
+</tr>
+<tr>
+  <td>annotations_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the annotations structure
+    for this class, or <code>0</code> if there are no annotations on
+    this class. This offset, if non-zero, should be in the
+    <code>data</code> section, and the data there should be in
+    the format specified by "<code>annotations_directory_item</code>" below,
+    with all items referring to this class as the definer.
+  </td>
+</tr>
+<tr>
+  <td>class_data_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the associated
+    class data for this item, or <code>0</code> if there is no class
+    data for this class. (This may be the case, for example, if this class
+    is a marker interface.) The offset, if non-zero, should be in the
+    <code>data</code> section, and the data there should be in the
+    format specified by "<code>class_data_item</code>" below, with all
+    items referring to this class as the definer.
+  </td>
+</tr>
+<tr>
+  <td>static_values_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the list of initial
+    values for <code>static</code> fields, or <code>0</code> if there
+    are none (and all <code>static</code> fields are to be initialized with
+    <code>0</code> or <code>null</code>). This offset should be in the
+    <code>data</code> section, and the data there should be in the
+    format specified by "<code>encoded_array_item</code>" below. The size
+    of the array must be no larger than the number of <code>static</code>
+    fields declared by this class, and the elements correspond to the
+    <code>static</code> fields in the same order as declared in the
+    corresponding <code>field_list</code>. The type of each array
+    element must match the declared type of its corresponding field.
+    If there are fewer elements in the array than there are
+    <code>static</code> fields, then the leftover fields are initialized
+    with a type-appropriate <code>0</code> or <code>null</code>.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>class_data_item</code></h2>
+<h4>referenced from <code>class_def_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>static_fields_size</td>
+  <td>uleb128</td>
+  <td>the number of static fields defined in this item</td>
+</tr>
+<tr>
+  <td>instance_fields_size</td>
+  <td>uleb128</td>
+  <td>the number of instance fields defined in this item</td>
+</tr>
+<tr>
+  <td>direct_methods_size</td>
+  <td>uleb128</td>
+  <td>the number of direct methods defined in this item</td>
+</tr>
+<tr>
+  <td>virtual_methods_size</td>
+  <td>uleb128</td>
+  <td>the number of virtual methods defined in this item</td>
+</tr>
+<tr>
+  <td>static_fields</td>
+  <td>encoded_field[static_fields_size]</td>
+  <td>the defined static fields, represented as a sequence of
+    encoded elements. The fields must be sorted by
+    <code>field_idx</code> in increasing order.
+  </td>
+</tr>
+<tr>
+  <td>instance_fields</td>
+  <td>encoded_field[instance_fields_size]</td>
+  <td>the defined instance fields, represented as a sequence of
+    encoded elements. The fields must be sorted by
+    <code>field_idx</code> in increasing order.
+  </td>
+</tr>
+<tr>
+  <td>direct_methods</td>
+  <td>encoded_method[direct_methods_size]</td>
+  <td>the defined direct (any of <code>static</code>, <code>private</code>,
+    or constructor) methods, represented as a sequence of
+    encoded elements. The methods must be sorted by
+    <code>method_idx</code> in increasing order.
+  </td>
+</tr>
+<tr>
+  <td>virtual_methods</td>
+  <td>encoded_method[virtual_methods_size]</td>
+  <td>the defined virtual (none of <code>static</code>, <code>private</code>,
+    or constructor) methods, represented as a sequence of
+    encoded elements. This list should <i>not</i> include inherited
+    methods unless overridden by the class that this item represents. The
+    methods must be sorted by <code>method_idx</code> in increasing order.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> All elements' <code>field_id</code>s and
+<code>method_id</code>s must refer to the same defining class.</p>
+
+<h3><code>encoded_field</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>field_idx_diff</td>
+  <td>uleb128</td>
+  <td>index into the <code>field_ids</code> list for the identity of this
+    field (includes the name and descriptor), represented as a difference
+    from the index of previous element in the list. The index of the
+    first element in a list is represented directly.
+  </td>
+</tr>
+<tr>
+  <td>access_flags</td>
+  <td>uleb128</td>
+  <td>access flags for the field (<code>public</code>, <code>final</code>,
+    etc.). See "<code>access_flags</code> Definitions" for details.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_method</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>method_idx_diff</td>
+  <td>uleb128</td>
+  <td>index into the <code>method_ids</code> list for the identity of this
+    method (includes the name and descriptor), represented as a difference
+    from the index of previous element in the list. The index of the
+    first element in a list is represented directly.
+  </td>
+</tr>
+<tr>
+  <td>access_flags</td>
+  <td>uleb128</td>
+  <td>access flags for the method (<code>public</code>, <code>final</code>,
+    etc.). See "<code>access_flags</code> Definitions" for details.
+  </td>
+</tr>
+<tr>
+  <td>code_off</td>
+  <td>uleb128</td>
+  <td>offset from the start of the file to the code structure for this
+    method, or <code>0</code> if this method is either <code>abstract</code>
+    or <code>native</code>. The offset should be to a location in the
+    <code>data</code> section. The format of the data is specified by
+    "<code>code_item</code>" below.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>type_list</code></h2>
+<h4>referenced from <code>class_def_item</code> and
+<code>proto_id_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>size</td>
+  <td>uint</td>
+  <td>size of the list, in entries</td>
+</tr>
+<tr>
+  <td>list</td>
+  <td>type_item[size]</td>
+  <td>elements of the list</td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>type_item</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>type_idx</td>
+  <td>ushort</td>
+  <td>index into the <code>type_ids</code> list</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>code_item</code></h2>
+<h4>referenced from <code>method_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>registers_size</td>
+  <td>ushort</td>
+  <td>the number of registers used by this code</td>
+</tr>
+<tr>
+  <td>ins_size</td>
+  <td>ushort</td>
+  <td>the number of words of incoming arguments to the method that this
+    code is for</td>
+</tr>
+<tr>
+  <td>outs_size</td>
+  <td>ushort</td>
+  <td>the number of words of outgoing argument space required by this
+    code for method invocation
+  </td>
+</tr>
+<tr>
+  <td>tries_size</td>
+  <td>ushort</td>
+  <td>the number of <code>try_item</code>s for this instance. If non-zero,
+    then these appear as the <code>tries</code> array just after the
+    <code>insns</code> in this instance.
+  </td>
+</tr>
+<tr>
+  <td>debug_info_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the debug info (line numbers +
+    local variable info) sequence for this code, or <code>0</code> if
+    there simply is no information. The offset, if non-zero, should be
+    to a location in the <code>data</code> section. The format of
+    the data is specified by "<code>debug_info_item</code>" below.
+  </td>
+</tr>
+<tr>
+  <td>insns_size</td>
+  <td>uint</td>
+  <td>size of the instructions list, in 16-bit code units</td>
+</tr>
+<tr>
+  <td>insns</td>
+  <td>ushort[insns_size]</td>
+  <td>actual array of bytecode. The format of code in an <code>insns</code>
+    array is specified by the companion document
+    <a href="dalvik-bytecode.html">"Bytecode for the Dalvik VM"</a>. Note
+    that though this is defined as an array of <code>ushort</code>, there
+    are some internal structures that prefer four-byte alignment. Also,
+    if this happens to be in an endian-swapped file, then the swapping is
+    <i>only</i> done on individual <code>ushort</code>s and not on the
+    larger internal structures.
+  </td>
+</tr>
+<tr>
+  <td>padding</td>
+  <td>ushort <i>(optional)</i> = 0</td>
+  <td>two bytes of padding to make <code>tries</code> four-byte aligned.
+    This element is only present if <code>tries_size</code> is non-zero
+    and <code>insns_size</code> is odd.
+  </td>
+</tr>
+<tr>
+  <td>tries</td>
+  <td>try_item[tries_size] <i>(optional)</i></td>
+  <td>array indicating where in the code exceptions may be caught and
+    how to handle them. Elements of the array must be non-overlapping in
+    range and in order from low to high address. This element is only
+    present if <code>tries_size</code> is non-zero.
+  </td>
+</tr>
+<tr>
+  <td>handlers</td>
+  <td>encoded_catch_handler_list <i>(optional)</i></td>
+  <td>bytes representing a list of lists of catch types and associated
+    handler addresses. Each <code>try_item</code> has a byte-wise offset
+    into this structure. This element is only present if
+    <code>tries_size</code> is non-zero.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>try_item</code> Format </h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>start_addr</td>
+  <td>uint</td>
+  <td>start address of the block of code covered by this entry. The address
+    is a count of 16-bit code units to the start of the first covered
+    instruction.
+  </td>
+</tr>
+<tr>
+  <td>insn_count</td>
+  <td>ushort</td>
+  <td>number of 16-bit code units covered by this entry. The last code
+    unit covered (inclusive) is <code>start_addr + insn_count - 1</code>.
+  </td>
+</tr>
+<tr>
+  <td>handler_off</td>
+  <td>ushort</td>
+  <td>offset in bytes from the start of the associated encoded handler data
+    to the <code>catch_handler_item</code> for this entry
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_catch_handler_list</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>size</td>
+  <td>uleb128</td>
+  <td>size of this list, in entries</td>
+</tr>
+<tr>
+  <td>list</td>
+  <td>encoded_catch_handler[handlers_size]</td>
+  <td>actual list of handler lists, represented directly (not as offsets),
+    and concatenated sequentially</td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_catch_handler</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>size</td>
+  <td>sleb128</td>
+  <td>number of catch types in this list. If non-positive, then this is
+    the negative of the number of catch types, and the catches are followed
+    by a catch-all handler. For example: A <code>size</code> of <code>0</code>
+    means that there is a catch-all but no explicitly typed catches.
+    A <code>size</code> of <code>2</code> means that there are two explicitly
+    typed catches and no catch-all. And a <code>size</code> of <code>-1</code>
+    means that there is one typed catch along with a catch-all.
+  </td>
+</tr>
+<tr>
+  <td>handlers</td>
+  <td>encoded_type_addr_pair[abs(size)]</td>
+  <td>stream of <code>abs(size)</code> encoded items, one for each caught
+    type, in the order that the types should be tested.
+  </td>
+</tr>
+<tr>
+  <td>catch_all_addr</td>
+  <td>uleb128 <i>(optional)</i></td>
+  <td>bytecode address of the catch-all handler. This element is only
+    present if <code>size</code> is non-positive.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_type_addr_pair</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>type_idx</td>
+  <td>uleb128</td>
+  <td>index into the <code>type_ids</code> list for the type of the
+    exception to catch
+  </td>
+</tr>
+<tr>
+  <td>addr</td>
+  <td>uleb128</td>
+  <td>bytecode address of the associated exception handler</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>debug_info_item</code></h2>
+<h4>referenced from <code>code_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<p>Each <code>debug_info_item</code> defines a DWARF3-inspired byte-coded
+state machine that, when interpreted, emits the positions
+table and (potentially) the local variable information for a
+<code>code_item</code>. The sequence begins with a variable-length
+header (the length of which depends on the number of method
+parameters), is followed by the state machine bytecodes, and ends
+with an <code>DBG_END_SEQUENCE</code> byte.</p>
+
+<p>The state machine consists of five registers. The
+<code>address</code> register represents the instruction offset in the
+associated <code>insns_item</code> in 16-bit code units. The
+<code>address</code> register starts at <code>0</code> at the beginning of each
+<code>debug_info</code> sequence and may only monotonically increase.
+The <code>line</code> register represents what source line number
+should be associated with the next positions table entry emitted by
+the state machine. It is initialized in the sequence header, and may
+change in positive or negative directions but must never be less than
+<code>1</code>. The <code>source_file</code> register represents the
+source file that the line number entries refer to. It is initialized to
+the value of <code>source_file_idx</code> in <code>class_def_item</code>.
+The other two variables, <code>prologue_end</code> and
+<code>epilogue_begin</code>, are boolean flags (initialized to
+<code>false</code>) that indicate whether the next position emitted
+should be considered a method prologue or epilogue. The state machine
+must also track the name and type of the last local variable live in
+each register for the <code>DBG_RESTART_LOCAL</code> code.</p>
+
+<p>The header is as follows:</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>line_start</td>
+ <td>uleb128</td>
+ <td>the initial value for the state machine's <code>line</code> register.
+    Does not represent an actual positions entry.
+ </td>
+</tr>
+<tr>
+ <td>parameters_size</td>
+ <td>uleb128</td>
+ <td>the number of parameter names that are encoded. There should be
+   one per method parameter, excluding an instance method's <code>this</code>,
+   if any.
+ </td>
+</tr>
+<tr>
+ <td>parameter_names</td>
+ <td>uleb128p1[parameters_size]</td>
+ <td>string index of the method parameter name. An encoded value of
+   <code>NO_INDEX</code> indicates that no name
+   is available for the associated parameter. The type descriptor
+   and signature are implied from the method descriptor and signature.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<p>The byte code values are as follows:</p>
+
+<table class="debugByteCode">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Value</th>
+  <th>Format</th>
+  <th>Arguments</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>DBG_END_SEQUENCE</td>
+  <td>0x00</td>
+  <td></td>
+  <td><i>(none)</i></td>
+  <td>terminates a debug info sequence for a <code>code_item</code></td>
+</tr>
+<tr>
+  <td>DBG_ADVANCE_PC</td>
+  <td>0x01</td>
+  <td>uleb128&nbsp;addr_diff</td>
+  <td><code>addr_diff</code>: amount to add to address register</td>
+  <td>advances the address register without emitting a positions entry</td>
+</tr>
+<tr>
+  <td>DBG_ADVANCE_LINE</td>
+  <td>0x02</td>
+  <td>sleb128&nbsp;line_diff</td>
+  <td><code>line_diff</code>: amount to change line register by</td>
+  <td>advances the line register without emitting a positions entry</td>
+</tr>
+<tr>
+  <td>DBG_START_LOCAL</td>
+  <td>0x03</td>
+  <td>uleb128&nbsp;register_num<br/>
+    uleb128p1&nbsp;name_idx<br/>
+    uleb128p1&nbsp;type_idx
+  </td>
+  <td><code>register_num</code>: register that will contain local<br/>
+    <code>name_idx</code>: string index of the name<br/>
+    <code>type_idx</code>: type index of the type
+  </td>
+  <td>introduces a local variable at the current address. Either
+    <code>name_idx</code> or <code>type_idx</code> may be
+    <code>NO_INDEX</code> to indicate that that value is unknown.
+  </td>
+</tr>
+<tr>
+  <td>DBG_START_LOCAL_EXTENDED</td>
+  <td>0x04</td>
+  <td>uleb128&nbsp;register_num<br/>
+    uleb128p1&nbsp;name_idx<br/>
+    uleb128p1&nbsp;type_idx<br/>
+    uleb128p1&nbsp;sig_idx
+  </td>
+  <td><code>register_num</code>: register that will contain local<br/>
+    <code>name_idx</code>: string index of the name<br/>
+    <code>type_idx</code>: type index of the type<br/>
+    <code>sig_idx</code>: string index of the type signature
+  </td>
+  <td>introduces a local with a type signature at the current address.
+    Any of <code>name_idx</code>, <code>type_idx</code>, or
+    <code>sig_idx</code> may be <code>NO_INDEX</code>
+    to indicate that that value is unknown. (If <code>sig_idx</code> is
+    <code>-1</code>, though, the same data could be represented more
+    efficiently using the opcode <code>DBG_START_LOCAL</code>.)
+    <p><b>Note:</b> See the discussion under
+    "<code>dalvik.annotation.Signature</code>" below for caveats about
+    handling signatures.</p>
+  </td>
+</tr>
+<tr>
+  <td>DBG_END_LOCAL</td>
+  <td>0x05</td>
+  <td>uleb128&nbsp;register_num</td>
+  <td><code>register_num</code>: register that contained local</td>
+  <td>marks a currently-live local variable as out of scope at the current
+    address
+  </td>
+</tr>
+<tr>
+  <td>DBG_RESTART_LOCAL</td>
+  <td>0x06</td>
+  <td>uleb128&nbsp;register_num</td>
+  <td><code>register_num</code>: register to restart</td>
+  <td>re-introduces a local variable at the current address. The name
+    and type are the same as the last local that was live in the specified
+    register.
+  </td>
+</tr>
+<tr>
+  <td>DBG_SET_PROLOGUE_END</td>
+  <td>0x07</td>
+  <td></td>
+  <td><i>(none)</i></td>
+  <td>sets the <code>prologue_end</code> state machine register,
+    indicating that the next position entry that is added should be
+    considered the end of a method prologue (an appropriate place for
+    a method breakpoint). The <code>prologue_end</code> register is
+    cleared by any special (<code>&gt;= 0x0a</code>) opcode.
+  </td>
+</tr>
+<tr>
+  <td>DBG_SET_EPILOGUE_BEGIN</td>
+  <td>0x08</td>
+  <td></td>
+  <td><i>(none)</i></td>
+  <td>sets the <code>epilogue_begin</code> state machine register,
+    indicating that the next position entry that is added should be
+    considered the beginning of a method epilogue (an appropriate place
+    to suspend execution before method exit).
+    The <code>epilogue_begin</code> register is cleared by any special
+    (<code>&gt;= 0x0a</code>) opcode.
+  </td>
+</tr>
+<tr>
+  <td>DBG_SET_FILE</td>
+  <td>0x09</td>
+  <td>uleb128p1&nbsp;name_idx</td>
+  <td><code>name_idx</code>: string index of source file name;
+    <code>NO_INDEX</code> if unknown
+  </td>
+  <td>indicates that all subsequent line number entries make reference to this
+    source file name, instead of the default name specified in
+    <code>code_item</code>
+  </td>
+</tr>
+<tr>
+  <td><i>Special Opcodes</i></td>
+  <!-- When updating the range below, make sure to search for other
+  instances of 0x0a in this section. -->
+  <td>0x0a&hellip;0xff</td>
+  <td></td>
+  <td><i>(none)</i></td>
+  <td>advances the <code>line</code> and <code>address</code> registers,
+    emits a position entry, and clears <code>prologue_end</code> and
+    <code>epilogue_begin</code>. See below for description.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3>Special Opcodes</h3>
+
+<p>Opcodes with values between <code>0x0a</code> and <code>0xff</code>
+(inclusive) move both the <code>line</code> and <code>address</code>
+registers by a small amount and then emit a new position table entry.
+The formula for the increments are as follows:</p>
+
+<pre>
+DBG_FIRST_SPECIAL = 0x0a  // the smallest special opcode
+DBG_LINE_BASE   = -4      // the smallest line number increment
+DBG_LINE_RANGE  = 15      // the number of line increments represented
+
+adjusted_opcode = opcode - DBG_FIRST_SPECIAL
+
+line += DBG_LINE_BASE + (adjusted_opcode % DBG_LINE_RANGE)
+address += (adjusted_opcode / DBG_LINE_RANGE)
+</pre>
+
+<h2><code>annotations_directory_item</code></h2>
+<h4>referenced from <code>class_def_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>class_annotations_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the annotations made directly
+    on the class, or <code>0</code> if the class has no direct annotations.
+    The offset, if non-zero, should be to a location in the
+    <code>data</code> section. The format of the data is specified
+    by "<code>annotation_set_item</code>" below.
+  </td>
+</tr>
+<tr>
+  <td>fields_size</td>
+  <td>uint</td>
+  <td>count of fields annotated by this item</td>
+</tr>
+<tr>
+  <td>annotated_methods_size</td>
+  <td>uint</td>
+  <td>count of methods annotated by this item</td>
+</tr>
+<tr>
+  <td>annotated_parameters_size</td>
+  <td>uint</td>
+  <td>count of method parameter lists annotated by this item</td>
+</tr>
+<tr>
+  <td>field_annotations</td>
+  <td>field_annotation[fields_size] <i>(optional)</i></td>
+  <td>list of associated field annotations. The elements of the list must
+    be sorted in increasing order, by <code>field_idx</code>.
+  </td>
+</tr>
+<tr>
+  <td>method_annotations</td>
+  <td>method_annotation[methods_size] <i>(optional)</i></td>
+  <td>list of associated method annotations. The elements of the list must
+    be sorted in increasing order, by <code>method_idx</code>.
+  </td>
+</tr>
+<tr>
+  <td>parameter_annotations</td>
+  <td>parameter_annotation[parameters_size] <i>(optional)</i></td>
+  <td>list of associated method parameter annotations. The elements of the
+    list must be sorted in increasing order, by <code>method_idx</code>.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> All elements' <code>field_id</code>s and
+<code>method_id</code>s must refer to the same defining class.</p>
+
+<h3><code>field_annotation</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>field_idx</td>
+  <td>uint</td>
+  <td>index into the <code>field_ids</code> list for the identity of the
+    field being annotated
+  </td>
+</tr>
+<tr>
+  <td>annotations_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the list of annotations for
+    the field. The offset should be to a location in the <code>data</code>
+    section. The format of the data is specified by
+    "<code>annotation_set_item</code>" below.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>method_annotation</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>method_idx</td>
+  <td>uint</td>
+  <td>index into the <code>method_ids</code> list for the identity of the
+    method being annotated
+  </td>
+</tr>
+<tr>
+  <td>annotations_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the list of annotations for
+    the method. The offset should be to a location in the
+    <code>data</code> section. The format of the data is specified by
+    "<code>annotation_set_item</code>" below.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>parameter_annotation</code> Format</h2>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>method_idx</td>
+  <td>uint</td>
+  <td>index into the <code>method_ids</code> list for the identity of the
+    method whose parameters are being annotated
+  </td>
+</tr>
+<tr>
+  <td>annotations_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the list of annotations for
+    the method parameters. The offset should be to a location in the
+    <code>data</code> section. The format of the data is specified by
+    "<code>annotation_set_ref_list</code>" below.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>annotation_set_ref_list</code></h2>
+<h4>referenced from <code>parameter_annotations_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>size</td>
+  <td>uint</td>
+  <td>size of the list, in entries</td>
+</tr>
+<tr>
+  <td>list</td>
+  <td>annotation_set_ref_item[size]</td>
+  <td>elements of the list</td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>annotation_set_ref_item</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>annotations_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to the referenced annotation set
+    or <code>0</code> if there are no annotations for this element.
+    The offset, if non-zero, should be to a location in the <code>data</code>
+    section. The format of the data is specified by
+    "<code>annotation_set_item</code>" below.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>annotation_set_item</code></h2>
+<h4>referenced from <code>annotations_directory_item</code>,
+<code>field_annotations_item</code>,
+<code>method_annotations_item</code>, and
+<code>annotation_set_ref_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>size</td>
+  <td>uint</td>
+  <td>size of the set, in entries</td>
+</tr>
+<tr>
+  <td>entries</td>
+  <td>annotation_off_item[size]</td>
+  <td>elements of the set. The elements must be sorted in increasing order,
+    by <code>type_idx</code>.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>annotation_off_item</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>annotation_off</td>
+  <td>uint</td>
+  <td>offset from the start of the file to an annotation.
+    The offset should be to a location in the <code>data</code> section,
+    and the format of the data at that location is specified by
+    "<code>annotation_item</code>" below.
+  </td>
+</tr>
+</tbody>
+</table>
+
+
+<h2><code>annotation_item</code></h2>
+<h4>referenced from <code>annotation_set_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>visibility</td>
+  <td>ubyte</td>
+  <td>intended visibility of this annotation (see below)</td>
+</tr>
+<tr>
+  <td>annotation</td>
+  <td>encoded_annotation</td>
+  <td>encoded annotation contents, in the format described by
+    "<code>encoded_annotation</code> Format" under
+    "<code>encoded_value</code> Encoding" above.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h3>Visibility values</h3>
+
+<p>These are the options for the <code>visibility</code> field in an
+<code>annotation_item</code>:</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Value</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>VISIBILITY_BUILD</td>
+  <td>0x00</td>
+  <td>intended only to be visible at build time (e.g., during compilation
+    of other code)
+  </td>
+</tr>
+<tr>
+  <td>VISIBILITY_RUNTIME</td>
+  <td>0x01</td>
+  <td>intended to visible at runtime</td>
+</tr>
+<tr>
+  <td>VISIBILITY_SYSTEM</td>
+  <td>0x02</td>
+  <td>intended to visible at runtime, but only to the underlying system
+    (and not to regular user code)
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>encoded_array_item</code></h2>
+<h4>referenced from <code>class_def_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>value</td>
+  <td>encoded_array</td>
+  <td>bytes representing the encoded array value, in the format specified
+    by "<code>encoded_array</code> Format" under "<code>encoded_value</code>
+    Encoding" above.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h1>System Annotations</h1>
+
+<p>System annotations are used to represent various pieces of reflective
+information about classes (and methods and fields). This information is
+generally only accessed indirectly by client (non-system) code.</p>
+
+<p>System annotations are represented in <code>.dex</code> files as
+annotations with visibility set to <code>VISIBILITY_SYSTEM</code>.
+
+<h2><code>dalvik.annotation.AnnotationDefault</code></h2>
+<h4>appears on methods in annotation interfaces</h4>
+
+<p>An <code>AnnotationDefault</code> annotation is attached to each
+annotation interface which wishes to indicate default bindings.</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>value</td>
+  <td>Annotation</td>
+  <td>the default bindings for this annotation, represented as an annotation
+    of this type. The annotation need not include all names defined by the
+    annotation; missing names simply do not have defaults.
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.EnclosingClass</code></h2>
+<h4>appears on classes</h4>
+
+<p>An <code>EnclosingClass</code> annotation is attached to each class
+which is either defined as a member of another class, per se, or is
+anonymous but not defined within a method body (e.g., a synthetic
+inner class). Every class that has this annotation must also have an
+<code>InnerClass</code> annotation. Additionally, a class may not have
+both an <code>EnclosingClass</code> and an
+<code>EnclosingMethod</code> annotation.</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>value</td>
+  <td>Class</td>
+  <td>the class which most closely lexically scopes this class</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.EnclosingMethod</code></h2>
+<h4>appears on classes</h4>
+
+<p>An <code>EnclosingMethod</code> annotation is attached to each class
+which is defined inside a method body. Every class that has this
+annotation must also have an <code>InnerClass</code> annotation.
+Additionally, a class may not have both an <code>EnclosingClass</code>
+and an <code>EnclosingMethod</code> annotation.</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>value</td>
+  <td>Method</td>
+  <td>the method which most closely lexically scopes this class</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.InnerClass</code></h2>
+<h4>appears on classes</h4>
+
+<p>An <code>InnerClass</code> annotation is attached to each class
+which is defined in the lexical scope of another class's definition.
+Any class which has this annotation must also have <i>either</i> an
+<code>EnclosingClass</code> annotation <i>or</i> an
+<code>EnclosingMethod</code> annotation.</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>name</td>
+  <td>String</td>
+  <td>the originally declared simple name of this class (not including any
+    package prefix). If this class is anonymous, then the name is
+    <code>null</code>.
+  </td>
+</tr>
+<tr>
+  <td>accessFlags</td>
+  <td>int</td>
+  <td>the originally declared access flags of the class (which may differ
+    from the effective flags because of a mismatch between the execution
+    models of the source language and target virtual machine)
+  </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.MemberClasses</code></h2>
+<h4>appears on classes</h4>
+
+<p>A <code>MemberClasses</code> annotation is attached to each class
+which declares member classes. (A member class is a direct inner class
+that has a name.)</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>value</td>
+  <td>Class[]</td>
+  <td>array of the member classes</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.Signature</code></h2>
+<h4>appears on classes, fields, and methods</h4>
+
+<p>A <code>Signature</code> annotation is attached to each class,
+field, or method which is defined in terms of a more complicated type
+than is representable by a <code>type_id_item</code>. The
+<code>.dex</code> format does not define the format for signatures; it
+is merely meant to be able to represent whatever signatures a source
+language requires for successful implementation of that language's
+semantics. As such, signatures are not generally parsed (or verified)
+by virtual machine implementations. The signatures simply get handed
+off to higher-level APIs and tools (such as debuggers). Any use of a
+signature, therefore, should be written so as not to make any
+assumptions about only receiving valid signatures, explicitly guarding
+itself against the possibility of coming across a syntactically
+invalid signature.</p>
+
+<p>Because signature strings tend to have a lot of duplicated content,
+a <code>Signature</code> annotation is defined as an <i>array</i> of
+strings, where duplicated elements naturally refer to the same
+underlying data, and the signature is taken to be the concatenation of
+all the strings in the array. There are no rules about how to pull
+apart a signature into separate strings; that is entirely up to the
+tools that generate <code>.dex</code> files.</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>value</td>
+  <td>String[]</td>
+  <td>the signature of this class or member, as an array of strings that
+    is to be concatenated together</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.Throws</code></h2>
+<h4>appears on methods</h4>
+
+<p>A <code>Throws</code> annotation is attached to each method which is
+declared to throw one or more exception types.</p>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Name</th>
+  <th>Format</th>
+  <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>value</td>
+  <td>Class[]</td>
+  <td>the array of exception types thrown</td>
+</tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/docs/dexopt.html b/docs/dexopt.html
new file mode 100644
index 0000000..7f0b4bc
--- /dev/null
+++ b/docs/dexopt.html
@@ -0,0 +1,326 @@
+<html>
+<head>
+    <title>Dalvik Optimization and Verification</title>
+</head>
+
+<body>
+<h1>Dalvik Optimization and Verification With <i>dexopt</i></h1>
+
+<p>
+The Dalvik virtual machine was designed specifically for the Android
+mobile platform.  The target systems have little RAM, store data on slow
+internal flash memory, and generally have the performance characteristics
+of decade-old desktop systems.  They also run Linux, which provides
+virtual memory, processes and threads, and UID-based security mechanisms.
+<p>
+The features and limitations caused us to focus on certain goals:
+
+<ul>
+    <li>Class data, notably bytecode, must be shared between multiple
+    processes to minimize total system memory usage.
+    <li>The overhead in launching a new app must be minimized to keep
+    the device responsive.
+    <li>Storing class data in individual files results in a lot of
+    redundancy, especially with respect to strings.  To conserve disk
+    space we need to factor this out.
+    <li>Parsing class data fields adds unnecessary overhead during
+    class loading.  Accessing data values (e.g. integers and strings)
+    directly as C types is better.
+    <li>Bytecode verification is necessary, but slow, so we want to verify
+    as much as possible outside app execution.
+    <li>Bytecode optimization (quickened instructions, method pruning) is
+    important for speed and battery life.
+    <li>For security reasons, processes may not edit shared code.
+</ul>
+
+<p>
+The typical VM implementation uncompresses individual classes from a
+compressed archive and stores them on the heap.  This implies a separate
+copy of each class in every process, and slows application startup because
+the code must be uncompressed (or at least read off disk in many small
+pieces).  On the other hand, having the bytecode on the local heap makes
+it easy to rewrite instructions on first use, facilitating a number of
+different optimizations.
+<p>
+The goals led us to make some fundamental decisions:
+
+<ul>
+    <li>Multiple classes are aggregated into a single "DEX" file.
+    <li>DEX files are mapped read-only and shared between processes.
+    <li>Byte ordering and word alignment are adjusted to suit the local
+    system.
+    <li>Bytecode verification is mandatory for all classes, but we want
+    to "pre-verify" whatever we can.
+    <li>Optimizations that require rewriting bytecode must be done ahead
+    of time.
+</ul>
+
+<p>
+The consequences of these decisions are explained in the following sections.
+
+
+<h2>VM Operation</h2>
+
+<p>
+Application code is delivered to the system in a <code>.jar</code>
+or <code>.apk</code> file.  These are really just <code>.zip</code>
+archives with some meta-data files added.  The Dalvik DEX data file
+is always called <code>classes.dex</code>.
+<p>
+The bytecode cannot be memory-mapped and executed directly from the zip
+file, because the data is compressed and the start of the file is not
+guaranteed to be word-aligned.  These problems could be addressed by
+storing <code>classes.dex</code> without compression and padding out the zip
+file, but that would increase the size of the package sent across the
+data network.
+<p>
+We need to extract <code>classes.dex</code> from the zip archive before
+we can use it.  While we have the file available, we might as well perform
+some of the other actions (realignment, optimization, verification) described
+earlier.  This raises a new question however: who is responsible for doing
+this, and where do we keep the output?
+
+<h3>Preparation</h3>
+
+<p>
+There are at least three different ways to create a "prepared" DEX file,
+sometimes known as "ODEX" (for Optimized DEX):
+<ol>
+    <li>The VM does it "just in time".  The output goes into a special
+    <code>dalvik-cache</code> directory.  This works on the desktop and
+    engineering-only device builds where the permissions on the
+    <code>dalvik-cache</code> directory are not restricted.  On production
+    devices, this is not allowed.
+    <li>The system installer does it when an application is first added.
+    It has the privileges required to write to <code>dalvik-cache</code>.
+    <li>The build system does it ahead of time.  The relevant <code>jar</code>
+    / <code>apk</code> files are present, but the <code>classes.dex</code>
+    is stripped out.  The optimized DEX is stored next to the original
+    zip archive, not in <code>dalvik-cache</code>, and is part of the
+    system image.
+</ol>
+<p>
+The <code>dalvik-cache</code> directory is more accurately
+<code>$ANDROID_DATA/data/dalvik-cache</code>.  The files inside it have
+names derived from the full path of the source DEX.  On the device the
+directory is owned by <code>system</code> / <code>system</code>
+and has 0771 permissions, and the optimized DEX files stored there are
+owned by <code>system</code> and the
+application's group, with 0644 permissions.  DRM-locked applications will
+use 640 permissions to prevent other user applications from examining them.
+The bottom line is that you can read your own DEX file and those of most
+other applications, but you cannot create, modify, or remove them.
+<p>
+Preparation of the DEX file for the "just in time" and "system installer"
+approaches proceeds in three steps:
+<p>
+First, the dalvik-cache file is created.  This must be done in a process
+with appropriate privileges, so for the "system installer" case this is
+done within <code>installd</code>, which runs as root.
+<p>
+Second, the <code>classes.dex</code> entry is extracted from the the zip
+archive.  A small amount of space is left at the start of the file for
+the ODEX header.
+<p>
+Third, the file is memory-mapped for easy access and tweaked for use on
+the current system.  This includes byte-swapping and structure realigning,
+but no meaningful changes to the DEX file.  We also do some basic
+structure checks, such as ensuring that file offsets and data indices
+fall within valid ranges.
+<p>
+The build system uses a hairy process that involves starting the
+emulator, forcing just-in-time optimization of all relevant DEX files,
+and then extracting the results from <code>dalvik-cache</code>.  The
+reasons for doing this, rather than using a tool that runs on the desktop,
+will become more apparent when the optimizations are explained.
+<p>
+Once the code is byte-swapped and aligned, we're ready to go.  We append
+some pre-computed data, fill in the ODEX header at the start of the file,
+and start executing.  (The header is filled in last, so that we don't
+try to use a partial file.)  If we're interested in verification and
+optimization, however, we need to insert a step after the initial prep.
+
+<h3>dexopt</h3>
+
+<p>
+We want to verify and optimize all of the classes in the DEX file.  The
+easiest and safest way to do this is to load all of the classes into
+the VM and run through them.  Anything that fails to load is simply not
+verified or optimized.  Unfortunately, this can cause allocation of some
+resources that are difficult to release (e.g. loading of native shared
+libraries), so we don't want to do it in the same virtual machine that
+we're running applications in.
+<p>
+The solution is to invoke a program called <code>dexopt</code>, which
+is really just a back door into the VM.  It performs an abbreviated VM
+initialization, loads zero or more DEX files from the bootstrap class
+path, and then sets about verifying and optimizing whatever it can from
+the target DEX.  On completion, the process exits, freeing all resources.
+<p>
+It is possible for multiple VMs to want the same DEX file at the same
+time.  File locking is used to ensure that dexopt is only run once.
+
+
+<h2>Verification</h2>
+
+<p>
+The bytecode verification process involves scanning through the instructions
+in every method in every class in a DEX file.  The goal is to identify
+illegal instruction sequences so that we don't have to check for them at
+run time.  Many of the computations involved are also necessary for "exact"
+garbage collection.  See
+<a href="verifier.html">Dalvik Bytecode Verifier Notes</a> for more
+information.
+<p>
+For performance reasons, the optimizer (described in the next section)
+assumes that the verifier has run successfully, and makes some potentially
+unsafe assumptions.  By default, Dalvik insists upon verifying all classes,
+and only optimizes classes that have been verified.  If you want to
+disable the verifier, you can use command-line flags to do so.  See also
+<a href="embedded-vm-control.html"> Controlling the Embedded VM</a>
+for instructions on controlling these
+features within the Android application framework.
+<p>
+Reporting of verification failures is a tricky issue.  For example,
+calling a package-scope method on a class in a different package is
+illegal and will be caught by the verifier.  We don't necessarily want
+to report it during verification though -- we actually want to throw
+an exception when the method call is attempted.  Checking the access
+flags on every method call is expensive though.  The
+<a href="verifier.html">Dalvik Bytecode Verifier Notes</a> document
+addresses this issue.
+<p>
+Classes that have been verified successfully have a flag set in the ODEX.
+They will not be re-verified when loaded.  The Linux access permissions
+are expected to prevent tampering; if you can get around those, installing
+faulty bytecode is far from the easiest line of attack.  The ODEX file has
+a 32-bit checksum, but that's chiefly present as a quick check for
+corrupted data.
+
+
+<h2>Optimization</h2>
+
+<p>
+Virtual machine interpreters typically perform certain optimizations the
+first time a piece of code is used.  Constant pool references are replaced
+with pointers to internal data structures, operations that always succeed
+or always work a certain way are replaced with simpler forms.  Some of
+these require information only available at runtime, others can be inferred
+statically when certain assumptions are made.
+<p>
+The Dalvik optimizer does the following:
+<ul>
+    <li>For virtual method calls, replace the method index with a
+    vtable index.
+    <li>For instance field get/put, replace the field index with
+    a byte offset.  Also, merge the boolean / byte / char / short
+    variants into a single 32-bit form (less code in the interpreter
+    means more room in the CPU I-cache).
+    <li>Replace a handful of high-volume calls, like String.length(),
+    with "inline" replacements.  This skips the usual method call
+    overhead, directly switching from the interpreter to a native
+    implementation.
+    <li>Prune empty methods.  The simplest example is
+    <code>Object.&lt;init&gt;</code>, which does nothing, but must be
+    called whenever any object is allocated.  The instruction is
+    replaced with a new version that acts as a no-op unless a debugger
+    is attached.
+    <li>Append pre-computed data.  For example, the VM wants to have a
+    hash table for lookups on class name.  Instead of computing this
+    when the DEX file is loaded, we can compute it now, saving heap
+    space and computation time in every VM where the DEX is loaded.
+</ul>
+
+<p>
+All of the instruction modifications involve replacing the opcode with
+one not defined by the Dalvik specification.  This allows us to freely
+mix optimized and unoptimized instructions.  The set of optimized
+instructions, and their exact representation, is tied closely to the VM
+version.
+<p>
+Most of the optimizations are obvious "wins".  The use of raw indices
+and offsets not only allows us to execute more quickly, we can also
+skip the initial symbolic resolution.  Pre-computation eats up
+disk space, and so must be done in moderation.
+<p>
+There are a couple of potential sources of trouble with these
+optimizations.  First, vtable indices and byte offsets are subject to
+change if the VM is updated.  Second, if a superclass is in a different
+DEX, and that other DEX is updated, we need to ensure that our optimized
+indices and offsets are updated as well.  A similar but more subtle
+problem emerges when user-defined class loaders are employed: the class
+we actually call may not be the one we expected to call.
+<p>These problems are addressed with dependency lists and some limitations
+on what can be optimized.
+
+
+<h2>Dependencies and Limitations</h2>
+
+<p>
+The optimized DEX file includes a list of dependencies on other DEX files,
+plus the CRC-32 and modification date from the originating
+<code>classes.dex</code> zip file entry.  The dependency list includes the
+full path to the <code>dalvik-cache</code> file, and the file's SHA-1
+signature.  The timestamps of files on the device are unreliable and
+not used.  The dependency area also includes the VM version number.
+<p>
+An optimized DEX is dependent upon all of the DEX files in the bootstrap
+class path.  DEX files that are part of the bootstrap class path depend
+upon the DEX files that appeared earlier.  To ensure that nothing outside
+the dependent DEX files is available, <code>dexopt</code> only loads the
+bootstrap classes.  References to classes in other DEX files fail, which
+causes class loading and/or verification to fail, and classes with
+external dependencies are simply not optimized.
+<p>
+This means that splitting code out into many separate DEX files has a
+disadvantage: virtual method calls and instance field lookups between
+non-boot DEX files can't be optimized.  Because verification is pass/fail
+with class granularity, no method in a class that has any reliance on
+classes in external DEX files can be optimized.  This may be a bit
+heavy-handed, but it's the only way to guarantee that nothing breaks
+when individual pieces are updated.
+<p>
+Another negative consequence: any change to a bootstrap DEX will result
+in rejection of all optimized DEX files.  This makes it hard to keep
+system updates small.
+<p>
+Despite our caution, there is still a possibility that a class in a DEX
+file loaded by a user-defined class loader could ask for a bootstrap class
+(say, String) and be given a different class with the same name.  If a
+class in the DEX file being processed has the same name as a class in the
+bootstrap DEX files, the class will be flagged as ambiguous and references
+to it will not be resolved during verification / optimization.  The class
+linking code in the VM does additional checks to plug another hole;
+see the verbose description in the VM sources for details (vm/oo/Class.c).
+<p>
+If one of the dependencies is updated, we need to re-verify and
+re-optimize the DEX file.  If we can do a just-in-time <code>dexopt</code>
+invocation, this is easy.  If we have to rely on the installer daemon, or
+the DEX was shipped only in ODEX, then the VM has to reject the DEX.
+<p>
+The output of <code>dexopt</code> is byte-swapped and struct-aligned
+for the host, and contains indices and offsets that are highly VM-specific
+(both version-wise and platform-wise).  For this reason it's tricky to
+write a version of <code>dexopt</code> that runs on the desktop but
+generates output suitable for a particular device.  The safest way to
+invoke it is on the target device, or on an emulator for that device.
+
+
+<h2>Generated DEX</h2>
+
+<p>
+Some languages and frameworks rely on the ability to generate bytecode
+and execute it.  The rather heavy <code>dexopt</code> verification and
+optimization model doesn't work well with that.
+<p>
+We intend to support this in a future release, but the exact method is
+to be determined.  We may allow individual classes to be added or whole
+DEX files; may allow Java bytecode or Dalvik bytecode in instructions;
+may perform the usual set of optimizations, or use a separate interpreter
+that performs on-first-use optimizations directly on the bytecode (which
+won't be mapped read-only, since it's locally defined).
+
+<address>Copyright &copy; 2008 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/docs/embedded-vm-control.html b/docs/embedded-vm-control.html
new file mode 100644
index 0000000..ec2b694
--- /dev/null
+++ b/docs/embedded-vm-control.html
@@ -0,0 +1,310 @@
+<html>
+<head>
+    <title>Controlling the Embedded VM</title>
+    <link rel=stylesheet href="android.css">
+</head>
+
+<body>
+<h1>Controlling the Embedded VM</h1>
+
+<ul>
+    <li><a href="#introduction">Introduction</a> (read this first!)
+    <li><a href="#checkjni">Extended JNI Checks</a>
+    <li><a href="#assertions">Assertions</a>
+    <li><a href="#verifier">Bytecode Verification and Optimization</a>
+    <li><a href="#execmode">Execution Mode</a>
+    <li><a href="#dp">Deadlock Prediction</a>
+    <li><a href="#stackdump">Stack Dumps</a>
+    <li><a href="#dexcheck">DEX File Checksums</a>
+    <li><a href="#general">General Flags</a>
+</ul>
+
+<h2><a name="introduction">Introduction (read this first!)</a></h2>
+
+<p>The Dalvik VM supports a variety of command-line arguments
+(use <code>adb shell dalvikvm -help</code> to get a summary), but
+it's not possible to pass arbitrary arguments through the
+Android application runtime.  It is, however, possible to affect the
+VM behavior through certain system properties.
+
+<p>For all of the features described below, you would set the system property
+with <code>setprop</code>,
+issuing a shell command on the device like this:
+<pre>adb shell setprop &lt;name&gt; &lt;value&gt;</pre>
+
+<p><strong>The Android runtime must be restarted before the changes will take
+effect</strong> (<code>adb shell stop; adb shell start</code>).  This is because the
+settings are processed in the "zygote" process, which starts early and stays
+around "forever".
+
+<p>You may not be able to set <code>dalvik.*</code> properties or restart
+the system as an unprivileged user.  You can use
+<code>adb root</code> or run the <code>su</code> command from the device
+shell on "userdebug" builds to become root first.  When in doubt,
+<pre>adb shell getprop &lt;name&gt;</pre>
+will tell you if the <code>setprop</code> took.
+
+<p>If you don't want the property to evaporate when the device reboots,
+add a line to <code>/data/local.prop</code> that looks like:
+<pre>&lt;name&gt; = &lt;value&gt;</pre>
+
+<p>Such changes will survive reboots, but will be lost if the data
+partition is wiped.  (Hint: create a <code>local.prop</code>
+on your workstation, then <code>adb push local.prop /data</code>.  Or,
+use one-liners like
+<code>adb shell "echo name = value &gt;&gt; /data/local.prop"</code> -- note
+the quotes are important.)
+
+
+<h2><a name="checkjni">Extended JNI Checks</a></h2>
+
+<p>JNI, the Java Native Interface, provides a way for code written in the
+Java programming language
+interact with native (C/C++) code.  The extended JNI checks will cause
+the system to run more slowly, but they can spot a variety of nasty bugs
+before they have a chance to cause problems.
+
+<p>There are two system properties that affect this feature, which is
+enabled with the <code>-Xcheck:jni</code> command-line argument.  The
+first is <code>ro.kernel.android.checkjni</code>.  This is set by the
+Android build system for development builds.  (It may also be set by
+the Android emulator unless the <code>-nojni</code> flag is provided on the
+emulator command line.)  Because this is an "ro." property, the value cannot
+be changed once the device has started.
+
+<p>To allow toggling of the CheckJNI flag, a second
+property, <code>dalvik.vm.checkjni</code>, is also checked.  The value
+of this overrides the value from <code>ro.kernel.android.checkjni</code>.
+
+<p>If neither property is defined, or <code>dalvik.vm.checkjni</code>
+is set to <code>false</code>, the <code>-Xcheck:jni</code> flag is
+not passed in, and JNI checks will be disabled.
+
+<p>To enable JNI checking:
+<pre>adb shell setprop dalvik.vm.checkjni true</pre>
+
+<p>You can also pass JNI-checking options into the VM through a system
+property.  The value set for <code>dalvik.vm.jniopts</code> will
+be passed in as the <code>-Xjniopts</code> argument.  For example:
+<pre>adb shell setprop dalvik.vm.jniopts forcecopy</pre>
+
+<p>For more information about JNI checks, see
+<a href="jni-tips.html">JNI Tips</a>.
+
+
+<h2><a name="assertions">Assertions</a></h2>
+
+<p>Dalvik VM supports the Java programming language "assert" statement.
+By default they are off, but the <code>dalvik.vm.enableassertions</code>
+property provides a way to set the value for a <code>-ea</code> argument.
+
+<p>The argument behaves the same as it does in other desktop VMs.  You
+can provide a class name, a package name (followed by "..."), or the
+special value "all".
+
+<p>For example, this:
+<pre>adb shell setprop dalvik.vm.enableassertions all</pre>
+enables assertions in all non-system classes.
+
+<p>The system property is much more limited than the full command line.
+It is not possible to specify more than one <code>-ea</code> entry, and there
+is no way to specify a <code>-da</code> entry.  There is presently no
+equivalent for <code>-esa</code>/<code>-dsa</code>.
+
+
+<h2><a name="verifier">Bytecode Verification and Optimization</a></h2>
+
+<p>The system tries to pre-verify all classes in a DEX file to reduce
+class load overhead, and performs a series of optimizations to improve
+runtime performance.  Both of these are done by the <code>dexopt</code>
+command, either in the build system or by the installer.  On a development
+device, <code>dexopt</code> may be run the first time a DEX file is used
+and whenever it or one of its dependencies is updated ("just-in-time"
+optimization and verification).
+
+<p>There are two command-line flags that control the just-in-time
+verification and optimization,
+<code>-Xverify</code> and <code>-Xdexopt</code>.  The Android framework
+configures these based on the <code>dalvik.vm.dexopt-flags</code>
+property.
+
+<p>If you set:
+<pre>adb shell setprop dalvik.vm.dexopt-flags v=a,o=v</pre>
+then the framework will pass <code>-Xverify:all -Xdexopt:verified</code>
+to the VM.  This enables verification, and only optimizes classes that
+successfully verified.  This is the safest setting, and is the default.
+<p>You could also set <code>dalvik.vm.dexopt-flags</code> to <code>v=n</code>
+to have the framework pass <code>-Xverify:none -Xdexopt:verified</code>
+to disable verification.  (We could pass in <code>-Xdexopt:all</code> to
+allow optimization, but that wouldn't necessarily optimize more of the
+code, since classes that fail verification may well be skipped by the
+optimizer for the same reasons.)  Classes will not be verified by
+<code>dexopt</code>, and unverified code will be loaded and executed.
+
+<p>Enabling verification will make the <code>dexopt</code> command
+take significantly longer, because the verification process is fairly slow.
+Once the verified and optimized DEX files have been prepared, verification
+incurs no additional overhead except when loading classes that failed
+to pre-verify.
+
+<p>If your DEX files are processed with verification disabled, and you
+later turn the verifier on, application loading will be noticeably
+slower (perhaps 40% or more) as classes are verified on first use.
+
+<p>For best results you should force a re-dexopt of all DEX files when
+this property changes.  You can do this with:
+<pre>adb shell "rm /data/dalvik-cache/*"</pre>
+This removes the cached versions of the DEX files.  Remember to
+stop and restart the runtime (<code>adb shell stop; adb shell start</code>).
+
+<p>(Previous version of the runtime supported the boolean
+<code>dalvik.vm.verify-bytecode</code> property, but that has been
+superceded by <code>dalvik.vm.dexopt-flags</code>.)</p>
+
+
+<h2><a name="execmode">Execution Mode</a></h2>
+
+<p>The current implementation of the Dalvik VM includes three distinct
+interpreter cores.  These are referred to as "fast", "portable", and
+"debug".  The "fast" interpreter is optimized for the current
+platform, and might consist of hand-optimized assembly routines.  In
+constrast, the "portable" interpreter is written in C and expected to
+run on a broad range of platforms.  The "debug" interpreter is a variant
+of "portable" that includes support for profiling and single-stepping.
+
+<p>The VM may also support just-in-time compilation.  While not strictly
+a different interpreter, the JIT compiler may be enabled or disabled
+with the same flag.  (Check the output of <code>dalvikvm -help</code> to
+see if JIT compilation is enabled in your VM.)
+
+<p>The VM allows you to choose between "fast", "portable", and "jit" with an
+extended form of the <code>-Xint</code> argument.  The value of this
+argument can be set through the <code>dalvik.vm.execution-mode</code>
+system property.
+
+<p>To select the "portable" interpreter, you would use:
+<pre>adb shell setprop dalvik.vm.execution-mode int:portable</pre>
+If the property is not specified, the most appropriate interpreter
+will be selected automatically.  At some point this mechanism may allow
+selection of other modes, such as JIT compilation.
+
+<p>Not all platforms have an optimized implementation.  In such cases,
+the "fast" interpreter is generated as a series of C stubs, and the
+result will be slower than the
+"portable" version.  (When we have optimized versions for all popular
+architectures the naming convention will be more accurate.)
+
+<p>If profiling is enabled or a debugger is attached, the VM
+switches to the "debug" interpreter.  When profiling ends or the debugger
+disconnects, the original interpreter is resumed.  (The "debug" interpreter
+is substantially slower, something to keep in mind when evaluating
+profiling data.)
+
+<p>The JIT compiler can be disabled on a per-application basis by adding
+<code>android:vmSafeMode="true"</code> in the <code>application</code>
+tag in <code>AndroidManifest.xml</code>.  This can be useful if you
+suspect that JIT compilation is causing your application to behave
+incorrectly.
+
+
+<h2><a name="dp">Deadlock Prediction</a></h2>
+
+<p>If the VM is built with <code>WITH_DEADLOCK_PREDICTION</code>, the deadlock
+predictor can be enabled with the <code>-Xdeadlockpredict</code> argument.
+(The output from <code>dalvikvm -help</code> will tell you if the VM was
+built appropriately -- look for <code>deadlock_prediction</code> on the
+<code>Configured with:</code> line.)
+This feature tells the VM to keep track of the order in which object
+monitor locks are acquired.  If the program attempts to acquire a set
+of locks in a different order from what was seen earlier, the VM logs
+a warning and optionally throws an exception.
+
+<p>The command-line argument is set based on the
+<code>dalvik.vm.deadlock-predict</code> property.  Valid values are
+<code>off</code> to disable it (default), <code>warn</code> to log the
+problem but continue executing, <code>err</code> to cause a
+<code>dalvik.system.PotentialDeadlockError</code> to be thrown from the
+<code>monitor-enter</code> instruction, and <code>abort</code> to have
+the entire VM abort.
+
+<p>You will usually want to use:
+<pre>adb shell setprop dalvik.vm.deadlock-predict err</pre>
+unless you are keeping an eye on the logs as they scroll by.
+
+<p>Please note that this feature is deadlock prediction, not deadlock
+detection -- in the current implementation, the computations are performed
+after the lock is acquired (this simplifies the code, reducing the
+overhead added to every mutex operation).  You can spot a deadlock in a
+hung process by sending a <code>kill -3</code> and examining the stack
+trace written to the log.
+
+<p>This only takes monitors into account.  Native mutexes and other resources
+can also be the cause of deadlocks, but will not be detected by this.
+
+
+<h2><a name="stackdump">Stack Dumps</a></h2>
+
+<p>Like other desktop VMs, when the Dalvik VM receives a SIGQUIT
+(Ctrl-\ or <code>kill -3</code>), it dumps stack traces for all threads.
+By default this goes to the Android log, but it can also be written to a file.
+
+<p>The <code>dalvik.vm.stack-trace-file</code> property allows you to
+specify the name of the file where the thread stack traces will be written.
+The file will be created (world writable) if it doesn't exist, and the
+new information will be appended to the end of the file.  The filename
+is passed into the VM via the <code>-Xstacktracefile</code> argument.
+
+<p>For example:
+<pre>adb shell setprop dalvik.vm.stack-trace-file /tmp/stack-traces.txt</pre>
+
+<p>If the property is not defined, the VM will write the stack traces to
+the Android log when the signal arrives.
+
+
+<h2><a name="dexcheck">DEX File Checksums</a></h2>
+
+<p>For performance reasons, the checksum on "optimized" DEX files is
+ignored.  This is usually safe, because the files are generated on the
+device, and have access permissions that prevent modification.
+
+<p>If the storage on a device becomes unreliable, however, data corruption
+can occur.  This usually manifests itself as a repeatable virtual machine
+crash.  To speed diagnosis of such failures, the VM provides the
+<code>-Xcheckdexsum</code> argument.  When set, the checksums on all DEX
+files are verified before the contents are used.
+
+<p>The application framework will provide this argument during VM
+creation if the <code>dalvik.vm.check-dex-sum</code> property is enabled.
+
+<p>To enable extended DEX checksum verification:
+<pre>adb shell setprop dalvik.vm.check-dex-sum true</pre>
+
+<p>Incorrect checksums will prevent the DEX data from being used, and will
+cause errors to be written to the log file.  If a device has a history of
+problems it may be useful to add the property to
+<code>/data/local.prop</code>.
+
+<p>Note also that the
+<code>dexdump</code> tool always verifies DEX checksums, and can be used
+to check for corruption in a large set of files.
+
+
+<h2><a name="general">General Flags</a></h2>
+
+<p>In the "Honeycomb" release, a general mechanism for passing flags to
+the VM was introduced:
+
+<pre>adb shell setprop dalvik.vm.extra-opts "flag1 flag2 ... flagN"</pre>
+
+<p>The flags are separated by spaces.  You can specify as many as you want
+so long as they all fit within the system property value length limit
+(currently 92 characters).
+
+<p>The extra-opts flags will be added at the end of the command line,
+which means they will override earlier settings.  This can be used, for
+example, to experiment with different values for <code>-Xmx</code> even
+though the Android framework is setting it explicitly.
+
+<address>Copyright &copy; 2008 The Android Open Source Project</address>
+
+</body></html>
diff --git a/docs/heap-profiling.html b/docs/heap-profiling.html
new file mode 100644
index 0000000..9b9a58f
--- /dev/null
+++ b/docs/heap-profiling.html
@@ -0,0 +1,199 @@
+<html>
+<head>
+    <title>Dalvik Heap Profiling</title>
+</head>
+
+<body>
+<h1>Dalvik Heap Profiling</h1>
+
+<p>
+The Dalvik virtual machine can produce a complete dump of the contents
+of the virtual heap.  This is very useful for debugging memory usage
+and looking for memory leaks.  Getting at the information can be tricky,
+but has become easier in recent releases.
+</p><p>
+In what follows, the version number refers to the software release
+running on the phone.  To take advantage of the DDMS integration, you will
+also need a sufficiently recent version of DDMS.
+
+
+<h2>Getting the data</h2>
+<p>
+The first step is to cause the VM to dump its status, and then pull the hprof
+data off.  The exact manner for doing so has changed over time.
+</p><p>
+There is a <code>runhat</code> shell function, added by
+<code>build/envsetup.sh</code>, that partially automates these steps.  The
+function changes in each release to accommodate newer behavior, so you have
+to be careful that you don't use the wrong version.
+</p><p>
+
+<h3>Early releases (1.0/1.1)</h3>
+<p>
+You can only generate heap data on the emulator or a device with root
+access, because of the way the dump is initiated and where the output
+files go.
+</p><p>
+Get a command shell on the device:
+<blockquote><pre>
+$ adb shell
+</pre></blockquote>
+</p><p>
+You can verify that you're running as root with the <code>id</code> command.
+The response should look like <code>uid=0(root) gid=0(root)</code>.  If not,
+type <code>su</code> and try again.  If <code>su</code> fails, you're out
+of luck.
+
+</p><p>
+Next, ensure the target directory exists:
+<blockquote><pre>
+# mkdir /data/misc
+# chmod 777 /data/misc
+</pre></blockquote>
+
+</p><p>
+Use <code>ps</code> or DDMS to determine the process ID of your application,
+then send a <code>SIGUSR1</code> to the target process:
+
+<blockquote><pre>
+# kill -10 &lt;pid&gt;
+</pre></blockquote>
+
+</p><p>
+The signal causes a GC, followed by the heap dump (to be completely
+accurate, they actually happen concurrently, but the results in the heap
+dump reflect the post-GC state).  This can take a couple of seconds,
+so you have to watch for the GC log message to know when it's complete.
+</p><p>
+Next:
+
+<blockquote><pre>
+# ls /data/misc/heap-dump*
+# exit
+</pre></blockquote>
+
+</p><p>
+Use <code>ls</code> to check the file names, then <code>exit</code> to quit
+the device command shell.
+
+</p><p>
+You should see two output files, named
+<code>/data/misc/heap-dump-BLAH-BLAH.hprof</code> and
+<code>.hprof-head</code>, where BLAH is a runtime-generated value
+that ensures the filename is unique.  Pull them off of the device and
+remove the device-side copy:
+
+<blockquote><pre>
+$ adb pull /data/misc/heap-dump-BLAH-BLAH.hprof tail.hprof
+$ adb pull /data/misc/heap-dump-BLAH-BLAH.hprof-head head.hprof
+$ adb shell rm /data/misc/heap-dump-BLAH-BLAH.hprof /data/misc/heap-dump-BLAH-BLAH.hprof-head
+</pre></blockquote>
+
+</p><p>
+Merge them together and remove the intermediates:
+
+<blockquote><pre>
+$ cat head.hprof tail.hprof &gt; dump.hprof
+$ rm head.hprof tail.hprof
+</pre></blockquote>
+
+</p><p>
+You now have the hprof dump in <code>dump.hprof</code>.
+</p><p>
+
+
+<h3>Android 1.5 ("Cupcake")</h3>
+<p>
+Some steps were taken to make this simpler.  Notably, the two output
+files are now combined for you, and a new API call was added that allows
+a program to write the dump at will to a specific file.  If you're not
+using the API call, you still need to be on an emulator or running as root.
+(For some builds, you can use <code>adb root</code> to restart the adb
+daemon as root.)
+</p><p>
+The basic procedure is the same as for 1.0/1.1, but only one file will
+appear in <code>/data/misc</code> (no <code>-head</code>), and upon
+completion you will see a log message that says "hprof: heap dump completed".
+It looks like this in the log:
+
+<blockquote><pre>
+I/dalvikvm(  289): threadid=7: reacting to signal 10
+I/dalvikvm(  289): SIGUSR1 forcing GC and HPROF dump
+I/dalvikvm(  289): hprof: dumping VM heap to "/data/misc/heap-dump-tm1240861355-pid289.hprof-hptemp".
+I/dalvikvm(  289): hprof: dumping heap strings to "/data/misc/heap-dump-tm1240861355-pid289.hprof".
+I/dalvikvm(  289): hprof: heap dump completed, temp file removed
+</pre></blockquote>
+
+</p><p>
+Summary: as above, use <code>mkdir</code> and <code>chmod</code>
+to ensure the directory exists and is writable by your application.
+Send the <code>SIGUSR1</code> or use the API call to initiate a dump.
+Use <code>adb pull &lt;dump-file&gt;</code> and <code>adb shell rm
+&lt;dump-file&gt;</code> to retrieve the file and remove it from the
+device.  The concatenation step is not needed.
+
+</p><p>
+The new API is in the <code>android.os.Debug</code> class:
+<blockquote><pre>
+public static void dumpHprofData(String fileName) throws IOException
+</pre></blockquote>
+When called, the VM will go through the same series of steps (GC and
+generate a .hprof file), but the output will be written to a file of
+your choice, e.g. <code>/sdcard/myapp.hprof</code>.  Because you're
+initiating the action from within the app, and can write the file to
+removable storage or the app's private data area, you can do this on a
+device without root access.
+
+
+<h3>Android 1.6 ("Donut")</h3>
+<p>
+No real change to the way profiling works.
+However, 1.6 introduced the <code>WRITE_EXTERNAL_STORAGE</code>
+permission, which is required to write data to the SD card.  If you're
+accustomed to writing profile data to <code>/sdcard</code>, you will
+need to enable the permission in your application's manifest.
+</p>
+
+
+<h3>Android 2.0 ("Eclair")</h3>
+<p>
+In 2.0, features were added that allow DDMS to request a heap dump on
+demand, and automatically pull the result across.  Select your application
+and click the "dump HPROF file" button in the top left.  This always
+writes files to the SD card, so
+you must have a card inserted and the permission enabled in your application.
+</p>
+
+
+<h3>Android 2.2 ("Froyo")</h3>
+<p>
+DDMS heap dump requests are now streamed directly out of the VM, removing
+the external storage requirement.
+</p>
+
+<h3>Android 2.3 ("Gingerbread")</h3>
+<p>
+The <code>kill -10</code> (<code>SIGUSR1</code>) method of generating heap
+dumps has been removed from the VM.
+</p>
+
+<h2>Examining the data</h2>
+<p>
+The data file format was augmented slightly from the common hprof format,
+and due to licensing restrictions the modified <code>hat</code> tool cannot
+be distributed.  A conversion tool, <code>hprof-conv</code>, can be used
+to strip the Android-specific portions from the output.  This tool was
+first included in 1.5, but will work with older versions of Android.
+</p><p>
+The converted output should work with any hprof data analyzer, including
+<code>jhat</code>, which is available for free in the Sun JDK, and
+Eclipse MAT.
+
+<!-- say something about how to track down common problems, interesting
+     things to look for, ...? -->
+
+</p><p>
+<address>Copyright &copy; 2009 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/docs/hello-world.html b/docs/hello-world.html
new file mode 100644
index 0000000..7491a28
--- /dev/null
+++ b/docs/hello-world.html
@@ -0,0 +1,216 @@
+<html>
+<head>
+    <title>Basic Dalvik VM Invocation</title>
+</head>
+
+<body>
+<h1>Basic Dalvik VM Invocation</h1>
+
+<p>
+On an Android device, the Dalvik virtual machine usually executes embedded
+in the Android application framework.  It's also possible to run it directly,
+just as you would a virtual machine on your desktop system.
+</p><p>
+After compiling your Java language sources, convert and combine the .class
+files into a DEX file, and push that to the device.  Here's a simple example:
+
+</p><p><code>
+% <font color="green">echo 'class Foo {'\</font><br>
+&gt; <font color="green">'public static void main(String[] args) {'\</font><br>
+&gt; <font color="green">'System.out.println("Hello, world"); }}' &gt; Foo.java</font><br>
+% <font color="green">javac Foo.java</font><br>
+% <font color="green">dx --dex --output=foo.jar Foo.class</font><br>
+% <font color="green">adb push foo.jar /sdcard</font><br>
+% <font color="green">adb shell dalvikvm -cp /sdcard/foo.jar Foo</font><br>
+Hello, world
+</code>
+</p><p>
+The <code>-cp</code> option sets the classpath.  The initial directory
+for <code>adb shell</code> may not be what you expect it to be, so it's
+usually best to specify absolute pathnames.
+
+</p><p>
+The <code>dx</code> command accepts lists of individual class files,
+directories, or Jar archives.  When the <code>--output</code> filename
+ends with <code>.jar</code>, <code>.zip</code>, or <code>.apk</code>,
+a file called <code>classes.dex</code> is created and stored inside the
+archive.
+</p><p>
+Run <code>adb shell dalvikvm -help</code> to see a list of command-line
+options.
+</p><p>
+
+
+
+<h2>Using a debugger</h2>
+
+<p>
+You can debug stand-alone applications with any JDWP-compliant debugger.
+There are two basic approaches.
+</p><p>
+The first way is to connect directly through TCP.  Add, to the "dalvikvm"
+invocation line above, an argument like:
+</p><p>
+<code>&nbsp;&nbsp;-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=y</code>
+</p><p>
+This tells the VM to wait for a debugger to connect to it on TCP port 8000.
+You need to tell adb to forward local port 8000 to device port 8000:
+</p><p>
+<code>% <font color="green">adb forward tcp:8000 tcp:8000</font></code>
+</p><p>
+and then connect to it with your favorite debugger (using <code>jdb</code>
+as an example here):
+</p><p>
+<code>% <font color="green">jdb -attach localhost:8000</font></code>
+</p><p>
+When the debugger attaches, the VM will be in a suspended state.  You can
+set breakpoints and then tell it to continue.
+
+
+</p><p>
+You can also connect through DDMS, like you would for an Android application.
+Add, to the "dalvikvm" command line:
+</p><p>
+<code>&nbsp;&nbsp;-agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y</code>
+</p><p>
+Note the <code>transport</code> has changed, and you no longer need to
+specify a TCP port number.  When your application starts, it will appear
+in DDMS, with "?" as the application name.  Select it in DDMS, and connect
+to it as usual, e.g.:
+</p><p>
+<code>% <font color="green">jdb -attach localhost:8700</font></code>
+</p><p>
+Because command-line applications don't include the client-side
+DDM setup, features like thread monitoring and allocation tracking will not
+be available in DDMS.  It's strictly a debugger pass-through in this mode.
+</p><p>
+See <a href="debugger.html">Dalvik Debugger Support</a> for more information
+about using debuggers with Dalvik.
+
+
+
+<h2>Working with the desktop build</h2>
+
+<!-- largely lifted from
+http://groups.google.com/group/android-porting/browse_thread/thread/ab553116dbc960da/29167c58b3b49051#29167c58b3b49051
+-->
+
+<p>
+The Dalvik VM can also be used directly on the desktop.  This is somewhat
+more complicated however, because you won't have certain things set up in
+your environment, and several native code libraries are required to support
+the core Dalvik libs.
+</p><p>
+Start with:
+
+<pre>
+  . build/envsetup.sh
+  lunch sim-eng
+</pre>
+
+You should see something like:
+
+<pre>
+  ============================================
+  TARGET_PRODUCT=sim
+  TARGET_BUILD_VARIANT=eng
+  TARGET_SIMULATOR=true
+  TARGET_BUILD_TYPE=debug
+  TARGET_ARCH=x86
+  HOST_ARCH=x86
+  HOST_OS=linux
+  HOST_BUILD_TYPE=release
+  BUILD_ID=
+  ============================================
+</pre>
+
+</p></p>
+This configures you to build for the desktop, linking against glibc.
+This mode is NOT recommended for anything but experimental use.  It
+may go away in the future.
+</p></p>
+You may see <code>TARGET_BUILD_TYPE=release</code> or <code>=debug</code>
+or possibly nothing there at all.  You may want to replace the
+<code>lunch</code> command with
+<code>choosecombo Simulator debug sim eng</code>.
+</p></p>
+Build the world (add a <code>-j4</code> if you have multiple cores):
+
+<pre>
+  make
+</pre>
+
+</p></p>
+When that completes, you have a working dalvikm on your desktop
+machine:
+
+<pre>
+  % dalvikvm
+  E/dalvikvm(19521): ERROR: must specify non-'.' bootclasspath
+  W/dalvikvm(19521): JNI_CreateJavaVM failed
+  Dalvik VM init failed (check log file)
+</pre>
+
+</p></p>
+To actually do something, you need to specify the bootstrap class path
+and give it a place to put DEX data that it uncompresses from jar
+files.  You can do that with a script like this:
+
+<blockquote><pre>
+#!/bin/sh
+
+# base directory, at top of source tree; replace with absolute path
+base=`pwd`
+
+# configure root dir of interesting stuff
+root=$base/out/debug/host/linux-x86/product/sim/system
+export ANDROID_ROOT=$root
+
+# configure bootclasspath
+bootpath=$root/framework
+export BOOTCLASSPATH=$bootpath/core.jar:$bootpath/ext.jar:$bootpath/framework.jar:$bootpath/android.policy.jar:$bootpath/services.jar
+
+# this is where we create the dalvik-cache directory; make sure it exists
+export ANDROID_DATA=/tmp/dalvik_$USER
+mkdir -p $ANDROID_DATA/dalvik-cache
+
+exec dalvikvm $@
+</pre></blockquote>
+
+</p></p>
+The preparation with <code>dx</code> is the same as before:
+
+<pre>
+  % cat &gt; Foo.java
+  class Foo { public static void main(String[] args) {
+    System.out.println("Hello, world");
+  } }
+  (ctrl-D)
+  % javac Foo.java
+  % dx --dex --output=foo.jar Foo.class
+  % ./rund -cp foo.jar Foo
+  Hello, world
+</pre>
+
+As above, you can get some info about valid arguments like this:
+
+<pre>
+  % ./rund -help
+</pre>
+
+</p></p>
+This also shows what options the VM was configured with.  The sim "debug"
+build has all sorts of additional assertions and checks enabled,
+which slows the VM down, but since this is just for experiments it
+doesn't matter.
+
+</p></p>
+All of the above applies to x86 Linux.  Anything else will likely
+require a porting effort.  If libffi supports your system, the amount of
+work required should be minor.
+
+</p></p>
+<address>Copyright &copy; 2009 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/docs/instruction-formats.css b/docs/instruction-formats.css
new file mode 100644
index 0000000..a2dc42f
--- /dev/null
+++ b/docs/instruction-formats.css
@@ -0,0 +1,129 @@
+h1 {
+    font-family: serif;
+    color: #222266;
+}
+
+h2 {
+    font-family: serif;
+    border-top-style: solid;
+    border-top-width: 2px;
+    border-color: #ccccdd;
+    padding-top: 12px;
+    margin-top: 48px;
+    margin-bottom: 2px;
+    color: #222266;
+}
+
+h3 {
+    font-family: serif;
+    color: #222266;
+}
+
+@media print {
+    table {
+        font-size: 8pt;
+    }
+}
+
+@media screen {
+    table {
+        font-size: 10pt;
+    }
+}
+
+table th {
+    font-family: sans-serif;
+    background: #aaaaff;
+}
+
+table {
+    border-collapse: collapse;
+}
+
+table td {
+    font-family: sans-serif;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-width: 1px;
+    border-color: #aaaaff;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    padding-left: 2px;
+    padding-right: 2px;
+    background: #eeeeff;
+}
+
+
+/* the mnemonic guide */
+
+table.letters {
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table.letters td:first-child {
+    font-family: monospace;
+    width: 10%;
+    text-align: center;
+}
+
+table.letters td:first-child + td {
+    width: 10%;
+    text-align: center;
+}
+
+table.letters td:first-child + td + td {
+    width: 80%;
+}
+
+
+/* the formats, per se */
+
+table.format {
+    background: #aaaaaa;
+    border-collapse: collapse;
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table.format td {
+    font-family: monospace;
+}
+
+table.format td + td i {
+    font-family: sans-serif;
+}
+
+table.format td sub {
+    font-family: sans-serif;
+}
+
+table.format td sub {
+    font-family: sans-serif;
+    font-style: italic;
+    font-size: 70%
+}
+
+table.format th:first-child {
+    width: 28%;
+}
+
+table.format th:first-child + th {
+    width: 5%;
+}
+
+table.format th:first-child + th + th {
+    width: 45%;
+}
+
+table.format th:first-child + th + th + th {
+    width: 22%;
+}
+
+table.format p {
+    margin-bottom: 0pt;
+}
\ No newline at end of file
diff --git a/docs/instruction-formats.html b/docs/instruction-formats.html
new file mode 100644
index 0000000..d7bf690
--- /dev/null
+++ b/docs/instruction-formats.html
@@ -0,0 +1,430 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>Dalvik VM Instruction Formats</title>
+<link rel=stylesheet href="instruction-formats.css">
+</head>
+
+<body>
+
+<h1>Dalvik VM Instruction Formats</h1>
+<p>Copyright &copy; 2007 The Android Open Source Project
+
+<h2>Introduction and Overview</h2>
+
+<p>This document lists the instruction formats used by Dalvik bytecode
+and is meant to be used in conjunction with the
+<a href="dalvik-bytecode.html">bytecode reference document</a>.</p>
+
+<h3>Bitwise descriptions</h3>
+
+<p>The first column in the format table lists the bitwise layout of
+the format. It consists of one or more space-separated "words" each of
+which describes a 16-bit code unit. Each character in a word
+represents four bits, read from high bits to low, with vertical bars
+("<code>|</code>") interspersed to aid in reading. Uppercase letters
+in sequence from "<code>A</code>" are used to indicate fields within
+the format (which then get defined further by the syntax column). The term
+"<code>op</code>" is used to indicate the position of the eight-bit
+opcode within the format. A slashed zero ("<code>&Oslash;</code>") is
+used to indicate that all bits should be zero in the indicated
+position.</p>
+
+<p>For example, the format "<code>B|A|<i>op</i> CCCC</code>" indicates
+that the format consists of two 16-bit code units. The first word
+consists of the opcode in the low eight bits and a pair of four-bit
+values in the high eight bits; and the second word consists of a single
+16-bit value.</p>
+
+<h3>Format IDs</h3>
+
+<p>The second column in the format table indicates the short identifier
+for the format, which is used in other documents and in code to identify
+the format.</p>
+
+<p>Format IDs consist of three characters, two digits followed by a
+letter. The first digit indicates the number of 16-bit code units in the
+format. The second digit indicates the maximum number of registers that the
+format contains (maximum, since some formats can accomodate a variable
+number of registers), with the special designation "<code>r</code>" indicating
+that a range of registers is encoded. The final letter semi-mnemonically
+indicates the type of any extra data encoded by the format. For example,
+format "<code>21t</code>" is of length two, contains one register reference,
+and additionally contains a branch target.</p>
+
+<p>Suggested static linking formats have an additional "<code>s</code>" suffix,
+making them four characters total.</p>
+
+<p>The full list of typecode letters are as follows. Note that some
+forms have different sizes, depending on the format:</p>
+
+<table class="letters">
+<thead>
+<tr>
+  <th>Mnemonic</th>
+  <th>Bit Sizes</th>
+  <th>Meaning</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>b</td>
+  <td>8</td>
+  <td>immediate signed <b>b</b>yte</td>
+</tr>
+<tr>
+  <td>c</td>
+  <td>16, 32</td>
+  <td><b>c</b>onstant pool index</td>
+</tr>
+<tr>
+  <td>f</td>
+  <td>16</td>
+  <td>inter<b>f</b>ace constants (only used in statically linked formats)
+  </td>
+</tr>
+<tr>
+  <td>h</td>
+  <td>16</td>
+  <td>immediate signed <b>h</b>at (high-order bits of a 32- or 64-bit
+    value; low-order bits are all <code>0</code>)
+  </td>
+</tr>
+<tr>
+  <td>i</td>
+  <td>32</td>
+  <td>immediate signed <b>i</b>nt, or 32-bit float</td>
+</tr>
+<tr>
+  <td>l</td>
+  <td>64</td>
+  <td>immediate signed <b>l</b>ong, or 64-bit double</td>
+</tr>
+<tr>
+  <td>m</td>
+  <td>16</td>
+  <td><b>m</b>ethod constants (only used in statically linked formats)</td>
+</tr>
+<tr>
+  <td>n</td>
+  <td>4</td>
+  <td>immediate signed <b>n</b>ibble</td>
+</tr>
+<tr>
+  <td>s</td>
+  <td>16</td>
+  <td>immediate signed <b>s</b>hort</td>
+</tr>
+<tr>
+  <td>t</td>
+  <td>8, 16, 32</td>
+  <td>branch <b>t</b>arget</td>
+</tr>
+<tr>
+  <td>x</td>
+  <td>0</td>
+  <td>no additional data</td>
+</tr>
+</tbody>
+</table>
+
+<h3>Syntax</h3>
+
+<p>The third column of the format table indicates the human-oriented
+syntax for instructions which use the indicated format. Each instruction
+starts with the named opcode and is optionally followed by one or
+more arguments, themselves separated with commas.</p>
+
+<p>Wherever an argument refers to a field from the first column, the
+letter for that field is indicated in the syntax, repeated once for
+each four bits of the field. For example, an eight-bit field labeled
+"<code>BB</code>" in the first column would also be labeled
+"<code>BB</code>" in the syntax column.</p>
+
+<p>Arguments which name a register have the form "<code>v<i>X</i></code>".
+The prefix "<code>v</code>" was chosen instead of the more common
+"<code>r</code>" exactly to avoid conflicting with (non-virtual) architectures
+on which a Dalvik virtual machine might be implemented which themselves
+use the prefix "<code>r</code>" for their registers. (That is, this
+decision makes it possible to talk about both virtual and real registers
+together without the need for circumlocution.)</p>
+
+<p>Arguments which indicate a literal value have the form
+"<code>#+<i>X</i></code>". Some formats indicate literals that only
+have non-zero bits in their high-order bits; for these, the zeroes
+are represented explicitly in the syntax, even though they do not
+appear in the bitwise representation.</p>
+
+<p>Arguments which indicate a relative instruction address offset have the
+form "<code>+<i>X</i></code>".</p>
+
+<p>Arguments which indicate a literal constant pool index have the form
+"<code><i>kind</i>@<i>X</i></code>", where "<code><i>kind</i></code>"
+indicates which constant pool is being referred to. Each opcode that
+uses such a format explicitly allows only one kind of constant; see
+the opcode reference to figure out the correspondence. The four
+kinds of constant pool are "<code>string</code>" (string pool index),
+"<code>type</code>" (type pool index), "<code>field</code>" (field
+pool index), and "<code>meth</code>" (method pool index).</p>
+
+<p>Similar to the representation of constant pool indices, there are
+also suggested (optional) forms that indicate prelinked offsets or
+indices. These prelinked values include "<code>vtaboff</code>"
+(vtable offset), "<code>fieldoff</code>" (field offset), and
+"<code>iface</code>" (interface pool index).</p>
+
+<p>In the cases where a format value isn't explictly part of the syntax
+but instead picks a variant, each variant is listed with the prefix
+"<code>[<i>X</i>=<i>N</i>]</code>" (e.g., "<code>[B=2]</code>") to indicate
+the correspondence.</p>
+
+<h2>The Formats</h2>
+
+<table class="format">
+<thead>
+<tr>
+  <th>Format</th>
+  <th>ID</th>
+  <th>Syntax</th>
+  <th>Notable Opcodes Covered</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>&Oslash;&Oslash;|<i>op</i></td>
+  <td>10x</td>
+  <td><i><code>op</code></i></td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td rowspan="2">B|A|<i>op</i></td>
+  <td>12x</td>
+  <td><i><code>op</code></i> vA, vB</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>11n</td>
+  <td><i><code>op</code></i> vA, #+B</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td rowspan="2">AA|<i>op</i></td>
+  <td>11x</td>
+  <td><i><code>op</code></i> vAA</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>10t</td>
+  <td><i><code>op</code></i> +AA</td>
+  <td>goto</td>
+</tr>
+<tr>
+  <td>&Oslash;&Oslash;|<i>op</i> AAAA</td></td>
+  <td>20t</td>
+  <td><i><code>op</code></i> +AAAA</td>
+  <td>goto/16</td>
+</tr>
+<tr>
+  <td rowspan="5">AA|<i>op</i> BBBB</td>
+  <td>22x</td>
+  <td><i><code>op</code></i> vAA, vBBBB</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>21t</td>
+  <td><i><code>op</code></i> vAA, +BBBB</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>21s</td>
+  <td><i><code>op</code></i> vAA, #+BBBB</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>21h</td>
+  <td><i><code>op</code></i> vAA, #+BBBB0000<br/>
+    <i><code>op</code></i> vAA, #+BBBB000000000000
+  </td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>21c</td>
+  <td><i><code>op</code></i> vAA, type@BBBB<br/>
+    <i><code>op</code></i> vAA, field@BBBB<br/>
+    <i><code>op</code></i> vAA, string@BBBB
+  </td>
+  <td>check-cast<br/>
+    const-class<br/>
+    const-string
+  </td>
+</tr>
+<tr>
+  <td rowspan="2">AA|<i>op</i> CC|BB</td>
+  <td>23x</td>
+  <td><i><code>op</code></i> vAA, vBB, vCC</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>22b</td>
+  <td><i><code>op</code></i> vAA, vBB, #+CC</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td rowspan="4">B|A|<i>op</i> CCCC</td>
+  <td>22t</td>
+  <td><i><code>op</code></i> vA, vB, +CCCC</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>22s</td>
+  <td><i><code>op</code></i> vA, vB, #+CCCC</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>22c</td>
+  <td><i><code>op</code></i> vA, vB, type@CCCC<br/>
+    <i><code>op</code></i> vA, vB, field@CCCC
+  </td>
+  <td>instance-of</td>
+</tr>
+<tr>
+  <td>22cs</td>
+  <td><i><code>op</code></i> vA, vB, fieldoff@CCCC</td>
+  <td><i>(suggested format for statically linked field access instructions of
+    format 22c)</i>
+  </td>
+</tr>
+<tr>
+  <td>&Oslash;&Oslash;|<i>op</i> AAAA<sub>lo</sub> AAAA<sub>hi</sub></td></td>
+  <td>30t</td>
+  <td><i><code>op</code></i> +AAAAAAAA</td>
+  <td>goto/32</td>
+</tr>
+<tr>
+  <td>&Oslash;&Oslash;|<i>op</i> AAAA BBBB</td>
+  <td>32x</td>
+  <td><i><code>op</code></i> vAAAA, vBBBB</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td rowspan="3">AA|<i>op</i> BBBB<sub>lo</sub> BBBB<sub>hi</sub></td>
+  <td>31i</td>
+  <td><i><code>op</code></i> vAA, #+BBBBBBBB</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>31t</td>
+  <td><i><code>op</code></i> vAA, +BBBBBBBB</td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>31c</td>
+  <td><i><code>op</code></i> vAA, string@BBBBBBBB</td>
+  <td>const-string/jumbo</td>
+</tr>
+<tr>
+  <td>B|A|<i>op</i> CCCC G|F|E|D</td>
+  <td>35c</td>
+  <td><i>[<code>B=5</code>] <code>op</code></i> {vD, vE, vF, vG, vA},
+    meth@CCCC<br/>
+    <i>[<code>B=5</code>] <code>op</code></i> {vD, vE, vF, vG, vA},
+    type@CCCC<br/>
+    <i>[<code>B=4</code>] <code>op</code></i> {vD, vE, vF, vG},
+    <i><code>kind</code></i>@CCCC<br/>
+    <i>[<code>B=3</code>] <code>op</code></i> {vD, vE, vF},
+    <i><code>kind</code></i>@CCCC<br/>
+    <i>[<code>B=2</code>] <code>op</code></i> {vD, vE},
+    <i><code>kind</code></i>@CCCC<br/>
+    <i>[<code>B=1</code>] <code>op</code></i> {vD},
+    <i><code>kind</code></i>@CCCC<br/>
+    <i>[<code>B=0</code>] <code>op</code></i> {},
+    <i><code>kind</code></i>@CCCC
+  </td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>B|A|<i>op</i> CCCC G|F|E|D</td>
+  <td>35ms</td>
+
+  <td><i>[<code>B=5</code>] <code>op</code></i> {vD, vE, vF, vG, vA},
+    vtaboff@CCCC<br/>
+    <i>[<code>B=4</code>] <code>op</code></i> {vD, vE, vF, vG},
+    vtaboff@CCCC<br/>
+    <i>[<code>B=3</code>] <code>op</code></i> {vD, vE, vF},
+    vtaboff@CCCC<br/>
+    <i>[<code>B=2</code>] <code>op</code></i> {vD, vE},
+    vtaboff@CCCC<br/>
+    <i>[<code>B=1</code>] <code>op</code></i> {vD},
+    vtaboff@CCCC<br/>
+  </td>
+  <td><i>(suggested format for statically linked <code>invoke-virtual</code>
+    and <code>invoke-super</code> instructions of format 35c)</i>
+  </td>
+</tr>
+<tr>
+  <td>B|A|<i>op</i> DDCC H|G|F|E</td>
+  <td>35fs</td>
+  <td><i>[<code>B=5</code>] <code>op</code></i> {vE, vF, vG, vH, vA},
+    vtaboff@CC, iface@DD<br/>
+    <i>[<code>B=4</code>] <code>op</code></i> {vE, vF, vG, vH},
+    vtaboff@CC, iface@DD<br/>
+    <i>[<code>B=3</code>] <code>op</code></i> {vE, vF, vG},
+    vtaboff@CC, iface@DD<br/>
+    <i>[<code>B=2</code>] <code>op</code></i> {vE, vF},
+    vtaboff@CC, iface@DD<br/>
+    <i>[<code>B=1</code>] <code>op</code></i> {vE},
+    vtaboff@CC, iface@DD<br/>
+  </td>
+  <td><i>(suggested format for statically linked <code>invoke-interface</code>
+    instructions of format 35c)</i>
+  </td>
+</tr>
+<tr>
+  <td>AA|<i>op</i> BBBB CCCC</td>
+  <td>3rc</td>
+  <td><i><code>op</code></i> {vCCCC .. vNNNN}, meth@BBBB<br/>
+    <i><code>op</code></i> {vCCCC .. vNNNN}, type@BBBB<br/>
+    <p><i>(where <code>NNNN = CCCC+AA-1</code>, that is <code>A</code>
+    determines the count <code>0..255</code>, and <code>C</code>
+    determines the first register)</i></p>
+  </td>
+  <td>&nbsp;</td>
+</tr>
+<tr>
+  <td>AA|<i>op</i> BBBB CCCC</td>
+  <td>3rms</td>
+  <td><i><code>op</code></i> {vCCCC .. vNNNN}, vtaboff@BBBB<br/>
+    <p><i>(where <code>NNNN = CCCC+AA-1</code>, that is <code>A</code>
+    determines the count <code>0..255</code>, and <code>C</code>
+    determines the first register)</i></p>
+  </td>
+  <td><i>(suggested format for statically linked <code>invoke-virtual</code>
+    and <code>invoke-super</code> instructions of format <code>3rc</code>)</i>
+  </td>
+</tr>
+<tr>
+  <td>AA|<i>op</i> CCBB DDDD</td>
+  <td>3rfs</td>
+  <td><i><code>op</code></i> {vDDDD .. vNNNN}, vtaboff@BB,
+    iface@CC<br/>
+    <p><i>(where <code>NNNN = DDDD+AA-1</code>, that is <code>A</code>
+    determines the count <code>0..255</code>, and <code>D</code>
+    determines the first register)</i></p>
+  </td>
+  <td><i>(suggested format for statically linked <code>invoke-interface</code>
+    instructions of format <code>3rc</code>)</i>
+  </td>
+</tr>
+<tr>
+  <td>AA|<i>op</i> BBBB<sub>lo</sub> BBBB BBBB BBBB<sub>hi</sub></td>
+  <td>51l</td>
+  <td><i><code>op</code></i> vAA, #+BBBBBBBBBBBBBBBB</td>
+  <td>const-wide</td>
+</tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/docs/java-bytecode.css b/docs/java-bytecode.css
new file mode 100644
index 0000000..48984b2
--- /dev/null
+++ b/docs/java-bytecode.css
@@ -0,0 +1,54 @@
+@media print {
+    table {
+        font-size: 8pt;
+    }
+}
+
+@media screen {
+    table {
+        font-size: 10pt;
+    }
+}
+
+h1 {
+    text-align: center;
+}
+
+table {
+    vertical-align: top;
+    border-collapse: collapse;
+    font-family: sans-serif;
+}
+
+td {
+    vertical-align: top;
+    background: #f8f8f8;
+    border-width: 0;
+}
+
+td.outer {
+    width: 25%;
+    padding: 0;
+}
+
+td.outer table {
+    width: 100%;
+}
+
+td.outer td {
+    border-width: 0;
+    background: #f8f8f8;
+    padding: 1pt;
+    padding-left: 10pt;
+    padding-right: 2pt;
+}
+
+tr.d td {
+    background: #dddddd;
+}
+
+td.outer td + td + td {
+    font-family: monospace;
+    font-weight: bold;
+    padding-right: 5pt;
+}
\ No newline at end of file
diff --git a/docs/java-bytecode.html b/docs/java-bytecode.html
new file mode 100644
index 0000000..691ae54
--- /dev/null
+++ b/docs/java-bytecode.html
@@ -0,0 +1,228 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>Java Bytecode At A Glance</title>
+<link rel="stylesheet" href="java-bytecode.css">
+</head>
+
+<body>
+
+<h1>Java Bytecode At A Glance</h1>
+
+<table align="center">
+<tr><td class="outer"><table>
+<tr><td>0x00</td><td>0</td><td>nop</td></tr>
+<tr><td>0x01</td><td>1</td><td>aconst_null</td></tr>
+<tr class="d"><td>0x02</td><td>2</td><td>iconst_m1</td></tr>
+<tr class="d"><td>0x03</td><td>3</td><td>iconst_0</td></tr>
+<tr><td>0x04</td><td>4</td><td>iconst_1</td></tr>
+<tr><td>0x05</td><td>5</td><td>iconst_2</td></tr>
+<tr class="d"><td>0x06</td><td>6</td><td>iconst_3</td></tr>
+<tr class="d"><td>0x07</td><td>7</td><td>iconst_4</td></tr>
+<tr><td>0x08</td><td>8</td><td>iconst_5</td></tr>
+<tr><td>0x09</td><td>9</td><td>lconst_0</td></tr>
+<tr class="d"><td>0x0a</td><td>10</td><td>lconst_1</td></tr>
+<tr class="d"><td>0x0b</td><td>11</td><td>fconst_0</td></tr>
+<tr><td>0x0c</td><td>12</td><td>fconst_1</td></tr>
+<tr><td>0x0d</td><td>13</td><td>fconst_2</td></tr>
+<tr class="d"><td>0x0e</td><td>14</td><td>dconst_0</td></tr>
+<tr class="d"><td>0x0f</td><td>15</td><td>dconst_1</td></tr>
+<tr><td>0x10</td><td>16</td><td>bipush</td></tr>
+<tr><td>0x11</td><td>17</td><td>sipush</td></tr>
+<tr class="d"><td>0x12</td><td>18</td><td>ldc</td></tr>
+<tr class="d"><td>0x13</td><td>19</td><td>ldc_w</td></tr>
+<tr><td>0x14</td><td>20</td><td>ldc2_w</td></tr>
+<tr><td>0x15</td><td>21</td><td>iload</td></tr>
+<tr class="d"><td>0x16</td><td>22</td><td>lload</td></tr>
+<tr class="d"><td>0x17</td><td>23</td><td>fload</td></tr>
+<tr><td>0x18</td><td>24</td><td>dload</td></tr>
+<tr><td>0x19</td><td>25</td><td>aload</td></tr>
+<tr class="d"><td>0x1a</td><td>26</td><td>iload_0</td></tr>
+<tr class="d"><td>0x1b</td><td>27</td><td>iload_1</td></tr>
+<tr><td>0x1c</td><td>28</td><td>iload_2</td></tr>
+<tr><td>0x1d</td><td>29</td><td>iload_3</td></tr>
+<tr class="d"><td>0x1e</td><td>30</td><td>lload_0</td></tr>
+<tr class="d"><td>0x1f</td><td>31</td><td>lload_1</td></tr>
+<tr><td>0x20</td><td>32</td><td>lload_2</td></tr>
+<tr><td>0x21</td><td>33</td><td>lload_3</td></tr>
+<tr class="d"><td>0x22</td><td>34</td><td>fload_0</td></tr>
+<tr class="d"><td>0x23</td><td>35</td><td>fload_1</td></tr>
+<tr><td>0x24</td><td>36</td><td>fload_2</td></tr>
+<tr><td>0x25</td><td>37</td><td>fload_3</td></tr>
+<tr class="d"><td>0x26</td><td>38</td><td>dload_0</td></tr>
+<tr class="d"><td>0x27</td><td>39</td><td>dload_1</td></tr>
+<tr><td>0x28</td><td>40</td><td>dload_2</td></tr>
+<tr><td>0x29</td><td>41</td><td>dload_3</td></tr>
+<tr class="d"><td>0x2a</td><td>42</td><td>aload_0</td></tr>
+<tr class="d"><td>0x2b</td><td>43</td><td>aload_1</td></tr>
+<tr><td>0x2c</td><td>44</td><td>aload_2</td></tr>
+<tr><td>0x2d</td><td>45</td><td>aload_3</td></tr>
+<tr class="d"><td>0x2e</td><td>46</td><td>iaload</td></tr>
+<tr class="d"><td>0x2f</td><td>47</td><td>laload</td></tr>
+<tr><td>0x30</td><td>48</td><td>faload</td></tr>
+<tr><td>0x31</td><td>49</td><td>daload</td></tr>
+<tr class="d"><td>0x32</td><td>50</td><td>aaload</td></tr>
+</table></td>
+<td class="outer"><table>
+<tr><td>0x33</td><td>51</td><td>baload</td></tr>
+<tr><td>0x34</td><td>52</td><td>caload</td></tr>
+<tr class="d"><td>0x35</td><td>53</td><td>saload</td></tr>
+<tr class="d"><td>0x36</td><td>54</td><td>istore</td></tr>
+<tr><td>0x37</td><td>55</td><td>lstore</td></tr>
+<tr><td>0x38</td><td>56</td><td>fstore</td></tr>
+<tr class="d"><td>0x39</td><td>57</td><td>dstore</td></tr>
+<tr class="d"><td>0x3a</td><td>58</td><td>astore</td></tr>
+<tr><td>0x3b</td><td>59</td><td>istore_0</td></tr>
+<tr><td>0x3c</td><td>60</td><td>istore_1</td></tr>
+<tr class="d"><td>0x3d</td><td>61</td><td>istore_2</td></tr>
+<tr class="d"><td>0x3e</td><td>62</td><td>istore_3</td></tr>
+<tr><td>0x3f</td><td>63</td><td>lstore_0</td></tr>
+<tr><td>0x40</td><td>64</td><td>lstore_1</td></tr>
+<tr class="d"><td>0x41</td><td>65</td><td>lstore_2</td></tr>
+<tr class="d"><td>0x42</td><td>66</td><td>lstore_3</td></tr>
+<tr><td>0x43</td><td>67</td><td>fstore_0</td></tr>
+<tr><td>0x44</td><td>68</td><td>fstore_1</td></tr>
+<tr class="d"><td>0x45</td><td>69</td><td>fstore_2</td></tr>
+<tr class="d"><td>0x46</td><td>70</td><td>fstore_3</td></tr>
+<tr><td>0x47</td><td>71</td><td>dstore_0</td></tr>
+<tr><td>0x48</td><td>72</td><td>dstore_1</td></tr>
+<tr class="d"><td>0x49</td><td>73</td><td>dstore_2</td></tr>
+<tr class="d"><td>0x4a</td><td>74</td><td>dstore_3</td></tr>
+<tr><td>0x4b</td><td>75</td><td>astore_0</td></tr>
+<tr><td>0x4c</td><td>76</td><td>astore_1</td></tr>
+<tr class="d"><td>0x4d</td><td>77</td><td>astore_2</td></tr>
+<tr class="d"><td>0x4e</td><td>78</td><td>astore_3</td></tr>
+<tr><td>0x4f</td><td>79</td><td>iastore</td></tr>
+<tr><td>0x50</td><td>80</td><td>lastore</td></tr>
+<tr class="d"><td>0x51</td><td>81</td><td>fastore</td></tr>
+<tr class="d"><td>0x52</td><td>82</td><td>dastore</td></tr>
+<tr><td>0x53</td><td>83</td><td>aastore</td></tr>
+<tr><td>0x54</td><td>84</td><td>bastore</td></tr>
+<tr class="d"><td>0x55</td><td>85</td><td>castore</td></tr>
+<tr class="d"><td>0x56</td><td>86</td><td>sastore</td></tr>
+<tr><td>0x57</td><td>87</td><td>pop</td></tr>
+<tr><td>0x58</td><td>88</td><td>pop2</td></tr>
+<tr class="d"><td>0x59</td><td>89</td><td>dup</td></tr>
+<tr class="d"><td>0x5a</td><td>90</td><td>dup_x1</td></tr>
+<tr><td>0x5b</td><td>91</td><td>dup_x2</td></tr>
+<tr><td>0x5c</td><td>92</td><td>dup2</td></tr>
+<tr class="d"><td>0x5d</td><td>93</td><td>dup2_x1</td></tr>
+<tr class="d"><td>0x5e</td><td>94</td><td>dup2_x2</td></tr>
+<tr><td>0x5f</td><td>95</td><td>swap</td></tr>
+<tr><td>0x60</td><td>96</td><td>iadd</td></tr>
+<tr class="d"><td>0x61</td><td>97</td><td>ladd</td></tr>
+<tr class="d"><td>0x62</td><td>98</td><td>fadd</td></tr>
+<tr><td>0x63</td><td>99</td><td>dadd</td></tr>
+<tr><td>0x64</td><td>100</td><td>isub</td></tr>
+<tr class="d"><td>0x65</td><td>101</td><td>lsub</td></tr>
+</table></td>
+<td class="outer"><table>
+<tr><td>0x66</td><td>102</td><td>fsub</td></tr>
+<tr><td>0x67</td><td>103</td><td>dsub</td></tr>
+<tr class="d"><td>0x68</td><td>104</td><td>imul</td></tr>
+<tr class="d"><td>0x69</td><td>105</td><td>lmul</td></tr>
+<tr><td>0x6a</td><td>106</td><td>fmul</td></tr>
+<tr><td>0x6b</td><td>107</td><td>dmul</td></tr>
+<tr class="d"><td>0x6c</td><td>108</td><td>idiv</td></tr>
+<tr class="d"><td>0x6d</td><td>109</td><td>ldiv</td></tr>
+<tr><td>0x6e</td><td>110</td><td>fdiv</td></tr>
+<tr><td>0x6f</td><td>111</td><td>ddiv</td></tr>
+<tr class="d"><td>0x70</td><td>112</td><td>irem</td></tr>
+<tr class="d"><td>0x71</td><td>113</td><td>lrem</td></tr>
+<tr><td>0x72</td><td>114</td><td>frem</td></tr>
+<tr><td>0x73</td><td>115</td><td>drem</td></tr>
+<tr class="d"><td>0x74</td><td>116</td><td>ineg</td></tr>
+<tr class="d"><td>0x75</td><td>117</td><td>lneg</td></tr>
+<tr><td>0x76</td><td>118</td><td>fneg</td></tr>
+<tr><td>0x77</td><td>119</td><td>dneg</td></tr>
+<tr class="d"><td>0x78</td><td>120</td><td>ishl</td></tr>
+<tr class="d"><td>0x79</td><td>121</td><td>lshl</td></tr>
+<tr><td>0x7a</td><td>122</td><td>ishr</td></tr>
+<tr><td>0x7b</td><td>123</td><td>lshr</td></tr>
+<tr class="d"><td>0x7c</td><td>124</td><td>iushr</td></tr>
+<tr class="d"><td>0x7d</td><td>125</td><td>lushr</td></tr>
+<tr><td>0x7e</td><td>126</td><td>iand</td></tr>
+<tr><td>0x7f</td><td>127</td><td>land</td></tr>
+<tr class="d"><td>0x80</td><td>128</td><td>ior</td></tr>
+<tr class="d"><td>0x81</td><td>129</td><td>lor</td></tr>
+<tr><td>0x82</td><td>130</td><td>ixor</td></tr>
+<tr><td>0x83</td><td>131</td><td>lxor</td></tr>
+<tr class="d"><td>0x84</td><td>132</td><td>iinc</td></tr>
+<tr class="d"><td>0x85</td><td>133</td><td>i2l</td></tr>
+<tr><td>0x86</td><td>134</td><td>i2f</td></tr>
+<tr><td>0x87</td><td>135</td><td>i2d</td></tr>
+<tr class="d"><td>0x88</td><td>136</td><td>l2i</td></tr>
+<tr class="d"><td>0x89</td><td>137</td><td>l2f</td></tr>
+<tr><td>0x8a</td><td>138</td><td>l2d</td></tr>
+<tr><td>0x8b</td><td>139</td><td>f2i</td></tr>
+<tr class="d"><td>0x8c</td><td>140</td><td>f2l</td></tr>
+<tr class="d"><td>0x8d</td><td>141</td><td>f2d</td></tr>
+<tr><td>0x8e</td><td>142</td><td>d2i</td></tr>
+<tr><td>0x8f</td><td>143</td><td>d2l</td></tr>
+<tr class="d"><td>0x90</td><td>144</td><td>d2f</td></tr>
+<tr class="d"><td>0x91</td><td>145</td><td>i2b</td></tr>
+<tr><td>0x92</td><td>146</td><td>i2c</td></tr>
+<tr><td>0x93</td><td>147</td><td>i2s</td></tr>
+<tr class="d"><td>0x94</td><td>148</td><td>lcmp</td></tr>
+<tr class="d"><td>0x95</td><td>149</td><td>fcmpl</td></tr>
+<tr><td>0x96</td><td>150</td><td>fcmpg</td></tr>
+<tr><td>0x97</td><td>151</td><td>dcmpl</td></tr>
+<tr class="d"><td>0x98</td><td>152</td><td>dcmpg</td></tr>
+</table></td>
+<td class="outer"><table>
+<tr><td>0x99</td><td>153</td><td>ifeq</td></tr>
+<tr><td>0x9a</td><td>154</td><td>ifne</td></tr>
+<tr class="d"><td>0x9b</td><td>155</td><td>iflt</td></tr>
+<tr class="d"><td>0x9c</td><td>156</td><td>ifge</td></tr>
+<tr><td>0x9d</td><td>157</td><td>ifgt</td></tr>
+<tr><td>0x9e</td><td>158</td><td>ifle</td></tr>
+<tr class="d"><td>0x9f</td><td>159</td><td>if_icmpeq</td></tr>
+<tr class="d"><td>0xa0</td><td>160</td><td>if_icmpne</td></tr>
+<tr><td>0xa1</td><td>161</td><td>if_icmplt</td></tr>
+<tr><td>0xa2</td><td>162</td><td>if_icmpge</td></tr>
+<tr class="d"><td>0xa3</td><td>163</td><td>if_icmpgt</td></tr>
+<tr class="d"><td>0xa4</td><td>164</td><td>if_icmple</td></tr>
+<tr><td>0xa5</td><td>165</td><td>if_acmpeq</td></tr>
+<tr><td>0xa6</td><td>166</td><td>if_acmpne</td></tr>
+<tr class="d"><td>0xa7</td><td>167</td><td>goto</td></tr>
+<tr class="d"><td>0xa8</td><td>168</td><td>jsr</td></tr>
+<tr><td>0xa9</td><td>169</td><td>ret</td></tr>
+<tr><td>0xaa</td><td>170</td><td>tableswitch</td></tr>
+<tr class="d"><td>0xab</td><td>171</td><td>lookupswitch</td></tr>
+<tr class="d"><td>0xac</td><td>172</td><td>ireturn</td></tr>
+<tr><td>0xad</td><td>173</td><td>lreturn</td></tr>
+<tr><td>0xae</td><td>174</td><td>freturn</td></tr>
+<tr class="d"><td>0xaf</td><td>175</td><td>dreturn</td></tr>
+<tr class="d"><td>0xb0</td><td>176</td><td>areturn</td></tr>
+<tr><td>0xb1</td><td>177</td><td>return</td></tr>
+<tr><td>0xb2</td><td>178</td><td>getstatic</td></tr>
+<tr class="d"><td>0xb3</td><td>179</td><td>putstatic</td></tr>
+<tr class="d"><td>0xb4</td><td>180</td><td>getfield</td></tr>
+<tr><td>0xb5</td><td>181</td><td>putfield</td></tr>
+<tr><td>0xb6</td><td>182</td><td>invokevirtual</td></tr>
+<tr class="d"><td>0xb7</td><td>183</td><td>invokespecial</td></tr>
+<tr class="d"><td>0xb8</td><td>184</td><td>invokestatic</td></tr>
+<tr><td>0xb9</td><td>185</td><td>invokeinterface</td></tr>
+<tr><td>0xba</td><td>186</td><td><i>(unused)</i></td></tr>
+<tr class="d"><td>0xbb</td><td>187</td><td>new</td></tr>
+<tr class="d"><td>0xbc</td><td>188</td><td>newarray</td></tr>
+<tr><td>0xbd</td><td>189</td><td>anewarray</td></tr>
+<tr><td>0xbe</td><td>190</td><td>arraylength</td></tr>
+<tr class="d"><td>0xbf</td><td>191</td><td>athrow</td></tr>
+<tr class="d"><td>0xc0</td><td>192</td><td>checkcast</td></tr>
+<tr><td>0xc1</td><td>193</td><td>instanceof</td></tr>
+<tr><td>0xc2</td><td>194</td><td>monitorenter</td></tr>
+<tr class="d"><td>0xc3</td><td>195</td><td>monitorexit</td></tr>
+<tr class="d"><td>0xc4</td><td>196</td><td>wide</td></tr>
+<tr><td>0xc5</td><td>197</td><td>multianewarray</td></tr>
+<tr><td>0xc6</td><td>198</td><td>ifnull</td></tr>
+<tr class="d"><td>0xc7</td><td>199</td><td>ifnonnull</td></tr>
+<tr class="d"><td>0xc8</td><td>200</td><td>goto_w</td></tr>
+<tr><td>0xc9</td><td>201</td><td>jsr_w</td></tr>
+</table></td></tr>
+</table>
+
+</body>
+</html>
diff --git a/docs/java-constraints.css b/docs/java-constraints.css
new file mode 100644
index 0000000..a315a73
--- /dev/null
+++ b/docs/java-constraints.css
@@ -0,0 +1,59 @@
+h1 {
+    font-family: serif;
+    color: #222266;
+}
+
+h2 {
+    font-family: serif;
+    border-top-style: solid;
+    border-top-width: 2px;
+    border-color: #ccccdd;
+    padding-top: 12px;
+    margin-top: 48px;
+    margin-bottom: 2px;
+    color: #222266;
+}
+
+@media print {
+    table {
+        font-size: 8pt;
+    }
+}
+
+@media screen {
+    table {
+        font-size: 10pt;
+    }
+}
+
+
+/* general for all tables */
+
+table {
+    border-collapse: collapse;
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table th {
+    font-family: sans-serif;
+    background: #aabbff;
+    text-align: left;
+}
+
+table td {
+    font-family: sans-serif;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-width: 1px;
+    border-color: #aaaaff;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    padding-left: 4px;
+    padding-right: 6px;
+    background: #eeeeff;
+    margin-top: 4pt;
+    margin-bottom: 0pt;
+}
diff --git a/docs/java-constraints.html b/docs/java-constraints.html
new file mode 100644
index 0000000..9d3c434
--- /dev/null
+++ b/docs/java-constraints.html
@@ -0,0 +1,1080 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+  <head>
+    <title>Java bytecode constraints</title>
+    <link rel=stylesheet href="java-constraints.css">
+  </head>
+
+  <body>
+    <h1>
+      Bytecode constraints
+    </h1>
+
+    <p>
+      From the point of view of a piece of code written in the Java
+      programming language or targeted in the same way to <code>.class</code>
+      files, the Dalvik VM aims to behave in a way
+      that is fully consistent with the language's definition.
+      That is, the code running in Dalvik will behave the same as it
+      would have running in any other virtual machine. This includes
+      verification failures.
+      The Dx/Dalvik system will check roughly the same
+      constraints that any other VM would, except as noted in the file
+      <a href="verifier.html">verifier.html</a>. The following table briefly
+      lists all Dx/Dalvik verification constraints together their analogs
+      from the book <i>The Java<super>TM</super> Language Specification</i>,
+      second edition. In the numbering scheme, the first three
+      elements refer to the specification chapter, the fourth one to the
+      bullet inside that chapter. The failure mode specifies whether the
+      constraint will fail during the Dx conversion or during verification in
+      the VM itself.
+    </p>
+
+    <h2>
+      Static constraints
+    </h2>
+
+    <p>
+    Static constraints are constraints on individual elements of the bytecode.
+    They usually can be checked without employing control or data-flow analysis
+    techniques.
+    </p>
+
+    <table>
+      <tr>
+        <th>
+          Identifier
+        </th>
+
+        <th>
+          Description
+        </th>
+
+        <th>
+          Spec equivalent
+        </th>
+
+        <th>
+          Failure mode
+        </th>
+      </tr>
+
+      <tr>
+        <td>
+          A1
+        </td>
+
+        <td>
+          The <code>code</code> array must not be empty.
+        </td>
+
+        <td>
+          4.8.1.1
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A2
+        </td>
+
+        <td>
+          The <code>code</code> array must not be larger than 65535 bytes.
+        </td>
+
+        <td>
+          4.8.1.2
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A3
+        </td>
+
+        <td>
+          The first opcode in <code>code</code> array must have index
+          <code>0</code>.
+        </td>
+
+        <td>
+          4.8.1.3
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A4
+        </td>
+
+        <td>
+          The <code>code</code> array must only contain valid opcodes.
+        </td>
+
+        <td>
+          4.8.1.4
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A5
+        </td>
+
+        <td>
+          The index of instruction <code>n+1</code> must equal the index of
+          instruction <code>n</code> plus the length of instruction
+          <code>n</code>, taking into account a possible <code>wide</code>
+          instruction. Opcodes modified by a <code>wide</code> instruction must
+          not be directly reachable.
+        </td>
+
+        <td>
+          4.8.1.5
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A6
+        </td>
+
+        <td>
+          The last instruction in <code>code</code> array must end at index
+          <code>code_length-1</code>.
+        </td>
+
+        <td>
+          4.8.1.6
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A7
+        </td>
+
+        <td>
+          All jump and branch targets must be opcodes within the same method.
+          Opcodes modified by a <code>wide</code> instruction must not be
+          directly reachable via a jump or branch instruction.
+        </td>
+
+        <td>
+          4.8.1.7
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A8
+        </td>
+
+        <td>
+          All targets of a <code>tableswitch</code> instruction must be opcodes
+          within the same method. Upper and lower bounds must be consistent.
+          Opcodes modified by a <code>wide</code> instruction must not be
+          directly reachable via a <code>tableswitch</code> instruction.
+        </td>
+
+        <td>
+          4.8.1.8
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A9
+        </td>
+
+        <td>
+          All targets of a <code>lookupswitch</code> instruction must be opcodes
+          within the same method. Its table must be consistent and sorted
+          low-to-high. Opcodes modified by a <code>wide</code> instruction must
+          not be directly reachable via a <code>lookupswitch</code> instruction.
+        </td>
+
+        <td>
+          4.8.1.9
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A10
+        </td>
+
+        <td>
+          The operands of <code>ldc</code> and <code>ldc_w</code> instructions
+          must be valid indices into the constant pool. The respective entries
+          must be of type <code>CONSTANT_Integer</code>,
+          <code>CONSTANT_Float</code>, or <code>CONSTANT_String</code>.
+        </td>
+
+        <td>
+          4.8.1.10
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A11
+        </td>
+
+        <td>
+          The operands of <code>ldc2_w</code> instructions must be valid indices
+          into the constant pool. The respective entries must be of type
+          <code>CONSTANT_Long</code> or <code>CONSTANT_Double</code>. The
+          subsequent constant pool entry must be valid and remain unused.
+        </td>
+
+        <td>
+          4.8.1.11
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A12
+        </td>
+
+        <td>
+          The Operands of <code>get&lt;kind&gt;</code> and
+          <code>put&lt;kind&gt;</code> instructions must be valid indices into
+          constant pool. The respective entries must be of type
+          <code>CONSTANT_Fieldref</code>.
+        </td>
+
+        <td>
+          4.8.1.12
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A13
+        </td>
+
+        <td>
+          The first two operands of <code>invokevirtual</code>,
+          <code>invokespecial</code>, and <code>invokestatic</code> must form a
+          valid 16-bit index into the constant pool. The respective entries must
+          be of type <code>CONSTANT_Methodref</code>.
+        </td>
+
+        <td>
+          4.8.1.13
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A14
+        </td>
+
+        <td>
+          Methods whose names start with '<' must only be invoked implicitly by
+          the VM, not by class file code. The only exception is the instance
+          initializer, which may be invoked by <code>invokespecial</code>.
+        </td>
+
+        <td>
+          4.8.1.14
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A15
+        </td>
+
+        <td>
+          The first two operands of <code>invokeinterface</code> must form a
+          valid 16-bit index into the constant pool. The entry must be of type
+          <code>CONSTANT_Interface_Methodref</code>. The third operand must
+          specify number of local variables and the fourth operand must always
+          be zero.
+        </td>
+
+        <td>
+          4.8.1.15
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A16
+        </td>
+
+        <td>
+          The operands of <code>instanceof</code>, <code>checkcast</code>,
+          <code>new</code>, and <code>anewarray</code> instructions must
+          be a valid index into the constant pool. The first two operands of
+          <code>multianewarray</code> instruction must form a valid 16-bit index
+          into the constant pool. All respective entries must be of type
+          <code>CONSTANT_Class</code>.
+        </td>
+
+        <td>
+          4.8.1.16
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+     <tr>
+        <td>
+          A17
+        </td>
+
+        <td>
+          The dimensions of an array created by <code>anewarray</code>
+          instructions must be less than <code>256</code>.
+        </td>
+
+        <td>
+          4.8.1.17
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A18
+        </td>
+
+        <td>
+          The <code>new</code> instruction must not reference array classes,
+          interfaces, or abstract classes.
+        </td>
+
+        <td>
+          4.8.1.18
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A19
+        </td>
+
+        <td>
+          The type referenced by a <code>multinewarray</code> instruction must
+          have at least as many dimensions as specified in the instruction. The
+          dimensions operand must not be <code>0</code>
+        </td>
+
+        <td>
+          4.8.1.19
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A20
+        </td>
+
+        <td>
+          The type referenced by a <code>newarray</code> instruction must be a
+          valid, non-reference type.
+        </td>
+
+        <td>
+          4.8.1.20
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A21
+        </td>
+
+        <td>
+          The index operand of instructions explicitly referencing single-width
+          local variables must be non-negative and smaller than
+          <code>max_locals</code>.
+        </td>
+
+        <td>
+          4.8.1.21
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A22
+        </td>
+
+        <td>
+          The index operand of instructions implicitly referencing single-width
+          local variables must be non-negative and smaller than
+          <code>max_locals</code>.
+        </td>
+
+        <td>
+          4.8.1.22
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A23
+        </td>
+
+        <td>
+          The index operand of instructions explicitly referencing double-width
+          local variables must be non-negative and smaller than
+          <code>max_locals-1</code>.
+        </td>
+
+        <td>
+          4.8.1.23
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A24
+        </td>
+
+        <td>
+          The index operand of instructions implicitly referencing double-width
+          local variables must be non-negative and smaller than
+          <code>max_locals-1</code>.
+        </td>
+
+        <td>
+          4.8.1.24
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A25
+        </td>
+
+        <td>
+          The index operand of <code>wide</code> instructions explicitly
+          referencing single-width local variables must be non-negative and
+          smaller than <code>max_locals</code>.
+        </td>
+
+        <td>
+          4.8.1.25
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          A26
+        </td>
+
+        <td>
+          The index operand of <code>wide</code> instructions explicitly
+          referencing double-width local variables must be non-negative and
+          smaller than <code>max_locals-1</code>.
+        </td>
+
+        <td>
+          4.8.1.25
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+    </table>
+
+    <h2>
+      Structural constraints
+    </h2>
+
+    <p>
+    Structural constraints are constraints on relationships between several
+    elements of the bytecode. They usually can't be checked without employing
+    control or data-flow analysis techniques.
+    </p>
+
+    <table>
+      <tr>
+        <th>
+          Identifier
+        </th>
+
+        <th>
+          Description
+        </th>
+
+        <th>
+          Spec equivalent
+        </th>
+
+        <th>
+          Failure mode
+        </th>
+      </tr>
+
+      <tr>
+        <td>
+          B1
+        </td>
+
+        <td>
+          The number and types of arguments (operands and local variables) must
+          always match the instruction.
+        </td>
+
+        <td>
+          4.8.2.1
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B2
+        </td>
+
+        <td>
+          The operand stack must have the same depth for all executions paths
+          leading to an instruction.
+        </td>
+
+        <td>
+          4.8.2.2
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B3
+        </td>
+
+        <td>
+          Local variable pairs must never be broken up.
+        </td>
+
+        <td>
+          4.8.2.3
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B4
+        </td>
+
+        <td>
+          A local variable (or pair) has to be assigned first before it can be
+          read.
+        </td>
+
+        <td>
+          4.8.2.4
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B5
+        </td>
+
+        <td>
+          The operand stack must never grow beyond <code>max_stack</code>.
+        </td>
+
+        <td>
+          4.8.2.5
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B6
+        </td>
+
+        <td>
+          The operand stack must never underflow.
+        </td>
+
+        <td>
+          4.8.2.6
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B7
+        </td>
+
+        <td>
+          An <code>invokespecial</code> instruction must only invoke an instance
+          initializer or a method in the current class or one of its
+          superclasses.
+        </td>
+
+        <td>
+          4.8.2.7
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B8
+        </td>
+
+        <td>
+          An instance initializer must only be invoked on an uninitialized
+          instance residing on the operand stack.
+        </td>
+
+        <td>
+          4.8.2.8
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B9
+        </td>
+
+        <td>
+          Instance methods may only be invoked on and instance fields may only
+          be accessed on already initialized instances.
+        </td>
+
+        <td>
+          4.8.2.9
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B10
+        </td>
+
+        <td>
+           The must be no backwards branches with uninitialized instances on the
+           operand stack or in local variables. There must be no code protected
+           by an exception handler that contains local variables with
+           uninitialized instances.
+        </td>
+
+        <td>
+          4.8.2.10
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B11
+        </td>
+
+        <td>
+           An instance initializer must call another instance initializer (same
+           class or superclass) before any instance members can be accessed.
+           Exceptions are non-inherited instance fields, which can be assigned
+           before calling another initializer, and the <code>Object</code> class
+           in general.
+        </td>
+
+        <td>
+          4.8.2.11
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B12
+        </td>
+
+        <td>
+           All actual method arguments must be assignment-compatible with formal
+           arguments.
+        </td>
+
+        <td>
+          4.8.2.12
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B13
+        </td>
+
+        <td>
+           For each instance method invocation, the actual instance must be
+           assignment-compatible with the class or interface specified in the
+           instruction.
+        </td>
+
+        <td>
+          4.8.2.13
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B14
+        </td>
+
+        <td>
+           A returns instruction must match its method's return type.
+        </td>
+
+        <td>
+          4.8.2.14
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B15
+        </td>
+
+        <td>
+           When accessing protected members of a superclass, the actual type of
+           the instance being accessed must be either the current class or one
+           of its subclasses.
+        </td>
+
+        <td>
+          4.8.2.15
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+     <tr>
+        <td>
+          B16
+        </td>
+
+        <td>
+           The type of a value stored into a static field must be
+           assignment-compatible with or convertible to the field's type.
+        </td>
+
+        <td>
+          4.8.2.16
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B17
+        </td>
+
+        <td>
+           The type of a value stored into a field must be assignment-compatible
+           with or convertible to the field's type.
+        </td>
+
+        <td>
+          4.8.2.17
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B18
+        </td>
+
+        <td>
+           The type of every value stored into an array must be
+           assignment-compatible with the array's component type.
+        </td>
+
+        <td>
+          4.8.2.18
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B19
+        </td>
+
+        <td>
+           The operand of an <code>athrow</code> instruction must be
+           assignment-compatible with <code>java.lang.Throwable</code>.
+        </td>
+
+        <td>
+          4.8.2.19
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B20
+        </td>
+
+        <td>
+           The last reachable instruction of a method must either be a backwards
+           jump or branch, a return, or an <code>athrow</code> instruction. It
+           must not be possible to leave the <code>code</code> array at the
+           bottom.
+        </td>
+
+        <td>
+          4.8.2.20
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B21
+        </td>
+
+        <td>
+           Local variable values must not be used as return addresses.
+        </td>
+
+        <td>
+          4.8.2.21
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B22
+        </td>
+
+        <td>
+          There must be a single, uniquely determined return instruction per
+          subroutine call.
+        </td>
+
+        <td>
+          4.8.2.22
+        </td>
+
+        <td>
+          VM
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B23
+        </td>
+
+        <td>
+          Subroutine calls must not be directly or indirectly self-recursive.
+        </td>
+
+        <td>
+          4.8.2.23
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+      <tr>
+        <td>
+          B24
+        </td>
+
+        <td>
+           <code>ReturnAddress</code> instances must not be reused. If a
+           subroutine returns to a <code>ReturnAddress</code> further up the
+           stack than where its original call instruction is located, then all
+           <code>ReturnAddress</code> instances further down the stack must
+           never be used.
+        </td>
+
+        <td>
+          4.8.2.24
+        </td>
+
+        <td>
+          DX
+        </td>
+      </tr>
+
+    </table>
+  </body>
+</html>
diff --git a/docs/jni-tips.html b/docs/jni-tips.html
new file mode 100644
index 0000000..c01c107
--- /dev/null
+++ b/docs/jni-tips.html
@@ -0,0 +1,770 @@
+<html>
+  <head>
+    <title>Android JNI Tips</title>
+    <link rel=stylesheet href="android.css">
+  </head>
+
+  <body>
+    <h1><a name="JNI_Tips"></a>Android JNI Tips</h1>
+<p>
+</p><p>
+</p><ul>
+<li> <a href="#What_s_JNI_">What's JNI?</a>
+</li>
+<li> <a href="#JavaVM_and_JNIEnv">JavaVM and JNIEnv</a>
+</li>
+<li> <a href="#Threads">Threads</a>
+</li>
+<li> <a href="#jclass_jmethodID_and_jfieldID">jclass, jmethodID, and jfieldID</a>
+</li>
+<li> <a href="#local_vs_global_references">Local vs. Global References</a>
+</li>
+<li> <a href="#UTF_8_and_UTF_16_strings">UTF-8 and UTF-16 Strings</a>
+</li>
+<li> <a href="#Arrays">Primitive Arrays</a>
+</li>
+<li> <a href="#RegionCalls">Region Calls</a>
+</li>
+<li> <a href="#Exceptions">Exceptions</a>
+</li>
+
+<li> <a href="#Extended_checking">Extended Checking</a>
+</li>
+<li> <a href="#Native_Libraries">Native Libraries</a>
+</li>
+<li> <a href="#64bit">64-bit Considerations</a>
+</li>
+
+<li> <a href="#Unsupported">Unsupported Features</a>
+</li>
+
+<li> <a href="#FAQUnsatisfied">FAQ: UnsatisfiedLinkError</a>
+</li>
+<li> <a href="#FAQFindClass">FAQ: FindClass didn't find my class</a>
+</li>
+<li> <a href="#FAQSharing">FAQ: Sharing raw data with native code</a>
+</li>
+
+</ul>
+<p>
+<noautolink>
+</noautolink></p><p>
+</p><h2><a name="What_s_JNI_"> </a> What's JNI? </h2>
+<p>
+
+JNI is the Java Native Interface.  It defines a way for code written in the
+Java programming language to interact with native
+code, e.g. functions written in C/C++.  It's VM-neutral, has support for loading code from
+dynamic shared libraries, and while cumbersome at times is reasonably efficient.
+</p><p>
+You really should read through the
+<a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html">JNI spec for J2SE 1.6</a>
+to get a sense for how JNI works and what features are available.  Some
+aspects of the interface aren't immediately obvious on
+first reading, so you may find the next few sections handy.
+The more detailed <i>JNI Programmer's Guide and Specification</i> can be found
+<a href="http://java.sun.com/docs/books/jni/html/jniTOC.html">here</a>.
+</p><p>
+</p><p>
+</p><h2><a name="JavaVM_and_JNIEnv"> </a> JavaVM and JNIEnv </h2>
+<p>
+JNI defines two key data structures, "JavaVM" and "JNIEnv".  Both of these are essentially
+pointers to pointers to function tables.  (In the C++ version, it's a class whose sole member
+is a pointer to a function table.)  The JavaVM provides the "invocation interface" functions,
+which allow you to create and destroy the VM.  In theory you can have multiple VMs per process,
+but Android's VM only allows one.
+</p><p>
+The JNIEnv provides most of the JNI functions.  Your native functions all receive a JNIEnv as
+the first argument.
+</p><p>
+
+On some VMs, the JNIEnv is used for thread-local storage.  For this reason, <strong>you cannot share a JNIEnv between threads</strong>.
+If a piece of code has no other way to get its JNIEnv, you should share
+the JavaVM, and use JavaVM-&gt;GetEnv to discover the thread's JNIEnv.
+</p><p>
+The C declarations of JNIEnv and JavaVM are different from the C++
+declarations.  "jni.h" provides different typedefs
+depending on whether it's included into ".c" or ".cpp".  For this reason it's a bad idea to
+include JNIEnv arguments in header files included by both languages.  (Put another way: if your
+header file requires "#ifdef __cplusplus", you may have to do some extra work if anything in
+that header refers to JNIEnv.)
+
+</p><p>
+</p><h2><a name="Threads"> Threads </a></h2>
+<p>
+All VM threads are Linux threads, scheduled by the kernel.  They're usually
+started using Java language features (notably <code>Thread.start()</code>),
+but they can also be created elsewhere and then attached to the VM.  For
+example, a thread started with <code>pthread_create</code> can be attached
+with the JNI <code>AttachCurrentThread</code> or
+<code>AttachCurrentThreadAsDaemon</code> functions.  Until a thread is
+attached to the VM, it has no JNIEnv, and
+<strong>cannot make JNI calls</strong>.
+</p><p>
+Attaching a natively-created thread causes the VM to allocate and initialize
+a <code>Thread</code> object, add it to the "main" <code>ThreadGroup</code>,
+and add the thread to the set that is visible to the debugger.  Calling
+<code>AttachCurrentThread</code> on an already-attached thread is a no-op.
+</p><p>
+The Dalvik VM does not suspend threads executing native code.  If
+garbage collection is in progress, or the debugger has issued a suspend
+request, the VM will pause the thread the next time it makes a JNI call.
+</p><p>
+Threads attached through JNI <strong>must call
+<code>DetachCurrentThread</code> before they exit</strong>.
+If coding this directly is awkward, in Android &gt;= 2.0 you
+can use <code>pthread_key_create</code> to define a destructor
+function that will be called before the thread exits, and
+call <code>DetachCurrentThread</code> from there.  (Use that
+key with <code>pthread_setspecific</code> to store the JNIEnv in
+thread-local-storage; that way it'll be passed into your destructor as
+the argument.)
+
+
+</p><h2><a name="jclass_jmethodID_and_jfieldID"> jclass, jmethodID, and jfieldID </a></h2>
+<p>
+If you want to access an object's field from native code, you would do the following:
+</p><p>
+</p><ul>
+<li> Get the class object reference for the class with <code>FindClass</code>
+</li>
+<li> Get the field ID for the field with <code>GetFieldID</code>
+</li>
+<li> Get the contents of the field with something appropriate, e.g.
+<code>GetIntField</code>
+</li>
+</ul>
+<p>
+Similarly, to call a method, you'd first get a class object reference and then a method ID.  The IDs are often just
+pointers to internal VM data structures.  Looking them up may require several string
+comparisons, but once you have them the actual call to get the field or invoke the method
+is very quick.
+</p><p>
+If performance is important, it's useful to look the values up once and cache the results
+in your native code.  Because we are limiting ourselves to one VM per process, it's reasonable
+to store this data in a static local structure.
+</p><p>
+The class references, field IDs, and method IDs are guaranteed valid until the class is unloaded.  Classes
+are only unloaded if all classes associated with a ClassLoader can be garbage collected,
+which is rare but will not be impossible in our system.  Note however that
+the <code>jclass</code>
+is a class reference and <strong>must be protected</strong> with a call
+to <code>NewGlobalRef</code> (see the next section).
+</p><p>
+If you would like to cache the IDs when a class is loaded, and automatically re-cache them
+if the class is ever unloaded and reloaded, the correct way to initialize
+the IDs is to add a piece of code that looks like this to the appropriate class:
+</p><p>
+
+</p><pre>    /*
+     * We use a class initializer to allow the native code to cache some
+     * field offsets.
+     */
+
+    /*
+     * A native function that looks up and caches interesting
+     * class/field/method IDs for this class.  Returns false on failure.
+     */
+    native private static boolean nativeClassInit();
+
+    /*
+     * Invoke the native initializer when the class is loaded.
+     */
+    static {
+        if (!nativeClassInit())
+            throw new RuntimeException("native init failed");
+    }
+</pre>
+<p>
+Create a nativeClassInit method in your C/C++ code that performs the ID lookups.  The code
+will be executed once, when the class is initialized.  If the class is ever unloaded and
+then reloaded, it will be executed again.  (See the implementation of java.io.FileDescriptor
+for an example in our source tree.)
+</p><p>
+</p><p>
+</p><p>
+</p><h2><a name="local_vs_global_references"> Local vs. Global References </a></h2>
+<p>
+Every object that JNI returns is a "local reference".  This means that it's valid for the
+duration of the current native method in the current thread.
+<strong>Even if the object itself continues to live on after the native method returns, the reference is not valid.</strong>
+This applies to all sub-classes of <code>jobject</code>, including
+<code>jclass</code>, <code>jstring</code>, and <code>jarray</code>.
+(Dalvik VM will warn you about most reference mis-uses when extended JNI
+checks are enabled.)
+</p><p>
+
+If you want to hold on to a reference for a longer period, you must use
+a "global" reference.  The <code>NewGlobalRef</code> function takes the
+local reference as an argument and returns a global one.
+The global reference is guaranteed to be valid until you call
+<code>DeleteGlobalRef</code>.
+
+</p><p>
+This pattern is commonly used when caching copies of class objects obtained
+from <code>FindClass</code>, e.g.:
+<p><pre>jclass* localClass = env-&gt;FindClass("MyClass");
+jclass* globalClass = (jclass*) env-&gt;NewGlobalRef(localClass);
+</pre>
+
+</p><p>
+All JNI methods accept both local and global references as arguments.
+It's possible for references to the same object to have different values;
+for example, the return values from consecutive calls to
+<code>NewGlobalRef</code> on the same object may be different.
+<strong>To see if two references refer to the same object,
+you must use the <code>IsSameObject</code> function.</strong>  Never compare
+references with "==" in native code.
+</p><p>
+One consequence of this is that you
+<strong>must not assume object references are constant or unique</strong>
+in native code.  The 32-bit value representing an object may be different
+from one invocation of a method to the next, and it's possible that two
+different objects could have the same 32-bit value on consecutive calls.  Do
+not use <code>jobject</code> values as keys.
+</p><p>
+Programmers are required to "not excessively allocate" local references.  In practical terms this means
+that if you're creating large numbers of local references, perhaps while running through an array of
+Objects, you should free them manually with
+<code>DeleteLocalRef</code> instead of letting JNI do it for you.  The
+VM is only required to reserve slots for
+16 local references, so if you need more than that you should either delete as you go or use
+<code>EnsureLocalCapacity</code> to reserve more.
+</p><p>
+Note: method and field IDs are just 32-bit identifiers, not object
+references, and should not be passed to <code>NewGlobalRef</code>.  The raw data
+pointers returned by functions like <code>GetStringUTFChars</code>
+and <code>GetByteArrayElements</code> are also not objects.
+</p><p>
+One unusual case deserves separate mention.  If you attach a native
+thread to the VM with AttachCurrentThread, the code you are running will
+never "return" to the VM until the thread detaches from the VM.  Any local
+references you create will have to be deleted manually unless you're going
+to detach the thread soon.
+</p><p>
+</p><p>
+</p><p>
+</p><h2><a name="UTF_8_and_UTF_16_strings"> </a> UTF-8 and UTF-16 Strings </h2>
+<p>
+The Java programming language uses UTF-16.  For convenience, JNI provides methods that work with "modified UTF-8" encoding
+as well.  (Some VMs use the modified UTF-8 internally to store strings; ours do not.)  The
+modified encoding only supports the 8- and 16-bit forms, and stores ASCII NUL values in a 16-bit encoding.
+The nice thing about it is that you can count on having C-style zero-terminated strings,
+suitable for use with standard libc string functions.  The down side is that you cannot pass
+arbitrary UTF-8 data into the VM and expect it to work correctly.
+</p><p>
+It's usually best to operate with UTF-16 strings.  With our current VMs, the
+<code>GetStringChars</code> method
+does not require a copy, whereas <code>GetStringUTFChars</code> requires a malloc and a UTF conversion.  Note that
+<strong>UTF-16 strings are not zero-terminated</strong>, and \u0000 is allowed,
+so you need to hang on to the string length as well as
+the string pointer.
+
+</p><p>
+<strong>Don't forget to Release the strings you Get</strong>.  The
+string functions return <code>jchar*</code> or <code>jbyte*</code>, which
+are C-style pointers to primitive data rather than local references.  They
+are guaranteed valid until Release is called, which means they are not
+released when the native method returns.
+</p><p>
+<strong>Data passed to NewStringUTF must be in "modified" UTF-8 format</strong>.  A
+common mistake is reading character data from a file or network stream
+and handing it to <code>NewStringUTF</code> without filtering it.
+Unless you know the data is 7-bit ASCII, you need to strip out high-ASCII
+characters or convert them to proper "modified" UTF-8 form.  If you don't,
+the UTF-16 conversion will likely not be what you expect.  The extended
+JNI checks will scan strings and warn you about invalid data, but they
+won't catch everything.
+</p><p>
+</p><p>
+
+
+</p><h2><a name="Arrays"> </a> Primitive Arrays </h2>
+<p>
+JNI provides functions for accessing the contents of array objects.
+While arrays of objects must be accessed one entry at a time, arrays of
+primitives can be read and written directly as if they were declared in C.
+</p><p>
+To make the interface as efficient as possible without constraining
+the VM implementation,
+the <code>Get&lt;PrimitiveType&gt;ArrayElements</code> family of calls
+allows the VM to either return a pointer to the actual elements, or
+allocate some memory and make a copy.  Either way, the raw pointer returned
+is guaranteed to be valid until the corresponding <code>Release</code> call
+is issued (which implies that, if the data wasn't copied, the array object
+will be pinned down and can't be relocated as part of compacting the heap).
+<strong>You must Release every array you Get.</strong>  Also, if the Get
+call fails, you must ensure that your code doesn't try to Release a NULL
+pointer later.
+</p><p>
+You can determine whether or not the data was copied by passing in a
+non-NULL pointer for the <code>isCopy</code> argument.  This is rarely
+useful.
+</p><p>
+The <code>Release</code> call takes a <code>mode</code> argument that can
+have one of three values.  The actions performed by the VM depend upon
+whether it returned a pointer to the actual data or a copy of it:
+<ul>
+    <li><code>0</code>
+    <ul>
+        <li>Actual: the array object is un-pinned.
+        <li>Copy: data is copied back.  The buffer with the copy is freed.
+    </ul>
+    <li><code>JNI_COMMIT</code>
+    <ul>
+        <li>Actual: does nothing.
+        <li>Copy: data is copied back.  The buffer with the copy
+        <strong>is not freed</strong>.
+    </ul>
+    <li><code>JNI_ABORT</code>
+    <ul>
+        <li>Actual: the array object is un-pinned.  Earlier
+        writes are <strong>not</strong> aborted.
+        <li>Copy: the buffer with the copy is freed; any changes to it are lost.
+    </ul>
+</ul>
+</p><p>
+One reason for checking the <code>isCopy</code> flag is to know if
+you need to call <code>Release</code> with <code>JNI_COMMIT</code>
+after making changes to an array &mdash; if you're alternating between making
+changes and executing code that uses the contents of the array, you may be
+able to
+skip the no-op commit.  Another possible reason for checking the flag is for
+efficient handling of <code>JNI_ABORT</code>.  For example, you might want
+to get an array, modify it in place, pass pieces to other functions, and
+then discard the changes.  If you know that JNI is making a new copy for
+you, there's no need to create another "editable" copy.  If JNI is passing
+you the original, then you do need to make your own copy.
+</p><p>
+Some have asserted that you can skip the <code>Release</code> call if
+<code>*isCopy</code> is false.  This is not the case.  If no copy buffer was
+allocated, then the original memory must be pinned down and can't be moved by
+the garbage collector.
+</p><p>
+Also note that the <code>JNI_COMMIT</code> flag does NOT release the array,
+and you will need to call <code>Release</code> again with a different flag
+eventually.
+</p><p>
+</p><p>
+
+
+</p><h2><a name="RegionCalls"> Region Calls </a></h2>
+
+<p>
+There is an alternative to calls like <code>Get&lt;Type&gt;ArrayElements</code>
+and <code>GetStringChars</code> that may be very helpful when all you want
+to do is copy data in or out.  Consider the following:
+<pre>
+    jbyte* data = env->GetByteArrayElements(array, NULL);
+    if (data != NULL) {
+        memcpy(buffer, data, len);
+        env->ReleaseByteArrayElements(array, data, JNI_ABORT);
+    }
+</pre>
+<p>
+This grabs the array, copies the first <code>len</code> byte
+elements out of it, and then releases the array.  Depending upon the VM
+policies the <code>Get</code> call will either pin or copy the array contents.
+We copy the data (for perhaps a second time), then call Release; in this case
+we use <code>JNI_ABORT</code> so there's no chance of a third copy.
+</p><p>
+We can accomplish the same thing with this:
+<pre>
+    env->GetByteArrayRegion(array, 0, len, buffer);
+</pre>
+</p><p>
+This has several advantages:
+<ul>
+    <li>Requires one JNI call instead of 2, reducing overhead.
+    <li>Doesn't require pinning or extra data copies.
+    <li>Reduces the risk of programmer error &mdash; no risk of forgetting
+    to call <code>Release</code> after something fails.
+</ul>
+</p><p>
+Similarly, you can use the <code>Set&lt;Type&gt;ArrayRegion</code> call
+to copy data into an array, and <code>GetStringRegion</code> or
+<code>GetStringUTFRegion</code> to copy characters out of a
+<code>String</code>.
+
+
+</p><h2><a name="Exceptions"> Exceptions </a></h2>
+<p>
+<strong>You may not call most JNI functions while an exception is pending.</strong>
+Your code is expected to notice the exception (via the function's return value,
+<code>ExceptionCheck()</code>, or <code>ExceptionOccurred()</code>) and return,
+or clear the exception and handle it.
+</p><p>
+The only JNI functions that you are allowed to call while an exception is
+pending are:
+<font size="-1"><ul>
+    <li>DeleteGlobalRef
+    <li>DeleteLocalRef
+    <li>DeleteWeakGlobalRef
+    <li>ExceptionCheck
+    <li>ExceptionClear
+    <li>ExceptionDescribe
+    <li>ExceptionOccurred
+    <li>MonitorExit
+    <li>PopLocalFrame
+    <li>PushLocalFrame
+    <li>Release&lt;PrimitiveType&gt;ArrayElements
+    <li>ReleasePrimitiveArrayCritical
+    <li>ReleaseStringChars
+    <li>ReleaseStringCritical
+    <li>ReleaseStringUTFChars
+</ul></font>
+</p><p>
+Many JNI calls can throw an exception, but often provide a simpler way
+of checking for failure.  For example, if <code>NewString</code> returns
+a non-NULL value, you don't need to check for an exception.  However, if
+you call a method (using a function like <code>CallObjectMethod</code>),
+you must always check for an exception, because the return value is not
+going to be valid if an exception was thrown.
+</p><p>
+Note that exceptions thrown by interpreted code do not "leap over" native code,
+and C++ exceptions thrown by native code are not handled by Dalvik.
+The JNI <code>Throw</code> and <code>ThrowNew</code> instructions just
+set an exception pointer in the current thread.  Upon returning to the VM from
+native code, the exception will be noted and handled appropriately.
+</p><p>
+Native code can "catch" an exception by calling <code>ExceptionCheck</code> or
+<code>ExceptionOccurred</code>, and clear it with
+<code>ExceptionClear</code>.  As usual,
+discarding exceptions without handling them can lead to problems.
+</p><p>
+There are no built-in functions for manipulating the Throwable object
+itself, so if you want to (say) get the exception string you will need to
+find the Throwable class, look up the method ID for
+<code>getMessage "()Ljava/lang/String;"</code>, invoke it, and if the result
+is non-NULL use <code>GetStringUTFChars</code> to get something you can
+hand to printf or a LOG macro.
+
+</p><p>
+</p><p>
+</p><h2><a name="Extended_checking"> Extended Checking </a></h2>
+<p>
+JNI does very little error checking.  Calling <code>SetIntField</code>
+on an Object field will succeed, even if the field is marked
+<code>private</code> and <code>final</code>.  The
+goal is to minimize the overhead on the assumption that, if you've written it in native code,
+you probably did it for performance reasons.
+</p><p>
+In Dalvik, you can enable additional checks by setting the
+"<code>-Xcheck:jni</code>" flag.  If the flag is set, the VM directs
+the JavaVM and JNIEnv pointers to a different table of functions.
+These functions perform an extended series of checks before calling the
+standard implementation.
+
+</p><p>
+The additional tests include:
+</p><p>
+</p>
+<ul>
+<li> Check for null pointers where not allowed.
+</li>
+<li> Verify argument type correctness (jclass is a class object,
+jfieldID points to field data, jstring is a java.lang.String).
+</li>
+<li> Field type correctness, e.g. don't store a HashMap in a String field.
+</li>
+<li> Ensure jmethodID is appropriate when making a static or virtual
+method call.
+</li>
+<li> Check to see if an exception is pending on calls where pending exceptions are not legal.
+</li>
+<li> Check for calls to inappropriate functions between Critical get/release calls.
+</li>
+<li> Check that JNIEnv structs aren't being shared between threads.
+
+</li>
+<li> Make sure local references aren't used outside their allowed lifespan.
+</li>
+<li> UTF-8 strings contain only valid "modified UTF-8" data.
+</li>
+</ul>
+<p>Accessibility of methods and fields (i.e. public vs. private) is not
+checked.
+<p>
+For a description of how to enable CheckJNI for Android apps, see
+<a href="embedded-vm-control.html">Controlling the Embedded VM</a>.
+It's currently enabled by default in the Android emulator and on
+"engineering" device builds.
+
+</p><p>
+JNI checks can be modified with the <code>-Xjniopts</code> command-line
+flag.  Currently supported values include:
+</p>
+<blockquote><dl>
+<dt>forcecopy
+<dd>When set, any function that can return a copy of the original data
+(array of primitive values, UTF-16 chars) will always do so.  The buffers
+are over-allocated and surrounded with a guard pattern to help identify
+code writing outside the buffer, and the contents are erased before the
+storage is freed to trip up code that uses the data after calling Release.
+This will have a noticeable performance impact on some applications.
+<dt>warnonly
+<dd>By default, JNI "warnings" cause the VM to abort.  With this flag
+it continues on.
+</dl></blockquote>
+
+
+</p><p>
+</p><h2><a name="Native_Libraries"> Native Libraries </a></h2>
+<p>
+You can load native code from shared libraries with the standard
+<code>System.loadLibrary()</code> call.  The
+preferred way to get at your native code is:
+</p><p>
+</p><ul>
+<li> Call <code>System.loadLibrary()</code> from a static class
+initializer.  (See the earlier example, where one is used to call
+<code>nativeClassInit()</code>.)  The argument is the "undecorated"
+library name, e.g. to load "libfubar.so" you would pass in "fubar".
+
+</li>
+<li> Provide a native function: <code><strong>jint JNI_OnLoad(JavaVM* vm, void* reserved)</strong></code>
+</li>
+<li>In <code>JNI_OnLoad</code>, register all of your native methods.  You
+should declare
+the methods "static" so the names don't take up space in the symbol table
+on the device.
+</li>
+</ul>
+<p>
+The <code>JNI_OnLoad</code> function should look something like this if
+written in C:
+</p><blockquote><pre>jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK)
+        return -1;
+
+    /* get class with (*env)->FindClass */
+    /* register methods with (*env)->RegisterNatives */
+
+    return JNI_VERSION_1_6;
+}
+</pre></blockquote>
+</p><p>
+You can also call <code>System.load()</code> with the full path name of the
+shared library.  For Android apps, you may find it useful to get the full
+path to the application's private data storage area from the context object.
+</p><p>
+This is the recommended approach, but not the only approach.  The VM does
+not require explicit registration, nor that you provide a
+<code>JNI_OnLoad</code> function.
+You can instead use "discovery" of native methods that are named in a
+specific way (see <a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html#wp615">
+    the JNI spec</a> for details), though this is less desirable.
+It requires more space in the shared object symbol table,
+loading is slower because it requires string searches through all of the
+loaded shared libraries, and if a method signature is wrong you won't know
+about it until the first time the method is actually used.
+</p><p>
+One other note about <code>JNI_OnLoad</code>: any <code>FindClass</code>
+calls you make from there will happen in the context of the class loader
+that was used to load the shared library.  Normally <code>FindClass</code>
+uses the loader associated with the method at the top of the interpreted
+stack, or if there isn't one (because the thread was just attached to
+the VM) it uses the "system" class loader.  This makes
+<code>JNI_OnLoad</code> a convenient place to look up and cache class
+object references.
+</p><p>
+
+
+</p><h2><a name="64bit"> 64-bit Considerations </a></h2>
+
+<p>
+Android is currently expected to run on 32-bit platforms.  In theory it
+could be built for a 64-bit system, but that is not a goal at this time.
+For the most part this isn't something that you will need to worry about
+when interacting with native code,
+but it becomes significant if you plan to store pointers to native
+structures in integer fields in an object.  To support architectures
+that use 64-bit pointers, <strong>you need to stash your native pointers in a
+<code>long</code> field rather than an <code>int</code></strong>.
+
+
+</p><h2><a name="Unsupported"> Unsupported Features </a></h2>
+<p>All JNI 1.6 features are supported, with the following exceptions:
+<ul>
+    <li><code>DefineClass</code> is not implemented.  Dalvik does not use
+    Java bytecodes or class files, so passing in binary class data
+    doesn't work.  Translation facilities may be added in a future
+    version of the VM.</li>
+    <li>"Weak global" references are implemented, but may only be passed
+    to <code>NewLocalRef</code>, <code>NewGlobalRef</code>, and
+    <code>DeleteWeakGlobalRef</code>.  (The spec strongly encourages
+    programmers to create hard references to weak globals before doing
+    anything with them, so this should not be at all limiting.)</li>
+    <li><code>GetObjectRefType</code> (new in 1.6) is implemented but not fully
+    functional &mdash; it can't always tell the difference between "local" and
+    "global" references.</li>
+</ul>
+
+<p>For backward compatibility, you may need to be aware of:
+<ul>
+    <li>Until 2.0 ("Eclair"), the '$' character was not properly
+    converted to "_00024" during searches for method names.  Working
+    around this requires using explicit registration or moving the
+    native methods out of inner classes.
+    <li>Until 2.0, it was not possible to use a <code>pthread_key_create</code>
+    destructor function to avoid the VM's "thread must be detached before
+    exit" check.  (The VM also uses a pthread key destructor function,
+    so it'd be a race to see which gets called first.)
+    <li>"Weak global" references were not implemented until 2.2 ("Froyo").
+    Older VMs will vigorously reject attempts to use them.  You can use
+    the Android platform version constants to test for support.
+</ul>
+
+
+</p><h2><a name="FAQUnsatisfied"> FAQ: UnsatisfiedLinkError </a></h2>
+<p>
+When working on native code it's not uncommon to see a failure like this:
+<pre>java.lang.UnsatisfiedLinkError: Library foo not found</pre>
+<p>
+In some cases it means what it says &mdash; the library wasn't found.  In
+other cases the library exists but couldn't be opened by dlopen(), and
+the details of the failure can be found in the exception's detail message.
+<p>
+Common reasons why you might encounter "library not found" exceptions:
+<ul>
+    <li>The library doesn't exist or isn't accessible to the app.  Use
+    <code>adb shell ls -l &lt;path&gt;</code> to check its presence
+    and permissions.
+    <li>The library wasn't built with the NDK.  This can result in
+    dependencies on functions or libraries that don't exist on the device.
+</ul>
+</p><p>
+Another class of <code>UnsatisfiedLinkError</code> failures looks like:
+<pre>java.lang.UnsatisfiedLinkError: myfunc
+        at Foo.myfunc(Native Method)
+        at Foo.main(Foo.java:10)</pre>
+<p>
+In logcat, you'll see:
+<pre>W/dalvikvm(  880): No implementation found for native LFoo;.myfunc ()V</pre>
+<p>
+This means that the VM tried to find a matching method but was unsuccessful.
+Some common reasons for this are:
+<ul>
+    <li>The library isn't getting loaded.  Check the logcat output for
+    messages about library loading.
+    <li>The method isn't being found due to a name or signature mismatch.  This
+    is commonly caused by:
+    <ul>
+        <li>For lazy method lookup, failing to declare C++ functions
+        with <code>extern C</code>.  You can use <code>arm-eabi-nm</code>
+        to see the symbols as they appear in the library; if they look
+        mangled (e.g. <code>_Z15Java_Foo_myfuncP7_JNIEnvP7_jclass</code>
+        rather than <code>Java_Foo_myfunc</code>) then you need to
+        adjust the declaration.
+        <li>For explicit registration, minor errors when entering the
+        method signature.  Make sure that what you're passing to the
+        registration call matches the signature in the log file.
+        Remember that 'B' is <code>byte</code> and 'Z' is <code>boolean</code>.
+        Class name components in signatures start with 'L', end with ';',
+        use '/' to separate package/class names, and use '$' to separate
+        inner-class names
+        (e.g. <code>Ljava/util/Map$Entry;</code>).
+    </ul>
+</ul>
+<p>
+Using <code>javah</code> to automatically generate JNI headers may help
+avoid some problems.
+
+
+</p><h2><a name="FAQFindClass"> FAQ: FindClass didn't find my class </a></h2>
+<p>
+Make sure that the class name string has the correct format.  JNI class
+names start with the package name and are separated with slashes,
+e.g. <code>java/lang/String</code>.  If you're looking up an array class,
+you need to start with the appropriate number of square brackets and
+must also wrap the class with 'L' and ';', so a one-dimensional array of
+<code>String</code> would be <code>[Ljava/lang/String;</code>.
+</p><p>
+If the class name looks right, you could be running into a class loader
+issue.  <code>FindClass</code> wants to start the class search in the
+class loader associated with your code.  It examines the VM call stack,
+which will look something like:
+<pre>    Foo.myfunc(Native Method)
+    Foo.main(Foo.java:10)
+    dalvik.system.NativeStart.main(Native Method)</pre>
+<p>
+The topmost method is <code>Foo.myfunc</code>.  <code>FindClass</code>
+finds the <code>ClassLoader</code> object associated with the <code>Foo</code>
+class and uses that.
+</p><p>
+This usually does what you want.  You can get into trouble if you
+create a thread outside the VM (perhaps by calling <code>pthread_create</code>
+and then attaching it to the VM with <code>AttachCurrentThread</code>).
+Now the stack trace looks like this:
+<pre>    dalvik.system.NativeStart.run(Native Method)</pre>
+<p>
+The topmost method is <code>NativeStart.run</code>, which isn't part of
+your application.  If you call <code>FindClass</code> from this thread, the
+VM will start in the "system" class loader instead of the one associated
+with your application, so attempts to find app-specific classes will fail.
+</p><p>
+There are a few ways to work around this:
+<ul>
+    <li>Do your <code>FindClass</code> lookups once, in
+    <code>JNI_OnLoad</code>, and cache the class references for later
+    use.  Any <code>FindClass</code> calls made as part of executing
+    <code>JNI_OnLoad</code> will use the class loader associated with
+    the function that called <code>System.loadLibrary</code> (this is a
+    special rule, provided to make library initialization more convenient).
+    If your app code is loading the library, <code>FindClass</code>
+    will use the correct class loader.
+    <li>Pass an instance of the class into the functions that need
+    it, e.g. declare your native method to take a Class argument and
+    then pass <code>Foo.class</code> in.
+    <li>Cache a reference to the <code>ClassLoader</code> object somewhere
+    handy, and issue <code>loadClass</code> calls directly.  This requires
+    some effort.
+</ul>
+
+</p><p>
+
+
+</p><h2><a name="FAQSharing"> FAQ: Sharing raw data with native code </a></h2>
+<p>
+You may find yourself in a situation where you need to access a large
+buffer of raw data from code written in Java and C/C++.  Common examples
+include manipulation of bitmaps or sound samples.  There are two
+basic approaches.
+</p><p>
+You can store the data in a <code>byte[]</code>.  This allows very fast
+access from code written in Java.  On the native side, however, you're
+not guaranteed to be able to access the data without having to copy it.  In
+some implementations, <code>GetByteArrayElements</code> and
+<code>GetPrimitiveArrayCritical</code> will return actual pointers to the
+raw data in the managed heap, but in others it will allocate a buffer
+on the native heap and copy the data over.
+</p><p>
+The alternative is to store the data in a direct byte buffer.  These
+can be created with <code>java.nio.ByteBuffer.allocateDirect</code>, or
+the JNI <code>NewDirectByteBuffer</code> function.  Unlike regular
+byte buffers, the storage is not allocated on the managed heap, and can
+always be accessed directly from native code (get the address
+with <code>GetDirectBufferAddress</code>).  Depending on how direct
+byte buffer access is implemented in the VM, accessing the data from code
+written in Java can be very slow.
+</p><p>
+The choice of which to use depends on two factors:
+<ol>
+    <li>Will most of the data accesses happen from code written in Java
+    or in C/C++?
+    <li>If the data is eventually being passed to a system API, what form
+    must it be in?  (For example, if the data is eventually passed to a
+    function that takes a byte[], doing processing in a direct
+    <code>ByteBuffer</code> might be unwise.)
+</ol>
+If there's no clear winner, use a direct byte buffer.  Support for them
+is built directly into JNI, and access to them from code written in
+Java can be made faster with VM improvements.
+</p>
+
+<address>Copyright &copy; 2008 The Android Open Source Project</address>
+
+  </body>
+</html>
diff --git a/docs/libraries.html b/docs/libraries.html
new file mode 100644
index 0000000..ed2fa72
--- /dev/null
+++ b/docs/libraries.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+
+<title>Dalvik Libraries</title>
+
+<link rel=stylesheet href="dex-format.css">
+<link href="prettify.css" type="text/css" rel="stylesheet" />
+<script type="text/javascript" src="prettify.js"></script>
+
+<style>
+ul.code li {
+  font-family: monospace;
+}
+</style>
+
+</head>
+
+<body onload="prettyPrint()">
+
+<h1 class="title">Dalvik Libraries</h1>
+
+<p>The Dalvik Libraries, also known as the <i>Android core libraries</i>,
+implement general purpose APIs used by code written in the Java programming
+language. While the libraries themselves don't depend on Android, they do form
+the foundation of the Android framework. Android applications use the Dalvik
+libraries both directly and indirectly for data structures, networking,
+concurrency, I/O, and more.</p>
+
+<p>The Dalvik libraries break down into two categories:</p>
+
+<ul>
+    <li><a href="#vm-specific">Dalvik VM-specific libraries</a></li>
+    <li><a href="#interop">Java programming language interoperability
+        libraries</a></li>
+</ul>
+
+<p>Any system claiming to be Android-compatible must implement these libraries.
+Unless otherwise noted, both the signatures and the behavior of such a system
+need to conform to the Android 1.0 reference implementation. Both types of
+conformance will be checked by the upcoming Android Compatibility Test Suite
+(CTS).</p>
+
+<a name="vm-specific"/><h2>Dalvik VM-specific libraries</h2>
+
+<p>The VM-specific libraries enable requesting or modifying VM-specific
+information. Code that uses these classes is only portable across Dalvik-based
+systems. The VM-specific Dalvik packages include:</p>
+
+<ul class="code">
+  <li>dalvik.annotation</li>
+  <li>dalvik.bytecode</li>
+  <li>dalvik.system</li>
+</ul>
+
+<a name="interop"/><h2>Java programming language interoperability libraries</h2>
+
+<p>This category of library provides a familiar environment for programmers
+writing code in the Java programming language. Much of the implementation of
+this code comes from <a href="http://harmony.apache.org/">Apache Harmony</a>.
+Sometimes, we have to change the Harmony code to make it more suitable for the
+memory and CPU-constrained environments targeted by Dalvik. We delineate
+Dalvik-specific changes like so:
+
+<pre class="prettyprint">
+    private static final long serialVersionUID = 8683452581122892189L;
+
+// BEGIN android-added
+    /** zero-element array */
+    private static final Object[] emptyArray = new Object[0];
+// END android-added
+
+    private transient int firstIndex;
+</pre>
+
+<p>If you change existing Harmony code instead of just inserting new code, use
+<code>android-changed</code> instead of <code>android-added</code>. These
+markers help us keep track of our own changes when we pull down updates from
+Harmony.</p>
+
+<p>Packages in this category include:</p>
+
+<ul class="code">
+  <li>java.io</li>
+  <li>java.lang</li>
+  <li>java.lang.annotation</li>
+  <li>java.lang.ref</li>
+  <li>java.lang.reflect</li>
+  <li>java.math</li>
+  <li>java.net</li>
+  <li>java.nio</li>
+  <li>java.nio.channels</li>
+  <li>java.nio.channels.spi</li>
+  <li>java.nio.charset</li>
+  <li>java.nio.charset.spi</li>
+  <li>java.security</li>
+  <li>java.security.acl</li>
+  <li>java.security.cert</li>
+  <li>java.security.interfaces</li>
+  <li>java.security.spec</li>
+  <li>java.sql</li>
+  <li>java.text</li>
+  <li>java.util</li>
+  <li>java.util.concurrent</li>
+  <li>java.util.concurrent.atomic</li>
+  <li>java.util.concurrent.locks</li>
+  <li>java.util.jar</li>
+  <li>java.util.logging</li>
+  <li>java.util.prefs</li>
+  <li>java.util.regex</li>
+  <li>java.util.zip</li>
+  <li>javax.crypto</li>
+  <li>javax.crypto.interfaces</li>
+  <li>javax.crypto.spec</li>
+  <li>javax.net</li>
+  <li>javax.net.ssl</li>
+  <li>javax.security.auth</li>
+  <li>javax.security.auth.callback</li>
+  <li>javax.security.auth.login</li>
+  <li>javax.security.auth.x500</li>
+  <li>javax.security.cert</li>
+  <li>javax.sql</li>
+  <li>javax.xml</li>
+  <li>javax.xml.parsers</li>
+  <li>org.w3c.dom</li>
+  <li>org.xml.sax</li>
+  <li>org.xml.sax.ext</li>
+  <li>org.xml.sax.helpers</li>
+</ul>
+
+<p>We only provide the core functionality of <code>XMLParser</code> and
+<code>DocumentBuilder</code> in the XML packages. Some methods dealing with XML
+schema were left out because we don't provide the corresponding packages.</p>
+
+<p>In addition to the aforementioned packages, we plan to support the following
+packages some time in the future. We currently have an unfinished
+implementation of 2D drawing and image processing.</p>
+
+<ul class="code">
+  <li>java.awt</li>
+  <li>java.awt.color</li>
+  <li>java.awt.event</li>
+  <li>java.awt.font</li>
+  <li>java.awt.geom</li>
+  <li>java.awt.im</li>
+  <li>java.awt.im.spi</li>
+  <li>java.awt.image</li>
+  <li>java.awt.image.renderable</li>
+  <li>javax.imageio</li>
+  <li>javax.imageio.event</li>
+  <li>javax.imageio.metadata</li>
+  <li>javax.imageio.plugins.bmp</li>
+  <li>javax.imageio.plugins.jpeg</li>
+  <li>javax.imageio.spi</li>
+  <li>javax.imageio.stream</li>
+</ul>
+
+<p style="margin-top: 50px">Copyright &copy; 2008 The Android Open Source
+Project</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-00-nop.html b/docs/opcodes/opcode-00-nop.html
new file mode 100644
index 0000000..726f560
--- /dev/null
+++ b/docs/opcodes/opcode-00-nop.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>nop</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>nop</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Waste cycles.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>00 10x</td>
+  <td>nop</td>
+  <td>&nbsp;</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<p>
+None.
+</p>
+
+<h2>Behavior</h2>
+
+<p>
+No externally observable effects, that is, all registers and object state(s)
+stay the same. The program counter silently advances to the next instruction.
+</p>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-01-move.html b/docs/opcodes/opcode-01-move.html
new file mode 100644
index 0000000..13c1150
--- /dev/null
+++ b/docs/opcodes/opcode-01-move.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the contents of one non-object register to another.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>01 12x</td>
+  <td>move vA, vB</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> source register (4 bits)</td>
+</tr>
+<tr>
+  <td>02 22x</td>
+  <td>move/from16 vAA, vBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> source register (16 bits)</td>
+</tr>
+<tr>
+  <td>03 32x</td>
+  <td>move/16 vAAAA, vBBBB</td>
+  <td><code>A:</code> destination register (16 bits)<br/>
+    <code>B:</code> source register (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A and B must be valid register indices in the current stack frame.
+  </li>
+  <li>
+    Register vB must be defined.
+  </li>
+  <li>
+    Register vB must not contain a reference value.
+  </li>
+  <li>
+    Register vB must not be part of a register pair.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The value of register vB is moved to register vA, that is, vA' = vB.
+  </li>
+  <li>
+    If register v(A-1) is the first half of a register pair, register v(A-1)'
+    becomes undefined.
+  </li>
+  <li>
+    If register v(A+1) is the second half of a register pair, register v(A+1)'
+    becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-04-move-wide.html b/docs/opcodes/opcode-04-move-wide.html
new file mode 100644
index 0000000..8a3bd00
--- /dev/null
+++ b/docs/opcodes/opcode-04-move-wide.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-wide</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-wide</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the contents of one register-pair to another.
+</p>
+<p>
+Note: It is legal to move from vN to either vN-1 or vN+1, so implementations
+must arrange for both halves of a register pair to be read before anything is
+written.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>04 12x</td>
+  <td>move-wide vA, vB</td>
+  <td><code>A:</code> destination register pair (4 bits)<br/>
+    <code>B:</code> source register pair (4 bits)</td>
+</tr>
+<tr>
+  <td>05 22x</td>
+  <td>move-wide/from16 vAA, vBBBB</td>
+  <td><code>A:</code> destination register pair (8 bits)<br/>
+    <code>B:</code> source register pair (16 bits)</td>
+</tr>
+<tr>
+  <td>06 32x</td>
+  <td>move-wide/16 vAAAA, vBBBB</td>
+  <td><code>A:</code> destination register pair (16 bits)<br/>
+    <code>B:</code> source register pair (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A+1 and B+1 must be valid register indices in the current stackframe
+    (which includes A and B being valid).
+  </li>
+  <li>
+    Register vB must be the lower half of a register pair (which excludes the
+    case of it containing a reference).
+  </li>
+  <li>
+    Both register vB and v(B+1) must be defined.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The value of register vB is moved to register vA, that is, vA' = vB.
+  </li>
+  <li>
+    The value of register v(B+1) is moved to register v(A+1), that is, v(A+1)'
+    = v(B+1).
+  </li>
+  <li>
+    If register v(A-1) is the lower half of a register pair, then v(A-1)'
+    becomes undefined.
+  </li>
+  <li>
+    If register v(A+2) is the upper half of a register pair, then v(A+2)'
+    becomes undefined.
+  </li>
+  <li>
+    If A = B-1, then v(B+1)' becomes undefined.
+  </li>
+  <li>
+    If A = B+1, then v(B)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-07-move-object.html b/docs/opcodes/opcode-07-move-object.html
new file mode 100644
index 0000000..f290277
--- /dev/null
+++ b/docs/opcodes/opcode-07-move-object.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-object</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-object</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the contents of one object-bearing register to another.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>07 12x</td>
+  <td>move-object vA, vB</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> source register (4 bits)</td>
+</tr>
+<tr>
+  <td>08 22x</td>
+  <td>move-object/from16 vAA, vBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> source register (16 bits)</td>
+</tr>
+<tr>
+  <td>09 32x</td>
+  <td>move-object/16 vAAAA, vBBBB</td>
+  <td><code>A:</code> destination register (16 bits)<br/>
+    <code>B:</code> source register (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A and B must be legal register indices in the current stack frame.
+  </li>
+  <li>
+    Register vB must be defined.
+  </li>
+  <li>
+    Register vB must contain a reference value (which excludes the case of it
+    being part of a register pair).
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The value of register vB is moved to register vA, that is, vA' = vB.
+  </li>
+  <li>
+    If register v(A-1) is the lower half of a register pair, register v(A-1)'
+    becomes undefined.
+  </li>
+  <li>
+    If register v(A+1) is the upper half of a register pair, register v(A+1)'
+    becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0a-move-result.html b/docs/opcodes/opcode-0a-move-result.html
new file mode 100644
index 0000000..616087f
--- /dev/null
+++ b/docs/opcodes/opcode-0a-move-result.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-result</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-result</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the single-word non-object result of the most recent invoke-kind into the
+indicated register. This must be done as the instruction immediately after an
+invoke-kind whose (single-word, non-object) result is not to be ignored;
+anywhere else is invalid.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>0a 11x</td>
+  <td>move-result vAA</td>
+  <td><code>A:</code> destination register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    The instruction must be immediately preceded (in the code array) by an
+    invoke-kind instruction.
+  </li>
+  <li>
+    The instruction must be immediately reached (in the actual control flow)
+    through returning from this invoke-kind instruction (it must not be jumped
+    to).
+  </li>
+  <li>
+    The result delivered by the invoke-kind instruction must not be a reference
+    value or require a register pair.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The result delivered by the invoke-kind instruction is moved to register
+    vA, that is, vA' = result.
+  </li>
+  <li>
+    If register v(A-1) is the lower half of a register pair, register v(A-1)'
+    becomes undefined.
+  </li>
+  <li>
+    If register v(A+1) is the upper half of a register pair, register v(A+1)'
+    becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+<h2>Notes</h2>
+
+<p>
+This instruction can also be thought of as reading the contents of a special
+"result" register that is made valid and defined by executing a non-void return
+instruction or a filled-new-array instruction. The execution of any other
+instruction (including this one) renders this special register invalid.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0b-move-result-wide.html b/docs/opcodes/opcode-0b-move-result-wide.html
new file mode 100644
index 0000000..c53517a
--- /dev/null
+++ b/docs/opcodes/opcode-0b-move-result-wide.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-result-wide</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-result-wide</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the double-word result of the most recent invoke-kind into the indicated
+register pair. This must be done as the instruction immediately after an
+invoke-kind whose (double-word) result is not to be ignored; anywhere else is
+invalid.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>0b 11x</td>
+  <td>move-result-wide vAA</td>
+  <td><code>A:</code> destination register pair (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A+1 must be a valid register index in the current stack frame (which
+    includes A itself being valid).
+  </li>
+  <li>
+    The instruction must be immediately preceded (in the code array) by an
+    invoke-kind instruction.
+  </li>
+  <li>
+    The instruction must be immediately reached (in the actual control flow)
+    through returning from this invoke-kind instruction (it must not be jumped
+    to).
+  </li>
+  <li>
+    The result delivered by the invoke-kind instruction must be either a long
+    or a double value.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The upper 32 bits of the result delivered by the invoke-kind instruction are
+    moved to register vA, that is, vA' = result >> 0x20.
+  </li>
+  <li>
+    The lower 32 bits of the result delivered by the invoke-kind instruction are
+    moved to register v(A+1), that is, v(A+1)' = result & 0xffffffff.
+  </li>
+  <li>
+    If register v(A-1) is the lower half of a register pair, register v(A-1)'
+    becomes undefined.
+  </li>
+  <li>
+    If register v(A+2) is the upper half of a register pair, register v(A+2)'
+    becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+<h2>Notes</h2>
+
+<p>
+This instruction can also be thought of as reading the contents of a special
+"result" register that is made valid and defined by executing a non-void return
+instruction or a filled-new-array instruction. The execution of any other
+instruction (including this one) renders this special register invalid.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0c-move-result-object.html b/docs/opcodes/opcode-0c-move-result-object.html
new file mode 100644
index 0000000..1538735
--- /dev/null
+++ b/docs/opcodes/opcode-0c-move-result-object.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-result-object</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-result-object</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the object result of the most recent invoke-kind into the indicated
+register. This must be done as the instruction immediately after an invoke-kind
+or filled-new-array whose (object) result is not to be ignored; anywhere else
+is invalid.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>0c 11x</td>
+  <td>move-result-object vAA</td>
+  <td><code>A:</code> destination register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    The instruction must be immediately preceded (in the code array) by an
+    invoke-kind, filled-new-array, or filled-new-array/range instruction.
+  </li>
+  <li>
+    The instruction must be immediately reached (in the actual control flow)
+    through returning from this invoke-kind instruction or by passing a
+    filled-new-array or filled-new-array/range instruction (it must not be
+    jumped to).
+  </li>
+  <li>
+    The result delivered by the invoke-kind instruction must be a reference
+    value (which excludes the case of a long and double values).
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The result delivered by the invoke-kind instruction is moved to register
+    vA, that, is vA' = result.
+  </li>
+  <li>
+    If register v(A-1) is the lower half of a register pair, register v(A-1)'
+    becomes undefined.
+  </li>
+  <li>
+    If register v(A+1) is the upper half of a register pair, register v(A+1)'
+    becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+<h2>Notes</h2>
+
+<p>
+This instruction can also be thought of as reading the contents of a special
+"result" register that is made valid and defined by executing a non-void return
+instruction or a filled-new-array instruction. The execution of any other
+instruction (including this one) renders this special register invalid.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0d-move-exception.html b/docs/opcodes/opcode-0d-move-exception.html
new file mode 100644
index 0000000..0f756d0
--- /dev/null
+++ b/docs/opcodes/opcode-0d-move-exception.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-exception</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-exception</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Save a just-caught exception into the given register. This should be the first
+instruction of any exception handler whose caught exception is not to be
+ignored, and this instruction may only ever occur as the first instruction of an
+exception handler; anywhere else is invalid.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>0d 11x</td>
+  <td>move-exception vAA</td>
+  <td><code>A:</code> destination register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    The instruction must be the first instruction (in the code array) of an
+    instruction handler, that is, its offset in the code array must match one of
+    the handlers defined for the method in the Dex file.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The active exception of the current thread is moved to register vA, that is,
+    vA' = exception.
+  </li>
+  <li>
+    If register v(A-1) is the lower half of a register pair, register v(A-1)'
+    becomes undefined.
+  </li>
+  <li>
+    If register v(A+1) is the upper half of a register pair, register v(A+1)'
+    becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0e-return-void.html b/docs/opcodes/opcode-0e-return-void.html
new file mode 100644
index 0000000..0498f81
--- /dev/null
+++ b/docs/opcodes/opcode-0e-return-void.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>return-void</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>return-void</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Return from a void method.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>0e 10x</td>
+  <td>return-void</td>
+  <td>&nbsp;</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    The return type of the current method must be void.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    If the method is synchronized, the object's monitor is released in a way
+    similar to the monitor-exit instruction.
+  </li>
+  <li>
+    The stack frame of the current method invocation is removed from the stack.
+    This includes all its registers becoming invalid.
+  </li>
+  <li>
+    If the stack is now empty, the current thread terminates.
+  </li>
+  <li>
+    Otherwise, the following happens:
+    <ul>
+      <li>
+        The stack frame that caused this method invocation becomes valid. This
+        includes all its registers and their old values.
+      </li>
+      <li>
+        Execution continues at the bytecode instruction immediately following
+        the invoke-kind or invoke-kind/range instruction that caused this method
+        invocation.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0f-return.html b/docs/opcodes/opcode-0f-return.html
new file mode 100644
index 0000000..4de55ea
--- /dev/null
+++ b/docs/opcodes/opcode-0f-return.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>return</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>return</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Return from a single-width (32-bit) non-object value-returning method.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>0f 11x</td>
+  <td>return vAA</td>
+  <td><code>A:</code> return value register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    The return type of the current method must not be double, long, or a
+    reference.
+  </li>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    Register vA must not be part of a register pair.
+  </li>
+  <li>
+    The type of vA must match the return type of the method.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    If the method is synchronized, the object's monitor is released in a way
+    similar to the monitor-exit instruction.
+  </li>
+  <li>
+    The stack frame of the current method invocation is removed from the stack.
+    This includes all its registers becoming invalid.
+  </li>
+  <li>
+    If the stack is now empty, the current thread terminates.
+  </li>
+  <li>
+    Otherwise, the following happens:
+    <ul>
+      <li>
+        The stack frame that caused this method invocation becomes valid. This
+        includes all its registers and their old values.
+      </li>
+      <li>
+        Execution continues at the bytecode instruction immediately following
+        the invoke-kind or invoke-kind/range instruction that caused this
+        method invocation.
+      </li>
+      <li>
+        The return value can be consumed by (exactly) the first instruction
+        following the invoke-kind or invoke-kind/range instruction that caused
+        this method invocation, and this instructions needs to be a move-result
+        instruction.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-10-return-wide.html b/docs/opcodes/opcode-10-return-wide.html
new file mode 100644
index 0000000..4ccfce4
--- /dev/null
+++ b/docs/opcodes/opcode-10-return-wide.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>return-wide</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>return-wide</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Return from a double-width (64-bit) value-returning method.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>10 11x</td>
+  <td>return-wide vAA</td>
+  <td><code>A:</code> return value register-pair (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    The return type of the current method must be double or long.
+  </li>
+  <li>
+    A+1 must be a valid register index in the current stack frame (which
+    includes A being valid).
+  </li>
+  <li>
+    Register vA must be the lower half of a register pair.
+  </li>
+  <li>
+    The type of vA must match the return type of the method.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    If the method is synchronized, the object's monitor is released in a way
+    similar to the monitor-exit instruction.
+  </li>
+  <li>
+    The stack frame of the current method invocation is removed from the stack.
+    This includes all its registers becoming invalid.
+  </li>
+  <li>
+    If the stack is now empty, the current thread terminates.
+  </li>
+  <li>
+    Otherwise, the following happens:
+    <ul>
+      <li>
+        The stack frame that caused this method invocation becomes valid. This
+        includes all its registers and their old values.
+      </li>
+      <li>
+        Execution continues at the bytecode instruction immediately following
+        the invoke instruction that caused this method invocation.
+      </li>
+      <li>
+        The return value can be consumed by (exactly) the first instruction
+        following the invoke-kind or invoke-kind/range instruction that caused
+        this method invocation, and this instructions needs to be a
+        move-result-wide instruction.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-11-return-object.html b/docs/opcodes/opcode-11-return-object.html
new file mode 100644
index 0000000..b4866ed
--- /dev/null
+++ b/docs/opcodes/opcode-11-return-object.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>return-object</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>return-object</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Return from an object-returning method.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>11 11x</td>
+  <td>return-object vAA</td>
+  <td><code>A:</code> return value register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    The return type of the current method must be a reference.
+  </li>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    Register vA must be known to be reference-bearing.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    If the method is synchronized, the object's monitor is released in a way
+    similar to the monitor-exit instruction.
+  </li>
+  <li>
+    The stack frame of the current method invocation is removed from the stack.
+    This includes all its registers becoming invalid.
+  </li>
+  <li>
+    If the stack is now empty, the current thread terminates.
+  </li>
+  <li>
+    Otherwise, the following happens:
+  </li>
+    <ul>
+      <li>
+        The stack frame that caused this method invocation becomes valid. This
+        includes all its registers and their old values.
+      </li>
+      <li>
+        Execution continues at the bytecode instruction immediately following
+        the invoke instruction that caused this method invocation.
+      </li>
+      <li>
+        The return value can be consumed by (exactly) the first instruction
+        following the invoke-kind or invoke-kind/range instruction that caused
+        this method invocation, and this instructions needs to be a
+        move-result-object instruction.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-12-const.html b/docs/opcodes/opcode-12-const.html
new file mode 100644
index 0000000..d2b6ef9
--- /dev/null
+++ b/docs/opcodes/opcode-12-const.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>const</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>const</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the given literal value (sign-extended to 32 bits, if necessary) into the
+specified register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>12 11n</td>
+  <td>const/4 vA, #+B</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> signed int (4 bits)</td>
+</tr>
+<tr>
+  <td>13 21s</td>
+  <td>const/16 vAA, #+BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (16 bits)</td>
+</tr>
+<tr>
+  <td>14 31i</td>
+  <td>const vAA, #+BBBBBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> arbitrary 32-bit constant</td>
+</tr>
+<tr>
+  <td>15 21h</td>
+  <td>const/high16 vAA, #+BBBB0000</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stackframe.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    First, an adjusted value B' is determined as follows:
+    <ul>
+      <li>
+        If we are executing the /high16 variant, then B is left-shifted by 16
+        bits, that is, B'=B << 0x10
+      <li>
+        Otherwise, if B is a 4 bit or 16 bit constant, it is sign-extended to 32
+        bits, that is, B'=sign-extended(B).
+      </li>
+      <li>
+        Otherwise, B'=B.
+      </li>
+    </ul>
+  <li>
+    Then, the adjusted value B' is moved into the register A, that is, vA'=B'
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-16-const-wide.html b/docs/opcodes/opcode-16-const-wide.html
new file mode 100644
index 0000000..6197e35
--- /dev/null
+++ b/docs/opcodes/opcode-16-const-wide.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>const-wide</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>const-wide</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the given literal value (sign-extended to 64 bits) into the specified
+register-pair.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>16 21s</td>
+  <td>const-wide/16 vAA, #+BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (16 bits)</td>
+</tr>
+<tr>
+  <td>17 31i</td>
+  <td>const-wide/32 vAA, #+BBBBBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (32 bits)</td>
+</tr>
+<tr>
+  <td>18 51l</td>
+  <td>const-wide vAA, #+BBBBBBBBBBBBBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> arbitrary double-width (64-bit) constant</td>
+</tr>
+<tr>
+  <td>19 21h</td>
+  <td>const-wide/high16 vAA, #+BBBB000000000000</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> signed int (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    First, an adjusted value B' is determined as follows:
+    <ul>
+      <li>
+        If we are executing the /high16 variant, then B is left-shifted by 40
+        bits, that is, B'=B << 0x28
+      <li>
+        Otherwise, if B is a 16 bit or 32 bit constant, it is sign-extended to
+        64 bits, that is, B'=sign-extended(B).
+      </li>
+      <li>
+        Otherwise, B'=B.
+      </li>
+    </ul>
+  <li>
+    The immediate value B is moved into the register pair (vA, v(A+1)), that is,
+    <ul>
+      <li>
+        vA' = B << 0x20
+      </li>
+      <li>
+        v(A+1)' = B & 0xffffffff
+      </li>
+    </ul>
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    If v(A+2) is the upper half of a register pair, v(A+2)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1a-const-string.html b/docs/opcodes/opcode-1a-const-string.html
new file mode 100644
index 0000000..d10c115
--- /dev/null
+++ b/docs/opcodes/opcode-1a-const-string.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>const-string</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>const-string</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move a reference to the string specified by the given index into the specified
+register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>1a 21c</td>
+  <td>const-string vAA, string@BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> string index</td>
+</tr>
+<tr>
+  <td>1b 31c</td>
+  <td>const-string/jumbo vAA, string@BBBBBBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> string index</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    B must be a valid index into the string constant pool.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    A new java.lang.String object S is allocated on the heap and filled with the
+    contents of string pool entry B.
+  </li>
+  <li>
+    A reference to an internalized version of the new object is moved into
+    register vA, that is, the instruction behaves as if vA' = S.intern() was
+    called.
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1b-const-class.html b/docs/opcodes/opcode-1b-const-class.html
new file mode 100644
index 0000000..f40b986
--- /dev/null
+++ b/docs/opcodes/opcode-1b-const-class.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>const-class</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>const-class</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move a reference to the class specified by the given index into the specified
+register. In the case where the indicated type is primitive, this will store a
+reference to the primitive type's degenerate class.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>1c 21c</td>
+  <td>const-class vAA, type@BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> type index</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    B must be a valid index into the type constant pool.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made to get a reference to the class C the name of which is
+    contained in type pool entry B.
+  </li>
+  <li>
+    If B refers to a primitive type, the corresponding degenerate class is used
+    instead.
+  </li>
+  <li>
+    If C has not been loaded and resolved before, it is being loaded and
+    resolved. All exceptions that are possible during class loading can occur at
+    this point.
+  </li>
+  <li>
+    A reference to C is moved into register vA, that is, vA' = C.
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    ClassNotFoundException is thrown if the class does not exist at all.
+  </li>
+  <li>
+    VerifyError is thrown if the class does exist, but could not be verified.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1d-monitor-enter.html b/docs/opcodes/opcode-1d-monitor-enter.html
new file mode 100644
index 0000000..28c10f4
--- /dev/null
+++ b/docs/opcodes/opcode-1d-monitor-enter.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>monitor-enter</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>monitor-enter</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Acquire the monitor for the indicated object.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>1d 11x</td>
+  <td>monitor-enter vAA</td>
+  <td><code>A:</code> reference-bearing register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index for the current stack frame.
+  </li>
+  <li>
+    Register vA must contain a reference to an object.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made for the current thread to acquire the monitor of the
+    indicated object. Various results are possible:
+    <ul>
+      <li>
+        If the monitor is not owned by any thread at this point, then the
+        current thread becomes owner of the monitor. The entry count of the
+        indicated object is set to 1.
+      </li>
+      <li>
+        Otherwise, if the monitor is owned by the same thread that attempts the
+        acquiration, then the entry count of the indicated object is increased
+        by 1.
+      </li>
+      <li>
+        Otherwise the monitor is owned by a different thread. The current thread
+        sleeps until the monitor of the object is released. Once that happens, a
+        new attempt to acquire the monitor is made, as described here. There is
+        no guarantee that the second attempt (or any subsequent attempt) will be
+        successful.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if vA is null.
+  </li>
+  <li>
+    IllegalMonitorStateException if the entry count exceeds an
+    (implementation-dependent) upper bound for recursive monitor entries. Note
+    that it is unlikely this bound is ever hit, since for most implementations
+    the call stack will overflow before.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1e-monitor-exit.html b/docs/opcodes/opcode-1e-monitor-exit.html
new file mode 100644
index 0000000..cd7b165
--- /dev/null
+++ b/docs/opcodes/opcode-1e-monitor-exit.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>monitor-exit</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>monitor-exit</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Release the monitor for the indicated object.
+</p>
+<p>
+Note: If this instruction needs to throw an exception, it must do so as if the
+pc has already advanced past the instruction. It may be useful to think of this
+as the instruction successfully executing (in a sense), and the exception
+getting thrown after the instruction but before the next one gets a chance to
+run. This definition makes it possible for a method to use a monitor cleanup
+catch-all (e.g., finally) block as the monitor cleanup for that block itself,
+as a way to handle the arbitrary exceptions that might get thrown due to the
+historical implementation of Thread.stop(), while still managing to have proper
+monitor hygiene.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>1e 11x</td>
+  <td>monitor-exit vAA</td>
+  <td><code>A:</code> reference-bearing register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index for the current stack frame.
+  </li>
+  <li>
+    Register vA must contain a reference to an object.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made for the current thread to release the monitor of the
+    indicated object.
+  </li>
+  <li>
+    If the current thread is the owner, the following happens:
+    <ul>
+      <li>
+        The monitor's entry count is decreased by one.
+      </li>
+      <li>
+        If the entry count has reached zero, the monitor is released. Other
+        threads waiting for the same monitor have a chance to acquire it.
+      </li>
+    </ul>
+  </li>
+  <li>
+    Any exception that gets thrown by this instruction bears the PC of the
+    instruction following the monitor-exit. That is, from the point of view of
+    an exception handler it cannot be distinguished from the same type of
+    exception being thrown immediately after the monitor-exit instruction.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException is thrown if vA is null.
+  </li>
+  <li>
+    IllegalMonitorStateException is thrown if the current thread is not the
+    owner of that monitor.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1f-check-cast.html b/docs/opcodes/opcode-1f-check-cast.html
new file mode 100644
index 0000000..8eedd2d
--- /dev/null
+++ b/docs/opcodes/opcode-1f-check-cast.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>check-cast</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>check-cast</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Throw if the reference in the given register cannot be cast to the indicated
+type. The type must be a reference type (not a primitive type).
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>1f 21c</td>
+  <td>check-cast vAA, type@BBBB</td>
+  <td><code>A:</code> reference-bearing register (8 bits)<br/>
+    <code>B:</code> type index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    Register vA must contain a reference value.
+  </li>
+  <li>
+    B must be a valid index into the type pool.
+  </li>
+  <li>
+    Type pool entry B must contain a valid type descriptor for a reference type.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made to get a reference to the class C the name of which is
+    contained in type pool entry B.
+  </li>
+  <li>
+    If C has not been loaded and resolved before, it is being loaded and
+    resolved. All exceptions that are possible during class loading can occur at
+    this point.
+  </li>
+  <li>
+    The run-time type of the object reference vA is compared against C.
+    <ul>
+      <li>
+        If vA is null, the instruction succeeds (without further effects).
+      </li>
+      <li>
+        If vA is assignment compatible with C according to the usual rules of
+        the Java programming language, the instruction succeeds (without further
+        effects).
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    ClassCastException is thrown if vA is either not null or not assignment
+    compatible with C.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-20-instance-of.html b/docs/opcodes/opcode-20-instance-of.html
new file mode 100644
index 0000000..88076d8
--- /dev/null
+++ b/docs/opcodes/opcode-20-instance-of.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>instance-of</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>instance-of</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Store in the given destination register 1 if the indicated reference is an
+instance of the given type, or 0 if not. The type must be a reference type (not
+a primitive type).
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>20 22c</td>
+  <td>instance-of vA, vB, type@CCCC</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> reference-bearing register (4 bits)<br/>
+    <code>C:</code> type index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A and B must be a valid register indices for the current stack frame.
+  </li>
+  <li>
+    Register vB must contain a reference value.
+  </li>
+  <li>
+    C must be a valid index into the type constant pool.
+  </li>
+  <li>
+    Type constant pool entry C must contain a valid type descriptor for a
+    reference type.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made to get a reference to the class K the name of which is
+    contained in type pool entry C.
+  </li>
+  <li>
+    If K has not been loaded and resolved before, it is being loaded and
+    resolved. All exceptions that are possible during class loading can occur at
+    this point.
+  </li>
+  <li>
+    The run-time type of the object reference vB is compared against K. The
+    register vA reflects the result:
+    <ul>
+      <li>
+        vA' = 1 if (and only if) vB is not null and vB is assignment compatible
+        with K according to the usual rules of the Java programming language.
+      </li>
+      <li>
+        vA' = 0 otherwise
+      </li>
+    </ul>
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+  </li>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-21-array-length.html b/docs/opcodes/opcode-21-array-length.html
new file mode 100644
index 0000000..8072a7c
--- /dev/null
+++ b/docs/opcodes/opcode-21-array-length.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>array-length</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>array-length</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Store in the given destination register the length of the indicated array,
+in entries.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>21 12x</td>
+  <td>array-length vA, vB</td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> array reference-bearing register (4 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A and B must be valid register indices for the current stack frame.
+  </li>
+  <li>
+    Register vB must contain a reference to an array.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The length of the array referenced by vB is stored in vA, that is
+    vA' = length(vB).
+  </li>
+  <li>
+    If register v(A-1) is the lower half of a register pair, register v(A-1)'
+    becomes undefined.
+  </li>
+  <li>
+    If register v(A+1) is the upper half of a register pair, register v(A+1)'
+    becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException is thrown if the value of register vB is null.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-22-new-instance.html b/docs/opcodes/opcode-22-new-instance.html
new file mode 100644
index 0000000..bdcfc3e
--- /dev/null
+++ b/docs/opcodes/opcode-22-new-instance.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>new-instance</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>new-instance</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Construct a new instance of the indicated type, storing a reference to it in the
+destination. The type must refer to a non-array class.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>22 21c</td>
+  <td>new-instance vAA, type@BBBB</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> type index</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index for the current stack frame.
+  </li>
+  <li>
+    B must be a valid index into the type pool.
+  </li>
+  <li>
+    Type constant pool entry B must contain a valid type descriptor for a
+    non-array class.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made to get a reference to the class C the name of which is
+    contained in type pool entry B.
+  </li>
+  <li>
+    If C has not been loaded and resolved before, it is being loaded and
+    resolved. All exceptions that are possible during class loading can occur at
+    this point.
+  </li>
+  <li>
+    An attempt is made to create a new instance I of C. All exceptions that are
+    possible during instantiation can occur at this point.
+  </li>
+  <li>
+    A reference to the new instance is stored in register vA, that is vA' = I.
+  </li>
+  <li>
+    If v(A-1) is the lower part of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    If v(A+1) is the upper part of a register pair, v(A+1)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    All exceptions that are possible during class loading can occur.
+  </li>
+  <li>
+    All exceptions that are possible during instantiation can occur.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-23-new-array.html b/docs/opcodes/opcode-23-new-array.html
new file mode 100644
index 0000000..29327e9
--- /dev/null
+++ b/docs/opcodes/opcode-23-new-array.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>new-array</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>new-array</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Construct a new array of the indicated type and size. The type must be an array
+type.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>23 22c</td>
+  <td>new-array vA, vB, type@CCCC</td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> size register<br/>
+    <code>C:</code> type index</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A and B must be valid register indices for the current stack frame.
+  </li>
+  <li>
+    Register vB must not contain a reference value.
+  </li>
+  <li>
+    Register vB must not be part of a register pair.
+  </li>
+  <li>
+    C must be a valid index into the type pool.
+  </li>
+  <li>
+    Type constant pool entry C must contain a valid array type descriptor.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made to get a reference to the class K the name of which is
+    contained in type pool entry C.
+  </li>
+  <li>
+    If K has not been loaded and resolved before, it is being loaded and
+    resolved. All exceptions that are possible during class loading can occur at
+    this point.
+  </li>
+  <li>
+    An attempt is made to create a new instance I of K and length B. All
+    exceptions that are possible during instantiation can occur at this point.
+  </li>
+  <li>
+    All elements of the new array are initialized to null (for object arrays) or
+    0 (for numeric arrays) or false (for boolean arrays).
+  </li>
+  <li>
+    A reference to the new array is moved to register vA, that is, vA' = I.
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NegativeArraySizeException if vB < 0
+  </li>
+  <li>
+    All exceptions that are possible during class loading can occur.
+  </li>
+  <li>
+    All exceptions that are possible during instantiation can occur.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-24-filled-new-array.html b/docs/opcodes/opcode-24-filled-new-array.html
new file mode 100644
index 0000000..1dfa089
--- /dev/null
+++ b/docs/opcodes/opcode-24-filled-new-array.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>filled-new-array</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>filled-new-array</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Construct an array of the given type and size, filling it with the supplied
+contents. The type must be an array type. The array's contents must be
+single-word (that is, no arrays of long or double). The constructed instance is
+stored as a "result" in the same way that the method invocation instructions
+store their results, so the constructed instance must be moved to a register
+with a subsequent move-result-object instruction (if it is to be used).
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>24 35c</td>
+  <td>filled-new-array {vD, vE, vF, vG, vA}, type@CCCC</td>
+  <td><code>B:</code> array size and argument word count (4 bits)<br/>
+    <code>C:</code> type index (16 bits)<br/>
+    <code>D..G, A:</code> argument registers (4 bits each)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    The value B must not be greater than 5.
+  </li>
+  <li>
+    If B > 0, then D must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    If B > 1, then E must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    If B > 2, then F must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    If B > 3, then G must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    If B > 4, then A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    C must be a valid index into the type pool.
+  </li>
+  <li>
+    The type denoted by C must be a valid array type descriptor.
+  </li>
+  <li>
+    The element size of the type denoted by C must be no larger than 32 bits.
+  </li>
+  <li>
+    If the element type is a primitive type, then all actual arguments
+    (vD .. vA, depending on B) must be primitive, too.
+  </li>
+  <li>
+    If the element type is a reference type, then all actual arguments
+    (vD .. vA, depending on B) must be references, too.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made to get a reference to the type T the name of which is
+    contained in type pool entry C.
+  </li>
+  <li>
+    If T is a reference type and it has not been loaded and resolved before, it
+    is being loaded and resolved. All exceptions that are possible during class
+    loading can occur at this point.
+  </li>
+  <li>
+    An attempt is made to create a new array R of type T and length B. All
+    exceptions that are possible during instantiation can occur at this point.
+  </li>
+  <li>
+    The elements of R are filled according to the following rules:
+    <ul>
+      <li>
+        If B > 0 then R[0] = vD
+      </li>
+      <li>
+        If B > 1 then R[1] = vE
+      </li>
+      <li>
+        If B > 2 then R[2] = vF
+      </li>
+      <li>
+        If B > 3 then R[3] = vG
+      </li>
+      <li>
+        If B > 4 then R[4] = vA
+      </li>
+    </ul>
+  </li>
+  <li>
+    No reference to R is stored in any register. Instead, R can be accessed by a
+    move-result-object instruction immediately following this filled-new-array
+    instruction.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NegativeArraySizeException if vB < 0
+  </li>
+  <li>
+    All exceptions that are possible during class loading can occur.
+  </li>
+  <li>
+    All exceptions that are possible during instantiation can occur.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-25-filled-new-array-range.html b/docs/opcodes/opcode-25-filled-new-array-range.html
new file mode 100644
index 0000000..2ee7505
--- /dev/null
+++ b/docs/opcodes/opcode-25-filled-new-array-range.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>filled-new-array/range</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>filled-new-array/range</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Construct an array of the given type and size, filling it with the supplied
+contents. Clarifications and restrictions are the same as filled-new-array,
+described above.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>25 3rc</td>
+  <td>filled-new-array/range {vCCCC .. vNNNN}, type@BBBB</td>
+  <td><code>A:</code> array size and argument word count (8 bits)<br/>
+    <code>B:</code> type index (16 bits)<br/>
+    <code>C:</code> first argument register (16 bits)<br/>
+    <code>N = A + C - 1</code></td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    vN must be a valid register index in the current stack frame (this means
+    everything below vN is valid, too).
+  </li>
+  <li>
+    For all values I in the interval [C .. N] the following must hold:
+    <ul>
+      <li>
+        vI must not be part of a register pair
+      </li>
+      <li>
+        If the array type is a simple type, vI must be a simple type, too.
+      </li>
+      <li>
+        If the array type is a reference type, vI must be a reference type, too.
+      </li>
+    </ul>
+  </li>
+  <li>
+    B must be a valid index into the type pool.
+  </li>
+  <li>
+    The type denoted by B must be an array type.
+  </li>
+  <li>
+    The element size of the type denoted by B must be no larger than 32 bits.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    An attempt is made to get a reference to the type T the name of which is
+    contained in type pool entry B.
+  </li>
+  <li>
+    If T has not been loaded and resolved before, it is being loaded and
+    resolved. All exceptions that are possible during class loading can occur at
+    this point.
+  </li>
+  <li>
+    An attempt is made to create a new instance J of type T and length vA. All
+    exceptions that are possible during instantiation can occur at this point.
+  </li>
+  <li>
+    The elements of R are filled according to the following rules:
+    <ul>
+      <li>
+        J[0] = vC
+      </li>
+      <li>
+        J[1] = v(C+1)
+      </li>
+      <li>
+        ...
+      </li>
+      <li>
+        J[vA] = vN
+      </li>
+    </ul>
+  </li>
+  <li>
+    No reference to J is stored in any register. Instead, J can be accessed by a
+    move-result-object instruction immediately following this filled-new-array
+    instruction.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NegativeArraySizeException if vA < 0
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-26-fill-array-data.html b/docs/opcodes/opcode-26-fill-array-data.html
new file mode 100644
index 0000000..77b45ae
--- /dev/null
+++ b/docs/opcodes/opcode-26-fill-array-data.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>fill-array-data</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>fill-array-data</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Fill the given array with the indicated data. The reference must be to an array
+of primitives, and the data table must match it in type and size.
+</p>
+<p>
+Note: The address of the table is guaranteed to be even (that is, 4-byte
+aligned). If the code size of the method is otherwise odd, then an extra code
+unit is inserted between the main code and the table whose value is the same as
+a nop.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+  <td>26 31t</td>
+  <td>fill-array-data vAA, +BBBBBBBB <i>(with supplemental data as specified
+    below in "<code>fill-array-data</code> Format")</i></td>
+  <td><code>A:</code> array reference (8 bits)<br/>
+    <code>B:</code> signed "branch" offset to table data (32 bits)</td>
+  </td>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    vA must be a reference-bearing register (according to data flow) and contain
+    an array-reference.
+  </li>
+  <li>
+    B must be branch offset in the same method.
+  </li>
+  <li>
+    The target address (PC+B) must be 4-byte aligned.
+  </li>
+  <li>
+    The target address must hold the pseudo-opcode 0x300.
+  </li>
+  <li>
+    The table entry size must match the size of the data type of the array.
+  </li>
+  <li>
+    The table size must be equal or smaller than the array length.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The contents of the array referenced by vA are filled with the table data,
+    starting from array index 0 and in the given order.
+  </li>
+  <li>
+    If there are less elements in the table than the array provides space for,
+    the remaining array elements stay untouched.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if vA is null.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-27-throw.html b/docs/opcodes/opcode-27-throw.html
new file mode 100644
index 0000000..1a0eb09
--- /dev/null
+++ b/docs/opcodes/opcode-27-throw.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>throw</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>throw</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Throw the indicated exception.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>27 11x</td>
+  <td>throw vAA</td>
+  <td><code>A:</code> exception-bearing register (8 bits)<br/></td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    Register vA must be a reference-bearing register
+  </li>
+  <li>
+    Register vA must be assignment-compatible with java.lang.Throwable according
+    to the usual rules of the Java programming language.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    Throws the given exception vA, resulting in a search for a matching handler
+    according to the usual rules of the Java programming language.
+  </li>
+  <li>
+    If no matching handler is found for the current thread, the thread
+    terminates, possibly notifying its uncaught exception handler or thread
+    group before.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if vA is null.
+  </li>
+  <li>
+    Otherwise, the indicated exception.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-28-goto.html b/docs/opcodes/opcode-28-goto.html
new file mode 100644
index 0000000..fec294c
--- /dev/null
+++ b/docs/opcodes/opcode-28-goto.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>goto</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>goto</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Unconditionally jump to the indicated instruction.
+</p>
+<p>
+Note: The branch offset may not be 0. (A spin loop may be legally constructed
+either with goto/32 or by including a nop as a target before the branch.)
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>28 10t</td>
+  <td>goto +AA</td>
+  <td><code>A:</code> signed branch offset (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must point to a valid bytecode instruction inside the current method.
+  </li>
+  <li>
+    A must not be 0.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The (otherwise invisible) program counter PC is set to the address of the
+    instruction plus the given offset, that is, PC' = PC(goto) + A.
+  </li>
+  <li>
+    Executions resumes at PC'.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-29-goto-16.html b/docs/opcodes/opcode-29-goto-16.html
new file mode 100644
index 0000000..791456b
--- /dev/null
+++ b/docs/opcodes/opcode-29-goto-16.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>goto/16</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>goto/16</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Unconditionally jump to the indicated instruction.
+</p>
+<p>
+Note: The branch offset may not be 0. (A spin loop may be legally constructed
+either with goto/32 or by including a nop as a target before the branch.)
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>29 20t</td>
+  <td>goto/16 +AAAA</td>
+  <td><code>A:</code> signed branch offset (16 bits)<br/></td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must point to a valid bytecode instruction inside the current method.
+  </li>
+  <li>
+    A must not be 0.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The (otherwise invisible) program counter PC is set to the address of the
+    instruction plus the given offset, that is, PC' = PC(goto) + A.
+  </li>
+  <li>
+    Executions resumes at PC'.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-2a-goto-32.html b/docs/opcodes/opcode-2a-goto-32.html
new file mode 100644
index 0000000..b98dd85
--- /dev/null
+++ b/docs/opcodes/opcode-2a-goto-32.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>goto/32</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>goto/32</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Unconditionally jump to the indicated instruction.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>2a 30t</td>
+  <td>goto/32 +AAAAAAAA</td>
+  <td><code>A:</code> signed branch offset (32 bits)<br/></td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must point to a valid bytecode instruction inside the current method.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The (otherwise invisible) program counter PC is set to the address of the
+    instruction plus the given offset, that is, PC' = PC(goto) + A.
+  </li>
+  <li>
+    Executions resumes at PC'.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-2b-packed-switch.html b/docs/opcodes/opcode-2b-packed-switch.html
new file mode 100644
index 0000000..b2d5251
--- /dev/null
+++ b/docs/opcodes/opcode-2b-packed-switch.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>packed-switch</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>packed-switch</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Jump to a new instruction based on the value in the given register, using a
+table of offsets corresponding to each value in a particular integral range, or
+fall through to the next instruction if there is no match.
+</p>
+<p>
+Note: The address of the table is guaranteed to be even (that is, 4-byte
+aligned). If the code size of the method is otherwise odd, then an extra code
+unit is inserted between the main code and the table whose value is the same as
+a nop.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>2b 31t</td>
+  <td>packed-switch vAA, +BBBBBBBB <i>(with supplemental data as
+    specified below in "<code>packed-switch</code> Format")</i></td>
+  <td><code>A:</code> register to test<br/>
+    <code>B:</code> signed "branch" offset to table data (32 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    Let PC be the address of the packed-switch instruction in the code array of
+    the current method. Then T = PC + B with the following properties:
+    <ul>
+      <li>
+        T must be 4-byte-aligned.
+      </li
+      <li>
+        T must be in the same method.
+      </li>
+      <li>
+        T must point to a packed-switch data table.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The value of vA is used as an index into the given table data.
+  </li>
+  <li>
+    If vA is in the range of the table, that is, if vA >= table.first_key and
+    vA < first_key + size, then the jump target is determined as follows:
+    <ul>
+      <li>
+        PC' = PC + table.targets[vA - table.firstKey].
+      </li>
+      <li>
+        Execution resumes at this address.
+      </li>
+    </ul>
+  </li>
+  <li>
+    Otherwise execution continues at the instruction following the packed-switch
+    statement.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-2c-sparse-switch.html b/docs/opcodes/opcode-2c-sparse-switch.html
new file mode 100644
index 0000000..9d81eda
--- /dev/null
+++ b/docs/opcodes/opcode-2c-sparse-switch.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>sparse-switch</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>sparse-switch</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Jump to a new instruction based on the value in the given register, using an
+ordered table of value-offset pairs, or fall through to the next instruction if
+there is no match.
+</p>
+<p>
+Note: The address of the table is guaranteed to be even (that is, 4-byte
+aligned). If the code size of the method is otherwise odd, then an extra code
+unit is inserted between the main code and the table whose value is the same as
+a nop.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>2c 31t</td>
+  <td>sparse-switch vAA, +BBBBBBBB <i>(with supplemental data as
+    specified below in "<code>sparse-switch</code> Format")</i></td>
+  <td><code>A:</code> register to test<br/>
+    <code>B:</code> signed "branch" offset to table data (32 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stack frame.
+  </li>
+  <li>
+    Let PC be the address of the packed-switch instruction in the code array of
+    the current method. Then T = PC + B with the following properties:
+    <ul>
+      <li>
+        T must be 4-byte-aligned.
+      </li>
+      <li>
+        T must be in the same method.
+      </li>
+      <li>
+        T must point to a sparse-switch data table.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The value of vA is used as a lookup key inside the sparse table data.
+  </li>
+  <li>
+    If there exists an I with 0 <= I < table.size such that table.keys[I] = vA,
+    then the jump target is determined as follows:
+    <ul>
+      <li>
+        PC' = PC + table.targets[I].
+      </li>
+      <li>
+        Execution will resume at this address.
+      </li>
+    </ul>
+  </li>
+  <li>
+    Otherwise execution continues at the instruction following the sparse-switch
+    statement.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+<h2>Notes</h2>
+
+<p>
+The low-to-high ordering of the keys allows the VM to employ binary search for
+the lookup, resulting in O(log table.size) comparisons.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-2d-cmp-kind.html b/docs/opcodes/opcode-2d-cmp-kind.html
new file mode 100644
index 0000000..f55a006
--- /dev/null
+++ b/docs/opcodes/opcode-2d-cmp-kind.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>cmp&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>cmp&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the indicated floating point or long comparison, storing 0 if the two
+arguments are equal, 1 if the second argument is larger, or -1 if the first
+argument is larger. The "bias" listed for the floating point operations
+indicates how NaN comparisons are treated: "Gt bias" instructions return 1 for
+NaN comparisons, and "lt bias" instructions return -1.
+</p>
+<p>
+For example, to check to see if floating point a < b, then it is advisable to
+use cmpg-float; a result of -1 indicates that the test was true, and the other
+values indicate it was false either due to a valid comparison or because one
+or the other values was NaN.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>2d..31 23x</td>
+  <td>cmp<i>kind</i> vAA, vBB, vCC<br/>
+    2d: cmpl-float <i>(lt bias)</i><br/>
+    2e: cmpg-float <i>(gt bias)</i><br/>
+    2f: cmpl-double <i>(lt bias)</i><br/>
+    30: cmpg-double <i>(gt bias)</i><br/>
+    31: cmp-long
+  </td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> first source register or pair<br/>
+    <code>C:</code> second source register or pair</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A, B and C must be valid register indices in the current stack frame.
+  </li>
+  <li>
+    For the two -float variants, both vB and vC must be of type float.
+  </li>
+  <li>
+    For the two -double variants, both vB and vC must be the lower part of a
+    register pair holding a double value.
+  </li>
+  <li>
+    For the -long variant, both both vB and vC must be the lower part of a
+    register pair holding a long value.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The values of registers vB and vC are compared. The result, which is stored
+    in vA, is one of the following three:
+    <ul>
+      <li>
+        If vB < vC, then vA'=-1.
+      </li>
+      <li>
+        If vB == vC, then vA'=0.
+      </li>
+      <li>
+        If vC > vC, then vA'=1.
+      </li>
+    </ul>
+  </li>
+  <li>
+    For the -float and -double variants, an addition "bias" specifies what
+    happens if one or both of the arguments are NaN:
+    <ul>
+      <li>
+        A "lt bias" results in vA'=-1.
+      </li>
+      <li>
+        A "gt bias" results in vA'=1.
+      </li>
+    </ul>
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-32-if-test.html b/docs/opcodes/opcode-32-if-test.html
new file mode 100644
index 0000000..ee394f6
--- /dev/null
+++ b/docs/opcodes/opcode-32-if-test.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>if-test</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>if-test</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Branch to the given destination if the given two registers' values compare as
+specified.
+</p>
+<p>
+Note: The branch offset may not be 0. (A spin loop may be legally constructed
+either by branching around a backward goto or by including a nop as a target
+before the branch.)
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>32..37 22t</td>
+  <td>if-<i>test</i> vA, vB, +CCCC<br/>
+    32: if-eq<br/>
+    33: if-ne<br/>
+    34: if-lt<br/>
+    35: if-ge<br/>
+    36: if-gt<br/>
+    37: if-le<br/>
+  </td>
+  <td><code>A:</code> first register to test (4 bits)<br/>
+    <code>B:</code> second register to test (4 bits)<br/>
+    <code>C:</code> signed branch offset (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A and B must be valid register indices for the current stack frame.
+  </li>
+  <li>
+    Registers vA and vB must not contain a reference value.
+  </li>
+  <li>
+    Registers vA and vB must not be part of a register pair.
+  </li>
+  <li>
+    Registers vA and vB must not contain a floating point value (???).
+  </li>
+    C must of a signed offset that, when added to the PC of the instruction,
+    points to a valid bytecode instruction inside the same method.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The values of registers vA and vB are compared according to the &lt;test&gt;
+    condition. Two results are possible:
+    <ul>
+      <li>
+        The condition holds. The value of C is used as a signed offset to the
+        address of the if-&lt;test&gt; instruction. Execution continues at the
+        resulting address.
+      </li>
+      <li>
+        The condition does not hold. Execution continues at the instruction
+        following the if-&lt;test&gt; instruction.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-38-if-testz.html b/docs/opcodes/opcode-38-if-testz.html
new file mode 100644
index 0000000..060bbdb
--- /dev/null
+++ b/docs/opcodes/opcode-38-if-testz.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>if-&lt;test&gt;z</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>if-&lt;test&gt;z</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Branch to the given destination if the given register's value compares with 0
+as specified.
+</p>
+<p>
+  Note: The branch offset may not be 0. (A spin loop may be legally constructed
+  either by branching around a backward goto or by including a nop as a target
+  before the branch.)
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>38..3d 21t</td>
+  <td>if-<i>test</i>z vAA, +BBBB<br/>
+    38: if-eqz<br/>
+    39: if-nez<br/>
+    3a: if-ltz<br/>
+    3b: if-gez<br/>
+    3c: if-gtz<br/>
+    3d: if-lez<br/>
+  </td>
+  <td><code>A:</code> register to test (8 bits)<br/>
+    <code>B:</code> signed branch offset (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index for the current stackframe.
+  </li>
+  <li>
+    Register vA must not contain a reference value.
+  </li>
+  <li>
+    Register vA must not be part of a register pair.
+  </li>
+  <li>
+    Register vA must not contain a floating point value (???).
+  </li>
+  <li>
+    B must not be 0.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The value of register vA is compared to zero according to the &lt;test&gt;
+    condition. Two results are possible:
+    <ul>
+      <li>
+        The condition holds. The value of B is used as a signed offset to the
+        address of the if-&lt;test&gt;z instruction. Execution continues at the
+        resulting address.
+      </li>
+      <li>
+        The condition does not hold. Execution continues at the instruction
+        following the if-&lt;test&gt;z instruction.
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-44-aget.html b/docs/opcodes/opcode-44-aget.html
new file mode 100644
index 0000000..6e8836f
--- /dev/null
+++ b/docs/opcodes/opcode-44-aget.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>aget&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>aget&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified array operation at the identified index of the given
+array, storing into the value register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>44..51 23x</td>
+  <td><i>arrayop</i> vAA, vBB, vCC<br/>
+    44: aget<br/>
+    45: aget-wide<br/>
+    46: aget-object<br/>
+    47: aget-boolean<br/>
+    48: aget-byte<br/>
+    49: aget-char<br/>
+    4a: aget-short<br/>
+  </td>
+  <td><code>A:</code> dest value register or pair; (8 bits)<br/>
+    <code>B:</code> array register (8 bits)<br/>
+    <code>C:</code> index register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A, B and C must be valid register indices in the current stackframe.
+  </li>
+  <li>
+    For the aget-wide variant, also A+1 must be a valid register index in the
+    current stackframe.
+  </li>
+  <li>
+    Register vB must contain an array reference. The component type of the
+    array must match the variant of the instruction.
+  </li>
+  <li>
+    Register vC must contain an integer value.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    For all but the -wide variant, the array element at the given index is moved
+    into register vA, that is, vA'=array[index].
+  </li>
+  <li>
+    For the -wide variant, the array element at the given index is moved into
+    registers vA and v(A+1) as follows:
+    <ul>
+      <li>
+        vA'=array[index] >> 0x20
+      </li>
+      <li>
+        v(A+1)'=array[index] & 0xffffffff;
+      </li>
+    </ul>
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    For all but the -wide variant, if v(A+1) is the upper half of a register
+    pair, v(A+1)' becomes undefined.
+  </li>
+  <li>
+    For the -wide variant, if v(A+2) is the upper half of a register pair,
+    v(A+2)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if vB=null.
+  </li>
+  <li>
+    ArrayIndexOutOfBoundsException if vC < 0 or vC >= array.length.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-4b-aput.html b/docs/opcodes/opcode-4b-aput.html
new file mode 100644
index 0000000..089c1ca
--- /dev/null
+++ b/docs/opcodes/opcode-4b-aput.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>aput&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>aput&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Waste cycles.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>4b..51 23x</td>
+  <td><i>arrayop</i> vAA, vBB, vCC<br/>
+    4b: aput<br/>
+    4c: aput-wide<br/>
+    4d: aput-object<br/>
+    4e: aput-boolean<br/>
+    4f: aput-byte<br/>
+    50: aput-char<br/>
+    51: aput-short
+  </td>
+  <td><code>A:</code> source value register or pair; (8 bits)<br/>
+    <code>B:</code> array register (8 bits)<br/>
+    <code>C:</code> index register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A, B and C must be valid register indices in the current stack frame.
+  </li>
+  <li>
+    For the aget-wide variant, also A+1 must be a valid register index in the
+    current stack frame.
+  </li>
+  <li>
+    Register vB must contain an array reference. The component type of the array
+    must match the variant of the instruction.
+  </li>
+  <li>
+    Register vC must contain an integer value.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    For all but the -wide variant, the value of register vA is move into the
+    array element at the given index, that is, array[index]'=vA.
+  </li>
+  <li>
+    For the -wide variant, the registers vA and v(A+1) are moved into the array
+    element at the given index as follows:
+    <ul>
+      <li>
+        array[index]' = vA &lt;&lt; 0x20 | v(A+1)
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if vB=null.
+  </li>
+  <li>
+    ArrayIndexOutOfBoundsException if vC &lt; 0 or vC &gt;= array.length.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-52-iget.html b/docs/opcodes/opcode-52-iget.html
new file mode 100644
index 0000000..837b511
--- /dev/null
+++ b/docs/opcodes/opcode-52-iget.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>iget&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>iget&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified object instance field operation with the identified
+field, loading or storing into the value register.
+</p>
+<p>
+Note: These opcodes are reasonable candidates for static linking, altering the
+field argument to be a more direct offset.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>52..58 22c</td>
+  <td>i<i>instanceop</i> vA, vB, field@CCCC<br/>
+    52: iget<br/>
+    53: iget-wide<br/>
+    54: iget-object<br/>
+    55: iget-boolean<br/>
+    56: iget-byte<br/>
+    57: iget-char<br/>
+    58: iget-short<br/>
+  </td>
+  <td><code>A:</code> dest value register or pair; (4 bits)<br/>
+    <code>B:</code> object register (4 bits)<br/>
+    <code>C:</code> instance field reference index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A and B must be valid register indices in the current stackframe.
+  </li>
+  <li>
+    For the -wide variant, also A+1 must be a valid register index in the
+    current stackframe.
+  </li>
+  <li>
+    Register vB must contain an object reference.
+  </li>
+  <li>
+    C must be a valid index into the field reference pool.
+  </li>
+  <li>
+    The field must be an instance field. The type of the field denoted by C must
+    match the variant of the instruction.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The value of the given instance field is read from the given object and
+    moved into the given register vA, that is, vA'=&lt;object&gt;.&lt;field&gt;.
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+    <li>For all but the -wide variant, if v(A+1) is the upper half of a register
+    pair, v(A+1)' becomes undefined.
+  </li>
+  <li>
+    For the -wide variant, if v(A+2) is the upper half of a register pair,
+    v(A+2)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if object is null.
+  </li>
+  <li>
+    IllegalAccessException if &lt;object&gt;.&lt;field&gt; is not visible from
+    the current context according to the usual visibility and access rules of
+    the Java programming language.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-59-iput.html b/docs/opcodes/opcode-59-iput.html
new file mode 100644
index 0000000..22a3479
--- /dev/null
+++ b/docs/opcodes/opcode-59-iput.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>iget&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>iget&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified object instance field operation with the identified
+field, loading or storing into the value register.
+</p>
+<p>
+Note: These opcodes are reasonable candidates for static linking, altering the
+field argument to be a more direct offset.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>59..5f 22c</td>
+  <td>i<i>instanceop</i> vA, vB, field@CCCC<br/>
+    59: iput<br/>
+    5a: iput-wide<br/>
+    5b: iput-object<br/>
+    5c: iput-boolean<br/>
+    5d: iput-byte<br/>
+    5e: iput-char<br/>
+    5f: iput-short
+  </td>
+  <td><code>A:</code> source value register or pair; (4 bits)<br/>
+    <code>B:</code> object register (4 bits)<br/>
+    <code>C:</code> instance field reference index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A and B must be valid register indices in the current stack frame.
+  </li>
+  <li>
+    For the -wide variant, also A+1 must be a valid register index in the
+    current stack frame.
+  </li>
+  <li>
+    Register vB must contain an object reference.
+  </li>
+  <li>
+    C must be a valid index into the field reference pool.
+  </li>
+  <li>
+    The field must be an instance field. The type of the field denoted by C must
+    match the variant of the instruction.
+  </li>
+  <li>
+    For the -object variant, the instance referenced by register vA must be
+    assignment-compatible to the type of the field.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    For all but the -wide variant, the value of register vA is move into the
+    field, that is, &lt;object&gt;.&lt;field&gt;'=vA.
+  </li>
+  <li>
+    For the -wide variant, the registers vA and v(A+1) are moved into the
+    field as follows:
+    <ul>
+      <li>
+        &lt;object&gt;.&lt;field&gt;' = vA &lt;&lt; 0x20 | v(A+1)
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if vB=null.
+  </li>
+  <li>
+    IllegalAccessException if &lt;object&gt;.&lt;field&gt; is not visible from
+    the current context according to the usual visibility and access rules of
+    the Java programming language, or final.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-60-sget.html b/docs/opcodes/opcode-60-sget.html
new file mode 100644
index 0000000..820886e
--- /dev/null
+++ b/docs/opcodes/opcode-60-sget.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>sget&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>sget&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified object static field operation with the identified static
+field, loading or storing into the value register.
+</p>
+<p>
+Note: These opcodes are reasonable candidates for static linking, altering the
+field argument to be a more direct offset.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>60..6d 21c</td>
+  <td>s<i>staticop</i> vAA, field@BBBB<br/>
+    60: sget<br/>
+    61: sget-wide<br/>
+    62: sget-object<br/>
+    63: sget-boolean<br/>
+    64: sget-byte<br/>
+    65: sget-char<br/>
+    66: sget-short<br/>
+  </td>
+  <td><code>A:</code> dest value register or pair; (8 bits)<br/>
+    <code>B:</code> static field reference index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stackframe.
+  </li>
+  <li>
+    For the -wide variant, also A+1 must be a valid register index in the
+    current stackframe.
+  </li>
+  <li>
+    B must be a valid index into the field reference pool.
+  </li>
+  <li>
+    The field denoted by B must be static. The type of the field denoted by B
+    must match the variant of the instruction.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The value of the given instance field is read from the given object and
+    moved into the given register vA, that is, vA'=&lt;class&gt;.&lt;field&gt;.
+  </li>
+  <li>
+    If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+  </li>
+  <li>
+    For all but the -wide variant, if v(A+1) is the upper half of a register
+    pair, v(A+1)' becomes undefined.
+  </li>
+  <li>
+    For the -wide variant, if v(A+2) is the upper half of a register pair,
+    v(A+2)' becomes undefined.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if object is null.
+  </li>
+  <li>
+    IllegalAccessException if &lt;object&gt;.&lt;field&gt; is not visible from
+    the current context according to the usual visibility and access rules of
+    the Java programming language.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-67-sput.html b/docs/opcodes/opcode-67-sput.html
new file mode 100644
index 0000000..b4d88bb
--- /dev/null
+++ b/docs/opcodes/opcode-67-sput.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>sput&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>sput&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified object static field operation with the identified static
+field, loading or storing into the value register.
+</p>
+<p>
+Note: These opcodes are reasonable candidates for static linking, altering the
+field argument to be a more direct offset.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>67..6d 21c</td>
+  <td>s<i>staticop</i> vAA, field@BBBB<br/>
+    67: sput<br/>
+    68: sput-wide<br/>
+    69: sput-object<br/>
+    6a: sput-boolean<br/>
+    6b: sput-byte<br/>
+    6c: sput-char<br/>
+    6d: sput-short
+  </td>
+  <td><code>A:</code> source value register or pair; (8 bits)<br/>
+    <code>B:</code> static field reference index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    A must be a valid register index in the current stackframe.
+  </li>
+  <li>
+    For the -wide variant, also A+1 must be a valid register index in the
+    current stackframe.
+  </li>
+  <li>
+    B must be a valid index into the field reference pool.
+  </li>
+  <li>
+    The field must be static. The type of the field denoted by C must match the
+    variant of the instruction.
+  </li>
+  <li>
+    For the -object variant, the instance referenced by register vA must be
+    assignment-compatible to the type of the field.
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    For all but the -wide variant, the value of register vA is move into the
+    field, that is, &lt;class&gt;.&lt;field&gt;'=vA.
+  </li>
+  <li>
+    For the -wide variant, the registers vA and v(A+1) are moved into the field
+    as follows:
+    <ul>
+      <li>
+        &lt;class&gt;.&lt;field&gt;' = vA &lt;&lt; 0x20 | v(A+1)
+      </li>
+    </ul>
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    NullPointerException if vB=null.
+  </li>
+  <li>
+    IllegalAccessException if &lt;object&gt;.&lt;field&gt; is not visible from
+    the current context according to the usual visibility and access rules of
+    the Java programming language, or final.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-7b-unop.html b/docs/opcodes/opcode-7b-unop.html
new file mode 100644
index 0000000..8b06092
--- /dev/null
+++ b/docs/opcodes/opcode-7b-unop.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>unop</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>unop</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified unary operation on the source register, storing the
+result in the destination register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>7b..8f 12x</td>
+  <td><i>unop</i> vA, vB<br/>
+    7b: neg-int<br/>
+    7c: not-int<br/>
+    7d: neg-long<br/>
+    7e: not-long<br/>
+    7f: neg-float<br/>
+    80: neg-double<br/>
+    81: int-to-long<br/>
+    82: int-to-float<br/>
+    83: int-to-double<br/>
+    84: long-to-int<br/>
+    85: long-to-float<br/>
+    86: long-to-double<br/>
+    87: float-to-int<br/>
+    88: float-to-long<br/>
+    89: float-to-double<br/>
+    8a: double-to-int<br/>
+    8b: double-to-long<br/>
+    8c: double-to-float<br/>
+    8d: int-to-byte<br/>
+    8e: int-to-char<br/>
+    8f: int-to-short
+  </td>
+  <td><code>A:</code> destination register or pair (4 bits)<br/>
+    <code>B:</code> source register or pair (4 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A and B must be valid register indices for the current stackframe.
+  </li>
+  <li>
+    If the input type of &lt;unop&gt; is double or long, also B+1 must be a
+    valid register index in the current stackframe.
+  </li>
+  <li>
+    If the output type of &lt;unop&gt; is double or long, also A+1 must be a
+    valid register index in the current stackframe.
+  </li>
+  <li>
+    The type of register vB must match the source type of the instruction (this
+    probably needs more detail).
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The given operation &lt;unop&gt; is performed according to the semantics
+    specified in table XXX.
+  </li>
+  <li>
+    The result is stored in register vA, that is, vA'=&lt;unop&gt; vB.
+  </li>
+  <li>
+    It gets a bit messy if we want to describe all the combinations of input and
+    output with and without pairs here. Probably it's better to split it up.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    ArithmeticException if an arithmetic error occurs during the instruction.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-90-binop.html b/docs/opcodes/opcode-90-binop.html
new file mode 100644
index 0000000..cdc08a8
--- /dev/null
+++ b/docs/opcodes/opcode-90-binop.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>binop</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>binop</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified binary operation on the two source registers, storing
+the result in the first source register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>90..af 23x</td>
+  <td><i>binop</i> vAA, vBB, vCC<br/>
+    90: add-int<br/>
+    91: sub-int<br/>
+    92: mul-int<br/>
+    93: div-int<br/>
+    94: rem-int<br/>
+    95: and-int<br/>
+    96: or-int<br/>
+    97: xor-int<br/>
+    98: shl-int<br/>
+    99: shr-int<br/>
+    9a: ushr-int<br/>
+    9b: add-long<br/>
+    9c: sub-long<br/>
+    9d: mul-long<br/>
+    9e: div-long<br/>
+    9f: rem-long<br/>
+    a0: and-long<br/>
+    a1: or-long<br/>
+    a2: xor-long<br/>
+    a3: shl-long<br/>
+    a4: shr-long<br/>
+    a5: ushr-long<br/>
+    a6: add-float<br/>
+    a7: sub-float<br/>
+    a8: mul-float<br/>
+    a9: div-float<br/>
+    aa: rem-float<br/>
+    ab: add-double<br/>
+    ac: sub-double<br/>
+    ad: mul-double<br/>
+    ae: div-double<br/>
+    af: rem-double
+  </td>
+  <td><code>A:</code> destination register or pair (8 bits)<br/>
+    <code>B:</code> first source register or pair (8 bits)<br/>
+    <code>C:</code> second source register or pair (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    All A, B and C must be valid register indices in the current stackframe.
+  </li>
+  <li>
+    For the -long and -double variants, also A+1, B+1 and C+1 must be valid
+    register indices.
+  </li>
+  <li>
+    Registers vB and vC must be defined. They must both contain values that
+    match the variant of the instruction (it's probably better to split this up
+    into multiple pages again).
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The given operation &lt;binop&gt; is performed according to the semantics
+    specified in table XXX.
+  </li>
+  <li>
+    The result is stored in register vA, that is, vA'=&lt;biop&gt; vB.
+  </li>
+  <li>
+    For the -double and -long variants, (vA+1) is also affected.
+  </li>
+  <li>
+    As usual, neighboring registers might get undefined, if vA (and vA+1) were
+    part of a register pair originally.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    ArithmeticException if an error occurs during the instruction.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-b0-binop-2addr.html b/docs/opcodes/opcode-b0-binop-2addr.html
new file mode 100644
index 0000000..b3374f4
--- /dev/null
+++ b/docs/opcodes/opcode-b0-binop-2addr.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>binop/2addr</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>binop/2addr</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified binary operation on the two source registers, storing the
+result in the first source register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>b0..cf 12x</td>
+  <td><i>binop</i>/2addr vA, vB<br/>
+    b0: add-int/2addr<br/>
+    b1: sub-int/2addr<br/>
+    b2: mul-int/2addr<br/>
+    b3: div-int/2addr<br/>
+    b4: rem-int/2addr<br/>
+    b5: and-int/2addr<br/>
+    b6: or-int/2addr<br/>
+    b7: xor-int/2addr<br/>
+    b8: shl-int/2addr<br/>
+    b9: shr-int/2addr<br/>
+    ba: ushr-int/2addr<br/>
+    bb: add-long/2addr<br/>
+    bc: sub-long/2addr<br/>
+    bd: mul-long/2addr<br/>
+    be: div-long/2addr<br/>
+    bf: rem-long/2addr<br/>
+    c0: and-long/2addr<br/>
+    c1: or-long/2addr<br/>
+    c2: xor-long/2addr<br/>
+    c3: shl-long/2addr<br/>
+    c4: shr-long/2addr<br/>
+    c5: ushr-long/2addr<br/>
+    c6: add-float/2addr<br/>
+    c7: sub-float/2addr<br/>
+    c8: mul-float/2addr<br/>
+    c9: div-float/2addr<br/>
+    ca: rem-float/2addr<br/>
+    cb: add-double/2addr<br/>
+    cc: sub-double/2addr<br/>
+    cd: mul-double/2addr<br/>
+    ce: div-double/2addr<br/>
+    cf: rem-double/2addr
+  </td>
+  <td><code>A:</code> destination and first source register or pair
+      (4 bits)<br/>
+    <code>B:</code> second source register or pair (4 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A and B must be valid register indices in the current stackframe.
+  </li>
+  <li>
+    For the -long and -double variants, also A+1 and B+1 must be valid register
+    indices.
+  </li>
+  <li>
+    Registers vA and vB must be defined. They must both contain values that
+    match the variant of the instruction (it's probably better to split this up
+    into multiple pages again).
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The given operation &lt;binop&gt; is performed according to the semantics
+    specified in table XXX.
+  </li>
+  <li>
+    The result is stored in register vA, that is, vA'=vA &lt;binop&gt; vB.
+  </li>
+  <li>
+    For the -double and -long variants, (vA+1) is also affected.
+  </li>
+  <li>
+    As usual, neighboring registers might get undefined, if vA (and vA+1) were
+    part of a register pair originally.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    ArithmeticException if an error occurs during the instruction.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-d0-binop-lit16.html b/docs/opcodes/opcode-d0-binop-lit16.html
new file mode 100644
index 0000000..f9d3327
--- /dev/null
+++ b/docs/opcodes/opcode-d0-binop-lit16.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>binop/lit16</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>binop/lit16</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the indicated binary op on the indicated register (first argument) and
+literal value (second argument), storing the result in the destination register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>d0..d7 22s</td>
+  <td><i>binop</i>/lit16 vA, vB, #+CCCC<br/>
+    d0: add-int/lit16<br/>
+    d1: rsub-int (reverse subtract)<br/>
+    d2: mul-int/lit16<br/>
+    d3: div-int/lit16<br/>
+    d4: rem-int/lit16<br/>
+    d5: and-int/lit16<br/>
+    d6: or-int/lit16<br/>
+    d7: xor-int/lit16
+  </td>
+  <td><code>A:</code> destination register (4 bits)<br/>
+    <code>B:</code> source register (4 bits)<br/>
+    <code>C:</code> signed int constant (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A and B must be valid register indices in the current stackframe.
+  </li>
+  <li>
+    Registers vA and vB must be defined. They must both contain integer values.
+  </li>
+  <li>
+    C is an immediate, signed integer constant taken from the instruction stream
+    (actually this means there are no special requirements for C at all).
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The given operation &lt;binop&gt; is performed according to the semantics
+    specified in table XXX.
+  </li>
+  <li>
+    Argument C is sign-extended to 32 bits before.
+  </li>
+  <li>
+    The result is stored in register vA, that is, vA'=vB &lt;binop&gt; vC.
+  </li>
+  <li>
+    As usual, neighboring registers might get undefined, if vA was part of a
+    register pair originally.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    ArithmeticException if an error occurs during the instruction.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-d8-binop-lit8.html b/docs/opcodes/opcode-d8-binop-lit8.html
new file mode 100644
index 0000000..26005e9
--- /dev/null
+++ b/docs/opcodes/opcode-d8-binop-lit8.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>binop/lit8</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>binop/lit8</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the indicated binary op on the indicated register (first argument) and
+literal value (second argument), storing the result in the destination register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+  <th>Op &amp; Format</th>
+  <th>Mnemonic / Syntax</th>
+  <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+  <td>d8..e2 22b</td>
+  <td><i>binop</i>/lit8 vAA, vBB, #+CC<br/>
+    d8: add-int/lit8<br/>
+    d9: rsub-int/lit8<br/>
+    da: mul-int/lit8<br/>
+    db: div-int/lit8<br/>
+    dc: rem-int/lit8<br/>
+    dd: and-int/lit8<br/>
+    de: or-int/lit8<br/>
+    df: xor-int/lit8<br/>
+    e0: shl-int/lit8<br/>
+    e1: shr-int/lit8<br/>
+    e2: ushr-int/lit8
+  </td>
+  <td><code>A:</code> destination register (8 bits)<br/>
+    <code>B:</code> source register (8 bits)<br/>
+    <code>C:</code> signed int constant (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+  <li>
+    Both A and B must be valid register indices in the current stackframe.
+  </li>
+  <li>
+    Registers vA and vB must be defined. They must both contain integer values.
+  </li>
+  <li>
+    C is an immediate, signed integer constant taken from the instruction stream
+    (actually this means there are no special requirements for C at all).
+  </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+  <li>
+    The given operation &lt;binop&gt; is performed according to the semantics
+    specified in table XXX.
+  </li>
+  <li>
+    Argument C is sign-extended to 32 bits before.
+  </li>
+  <li>
+    The result is stored in register vA, that is, vA'=vB &lt;binop&gt; vC.
+  </li>
+  <li>
+    As usual, neighboring registers might get undefined, if vA was part of a
+    register pair originally.
+  </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+  <li>
+    ArithmeticException if an error occurs during the instruction.
+  </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode.css b/docs/opcodes/opcode.css
new file mode 100644
index 0000000..c3c1304
--- /dev/null
+++ b/docs/opcodes/opcode.css
@@ -0,0 +1,166 @@
+h1 {
+    font-family: serif;
+    color: #222266;
+}
+
+h2 {
+    font-family: serif;
+    border-top-style: solid;
+    border-top-width: 2px;
+    border-color: #ccccdd;
+    padding-top: 12px;
+    margin-top: 48px;
+    margin-bottom: 2px;
+    color: #222266;
+}
+
+@media print {
+    table {
+        font-size: 8pt;
+    }
+}
+
+@media screen {
+    table {
+        font-size: 10pt;
+    }
+}
+
+
+/* general for all tables */
+
+table {
+    border-collapse: collapse;
+    margin-top: 12px;
+}
+
+table th {
+    font-family: sans-serif;
+    background: #aabbff;
+    text-align: left;
+}
+
+table td {
+    font-family: sans-serif;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-width: 1px;
+    border-color: #aaaaff;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    padding-left: 4px;
+    padding-right: 6px;
+    background: #eeeeff;
+}
+
+table td p {
+    margin-top: 4pt;
+    margin-bottom: 0pt;
+}
+
+
+
+/* opcodes table */
+
+table.instruc {
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table.instruc td {
+    font-family: sans-serif;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-width: 1px;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    padding-left: 2px;
+    padding-right: 2px;
+}
+
+table.instruc td:first-child {
+    font-family: monospace;
+    font-size: 90%;
+    vertical-align: top;
+    width: 12%;
+}
+
+table.instruc td:first-child + td {
+    font-family: monospace;
+    font-size: 90%;
+    vertical-align: top;
+    width: 23%;
+}
+
+table.instruc td:first-child + td i {
+    font-family: sans-serif;
+    font-size: 90%;
+}
+
+table.instruc td:first-child + td + td {
+    vertical-align: top;
+    width: 28%;
+}
+
+table.instruc td:first-child + td + td + td {
+    vertical-align: top;
+    width: 37%;
+}
+
+
+/* supplemental opcode format table */
+
+table.supplement {
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table.supplement td:first-child {
+    font-family: monospace;
+    vertical-align: top;
+    width: 20%;
+}
+
+table.supplement td:first-child + td {
+    font-family: monospace;
+    vertical-align: top;
+    width: 20%;
+}
+
+table.supplement td:first-child + td + td {
+    font-family: sans-serif;
+    vertical-align: top;
+    width: 60%;
+}
+
+
+/* math details table */
+
+table.math {
+    margin-top: 24px;
+    margin-bottom: 24px;
+    margin-left: 48px;
+    margin-right: 48px;
+}
+
+table.math td:first-child {
+    font-family: monospace;
+    vertical-align: top;
+    width: 10%;
+}
+
+table.math td:first-child + td {
+    font-family: monospace;
+    vertical-align: top;
+    width: 30%;
+}
+
+table.math td:first-child + td + td {
+    font-family: sans-serif;
+    vertical-align: top;
+    width: 60%;
+}
diff --git a/docs/porting-guide.html b/docs/porting-guide.html
new file mode 100644
index 0000000..d23b903
--- /dev/null
+++ b/docs/porting-guide.html
@@ -0,0 +1,356 @@
+<html>
+<head>
+    <title>Dalvik Porting Guide</title>
+</head>
+
+<body>
+<h1>Dalvik Porting Guide</h1>
+
+<p>
+The Dalvik virtual machine is intended to run on a variety of platforms.
+The baseline system is expected to be a variant of UNIX (Linux, BSD, Mac
+OS X) running the GNU C compiler.  Little-endian CPUs have been exercised
+the most heavily, but big-endian systems are explicitly supported.
+</p><p>
+There are two general categories of work: porting to a Linux system
+with a previously unseen CPU architecture, and porting to a different
+operating system.  This document covers the former.
+</p><p>
+Basic familiarity with the Android platform, source code structure, and
+build system is assumed.
+</p>
+
+
+<h2>Core Libraries</h2>
+
+<p>
+The native code in the core libraries (chiefly <code>libcore</code>,
+but also <code>dalvik/vm/native</code>) is written in C/C++ and is expected
+to work without modification in a Linux environment.  Much of the code
+comes directly from the Apache Harmony project.
+</p><p>
+The core libraries pull in code from many other projects, including
+OpenSSL, zlib, and ICU.  These will also need to be ported before the VM
+can be used.
+</p>
+
+
+<h2>JNI Call Bridge</h2>
+
+<p>
+Most of the Dalvik VM runtime is written in portable C.  The one
+non-portable component of the runtime is the JNI call bridge.  Simply put,
+this converts an array of integers into function arguments of various
+types, and calls a function.  This must be done according to the C calling
+conventions for the platform.  The task could be as simple as pushing all
+of the arguments onto the stack, or involve complex rules for register
+assignment and stack alignment.
+</p><p>
+To ease porting to new platforms, the <a href="http://sourceware.org/libffi/">
+open-source FFI library</a> (Foreign Function Interface) is used when a
+custom bridge is unavailable.  FFI is not as fast as a native implementation,
+and the optional performance improvements it does offer are not used, so
+writing a replacement is a good first step.
+</p><p>
+The code lives in <code>dalvik/vm/arch/*</code>, with the FFI-based version
+in the "generic" directory.  There are two source files for each architecture.
+One defines the call bridge itself:
+</p><p><blockquote>
+<code>void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo,
+int argc, const u4* argv, const char* signature, void* func,
+JValue* pReturn)</code>
+</blockquote></p><p>
+This will invoke a C/C++ function declared:
+</p><p><blockquote>
+    <code>return_type func(JNIEnv* pEnv, Object* this [, <i>args</i>])<br></code>
+</blockquote>or (for a "static" method):<blockquote>
+    <code>return_type func(JNIEnv* pEnv, ClassObject* clazz [, <i>args</i>])</code>
+</blockquote></p><p>
+The role of <code>dvmPlatformInvoke</code> is to convert the values in
+<code>argv</code> into C-style calling conventions, call the method, and
+then place the return type into <code>pReturn</code> (a union that holds
+all of the basic JNI types).  The code may use the method signature
+(a DEX "shorty" signature, with one character for the return type and one
+per argument) to determine how to handle the values.
+</p><p>
+The other source file involved here defines a 32-bit "hint".  The hint
+is computed when the method's class is loaded, and passed in as the
+"argInfo" argument.  The hint can be used to avoid scanning the ASCII
+method signature for things like the return value, total argument size,
+or inter-argument 64-bit alignment restrictions.
+
+
+<h2>Interpreter</h2>
+
+<p>
+The Dalvik runtime includes two interpreters, labeled "portable" and "fast".
+The portable interpreter is largely contained within a single C function,
+and should compile on any system that supports gcc.  (If you don't have gcc,
+you may need to disable the "threaded" execution model, which relies on
+gcc's "goto table" implementation; look for the THREADED_INTERP define.)
+</p><p>
+The fast interpreter uses hand-coded assembly fragments.  If none are
+available for the current architecture, the build system will create an
+interpreter out of C "stubs".  The resulting "all stubs" interpreter is
+quite a bit slower than the portable interpreter, making "fast" something
+of a misnomer.
+</p><p>
+The fast interpreter is enabled by default.  On platforms without native
+support, you may want to switch to the portable interpreter.  This can
+be controlled with the <code>dalvik.vm.execution-mode</code> system
+property.  For example, if you:
+</p><p><blockquote>
+<code>adb shell "echo dalvik.vm.execution-mode = int:portable >> /data/local.prop"</code>
+</blockquote></p><p>
+and reboot, the Android app framework will start the VM with the portable
+interpreter enabled.
+</p>
+
+
+<h3>Mterp Interpreter Structure</h3>
+
+<p>
+There may be significant performance advantages to rewriting the
+interpreter core in assembly language, using architecture-specific
+optimizations.  In Dalvik this can be done one instruction at a time.
+</p><p>
+The simplest way to implement an interpreter is to have a large "switch"
+statement.  After each instruction is handled, the interpreter returns to
+the top of the loop, fetches the next instruction, and jumps to the
+appropriate label.
+</p><p>
+An improvement on this is called "threaded" execution.  The instruction
+fetch and dispatch are included at the end of every instruction handler.
+This makes the interpreter a little larger overall, but you get to avoid
+the (potentially expensive) branch back to the top of the switch statement.
+</p><p>
+Dalvik mterp goes one step further, using a computed goto instead of a goto
+table.  Instead of looking up the address in a table, which requires an
+extra memory fetch on every instruction, mterp multiplies the opcode number
+by a fixed value.  By default, each handler is allowed 64 bytes of space.
+</p><p>
+Not all handlers fit in 64 bytes.  Those that don't can have subroutines
+or simply continue on to additional code outside the basic space.  Some of
+this is handled automatically by Dalvik, but there's no portable way to detect
+overflow of a 64-byte handler until the VM starts executing.
+</p><p>
+The choice of 64 bytes is somewhat arbitrary, but has worked out well for
+ARM and x86.
+</p><p>
+In the course of development it's useful to have C and assembly
+implementations of each handler, and be able to flip back and forth
+between them when hunting problems down.  In mterp this is relatively
+straightforward.  You can always see the files being fed to the compiler
+and assembler for your platform by looking in the
+<code>dalvik/vm/mterp/out</code> directory.
+</p><p>
+The interpreter sources live in <code>dalvik/vm/mterp</code>.  If you
+haven't yet, you should read <code>dalvik/vm/mterp/README.txt</code> now.
+</p>
+
+
+<h3>Getting Started With Mterp</h3>
+
+</p><p>
+Getting started:
+<ol>
+<li>Decide on the name of your architecture.  For the sake of discussion,
+let's call it <code>myarch</code>.
+<li>Make a copy of <code>dalvik/vm/mterp/config-allstubs</code> to
+<code>dalvik/vm/mterp/config-myarch</code>.
+<li>Create a <code>dalvik/vm/mterp/myarch</code> directory to hold your
+source files.
+<li>Add <code>myarch</code> to the list in
+<code>dalvik/vm/mterp/rebuild.sh</code>.
+<li>Make sure <code>dalvik/vm/Android.mk</code> will find the files for
+your architecture.  If <code>$(TARGET_ARCH)</code> is configured this
+will happen automatically.
+</ol>
+</p><p>
+You now have the basic framework in place.  Whenever you make a change, you
+need to perform two steps: regenerate the mterp output, and build the
+core VM library.  (It's two steps because we didn't want the build system
+to require Python 2.5.  Which, incidentally, you need to have.)
+<ol>
+<li>In the <code>dalvik/vm/mterp</code> directory, regenerate the contents
+of the files in <code>dalvik/vm/mterp/out</code> by executing
+<code>./rebuild.sh</code>.  Note there are two files, one in C and one
+in assembly.
+<li>In the <code>dalvik</code> directory, regenerate the
+<code>libdvm.so</code> library with <code>mm</code>.  You can also use
+<code>make libdvm</code> from the top of the tree.
+</ol>
+</p><p>
+This will leave you with an updated libdvm.so, which can be pushed out to
+a device with <code>adb sync</code> or <code>adb push</code>.  If you're
+using the emulator, you need to add <code>make snod</code> (System image,
+NO Dependency check) to rebuild the system image file.  You should not
+need to do a top-level "make" and rebuild the dependent binaries.
+</p><p>
+At this point you have an "all stubs" interpreter.  You can see how it
+works by examining <code>dalvik/vm/mterp/cstubs/entry.c</code>.  The
+code runs in a loop, pulling out the next opcode, and invoking the
+handler through a function pointer.  Each handler takes a "glue" argument
+that contains all of the useful state.
+</p><p>
+Your goal is to replace the entry method, exit method, and each individual
+instruction with custom implementations.  The first thing you need to do
+is create an entry function that calls the handler for the first instruction.
+After that, the instructions chain together, so you don't need a loop.
+(Look at the ARM or x86 implementation to see how they work.)
+</p><p>
+Once you have that, you need something to jump to.  You can't branch
+directly to the C stub because it's expecting to be called with a "glue"
+argument and then return.  We need a C stub "wrapper" that does the
+setup and jumps directly to the next handler.  We write this in assembly
+and then add it to the config file definition.
+</p><p>
+To see how this works, create a file called
+<code>dalvik/vm/mterp/myarch/stub.S</code> that contains one line:
+<pre>
+/* stub for ${opcode} */
+</pre>
+Then, in <code>dalvik/vm/mterp/config-myarch</code>, add this below the
+<code>handler-size</code> directive:
+<pre>
+# source for the instruction table stub
+asm-stub myarch/stub.S
+</pre>
+</p><p>
+Regenerate the sources with <code>./rebuild.sh</code>, and take a look
+inside <code>dalvik/vm/mterp/out/InterpAsm-myarch.S</code>.  You should
+see 256 copies of the stub function in a single large block after the
+<code>dvmAsmInstructionStart</code> label.  The <code>stub.S</code>
+code will be used anywhere you don't provide an assembly implementation.
+</p><p>
+Note that each block begins with a <code>.balign 64</code> directive.
+This is what pads each handler out to 64 bytes.  Note also that the
+<code>${opcode}</code> text changed into an opcode name, which should
+be used to call the C implementation (<code>dvmMterp_${opcode}</code>).
+</p><p>
+The actual contents of <code>stub.S</code> are up to you to define.
+See <code>entry.S</code> and <code>stub.S</code> in the <code>armv5te</code>
+or <code>x86</code> directories for working examples.
+</p><p>
+If you're working on a variation of an existing architecture, you may be
+able to use most of the existing code and just provide replacements for
+a few instructions.  Look at the <code>armv4t</code> implementation as
+an example.
+</p>
+
+
+<h3>Replacing Stubs</h3>
+
+<p>
+There are roughly 230 Dalvik opcodes, including some that are inserted by
+<a href="dexopt.html">dexopt</a> and aren't described in the
+<a href="dalvik-bytecode.html">Dalvik bytecode</a> documentation.  Each
+one must perform the appropriate actions, fetch the next opcode, and
+branch to the next handler.  The actions performed by the assembly version
+must exactly match those performed by the C version (in
+<code>dalvik/vm/mterp/c/OP_*</code>).
+</p><p>
+It is possible to customize the set of "optimized" instructions for your
+platform.  This is possible because optimized DEX files are not expected
+to work on multiple devices.  Adding, removing, or redefining instructions
+is beyond the scope of this document, and for simplicity it's best to stick
+with the basic set defined by the portable interpreter.
+</p><p>
+Once you have written a handler that looks like it should work, add
+it to the config file.  For example, suppose we have a working version
+of <code>OP_NOP</code>.  For demonstration purposes, fake it for now by
+putting this into <code>dalvik/vm/mterp/myarch/OP_NOP.S</code>:
+<pre>
+/* This is my NOP handler */
+</pre>
+</p><p>
+Then, in the <code>op-start</code> section of <code>config-myarch</code>, add:
+<pre>
+    op OP_NOP myarch
+</pre>
+</p><p>
+This tells the generation script to use the assembly version from the
+<code>myarch</code> directory instead of the C version from the <code>c</code>
+directory.
+</p><p>
+Execute <code>./rebuild.sh</code>.  Look at <code>InterpAsm-myarch.S</code>
+and <code>InterpC-myarch.c</code> in the <code>out</code> directory.  You
+will see that the <code>OP_NOP</code> stub wrapper has been replaced with our
+new code in the assembly file, and the C stub implementation is no longer
+included.
+</p><p>
+As you implement instructions, the C version and corresponding stub wrapper
+will disappear from the output files.  Eventually you will have a 100%
+assembly interpreter.  You may find it saves a little time to examine
+the output of your compiler for some of the operations.  The
+<a href="porting-proto.c.txt">porting-proto.c</a> sample code can be
+helpful here.
+</p>
+
+
+<h3>Interpreter Switching</h3>
+
+<p>
+The Dalvik VM actually includes a third interpreter implementation: the debug
+interpreter.  This is a variation of the portable interpreter that includes
+support for debugging and profiling.
+</p><p>
+When a debugger attaches, or a profiling feature is enabled, the VM
+will switch interpreters at a convenient point.  This is done at the
+same time as the GC safe point check: on a backward branch, a method
+return, or an exception throw.  Similarly, when the debugger detaches
+or profiling is discontinued, execution transfers back to the "fast" or
+"portable" interpreter.
+</p><p>
+Your entry function needs to test the "entryPoint" value in the "glue"
+pointer to determine where execution should begin.  Your exit function
+will need to return a boolean that indicates whether the interpreter is
+exiting (because we reached the "bottom" of a thread stack) or wants to
+switch to the other implementation.
+</p><p>
+See the <code>entry.S</code> file in <code>x86</code> or <code>armv5te</code>
+for examples.
+</p>
+
+
+<h3>Testing</h3>
+
+<p>
+A number of VM tests can be found in <code>dalvik/tests</code>.  The most
+useful during interpreter development is <code>003-omnibus-opcodes</code>,
+which tests many different instructions.
+</p><p>
+The basic invocation is:
+<pre>
+$ cd dalvik/tests
+$ ./run-test 003
+</pre>
+</p><p>
+This will run test 003 on an attached device or emulator.  You can run
+the test against your desktop VM by specifying <code>--reference</code>
+if you suspect the test may be faulty.  You can also use
+<code>--portable</code> and <code>--fast</code> to explictly specify
+one Dalvik interpreter or the other.
+</p><p>
+Some instructions are replaced by <code>dexopt</code>, notably when
+"quickening" field accesses and method invocations.  To ensure
+that you are testing the basic form of the instruction, add the
+<code>--no-optimize</code> option.
+</p><p>
+There is no in-built instruction tracing mechanism.  If you want
+to know for sure that your implementation of an opcode handler
+is being used, the easiest approach is to insert a "printf"
+call.  For an example, look at <code>common_squeak</code> in
+<code>dalvik/vm/mterp/armv5te/footer.S</code>.
+</p><p>
+At some point you need to ensure that debuggers and profiling work with
+your interpreter.  The easiest way to do this is to simply connect a
+debugger or toggle profiling.  (A future test suite may include some
+tests for this.)
+</p>
+
+<p>
+<address>Copyright &copy; 2009 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/docs/porting-proto.c.txt b/docs/porting-proto.c.txt
new file mode 100644
index 0000000..98c6fd3
--- /dev/null
+++ b/docs/porting-proto.c.txt
@@ -0,0 +1,244 @@
+/*
+ * 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 instruction fragments, useful when porting mterp.
+ *
+ * Compile this and examine the output to see what your compiler generates.
+ * This can give you a head start on some of the more complicated operations.
+ *
+ * Example:
+ *   % gcc -c -O2 -save-temps -fverbose-asm porting-proto.c
+ *   % less porting-proto.s
+ */
+#include <stdint.h>
+
+typedef int8_t s1;
+typedef uint8_t u1;
+typedef int16_t s2;
+typedef uint16_t u2;
+typedef int32_t s4;
+typedef uint32_t u4;
+typedef int64_t s8;
+typedef uint64_t u8;
+
+s4 iadd32(s4 x, s4 y) { return x + y; }
+s8 iadd64(s8 x, s8 y) { return x + y; }
+float fadd32(float x, float y) { return x + y; }
+double fadd64(double x, double y) { return x + y; }
+
+s4 isub32(s4 x, s4 y) { return x - y; }
+s8 isub64(s8 x, s8 y) { return x - y; }
+float fsub32(float x, float y) { return x - y; }
+double fsub64(double x, double y) { return x - y; }
+
+s4 irsub32lit8(s4 x) { return 25 - x; }
+
+s4 imul32(s4 x, s4 y) { return x * y; }
+s8 imul64(s8 x, s8 y) { return x * y; }
+float fmul32(float x, float y) { return x * y; }
+double fmul64(double x, double y) { return x * y; }
+
+s4 idiv32(s4 x, s4 y) { return x / y; }
+s8 idiv64(s8 x, s8 y) { return x / y; }
+float fdiv32(float x, float y) { return x / y; }
+double fdiv64(double x, double y) { return x / y; }
+
+s4 irem32(s4 x, s4 y) { return x % y; }
+s8 irem64(s8 x, s8 y) { return x % y; }
+
+s4 iand32(s4 x, s4 y) { return x & y; }
+s8 iand64(s8 x, s8 y) { return x & y; }
+
+s4 ior32(s4 x, s4 y) { return x | y; }
+s8 ior64(s8 x, s8 y) { return x | y; }
+
+s4 ixor32(s4 x, s4 y) { return x ^ y; }
+s8 ixor64(s8 x, s8 y) { return x ^ y; }
+
+s4 iasl32(s4 x, s4 count) { return x << (count & 0x1f); }
+s8 iasl64(s8 x, s4 count) { return x << (count & 0x3f); }
+
+s4 iasr32(s4 x, s4 count) { return x >> (count & 0x1f); }
+s8 iasr64(s8 x, s4 count) { return x >> (count & 0x3f); }
+
+s4 ilsr32(s4 x, s4 count) { return ((u4)x) >> (count & 0x1f); } // unsigned
+s8 ilsr64(s8 x, s4 count) { return ((u8)x) >> (count & 0x3f); } // unsigned
+
+s4 ineg32(s4 x) { return -x; }
+s8 ineg64(s8 x) { return -x; }
+float fneg32(float x) { return -x; }
+double fneg64(double x) { return -x; }
+
+s4 inot32(s4 x) { return x ^ -1; }
+s8 inot64(s8 x) { return x ^ -1LL; }
+
+s4 float2int(float x) { return (s4) x; }
+double float2double(float x) { return (double) x; }
+s4 double2int(double x) { return (s4) x; }
+float double2float(double x) { return (float) x; }
+
+/*
+ * ARM lib doesn't clamp large values or NaN the way we want on these two.
+ * If the simple version isn't correct, use the long version.  (You can use
+ * dalvik/tests/041-narrowing to verify.)
+ */
+s8 float2long(float x) { return (s8) x; }
+s8 float2long_clamp(float x)
+{
+    static const float kMaxLong = (float)0x7fffffffffffffffULL;
+    static const float kMinLong = (float)0x8000000000000000ULL;
+
+    if (x >= kMaxLong) {
+        return 0x7fffffffffffffffULL;
+    } else if (x <= kMinLong) {
+        return 0x8000000000000000ULL;
+    } else if (x != x) {
+        return 0;
+    } else {
+        return (s8) x;
+    }
+}
+s8 double2long(double x) { return (s8) x; }
+s8 double2long_clamp(double x)
+{
+    static const double kMaxLong = (double)0x7fffffffffffffffULL;
+    static const double kMinLong = (double)0x8000000000000000ULL;
+
+    if (x >= kMaxLong) {
+        return 0x7fffffffffffffffULL;
+    } else if (x <= kMinLong) {
+        return 0x8000000000000000ULL;
+    } else if (x != x) {
+        return 0;
+    } else {
+        return (s8) x;
+    }
+}
+
+s1 int2byte(s4 x) { return (s1) x; }
+s2 int2short(s4 x) { return (s2) x; }
+u2 int2char(s4 x) { return (u2) x; }
+s8 int2long(s4 x) { return (s8) x; }
+float int2float(s4 x) { return (float) x; }
+double int2double(s4 x) { return (double) x; }
+
+s4 long2int(s8 x) { return (s4) x; }
+float long2float(s8 x) { return (float) x; }
+double long2double(s8 x) { return (double) x; }
+
+int cmpl_float(float x, float y)
+{
+    int result;
+
+    if (x == y)
+        result = 0;
+    else if (x > y)
+        result = 1;
+    else /* (x < y) or NaN */
+        result = -1;
+    return result;
+}
+
+int cmpg_float(float x, float y)
+{
+    int result;
+
+    if (x == y)
+        result = 0;
+    else if (x < y)
+        result = -1;
+    else /* (x > y) or NaN */
+        result = 1;
+    return result;
+}
+
+int cmpl_double(double x, double y)
+{
+    int result;
+
+    if (x == y)
+        result = 0;
+    else if (x > y)
+        result = 1;
+    else /* (x < y) or NaN */
+        result = -1;
+    return result;
+}
+
+int cmpg_double(double x, double y)
+{
+    int result;
+
+    if (x == y)
+        result = 0;
+    else if (x < y)
+        result = -1;
+    else /* (x > y) or NaN */
+        result = 1;
+    return result;
+}
+
+int cmp_long(s8 x, s8 y)
+{
+    int result;
+
+    if (x == y)
+        result = 0;
+    else if (x < y)
+        result = -1;
+    else /* (x > y) */
+        result = 1;
+    return result;
+}
+
+/* instruction decoding fragments */
+u1 unsignedAA(u2 x) { return x >> 8; }
+s1 signedAA(u2 x) { return (s4)(x << 16) >> 24; }
+s2 signedBB(u2 x) { return (s2) x; }
+u1 unsignedA(u2 x) { return (x >> 8) & 0x0f; }
+u1 unsignedB(u2 x) { return x >> 12; }
+
+/* some handy immediate constants when working with float/double */
+u4 const_43e00000(u4 highword) { return 0x43e00000; }
+u4 const_c3e00000(u4 highword) { return 0xc3e00000; }
+u4 const_ffc00000(u4 highword) { return 0xffc00000; }
+u4 const_41dfffff(u4 highword) { return 0x41dfffff; }
+u4 const_c1e00000(u4 highword) { return 0xc1e00000; }
+
+/*
+ * Test for some gcc-defined symbols.  If you're frequently switching
+ * between different cross-compiler architectures or CPU feature sets,
+ * this can help you keep track of which one you're compiling for.
+ */
+#ifdef __arm__
+# warning "found __arm__"
+#endif
+#ifdef __ARM_EABI__
+# warning "found __ARM_EABI__"
+#endif
+#ifdef __VFP_FP__
+# warning "found __VFP_FP__"    /* VFP-format doubles used; may not have VFP */
+#endif
+#if defined(__VFP_FP__) && !defined(__SOFTFP__)
+# warning "VFP in use"
+#endif
+#ifdef __ARM_ARCH_5TE__
+# warning "found __ARM_ARCH_5TE__"
+#endif
+#ifdef __ARM_ARCH_7A__
+# warning "found __ARM_ARCH_7A__"
+#endif
diff --git a/docs/prettify.css b/docs/prettify.css
new file mode 100644
index 0000000..351152b
--- /dev/null
+++ b/docs/prettify.css
@@ -0,0 +1,27 @@
+/* Pretty printing styles. Used with prettify.js. */
+
+.str { color: #080; }
+.kwd { color: #008; }
+.com { color: #800; }
+.typ { color: #606; }
+.lit { color: #066; }
+.pun { color: #660; }
+.pln { color: #000; }
+.tag { color: #008; }
+.atn { color: #606; }
+.atv { color: #080; }
+.dec { color: #606; }
+pre.prettyprint { padding: 2px; border: 1px solid #888; }
+
+@media print {
+  .str { color: #060; }
+  .kwd { color: #006; font-weight: bold; }
+  .com { color: #600; font-style: italic; }
+  .typ { color: #404; font-weight: bold; }
+  .lit { color: #044; }
+  .pun { color: #440; }
+  .pln { color: #000; }
+  .tag { color: #006; font-weight: bold; }
+  .atn { color: #404; }
+  .atv { color: #060; }
+}
diff --git a/docs/prettify.js b/docs/prettify.js
new file mode 100644
index 0000000..9e99fc6
--- /dev/null
+++ b/docs/prettify.js
@@ -0,0 +1,1280 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview
+ * some functions for browser-side pretty printing of code contained in html.
+ *
+ * The lexer should work on a number of languages including C and friends,
+ * Java, Python, Bash, SQL, HTML, XML, CSS, Javascript, and Makefiles.
+ * It works passably on Ruby, PHP and Awk and a decent subset of Perl, but,
+ * because of commenting conventions, doesn't work on Smalltalk, Lisp-like, or
+ * CAML-like languages.
+ *
+ * If there's a language not mentioned here, then I don't know it, and don't
+ * know whether it works.  If it has a C-like, Bash-like, or XML-like syntax
+ * then it should work passably.
+ *
+ * Usage:
+ * 1) include this source file in an html page via
+ * <script type="text/javascript" src="/path/to/prettify.js"></script>
+ * 2) define style rules.  See the example page for examples.
+ * 3) mark the <pre> and <code> tags in your source with class=prettyprint.
+ *    You can also use the (html deprecated) <xmp> tag, but the pretty printer
+ *    needs to do more substantial DOM manipulations to support that, so some
+ *    css styles may not be preserved.
+ * That's it.  I wanted to keep the API as simple as possible, so there's no
+ * need to specify which language the code is in.
+ *
+ * Change log:
+ * cbeust, 2006/08/22
+ *   Java annotations (start with "@") are now captured as literals ("lit")
+ */
+
+// JSLint declarations
+/*global console, document, navigator, setTimeout, window */
+
+/**
+ * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
+ * UI events.
+ * If set to {@code false}, {@code prettyPrint()} is synchronous.
+ */
+var PR_SHOULD_USE_CONTINUATION = true;
+
+/** the number of characters between tab columns */
+var PR_TAB_WIDTH = 8;
+
+/** Walks the DOM returning a properly escaped version of innerHTML.
+  * @param {Node} node
+  * @param {Array.<string>} out output buffer that receives chunks of HTML.
+  */
+var PR_normalizedHtml;
+
+/** Contains functions for creating and registering new language handlers.
+  * @type {Object}
+  */
+var PR;
+
+/** Pretty print a chunk of code.
+  *
+  * @param {string} sourceCodeHtml code as html
+  * @return {string} code as html, but prettier
+  */
+var prettyPrintOne;
+/** find all the < pre > and < code > tags in the DOM with class=prettyprint
+  * and prettify them.
+  * @param {Function} opt_whenDone if specified, called when the last entry
+  *     has been finished.
+  */
+var prettyPrint;
+
+/** browser detection. @extern */
+function _pr_isIE6() {
+  var isIE6 = navigator && navigator.userAgent &&
+      /\bMSIE 6\./.test(navigator.userAgent);
+  _pr_isIE6 = function () { return isIE6; };
+  return isIE6;
+}
+
+
+(function () {
+  /** Splits input on space and returns an Object mapping each non-empty part to
+    * true.
+    */
+  function wordSet(words) {
+    words = words.split(/ /g);
+    var set = {};
+    for (var i = words.length; --i >= 0;) {
+      var w = words[i];
+      if (w) { set[w] = null; }
+    }
+    return set;
+  }
+
+  // Keyword lists for various languages.
+  var FLOW_CONTROL_KEYWORDS =
+      "break continue do else for if return while ";
+  var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
+      "double enum extern float goto int long register short signed sizeof " +
+      "static struct switch typedef union unsigned void volatile ";
+  var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
+      "new operator private protected public this throw true try ";
+  var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
+      "concept concept_map const_cast constexpr decltype " +
+      "dynamic_cast explicit export friend inline late_check " +
+      "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
+      "template typeid typename typeof using virtual wchar_t where ";
+  var JAVA_KEYWORDS = COMMON_KEYWORDS +
+      "boolean byte extends final finally implements import instanceof null " +
+      "native package strictfp super synchronized throws transient ";
+  var CSHARP_KEYWORDS = JAVA_KEYWORDS +
+      "as base by checked decimal delegate descending event " +
+      "fixed foreach from group implicit in interface internal into is lock " +
+      "object out override orderby params readonly ref sbyte sealed " +
+      "stackalloc string select uint ulong unchecked unsafe ushort var ";
+  var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
+      "debugger eval export function get null set undefined var with " +
+      "Infinity NaN ";
+  var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
+      "goto if import last local my next no our print package redo require " +
+      "sub undef unless until use wantarray while BEGIN END ";
+  var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
+      "elif except exec finally from global import in is lambda " +
+      "nonlocal not or pass print raise try with yield " +
+      "False True None ";
+  var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
+      " defined elsif end ensure false in module next nil not or redo rescue " +
+      "retry self super then true undef unless until when yield BEGIN END ";
+  var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
+      "function in local set then until ";
+  var ALL_KEYWORDS = (
+      CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
+      PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
+
+  // token style names.  correspond to css classes
+  /** token style for a string literal */
+  var PR_STRING = 'str';
+  /** token style for a keyword */
+  var PR_KEYWORD = 'kwd';
+  /** token style for a comment */
+  var PR_COMMENT = 'com';
+  /** token style for a type */
+  var PR_TYPE = 'typ';
+  /** token style for a literal value.  e.g. 1, null, true. */
+  var PR_LITERAL = 'lit';
+  /** token style for a punctuation string. */
+  var PR_PUNCTUATION = 'pun';
+  /** token style for a punctuation string. */
+  var PR_PLAIN = 'pln';
+
+  /** token style for an sgml tag. */
+  var PR_TAG = 'tag';
+  /** token style for a markup declaration such as a DOCTYPE. */
+  var PR_DECLARATION = 'dec';
+  /** token style for embedded source. */
+  var PR_SOURCE = 'src';
+  /** token style for an sgml attribute name. */
+  var PR_ATTRIB_NAME = 'atn';
+  /** token style for an sgml attribute value. */
+  var PR_ATTRIB_VALUE = 'atv';
+
+  /**
+   * A class that indicates a section of markup that is not code, e.g. to allow
+   * embedding of line numbers within code listings.
+   */
+  var PR_NOCODE = 'nocode';
+
+  function isWordChar(ch) {
+    return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
+  }
+
+  /** Splice one array into another.
+    * Like the python <code>
+    * container[containerPosition:containerPosition + countReplaced] = inserted
+    * </code>
+    * @param {Array} inserted
+    * @param {Array} container modified in place
+    * @param {Number} containerPosition
+    * @param {Number} countReplaced
+    */
+  function spliceArrayInto(
+      inserted, container, containerPosition, countReplaced) {
+    inserted.unshift(containerPosition, countReplaced || 0);
+    try {
+      container.splice.apply(container, inserted);
+    } finally {
+      inserted.splice(0, 2);
+    }
+  }
+
+  /** A set of tokens that can precede a regular expression literal in
+    * javascript.
+    * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
+    * list, but I've removed ones that might be problematic when seen in
+    * languages that don't support regular expression literals.
+    *
+    * <p>Specifically, I've removed any keywords that can't precede a regexp
+    * literal in a syntactically legal javascript program, and I've removed the
+    * "in" keyword since it's not a keyword in many languages, and might be used
+    * as a count of inches.
+    * @private
+    */
+  var REGEXP_PRECEDER_PATTERN = function () {
+      var preceders = [
+          "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
+          "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
+          "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
+          "<", "<<", "<<=", "<=", "=", "==", "===", ">",
+          ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
+          "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
+          "||=", "~" /* handles =~ and !~ */,
+          "break", "case", "continue", "delete",
+          "do", "else", "finally", "instanceof",
+          "return", "throw", "try", "typeof"
+          ];
+      var pattern = '(?:' +
+          '(?:(?:^|[^0-9.])\\.{1,3})|' +  // a dot that's not part of a number
+          '(?:(?:^|[^\\+])\\+)|' +  // allow + but not ++
+          '(?:(?:^|[^\\-])-)';  // allow - but not --
+      for (var i = 0; i < preceders.length; ++i) {
+        var preceder = preceders[i];
+        if (isWordChar(preceder.charAt(0))) {
+          pattern += '|\\b' + preceder;
+        } else {
+          pattern += '|' + preceder.replace(/([^=<>:&])/g, '\\$1');
+        }
+      }
+      pattern += '|^)\\s*$';  // matches at end, and matches empty string
+      return new RegExp(pattern);
+      // CAVEAT: this does not properly handle the case where a regular
+      // expression immediately follows another since a regular expression may
+      // have flags for case-sensitivity and the like.  Having regexp tokens
+      // adjacent is not
+      // valid in any language I'm aware of, so I'm punting.
+      // TODO: maybe style special characters inside a regexp as punctuation.
+    }();
+
+  // Define regexps here so that the interpreter doesn't have to create an
+  // object each time the function containing them is called.
+  // The language spec requires a new object created even if you don't access
+  // the $1 members.
+  var pr_amp = /&/g;
+  var pr_lt = /</g;
+  var pr_gt = />/g;
+  var pr_quot = /\"/g;
+  /** like textToHtml but escapes double quotes to be attribute safe. */
+  function attribToHtml(str) {
+    return str.replace(pr_amp, '&amp;')
+        .replace(pr_lt, '&lt;')
+        .replace(pr_gt, '&gt;')
+        .replace(pr_quot, '&quot;');
+  }
+
+  /** escapest html special characters to html. */
+  function textToHtml(str) {
+    return str.replace(pr_amp, '&amp;')
+        .replace(pr_lt, '&lt;')
+        .replace(pr_gt, '&gt;');
+  }
+
+
+  var pr_ltEnt = /&lt;/g;
+  var pr_gtEnt = /&gt;/g;
+  var pr_aposEnt = /&apos;/g;
+  var pr_quotEnt = /&quot;/g;
+  var pr_ampEnt = /&amp;/g;
+  var pr_nbspEnt = /&nbsp;/g;
+  /** unescapes html to plain text. */
+  function htmlToText(html) {
+    var pos = html.indexOf('&');
+    if (pos < 0) { return html; }
+    // Handle numeric entities specially.  We can't use functional substitution
+    // since that doesn't work in older versions of Safari.
+    // These should be rare since most browsers convert them to normal chars.
+    for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) {
+      var end = html.indexOf(';', pos);
+      if (end >= 0) {
+        var num = html.substring(pos + 3, end);
+        var radix = 10;
+        if (num && num.charAt(0) === 'x') {
+          num = num.substring(1);
+          radix = 16;
+        }
+        var codePoint = parseInt(num, radix);
+        if (!isNaN(codePoint)) {
+          html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
+                  html.substring(end + 1));
+        }
+      }
+    }
+
+    return html.replace(pr_ltEnt, '<')
+        .replace(pr_gtEnt, '>')
+        .replace(pr_aposEnt, "'")
+        .replace(pr_quotEnt, '"')
+        .replace(pr_ampEnt, '&')
+        .replace(pr_nbspEnt, ' ');
+  }
+
+  /** is the given node's innerHTML normally unescaped? */
+  function isRawContent(node) {
+    return 'XMP' === node.tagName;
+  }
+
+  function normalizedHtml(node, out) {
+    switch (node.nodeType) {
+      case 1:  // an element
+        var name = node.tagName.toLowerCase();
+        out.push('<', name);
+        for (var i = 0; i < node.attributes.length; ++i) {
+          var attr = node.attributes[i];
+          if (!attr.specified) { continue; }
+          out.push(' ');
+          normalizedHtml(attr, out);
+        }
+        out.push('>');
+        for (var child = node.firstChild; child; child = child.nextSibling) {
+          normalizedHtml(child, out);
+        }
+        if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
+          out.push('<\/', name, '>');
+        }
+        break;
+      case 2: // an attribute
+        out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"');
+        break;
+      case 3: case 4: // text
+        out.push(textToHtml(node.nodeValue));
+        break;
+    }
+  }
+
+  var PR_innerHtmlWorks = null;
+  function getInnerHtml(node) {
+    // inner html is hopelessly broken in Safari 2.0.4 when the content is
+    // an html description of well formed XML and the containing tag is a PRE
+    // tag, so we detect that case and emulate innerHTML.
+    if (null === PR_innerHtmlWorks) {
+      var testNode = document.createElement('PRE');
+      testNode.appendChild(
+          document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
+      PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
+    }
+
+    if (PR_innerHtmlWorks) {
+      var content = node.innerHTML;
+      // XMP tags contain unescaped entities so require special handling.
+      if (isRawContent(node)) {
+        content = textToHtml(content);
+      }
+      return content;
+    }
+
+    var out = [];
+    for (var child = node.firstChild; child; child = child.nextSibling) {
+      normalizedHtml(child, out);
+    }
+    return out.join('');
+  }
+
+  /** returns a function that expand tabs to spaces.  This function can be fed
+    * successive chunks of text, and will maintain its own internal state to
+    * keep track of how tabs are expanded.
+    * @return {function (string) : string} a function that takes
+    *   plain text and return the text with tabs expanded.
+    * @private
+    */
+  function makeTabExpander(tabWidth) {
+    var SPACES = '                ';
+    var charInLine = 0;
+
+    return function (plainText) {
+      // walk over each character looking for tabs and newlines.
+      // On tabs, expand them.  On newlines, reset charInLine.
+      // Otherwise increment charInLine
+      var out = null;
+      var pos = 0;
+      for (var i = 0, n = plainText.length; i < n; ++i) {
+        var ch = plainText.charAt(i);
+
+        switch (ch) {
+          case '\t':
+            if (!out) { out = []; }
+            out.push(plainText.substring(pos, i));
+            // calculate how much space we need in front of this part
+            // nSpaces is the amount of padding -- the number of spaces needed
+            // to move us to the next column, where columns occur at factors of
+            // tabWidth.
+            var nSpaces = tabWidth - (charInLine % tabWidth);
+            charInLine += nSpaces;
+            for (; nSpaces >= 0; nSpaces -= SPACES.length) {
+              out.push(SPACES.substring(0, nSpaces));
+            }
+            pos = i + 1;
+            break;
+          case '\n':
+            charInLine = 0;
+            break;
+          default:
+            ++charInLine;
+        }
+      }
+      if (!out) { return plainText; }
+      out.push(plainText.substring(pos));
+      return out.join('');
+    };
+  }
+
+  // The below pattern matches one of the following
+  // (1) /[^<]+/ : A run of characters other than '<'
+  // (2) /<!--.*?-->/: an HTML comment
+  // (3) /<!\[CDATA\[.*?\]\]>/: a cdata section
+  // (3) /<\/?[a-zA-Z][^>]*>/ : A probably tag that should not be highlighted
+  // (4) /</ : A '<' that does not begin a larger chunk.  Treated as 1
+  var pr_chunkPattern =
+  /(?:[^<]+|<!--[\s\S]*?-->|<!\[CDATA\[([\s\S]*?)\]\]>|<\/?[a-zA-Z][^>]*>|<)/g;
+  var pr_commentPrefix = /^<!--/;
+  var pr_cdataPrefix = /^<\[CDATA\[/;
+  var pr_brPrefix = /^<br\b/i;
+  var pr_tagNameRe = /^<(\/?)([a-zA-Z]+)/;
+
+  /** split markup into chunks of html tags (style null) and
+    * plain text (style {@link #PR_PLAIN}), converting tags which are
+    * significant for tokenization (<br>) into their textual equivalent.
+    *
+    * @param {string} s html where whitespace is considered significant.
+    * @return {Object} source code and extracted tags.
+    * @private
+    */
+  function extractTags(s) {
+    // since the pattern has the 'g' modifier and defines no capturing groups,
+    // this will return a list of all chunks which we then classify and wrap as
+    // PR_Tokens
+    var matches = s.match(pr_chunkPattern);
+    var sourceBuf = [];
+    var sourceBufLen = 0;
+    var extractedTags = [];
+    if (matches) {
+      for (var i = 0, n = matches.length; i < n; ++i) {
+        var match = matches[i];
+        if (match.length > 1 && match.charAt(0) === '<') {
+          if (pr_commentPrefix.test(match)) { continue; }
+          if (pr_cdataPrefix.test(match)) {
+            // strip CDATA prefix and suffix.  Don't unescape since it's CDATA
+            sourceBuf.push(match.substring(9, match.length - 3));
+            sourceBufLen += match.length - 12;
+          } else if (pr_brPrefix.test(match)) {
+            // <br> tags are lexically significant so convert them to text.
+            // This is undone later.
+            sourceBuf.push('\n');
+            ++sourceBufLen;
+          } else {
+            if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
+              // A <span class="nocode"> will start a section that should be
+              // ignored.  Continue walking the list until we see a matching end
+              // tag.
+              var name = match.match(pr_tagNameRe)[2];
+              var depth = 1;
+              end_tag_loop:
+              for (var j = i + 1; j < n; ++j) {
+                var name2 = matches[j].match(pr_tagNameRe);
+                if (name2 && name2[2] === name) {
+                  if (name2[1] === '/') {
+                    if (--depth === 0) { break end_tag_loop; }
+                  } else {
+                    ++depth;
+                  }
+                }
+              }
+              if (j < n) {
+                extractedTags.push(
+                    sourceBufLen, matches.slice(i, j + 1).join(''));
+                i = j;
+              } else {  // Ignore unclosed sections.
+                extractedTags.push(sourceBufLen, match);
+              }
+            } else {
+              extractedTags.push(sourceBufLen, match);
+            }
+          }
+        } else {
+          var literalText = htmlToText(match);
+          sourceBuf.push(literalText);
+          sourceBufLen += literalText.length;
+        }
+      }
+    }
+    return { source: sourceBuf.join(''), tags: extractedTags };
+  }
+
+  /** True if the given tag contains a class attribute with the nocode class. */
+  function isNoCodeTag(tag) {
+    return !!tag
+        // First canonicalize the representation of attributes
+        .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
+                 ' $1="$2$3$4"')
+        // Then look for the attribute we want.
+        .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
+  }
+
+  /** Given triples of [style, pattern, context] returns a lexing function,
+    * The lexing function interprets the patterns to find token boundaries and
+    * returns a decoration list of the form
+    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
+    * where index_n is an index into the sourceCode, and style_n is a style
+    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
+    * all characters in sourceCode[index_n-1:index_n].
+    *
+    * The stylePatterns is a list whose elements have the form
+    * [style : string, pattern : RegExp, context : RegExp, shortcut : string].
+    &
+    * Style is a style constant like PR_PLAIN.
+    *
+    * Pattern must only match prefixes, and if it matches a prefix and context
+    * is null or matches the last non-comment token parsed, then that match is
+    * considered a token with the same style.
+    *
+    * Context is applied to the last non-whitespace, non-comment token
+    * recognized.
+    *
+    * Shortcut is an optional string of characters, any of which, if the first
+    * character, gurantee that this pattern and only this pattern matches.
+    *
+    * @param {Array} shortcutStylePatterns patterns that always start with
+    *   a known character.  Must have a shortcut string.
+    * @param {Array} fallthroughStylePatterns patterns that will be tried in
+    *   order if the shortcut ones fail.  May have shortcuts.
+    *
+    * @return {function (string, number?) : Array.<number|string>} a
+    *   function that takes source code and returns a list of decorations.
+    */
+  function createSimpleLexer(shortcutStylePatterns,
+                             fallthroughStylePatterns) {
+    var shortcuts = {};
+    (function () {
+      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
+      for (var i = allPatterns.length; --i >= 0;) {
+        var patternParts = allPatterns[i];
+        var shortcutChars = patternParts[3];
+        if (shortcutChars) {
+          for (var c = shortcutChars.length; --c >= 0;) {
+            shortcuts[shortcutChars.charAt(c)] = patternParts;
+          }
+        }
+      }
+    })();
+
+    var nPatterns = fallthroughStylePatterns.length;
+    var notWs = /\S/;
+
+    return function (sourceCode, opt_basePos) {
+      opt_basePos = opt_basePos || 0;
+      var decorations = [opt_basePos, PR_PLAIN];
+      var lastToken = '';
+      var pos = 0;  // index into sourceCode
+      var tail = sourceCode;
+
+      while (tail.length) {
+        var style;
+        var token = null;
+        var match;
+
+        var patternParts = shortcuts[tail.charAt(0)];
+        if (patternParts) {
+          match = tail.match(patternParts[1]);
+          token = match[0];
+          style = patternParts[0];
+        } else {
+          for (var i = 0; i < nPatterns; ++i) {
+            patternParts = fallthroughStylePatterns[i];
+            var contextPattern = patternParts[2];
+            if (contextPattern && !contextPattern.test(lastToken)) {
+              // rule can't be used
+              continue;
+            }
+            match = tail.match(patternParts[1]);
+            if (match) {
+              token = match[0];
+              style = patternParts[0];
+              break;
+            }
+          }
+
+          if (!token) {  // make sure that we make progress
+            style = PR_PLAIN;
+            token = tail.substring(0, 1);
+          }
+        }
+
+        decorations.push(opt_basePos + pos, style);
+        pos += token.length;
+        tail = tail.substring(token.length);
+        if (style !== PR_COMMENT && notWs.test(token)) { lastToken = token; }
+      }
+      return decorations;
+    };
+  }
+
+  var PR_MARKUP_LEXER = createSimpleLexer([], [
+      [PR_PLAIN,       /^[^<]+/, null],
+      [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/, null],
+      [PR_COMMENT,     /^<!--[\s\S]*?(?:-->|$)/, null],
+      [PR_SOURCE,      /^<\?[\s\S]*?(?:\?>|$)/, null],
+      [PR_SOURCE,      /^<%[\s\S]*?(?:%>|$)/, null],
+      [PR_SOURCE,
+       // Tags whose content is not escaped, and which contain source code.
+       /^<(script|style|xmp)\b[^>]*>[\s\S]*?<\/\1\b[^>]*>/i, null],
+      [PR_TAG,         /^<\/?\w[^<>]*>/, null]
+      ]);
+  // Splits any of the source|style|xmp entries above into a start tag,
+  // source content, and end tag.
+  var PR_SOURCE_CHUNK_PARTS = /^(<[^>]*>)([\s\S]*)(<\/[^>]*>)$/;
+  /** split markup on tags, comments, application directives, and other top
+    * level constructs.  Tags are returned as a single token - attributes are
+    * not yet broken out.
+    * @private
+    */
+  function tokenizeMarkup(source) {
+    var decorations = PR_MARKUP_LEXER(source);
+    for (var i = 0; i < decorations.length; i += 2) {
+      if (decorations[i + 1] === PR_SOURCE) {
+        var start, end;
+        start = decorations[i];
+        end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+        // Split out start and end script tags as actual tags, and leave the
+        // body with style SCRIPT.
+        var sourceChunk = source.substring(start, end);
+        var match = sourceChunk.match(PR_SOURCE_CHUNK_PARTS);
+        if (match) {
+          decorations.splice(
+              i, 2,
+              start, PR_TAG,  // the open chunk
+              start + match[1].length, PR_SOURCE,
+              start + match[1].length + (match[2] || '').length, PR_TAG);
+        }
+      }
+    }
+    return decorations;
+  }
+
+  var PR_TAG_LEXER = createSimpleLexer([
+      [PR_ATTRIB_VALUE, /^\'[^\']*(?:\'|$)/, null, "'"],
+      [PR_ATTRIB_VALUE, /^\"[^\"]*(?:\"|$)/, null, '"'],
+      [PR_PUNCTUATION,  /^[<>\/=]+/, null, '<>/=']
+      ], [
+      [PR_TAG,          /^[\w:\-]+/, /^</],
+      [PR_ATTRIB_VALUE, /^[\w\-]+/, /^=/],
+      [PR_ATTRIB_NAME,  /^[\w:\-]+/, null],
+      [PR_PLAIN,        /^\s+/, null, ' \t\r\n']
+      ]);
+  /** split tags attributes and their values out from the tag name, and
+    * recursively lex source chunks.
+    * @private
+    */
+  function splitTagAttributes(source, decorations) {
+    for (var i = 0; i < decorations.length; i += 2) {
+      var style = decorations[i + 1];
+      if (style === PR_TAG) {
+        var start, end;
+        start = decorations[i];
+        end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+        var chunk = source.substring(start, end);
+        var subDecorations = PR_TAG_LEXER(chunk, start);
+        spliceArrayInto(subDecorations, decorations, i, 2);
+        i += subDecorations.length - 2;
+      }
+    }
+    return decorations;
+  }
+
+  /** returns a function that produces a list of decorations from source text.
+    *
+    * This code treats ", ', and ` as string delimiters, and \ as a string
+    * escape.  It does not recognize perl's qq() style strings.
+    * It has no special handling for double delimiter escapes as in basic, or
+    * the tripled delimiters used in python, but should work on those regardless
+    * although in those cases a single string literal may be broken up into
+    * multiple adjacent string literals.
+    *
+    * It recognizes C, C++, and shell style comments.
+    *
+    * @param {Object} options a set of optional parameters.
+    * @return {function (string) : Array.<string|number>} a
+    *     decorator that takes sourceCode as plain text and that returns a
+    *     decoration list
+    */
+  function sourceDecorator(options) {
+    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
+    if (options.tripleQuotedStrings) {
+      // '''multi-line-string''', 'single-line-string', and double-quoted
+      shortcutStylePatterns.push(
+          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
+           null, '\'"']);
+    } else if (options.multiLineStrings) {
+      // 'multi-line-string', "multi-line-string"
+      shortcutStylePatterns.push(
+          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
+           null, '\'"`']);
+    } else {
+      // 'single-line-string', "single-line-string"
+      shortcutStylePatterns.push(
+          [PR_STRING,
+           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
+           null, '"\'']);
+    }
+    fallthroughStylePatterns.push(
+        [PR_PLAIN,   /^(?:[^\'\"\`\/\#]+)/, null, ' \r\n']);
+    if (options.hashComments) {
+      shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
+    }
+    if (options.cStyleComments) {
+      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
+      fallthroughStylePatterns.push(
+          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
+    }
+    if (options.regexLiterals) {
+      var REGEX_LITERAL = (
+          // A regular expression literal starts with a slash that is
+          // not followed by * or / so that it is not confused with
+          // comments.
+          '^/(?=[^/*])'
+          // and then contains any number of raw characters,
+          + '(?:[^/\\x5B\\x5C]'
+          // escape sequences (\x5C),
+          +    '|\\x5C[\\s\\S]'
+          // or non-nesting character sets (\x5B\x5D);
+          +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
+          // finally closed by a /.
+          + '(?:/|$)');
+      fallthroughStylePatterns.push(
+          [PR_STRING, new RegExp(REGEX_LITERAL), REGEXP_PRECEDER_PATTERN]);
+    }
+
+    var keywords = wordSet(options.keywords);
+
+    options = null;
+
+    /** splits the given string into comment, string, and "other" tokens.
+      * @param {string} sourceCode as plain text
+      * @return {Array.<number|string>} a decoration list.
+      * @private
+      */
+    var splitStringAndCommentTokens = createSimpleLexer(
+        shortcutStylePatterns, fallthroughStylePatterns);
+
+    var styleLiteralIdentifierPuncRecognizer = createSimpleLexer([], [
+        [PR_PLAIN,       /^\s+/, null, ' \r\n'],
+        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
+        [PR_PLAIN,       /^[a-z_$@][a-z_$@0-9]*/i, null],
+        // A hex number
+        [PR_LITERAL,     /^0x[a-f0-9]+[a-z]/i, null],
+        // An octal or decimal number, possibly in scientific notation
+        [PR_LITERAL,
+         /^(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?[a-z]*/i,
+         null, '123456789'],
+        [PR_PUNCTUATION, /^[^\s\w\.$@]+/, null]
+        // Fallback will handle decimal points not adjacent to a digit
+      ]);
+
+    /** splits plain text tokens into more specific tokens, and then tries to
+      * recognize keywords, and types.
+      * @private
+      */
+    function splitNonStringNonCommentTokens(source, decorations) {
+      for (var i = 0; i < decorations.length; i += 2) {
+        var style = decorations[i + 1];
+        if (style === PR_PLAIN) {
+          var start, end, chunk, subDecs;
+          start = decorations[i];
+          end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+          chunk = source.substring(start, end);
+          subDecs = styleLiteralIdentifierPuncRecognizer(chunk, start);
+          for (var j = 0, m = subDecs.length; j < m; j += 2) {
+            var subStyle = subDecs[j + 1];
+            if (subStyle === PR_PLAIN) {
+              var subStart = subDecs[j];
+              var subEnd = j + 2 < m ? subDecs[j + 2] : chunk.length;
+              var token = source.substring(subStart, subEnd);
+              if (token === '.') {
+                subDecs[j + 1] = PR_PUNCTUATION;
+              } else if (token in keywords) {
+                subDecs[j + 1] = PR_KEYWORD;
+              } else if (/^@?[A-Z][A-Z$]*[a-z][A-Za-z$]*$/.test(token)) {
+                // classify types and annotations using Java's style conventions
+                subDecs[j + 1] = token.charAt(0) === '@' ? PR_LITERAL : PR_TYPE;
+              }
+            }
+          }
+          spliceArrayInto(subDecs, decorations, i, 2);
+          i += subDecs.length - 2;
+        }
+      }
+      return decorations;
+    }
+
+    return function (sourceCode) {
+      // Split into strings, comments, and other.
+      // We do this because strings and comments are easily recognizable and can
+      // contain stuff that looks like other tokens, so we want to mark those
+      // early so we don't recurse into them.
+      var decorations = splitStringAndCommentTokens(sourceCode);
+
+      // Split non comment|string tokens on whitespace and word boundaries
+      decorations = splitNonStringNonCommentTokens(sourceCode, decorations);
+
+      return decorations;
+    };
+  }
+
+  var decorateSource = sourceDecorator({
+        keywords: ALL_KEYWORDS,
+        hashComments: true,
+        cStyleComments: true,
+        multiLineStrings: true,
+        regexLiterals: true
+      });
+
+  /** identify regions of markup that are really source code, and recursivley
+    * lex them.
+    * @private
+    */
+  function splitSourceNodes(source, decorations) {
+    for (var i = 0; i < decorations.length; i += 2) {
+      var style = decorations[i + 1];
+      if (style === PR_SOURCE) {
+        // Recurse using the non-markup lexer
+        var start, end;
+        start = decorations[i];
+        end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+        var subDecorations = decorateSource(source.substring(start, end));
+        for (var j = 0, m = subDecorations.length; j < m; j += 2) {
+          subDecorations[j] += start;
+        }
+        spliceArrayInto(subDecorations, decorations, i, 2);
+        i += subDecorations.length - 2;
+      }
+    }
+    return decorations;
+  }
+
+  /** identify attribute values that really contain source code and recursively
+    * lex them.
+    * @private
+    */
+  function splitSourceAttributes(source, decorations) {
+    var nextValueIsSource = false;
+    for (var i = 0; i < decorations.length; i += 2) {
+      var style = decorations[i + 1];
+      var start, end;
+      if (style === PR_ATTRIB_NAME) {
+        start = decorations[i];
+        end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+        nextValueIsSource = /^on|^style$/i.test(source.substring(start, end));
+      } else if (style === PR_ATTRIB_VALUE) {
+        if (nextValueIsSource) {
+          start = decorations[i];
+          end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+          var attribValue = source.substring(start, end);
+          var attribLen = attribValue.length;
+          var quoted =
+              (attribLen >= 2 && /^[\"\']/.test(attribValue) &&
+               attribValue.charAt(0) === attribValue.charAt(attribLen - 1));
+
+          var attribSource;
+          var attribSourceStart;
+          var attribSourceEnd;
+          if (quoted) {
+            attribSourceStart = start + 1;
+            attribSourceEnd = end - 1;
+            attribSource = attribValue;
+          } else {
+            attribSourceStart = start + 1;
+            attribSourceEnd = end - 1;
+            attribSource = attribValue.substring(1, attribValue.length - 1);
+          }
+
+          var attribSourceDecorations = decorateSource(attribSource);
+          for (var j = 0, m = attribSourceDecorations.length; j < m; j += 2) {
+            attribSourceDecorations[j] += attribSourceStart;
+          }
+
+          if (quoted) {
+            attribSourceDecorations.push(attribSourceEnd, PR_ATTRIB_VALUE);
+            spliceArrayInto(attribSourceDecorations, decorations, i + 2, 0);
+          } else {
+            spliceArrayInto(attribSourceDecorations, decorations, i, 2);
+          }
+        }
+        nextValueIsSource = false;
+      }
+    }
+    return decorations;
+  }
+
+  /** returns a decoration list given a string of markup.
+    *
+    * This code recognizes a number of constructs.
+    * <!-- ... --> comment
+    * <!\w ... >   declaration
+    * <\w ... >    tag
+    * </\w ... >   tag
+    * <?...?>      embedded source
+    * <%...%>      embedded source
+    * &[#\w]...;   entity
+    *
+    * It does not recognizes %foo; doctype entities from  .
+    *
+    * It will recurse into any <style>, <script>, and on* attributes using
+    * PR_lexSource.
+    */
+  function decorateMarkup(sourceCode) {
+    // This function works as follows:
+    // 1) Start by splitting the markup into text and tag chunks
+    //    Input:  string s
+    //    Output: List<PR_Token> where style in (PR_PLAIN, null)
+    // 2) Then split the text chunks further into comments, declarations,
+    //    tags, etc.
+    //    After each split, consider whether the token is the start of an
+    //    embedded source section, i.e. is an open <script> tag.  If it is, find
+    //    the corresponding close token, and don't bother to lex in between.
+    //    Input:  List<string>
+    //    Output: List<PR_Token> with style in
+    //            (PR_TAG, PR_PLAIN, PR_SOURCE, null)
+    // 3) Finally go over each tag token and split out attribute names and
+    //    values.
+    //    Input:  List<PR_Token>
+    //    Output: List<PR_Token> where style in
+    //            (PR_TAG, PR_PLAIN, PR_SOURCE, NAME, VALUE, null)
+    var decorations = tokenizeMarkup(sourceCode);
+    decorations = splitTagAttributes(sourceCode, decorations);
+    decorations = splitSourceNodes(sourceCode, decorations);
+    decorations = splitSourceAttributes(sourceCode, decorations);
+    return decorations;
+  }
+
+  /**
+    * @param {string} sourceText plain text
+    * @param {Array.<number|string>} extractedTags chunks of raw html preceded
+    *   by their position in sourceText in order.
+    * @param {Array.<number|string>} decorations style classes preceded by their
+    *   position in sourceText in order.
+    * @return {string} html
+    * @private
+    */
+  function recombineTagsAndDecorations(sourceText, extractedTags, decorations) {
+    var html = [];
+    // index past the last char in sourceText written to html
+    var outputIdx = 0;
+
+    var openDecoration = null;
+    var currentDecoration = null;
+    var tagPos = 0;  // index into extractedTags
+    var decPos = 0;  // index into decorations
+    var tabExpander = makeTabExpander(PR_TAB_WIDTH);
+
+    var adjacentSpaceRe = /([\r\n ]) /g;
+    var startOrSpaceRe = /(^| ) /gm;
+    var newlineRe = /\r\n?|\n/g;
+    var trailingSpaceRe = /[ \r\n]$/;
+    var lastWasSpace = true;  // the last text chunk emitted ended with a space.
+
+    // A helper function that is responsible for opening sections of decoration
+    // and outputing properly escaped chunks of source
+    function emitTextUpTo(sourceIdx) {
+      if (sourceIdx > outputIdx) {
+        if (openDecoration && openDecoration !== currentDecoration) {
+          // Close the current decoration
+          html.push('</span>');
+          openDecoration = null;
+        }
+        if (!openDecoration && currentDecoration) {
+          openDecoration = currentDecoration;
+          html.push('<span class="', openDecoration, '">');
+        }
+        // This interacts badly with some wikis which introduces paragraph tags
+        // into pre blocks for some strange reason.
+        // It's necessary for IE though which seems to lose the preformattedness
+        // of <pre> tags when their innerHTML is assigned.
+        // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
+        // and it serves to undo the conversion of <br>s to newlines done in
+        // chunkify.
+        var htmlChunk = textToHtml(
+            tabExpander(sourceText.substring(outputIdx, sourceIdx)))
+            .replace(lastWasSpace
+                     ? startOrSpaceRe
+                     : adjacentSpaceRe, '$1&nbsp;');
+        // Keep track of whether we need to escape space at the beginning of the
+        // next chunk.
+        lastWasSpace = trailingSpaceRe.test(htmlChunk);
+        html.push(htmlChunk.replace(newlineRe, '<br />'));
+        outputIdx = sourceIdx;
+      }
+    }
+
+    while (true) {
+      // Determine if we're going to consume a tag this time around.  Otherwise
+      // we consume a decoration or exit.
+      var outputTag;
+      if (tagPos < extractedTags.length) {
+        if (decPos < decorations.length) {
+          // Pick one giving preference to extractedTags since we shouldn't open
+          // a new style that we're going to have to immediately close in order
+          // to output a tag.
+          outputTag = extractedTags[tagPos] <= decorations[decPos];
+        } else {
+          outputTag = true;
+        }
+      } else {
+        outputTag = false;
+      }
+      // Consume either a decoration or a tag or exit.
+      if (outputTag) {
+        emitTextUpTo(extractedTags[tagPos]);
+        if (openDecoration) {
+          // Close the current decoration
+          html.push('</span>');
+          openDecoration = null;
+        }
+        html.push(extractedTags[tagPos + 1]);
+        tagPos += 2;
+      } else if (decPos < decorations.length) {
+        emitTextUpTo(decorations[decPos]);
+        currentDecoration = decorations[decPos + 1];
+        decPos += 2;
+      } else {
+        break;
+      }
+    }
+    emitTextUpTo(sourceText.length);
+    if (openDecoration) {
+      html.push('</span>');
+    }
+
+    return html.join('');
+  }
+
+  /** Maps language-specific file extensions to handlers. */
+  var langHandlerRegistry = {};
+  /** Register a language handler for the given file extensions.
+    * @param {function (string) : Array.<number|string>} handler
+    *     a function from source code to a list of decorations.
+    * @param {Array.<string>} fileExtensions
+    */
+  function registerLangHandler(handler, fileExtensions) {
+    for (var i = fileExtensions.length; --i >= 0;) {
+      var ext = fileExtensions[i];
+      if (!langHandlerRegistry.hasOwnProperty(ext)) {
+        langHandlerRegistry[ext] = handler;
+      } else if ('console' in window) {
+        console.log('cannot override language handler %s', ext);
+      }
+    }
+  }
+  registerLangHandler(decorateSource, ['default-code']);
+  registerLangHandler(decorateMarkup,
+                      ['default-markup', 'html', 'htm', 'xhtml', 'xml', 'xsl']);
+  registerLangHandler(sourceDecorator({
+          keywords: CPP_KEYWORDS,
+          hashComments: true,
+          cStyleComments: true
+        }), ['c', 'cc', 'cpp', 'cxx', 'cyc']);
+  registerLangHandler(sourceDecorator({
+          keywords: CSHARP_KEYWORDS,
+          hashComments: true,
+          cStyleComments: true
+        }), ['cs']);
+  registerLangHandler(sourceDecorator({
+          keywords: JAVA_KEYWORDS,
+          cStyleComments: true
+        }), ['java']);
+  registerLangHandler(sourceDecorator({
+          keywords: SH_KEYWORDS,
+          hashComments: true,
+          multiLineStrings: true
+        }), ['bsh', 'csh', 'sh']);
+  registerLangHandler(sourceDecorator({
+          keywords: PYTHON_KEYWORDS,
+          hashComments: true,
+          multiLineStrings: true,
+          tripleQuotedStrings: true
+        }), ['cv', 'py']);
+  registerLangHandler(sourceDecorator({
+          keywords: PERL_KEYWORDS,
+          hashComments: true,
+          multiLineStrings: true,
+          regexLiterals: true
+        }), ['perl', 'pl', 'pm']);
+  registerLangHandler(sourceDecorator({
+          keywords: RUBY_KEYWORDS,
+          hashComments: true,
+          multiLineStrings: true,
+          regexLiterals: true
+        }), ['rb']);
+  registerLangHandler(sourceDecorator({
+          keywords: JSCRIPT_KEYWORDS,
+          cStyleComments: true,
+          regexLiterals: true
+        }), ['js']);
+
+  function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
+    try {
+      // Extract tags, and convert the source code to plain text.
+      var sourceAndExtractedTags = extractTags(sourceCodeHtml);
+      /** Plain text. @type {string} */
+      var source = sourceAndExtractedTags.source;
+
+      /** Even entries are positions in source in ascending order.  Odd entries
+        * are tags that were extracted at that position.
+        * @type {Array.<number|string>}
+        */
+      var extractedTags = sourceAndExtractedTags.tags;
+
+      // Pick a lexer and apply it.
+      if (!langHandlerRegistry.hasOwnProperty(opt_langExtension)) {
+        // Treat it as markup if the first non whitespace character is a < and
+        // the last non-whitespace character is a >.
+        opt_langExtension =
+            /^\s*</.test(source) ? 'default-markup' : 'default-code';
+      }
+
+      /** Even entries are positions in source in ascending order.  Odd enties
+        * are style markers (e.g., PR_COMMENT) that run from that position until
+        * the end.
+        * @type {Array.<number|string>}
+        */
+      var decorations = langHandlerRegistry[opt_langExtension].call({}, source);
+
+      // Integrate the decorations and tags back into the source code to produce
+      // a decorated html string.
+      return recombineTagsAndDecorations(source, extractedTags, decorations);
+    } catch (e) {
+      if ('console' in window) {
+        console.log(e);
+        console.trace();
+      }
+      return sourceCodeHtml;
+    }
+  }
+
+  function prettyPrint(opt_whenDone) {
+    var isIE6 = _pr_isIE6();
+
+    // fetch a list of nodes to rewrite
+    var codeSegments = [
+        document.getElementsByTagName('pre'),
+        document.getElementsByTagName('code'),
+        document.getElementsByTagName('xmp') ];
+    var elements = [];
+    for (var i = 0; i < codeSegments.length; ++i) {
+      for (var j = 0; j < codeSegments[i].length; ++j) {
+        elements.push(codeSegments[i][j]);
+      }
+    }
+    codeSegments = null;
+
+    // the loop is broken into a series of continuations to make sure that we
+    // don't make the browser unresponsive when rewriting a large page.
+    var k = 0;
+
+    function doWork() {
+      var endTime = (PR_SHOULD_USE_CONTINUATION ?
+                     new Date().getTime() + 250 /* ms */ :
+                     Infinity);
+      for (; k < elements.length && new Date().getTime() < endTime; k++) {
+        var cs = elements[k];
+        if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
+          // If the classes includes a language extensions, use it.
+          // Language extensions can be specified like
+          //     <pre class="prettyprint lang-cpp">
+          // the language extension "cpp" is used to find a language handler as
+          // passed to PR_registerLangHandler.
+          var langExtension = cs.className.match(/\blang-(\w+)\b/);
+          if (langExtension) { langExtension = langExtension[1]; }
+
+          // make sure this is not nested in an already prettified element
+          var nested = false;
+          for (var p = cs.parentNode; p; p = p.parentNode) {
+            if ((p.tagName === 'pre' || p.tagName === 'code' ||
+                 p.tagName === 'xmp') &&
+                p.className && p.className.indexOf('prettyprint') >= 0) {
+              nested = true;
+              break;
+            }
+          }
+          if (!nested) {
+            // fetch the content as a snippet of properly escaped HTML.
+            // Firefox adds newlines at the end.
+            var content = getInnerHtml(cs);
+            content = content.replace(/(?:\r\n?|\n)$/, '');
+
+            // do the pretty printing
+            var newContent = prettyPrintOne(content, langExtension);
+
+            // push the prettified html back into the tag.
+            if (!isRawContent(cs)) {
+              // just replace the old html with the new
+              cs.innerHTML = newContent;
+            } else {
+              // we need to change the tag to a <pre> since <xmp>s do not allow
+              // embedded tags such as the span tags used to attach styles to
+              // sections of source code.
+              var pre = document.createElement('PRE');
+              for (var i = 0; i < cs.attributes.length; ++i) {
+                var a = cs.attributes[i];
+                if (a.specified) {
+                  var aname = a.name.toLowerCase();
+                  if (aname === 'class') {
+                    pre.className = a.value;  // For IE 6
+                  } else {
+                    pre.setAttribute(a.name, a.value);
+                  }
+                }
+              }
+              pre.innerHTML = newContent;
+
+              // remove the old
+              cs.parentNode.replaceChild(pre, cs);
+              cs = pre;
+            }
+
+            // Replace <br>s with line-feeds so that copying and pasting works
+            // on IE 6.
+            // Doing this on other browsers breaks lots of stuff since \r\n is
+            // treated as two newlines on Firefox, and doing this also slows
+            // down rendering.
+            if (isIE6 && cs.tagName === 'PRE') {
+              var lineBreaks = cs.getElementsByTagName('br');
+              for (var j = lineBreaks.length; --j >= 0;) {
+                var lineBreak = lineBreaks[j];
+                lineBreak.parentNode.replaceChild(
+                    document.createTextNode('\r\n'), lineBreak);
+              }
+            }
+          }
+        }
+      }
+      if (k < elements.length) {
+        // finish up in a continuation
+        setTimeout(doWork, 250);
+      } else if (opt_whenDone) {
+        opt_whenDone();
+      }
+    }
+
+    doWork();
+  }
+
+  window['PR_normalizedHtml'] = normalizedHtml;
+  window['prettyPrintOne'] = prettyPrintOne;
+  window['prettyPrint'] = prettyPrint;
+  window['PR'] = {
+        'createSimpleLexer': createSimpleLexer,
+        'registerLangHandler': registerLangHandler,
+        'sourceDecorator': sourceDecorator,
+        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
+        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
+        'PR_COMMENT': PR_COMMENT,
+        'PR_DECLARATION': PR_DECLARATION,
+        'PR_KEYWORD': PR_KEYWORD,
+        'PR_LITERAL': PR_LITERAL,
+        'PR_NOCODE': PR_NOCODE,
+        'PR_PLAIN': PR_PLAIN,
+        'PR_PUNCTUATION': PR_PUNCTUATION,
+        'PR_SOURCE': PR_SOURCE,
+        'PR_STRING': PR_STRING,
+        'PR_TAG': PR_TAG,
+        'PR_TYPE': PR_TYPE
+      };
+})();
diff --git a/docs/verifier.html b/docs/verifier.html
new file mode 100644
index 0000000..21bbdf0
--- /dev/null
+++ b/docs/verifier.html
@@ -0,0 +1,179 @@
+<html>
+<head>
+<title>Dalvik Bytecode Verifier Notes</title>
+</head>
+
+<body>
+<h1>Dalvik Bytecode Verifier Notes</h1>
+
+<p>
+The bytecode verifier in the Dalvik VM attempts to provide the same sorts
+of checks and guarantees that other popular virtual machines do.  We
+perform generally the same set of checks as are described in _The Java
+Virtual Machine Specification, Second Edition_, including the updates
+planned for the Third Edition.
+
+<p>
+Verification can be enabled for all classes, disabled for all, or enabled
+only for "remote" (non-bootstrap) classes.  It should be performed for any
+class that will be processed with the DEX optimizer, and in fact the
+default VM behavior is to only optimize verified classes.
+
+
+<h2>Why Verify?</h2>
+
+<p>
+The verification process adds additional time to the build and to
+the installation of new applications.  It's fairly quick for app-sized
+DEX files, but rather slow for the big "core" and "framework" files.
+Why do it all, when our system relies on UNIX processes for security?
+<p>
+<ol>
+    <li>Optimizations.  The interpreter can ignore a lot of potential
+    error cases because the verifier guarantees that they are impossible.
+    Also, we can optimize the DEX file more aggressively if we start
+    with a stronger set of assumptions about the bytecode.
+    <li>"Precise" GC.  The work peformed during verification has significant
+    overlap with the work required to compute register use maps for
+    type-precise GC.
+    <li>Intra-application security.  If an app wants to download bits
+    of interpreted code over the network and execute them, it can safely
+    do so using well-established security mechanisms.
+    <li>3rd party app failure analysis.  We have no way to control the
+    tools and post-processing utilities that external developers employ,
+    so when we get bug reports with a weird exception or native crash
+    it's very helpful to start with the assumption that the bytecode
+    is valid.
+</ol>
+<p>
+It's also a convenient framework to deal with certain situations, notably
+replacement of instructions that access volatile 64-bit fields with
+more rigorous versions that guarantee atomicity.
+
+
+<h2>Verifier Differences</h2>
+
+<p>
+There are a few checks that the Dalvik bytecode verifier does not perform,
+because they're not relevant.  For example:
+<ul>
+    <li>Type restrictions on constant pool references are not enforced,
+    because Dalvik does not have a pool of typed constants.  (Dalvik
+    uses a simple index into type-specific pools.)
+    <li>Verification of the operand stack size is not performed, because
+    Dalvik does not have an operand stack.
+    <li>Limitations on <code>jsr</code> and <code>ret</code> do not apply,
+    because Dalvik doesn't support subroutines.
+</ul>
+
+In some cases they are implemented differently, e.g.:
+<ul>
+    <li>In a conventional VM, backward branches and exceptions are
+    forbidden when a local variable holds an uninitialized reference.  The
+    restriction was changed to mark registers as invalid when they hold
+    references to the uninitialized result of a previous invocation of the
+    same <code>new-instance</code> instruction.
+    This solves the same problem -- trickery potentially allowing
+    uninitialized objects to slip past the verifier -- without unduly
+    limiting branches.
+</ul>
+
+There are also some new ones, such as:
+<ul>
+    <li>The <code>move-exception</code> instruction can only appear as
+    the first instruction in an exception handler.
+    <li>The <code>move-result*</code> instructions can only appear
+    immediately after an appropriate <code>invoke-*</code>
+    or <code>filled-new-array</code> instruction.
+</ul>
+
+<p>
+The VM is permitted but not required to enforce "structured locking"
+constraints, which are designed to ensure that, when a method returns, all
+monitors locked by the method have been unlocked an equal number of times.
+This is not currently implemented.
+
+<p>
+The Dalvik verifier is more restrictive than other VMs in one area:
+type safety on sub-32-bit integer widths.  These additional restrictions
+should make it impossible to, say, pass a value outside the range
+[-128, 127] to a function that takes a <code>byte</code> as an argument.
+
+
+<h2>Verification Failures</h2>
+
+<p>
+The verifier may reject a class immediately, or it may defer throwing
+an exception until the code is actually used.  For example, if a class
+attempts to perform an illegal access on a field, the VM should throw
+an IllegalAccessError the first time the instruction is encountered.
+On the other hand, if a class contains an invalid bytecode, it should be
+rejected immediately with a VerifyError.
+
+<p>
+Immediate VerifyErrors are accompanied by detailed, if somewhat cryptic,
+information in the log file.  From this it's possible to determine the
+exact instruction that failed, and the reason for the failure.
+
+<p>
+It's a bit tricky to implement deferred verification errors in Dalvik.
+A few approaches were considered:
+
+<ol>
+<li>We could replace the invalid field access instruction with a special
+instruction that generates an illegal access error, and allow class
+verification to complete successfully.  This type of verification must
+be deferred to first class load, rather than be performed ahead of time
+during DEX optimization, because some failures will depend on the current
+execution environment (e.g. not all classes are available at dexopt time).
+At that point the bytecode instructions are mapped read-only during
+verification, so rewriting them isn't possible.
+</li>
+
+<li>We can perform the access checks when the field/method/class is
+resolved.  In a typical VM implementation we would do the check when the
+entry is resolved in the context of the current classfile, but our DEX
+files combine multiple classfiles together, merging the field/method/class
+resolution results into a single large table.  Once one class successfully
+resolves the field, every other class in the same DEX file would be able
+to access the field.  This is incorrect.
+</li>
+
+<li>Perform the access checks on every field/method/class access.
+This adds significant overhead.  This is mitigated somewhat by the DEX
+optimizer, which will convert many field/method/class accesses into a
+simpler form after performing the access check.  However, not all accesses
+can be optimized (e.g. accesses to classes unknown at dexopt time),
+and we don't currently have an optimized form of certain instructions
+(notably static field operations).
+</li>
+</ol>
+
+<p>
+In early versions of Dalvik (as found in Android 1.6 and earlier), the verifier
+simply regarded all problems as immediately fatal.  This generally worked,
+but in some cases the VM was rejecting classes because of bits of code
+that were never used.  The VerifyError itself was sometimes difficult to
+decipher, because it was thrown during verification rather than at the
+point where the problem was first noticed during execution.
+<p>
+The current version uses a variation of approach #1.  The dexopt
+command works the way it did before, leaving the code untouched and
+flagging fully-correct classes as "pre-verified".  When the VM loads a
+class that didn't pass pre-verification, the verifier is invoked.  If a
+"deferrable" problem is detected, a modifiable copy of the instructions
+in the problematic method is made.  In that copy, the troubled instruction
+is replaced with an "always throw" opcode, and verification continues.
+
+<p>
+In the example used earlier, an attempt to read from an inaccessible
+field would result in the "field get" instruction being replaced by
+"always throw IllegalAccessError on field X".  Creating copies of method
+bodies requires additional heap space, but since this affects very few
+methods overall the memory impact should be minor.
+
+<p>
+<address>Copyright &copy; 2008 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/dvz/Android.mk b/dvz/Android.mk
new file mode 100644
index 0000000..4e4387e
--- /dev/null
+++ b/dvz/Android.mk
@@ -0,0 +1,19 @@
+# Copyright 2006 The Android Open Source Project
+
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+  	dvz.c
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_CFLAGS :=
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dvz
+
+include $(BUILD_EXECUTABLE)
diff --git a/dvz/dvz.c b/dvz/dvz.c
new file mode 100644
index 0000000..88fe086
--- /dev/null
+++ b/dvz/dvz.c
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#include <cutils/zygote.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#ifndef NELEM
+# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#endif
+
+// pid of child process
+static pid_t g_pid = -1;
+
+static void signal_forwarder (int signal, siginfo_t *si, void *context)
+{
+    if (g_pid >= 0) {
+        kill(g_pid, signal);
+    }
+}
+
+static void post_run_func (int pid) {
+    int my_pgid;
+    int spawned_pgid;
+    int i;
+    int err;
+
+    g_pid = pid;
+
+    my_pgid = getpgid(0);
+    if (my_pgid < 0) {
+        perror ("error with getpgid()");
+        exit (-1);
+    }
+
+    spawned_pgid = getpgid(pid);
+    if (spawned_pgid < 0) {
+        perror ("error with getpgid()");
+        exit (-1);
+    }
+
+    if (my_pgid != spawned_pgid) {
+        // The zygote was unable to move this process into our pgid
+        // We have to forward signals
+
+        int forward_signals[]
+            = {SIGHUP, SIGINT, SIGTERM, SIGWINCH,
+            SIGTSTP, SIGTTIN, SIGTTOU, SIGCONT};
+
+        struct sigaction sa;
+        memset(&sa, 0, sizeof(sa));
+
+        sa.sa_sigaction = signal_forwarder;
+        sa.sa_flags = SA_SIGINFO;
+
+        for (i = 0; i < NELEM(forward_signals); i++) {
+            err = sigaction(forward_signals[i], &sa, NULL);
+            if (err < 0) {
+                perror ("unexpected error");
+                exit (-1);
+            }
+        }
+    }
+}
+
+static void usage(const char *argv0) {
+    fprintf(stderr,"Usage: %s [--help] [-classpath <classpath>] \n"
+    "\t[additional zygote args] fully.qualified.java.ClassName [args]\n", argv0);
+    fprintf(stderr, "\nRequests a new Dalvik VM instance to be spawned from the zygote\n"
+    "process. stdin, stdout, and stderr are hooked up. This process remains\n"
+    "while the spawned VM instance is alive and forwards some signals.\n"
+    "The exit code of the spawned VM instance is dropped.\n");
+}
+
+int main (int argc, const char **argv) {
+    int err;
+
+    if (argc > 1 && 0 == strcmp(argv[1], "--help")) {
+        usage(argv[0]);
+        exit(0);
+    }
+
+    err = zygote_run_wait(argc - 1, argv + 1, post_run_func);
+
+    if (err < 0) {
+        fprintf(stderr, "%s error: no zygote process found\n", argv[0]);
+        exit(-1);
+    }
+    exit(0);
+}
diff --git a/dx/.classpath b/dx/.classpath
new file mode 100644
index 0000000..5b6d9c7
--- /dev/null
+++ b/dx/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3.8.1"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/dx/.project b/dx/.project
new file mode 100644
index 0000000..bcae232
--- /dev/null
+++ b/dx/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>dx</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/dx/Android.mk b/dx/Android.mk
new file mode 100644
index 0000000..3abf21a
--- /dev/null
+++ b/dx/Android.mk
@@ -0,0 +1,73 @@
+# Copyright 2006 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script files' timestamps are at least as new as the
+# .jar files they wrap.
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
+# the dx script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dx
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/dx$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/dx | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+endif # TARGET_BUILD_APPS
+
+# the jasmin script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := jasmin
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/jasmin.jar
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/jasmin | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the jasmin lib
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := jasmin.jar
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/jasmin.jar | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-target)
+	$(hide) chmod 644 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+		src \
+	))
+
+include $(subdirs)
diff --git a/dx/README.txt b/dx/README.txt
new file mode 100644
index 0000000..6a20c82
--- /dev/null
+++ b/dx/README.txt
@@ -0,0 +1,3 @@
+Home of Dalvik eXchange, the thing that takes in class files and
+reformulates them for consumption in the VM. It also does a few other
+things; use "dx --help" to see a modicum of self-documentation.
diff --git a/dx/etc/bytecode.txt b/dx/etc/bytecode.txt
new file mode 100644
index 0000000..f1df5bf
--- /dev/null
+++ b/dx/etc/bytecode.txt
@@ -0,0 +1,278 @@
+# 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.
+
+# Bytecode definition file
+#
+# Columns are:
+#   hex for opcode
+#   format
+#   has result register (y/n)
+#   opcode name
+
+00 10x n nop
+01 12x y move
+02 22x y move/from16
+03 32x y move/16
+04 12x y move-wide
+05 22x y move-wide/from16
+06 32x y move-wide/16
+07 12x y move-object
+08 22x y move-object/from16
+09 32x y move-object/16
+0a 11x y move-result
+0b 11x y move-result-wide
+0c 11x y move-result-object
+0d 11x y move-exception
+0e 10x n return-void
+0f 11x n return
+10 11x n return-wide
+11 11x n return-object
+12 11n y const/4
+13 21s y const/16
+14 31i y const
+15 21h y const/high16
+16 21s y const-wide/16
+17 31i y const-wide/32
+18 51l y const-wide
+19 21h y const-wide/high16
+1a 21c y const-string
+1b 31c y const-string/jumbo
+1c 21c y const-class
+1d 11x n monitor-enter
+1e 11x n monitor-exit
+1f 21c y check-cast
+20 22c y instance-of
+21 12x y array-length
+22 21c y new-instance
+23 22c y new-array
+24 35c n filled-new-array
+25 3rc n filled-new-array/range
+26 31t n fill-array-data
+27 11x n throw
+28 10t n goto
+29 20t n goto/16
+2a 30t n goto/32
+2b 31t n packed-switch
+2c 31t n sparse-switch
+2d 23x y cmpl-float
+2e 23x y cmpg-float
+2f 23x y cmpl-double
+30 23x y cmpg-double
+31 23x y cmp-long
+32 22t n if-eq
+33 22t n if-ne
+34 22t n if-lt
+35 22t n if-ge
+36 22t n if-gt
+37 22t n if-le
+38 21t n if-eqz
+39 21t n if-nez
+3a 21t n if-ltz
+3b 21t n if-gez
+3c 21t n if-gtz
+3d 21t n if-lez
+3e 10x n unused-3e
+3f 10x n unused-3f
+40 10x n unused-40
+41 10x n unused-41
+42 10x n unused-42
+43 10x n unused-43
+44 23x y aget
+45 23x y aget-wide
+46 23x y aget-object
+47 23x y aget-boolean
+48 23x y aget-byte
+49 23x y aget-char
+4a 23x y aget-short
+4b 23x n aput
+4c 23x n aput-wide
+4d 23x n aput-object
+4e 23x n aput-boolean
+4f 23x n aput-byte
+50 23x n aput-char
+51 23x n aput-short
+52 22c y iget
+53 22c y iget-wide
+54 22c y iget-object
+55 22c y iget-boolean
+56 22c y iget-byte
+57 22c y iget-char
+58 22c y iget-short
+59 22c n iput
+5a 22c n iput-wide
+5b 22c n iput-object
+5c 22c n iput-boolean
+5d 22c n iput-byte
+5e 22c n iput-char
+5f 22c n iput-short
+60 21c y sget
+61 21c y sget-wide
+62 21c y sget-object
+63 21c y sget-boolean
+64 21c y sget-byte
+65 21c y sget-char
+66 21c y sget-short
+67 21c n sput
+68 21c n sput-wide
+69 21c n sput-object
+6a 21c n sput-boolean
+6b 21c n sput-byte
+6c 21c n sput-char
+6d 21c n sput-short
+6e 35c n invoke-virtual
+6f 35c n invoke-super
+70 35c n invoke-direct
+71 35c n invoke-static
+72 35c n invoke-interface
+73 10x n unused-73
+74 3rc n invoke-virtual/range
+75 3rc n invoke-super/range
+76 3rc n invoke-direct/range
+77 3rc n invoke-static/range
+78 3rc n invoke-interface/range
+79 10x n unused-79
+7a 10x n unused-7a
+7b 12x y neg-int
+7c 12x y not-int
+7d 12x y neg-long
+7e 12x y not-long
+7f 12x y neg-float
+80 12x y neg-double
+81 12x y int-to-long
+82 12x y int-to-float
+83 12x y int-to-double
+84 12x y long-to-int
+85 12x y long-to-float
+86 12x y long-to-double
+87 12x y float-to-int
+88 12x y float-to-long
+89 12x y float-to-double
+8a 12x y double-to-int
+8b 12x y double-to-long
+8c 12x y double-to-float
+8d 12x y int-to-byte
+8e 12x y int-to-char
+8f 12x y int-to-short
+90 23x y add-int
+91 23x y sub-int
+92 23x y mul-int
+93 23x y div-int
+94 23x y rem-int
+95 23x y and-int
+96 23x y or-int
+97 23x y xor-int
+98 23x y shl-int
+99 23x y shr-int
+9a 23x y ushr-int
+9b 23x y add-long
+9c 23x y sub-long
+9d 23x y mul-long
+9e 23x y div-long
+9f 23x y rem-long
+a0 23x y and-long
+a1 23x y or-long
+a2 23x y xor-long
+a3 23x y shl-long
+a4 23x y shr-long
+a5 23x y ushr-long
+a6 23x y add-float
+a7 23x y sub-float
+a8 23x y mul-float
+a9 23x y div-float
+aa 23x y rem-float
+ab 23x y add-double
+ac 23x y sub-double
+ad 23x y mul-double
+ae 23x y div-double
+af 23x y rem-double
+b0 12x y add-int/2addr
+b1 12x y sub-int/2addr
+b2 12x y mul-int/2addr
+b3 12x y div-int/2addr
+b4 12x y rem-int/2addr
+b5 12x y and-int/2addr
+b6 12x y or-int/2addr
+b7 12x y xor-int/2addr
+b8 12x y shl-int/2addr
+b9 12x y shr-int/2addr
+ba 12x y ushr-int/2addr
+bb 12x y add-long/2addr
+bc 12x y sub-long/2addr
+bd 12x y mul-long/2addr
+be 12x y div-long/2addr
+bf 12x y rem-long/2addr
+c0 12x y and-long/2addr
+c1 12x y or-long/2addr
+c2 12x y xor-long/2addr
+c3 12x y shl-long/2addr
+c4 12x y shr-long/2addr
+c5 12x y ushr-long/2addr
+c6 12x y add-float/2addr
+c7 12x y sub-float/2addr
+c8 12x y mul-float/2addr
+c9 12x y div-float/2addr
+ca 12x y rem-float/2addr
+cb 12x y add-double/2addr
+cc 12x y sub-double/2addr
+cd 12x y mul-double/2addr
+ce 12x y div-double/2addr
+cf 12x y rem-double/2addr
+d0 22s y add-int/lit16
+d1 22s y rsub-int
+d2 22s y mul-int/lit16
+d3 22s y div-int/lit16
+d4 22s y rem-int/lit16
+d5 22s y and-int/lit16
+d6 22s y or-int/lit16
+d7 22s y xor-int/lit16
+d8 22b y add-int/lit8
+d9 22b y rsub-int/lit8
+da 22b y mul-int/lit8
+db 22b y div-int/lit8
+dc 22b y rem-int/lit8
+dd 22b y and-int/lit8
+de 22b y or-int/lit8
+df 22b y xor-int/lit8
+e0 22b y shl-int/lit8
+e1 22b y shr-int/lit8
+e2 22b y ushr-int/lit8
+e3 10x n unused-e3
+e4 10x n unused-e4
+e5 10x n unused-e5
+e6 10x n unused-e6
+e7 10x n unused-e7
+e8 10x n unused-e8
+e9 10x n unused-e9
+ea 10x n unused-ea
+eb 10x n unused-eb
+ec 10x n unused-ec
+ed 10x n unused-ed
+ee 10x n unused-ee
+ef 10x n unused-ef
+f0 10x n unused-f0
+f1 10x n unused-f1
+f2 10x n unused-f2
+f3 10x n unused-f3
+f4 10x n unused-f4
+f5 10x n unused-f5
+f6 10x n unused-f6
+f7 10x n unused-f7
+f8 10x n unused-f8
+f9 10x n unused-f9
+fa 10x n unused-fa
+fb 10x n unused-fb
+fc 10x n unused-fc
+fd 10x n unused-fd
+fe 10x n unused-fe
+ff 10x n unused-ff
diff --git a/dx/etc/dx b/dx/etc/dx
new file mode 100644
index 0000000..e5cedff
--- /dev/null
+++ b/dx/etc/dx
@@ -0,0 +1,89 @@
+#!/bin/bash
+#
+# 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=dx.jar
+libdir="$progdir"
+
+if [ ! -r "$libdir/$jarfile" ]; then
+    # set dx.jar location for the SDK case
+    libdir=`dirname "$progdir"`/platform-tools/lib
+fi
+
+
+if [ ! -r "$libdir/$jarfile" ]; then
+    # set dx.jar location for the Android tree case
+    libdir=`dirname "$progdir"`/framework
+fi
+
+if [ ! -r "$libdir/$jarfile" ]; then
+    echo `basename "$prog"`": can't find $jarfile"
+    exit 1
+fi
+
+# By default, give dx a max heap size of 1 gig. This can be overridden
+# by using a "-J" option (see below).
+defaultMx="-Xmx1024M"
+
+# The following will extract any initial parameters of the form
+# "-J<stuff>" from the command line and pass them to the Java
+# invocation (instead of to dx). This makes it possible for you to add
+# a command-line parameter such as "-JXmx256M" in your scripts, for
+# example. "java" (with no args) and "java -X" give a summary of
+# available options.
+
+javaOpts=""
+
+while expr "x$1" : 'x-J' >/dev/null; do
+    opt=`expr "x$1" : 'x-J\(.*\)'`
+    javaOpts="${javaOpts} -${opt}"
+    if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then
+        defaultMx="no"
+    fi
+    shift
+done
+
+if [ "${defaultMx}" != "no" ]; then
+    javaOpts="${javaOpts} ${defaultMx}"
+fi
+
+if [ "$OSTYPE" = "cygwin" ]; then
+    # For Cygwin, convert the jarfile path into native Windows style.
+    jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+    jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/dx/etc/dx.bat b/dx/etc/dx.bat
new file mode 100755
index 0000000..36b6206
--- /dev/null
+++ b/dx/etc/dx.bat
@@ -0,0 +1,85 @@
+@echo off
+REM Copyright (C) 2007 The Android Open Source Project
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM     http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+
+REM don't modify the caller's environment
+setlocal
+
+REM Locate dx.jar in the directory where dx.bat was found and start it.
+
+REM Set up prog to be the path of this script, including following symlinks,
+REM and set up progdir to be the fully-qualified pathname of its directory.
+set prog=%~f0
+
+REM Change current directory to where dx is, to avoid issues with directories
+REM containing whitespaces.
+cd /d %~dp0
+
+set jarfile=dx.jar
+set frameworkdir=
+
+if exist %frameworkdir%%jarfile% goto JarFileOk
+    set frameworkdir=lib\
+
+if exist %frameworkdir%%jarfile% goto JarFileOk
+    set frameworkdir=..\framework\
+
+:JarFileOk
+
+set jarpath=%frameworkdir%%jarfile%
+
+set javaOpts=
+set args=
+
+REM By default, give dx a max heap size of 1 gig. This can be overridden
+REM by using a "-JXmx..." option (see below).
+set defaultMx=-Xmx1024M
+
+REM capture all arguments to process them below
+set params=%*
+
+:nextArg
+if "%params%"=="" goto endArgs
+    REM Note: advanced substitions don't work on %1..%N. We need to assign to
+    REM a variable first.
+    REM We also can't use %1..%N directly because an option such as --output=name
+    REM gets automagically converted into %1=--output and %2=name (yes, really!)
+    REM Instead we manually extract the first token from the params variable.
+    for /F "tokens=1*" %%a in ("%params%") do call :getArg "%%a" "%%b"
+
+    if "%defaultMx%"=="" goto notXmx
+    if "%A:~0,5%" NEQ "-JXmx" goto notXmx
+        set defaultMx=
+    :notXmx
+
+    if "%A:~0,2%" NEQ "-J" goto notJ
+        set javaOpts=%javaOpts% -%A:~2%
+        goto nextArg
+
+    :notJ
+        set args=%args% %A%
+        goto nextArg
+
+:getArg
+    REM this subroutine is called by the for /F with the first argument of params
+    REM and the rest of the line. The "goto :eof" actually exits the subroutine.
+    set A=%~1
+    set params=%~2
+    goto :eof
+
+:endArgs
+
+set javaOpts=%javaOpts% %defaultMx%
+
+call java %javaOpts% -Djava.ext.dirs=%frameworkdir% -jar %jarpath% %args%
diff --git a/dx/etc/jasmin b/dx/etc/jasmin
new file mode 100644
index 0000000..f44c16f
--- /dev/null
+++ b/dx/etc/jasmin
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+exec java -jar $libdir/jasmin.jar "$@"
diff --git a/dx/etc/jasmin.jar b/dx/etc/jasmin.jar
new file mode 100644
index 0000000..87db0d0
--- /dev/null
+++ b/dx/etc/jasmin.jar
Binary files differ
diff --git a/dx/etc/manifest.txt b/dx/etc/manifest.txt
new file mode 100644
index 0000000..46bbe63
--- /dev/null
+++ b/dx/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.dx.command.Main
diff --git a/dx/etc/opcode-gen b/dx/etc/opcode-gen
new file mode 100755
index 0000000..390a6c3
--- /dev/null
+++ b/dx/etc/opcode-gen
@@ -0,0 +1,141 @@
+#!/bin/bash
+#
+# 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.
+
+# opcode-gen <file>
+#
+# Use the file bytecodes.txt to generate code inside <file>, based on
+# the directives found in that file:
+#
+#     opcodes:   static final ints for each opcode
+#     dops:      static final objects for each opcode
+#     dops-init: initialization code for the "dops"
+
+file="$1"
+tmpfile="/tmp/$$.txt"
+
+if [ "x$1" = "x" ]; then
+    echo "must specify a file"
+    exit 1
+fi
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+bytecodeFile="$progdir/bytecode.txt"
+
+awk -v "bytecodeFile=$bytecodeFile" '
+
+BEGIN {
+    readBytecodes();
+    consumeUntil = "";
+}
+
+consumeUntil != "" {
+    if (index($0, consumeUntil) != 0) {
+        consumeUntil = "";
+    } else {
+        next;
+    }
+}
+
+/BEGIN\(opcodes\)/ {
+    consumeUntil = "END(opcodes)";
+    print;
+
+    for (i = 0; i < 256; i++) {
+        printf("    public static final int %s = 0x%s;\n",
+               uppername[i], hex[i]);
+    }
+
+    next;
+}
+
+/BEGIN\(dops\)/ {
+    consumeUntil = "END(dops)";
+    print;
+
+    for (i = 0; i < 256; i++) {
+        if (index(name[i], "unused") != 0) {
+            continue;
+        }
+        printf("    public static final Dop %s =\n" \
+               "        new Dop(DalvOps.%s, DalvOps.%s,\n" \
+               "            Form%s.THE_ONE, %s, \"%s\");\n\n",
+               uppername[i], uppername[i], family[i], format[i], hasres[i],
+               name[i]);
+    }
+
+    next;
+}
+
+/BEGIN\(dops-init\)/ {
+    consumeUntil = "END(dops-init)";
+    print;
+
+    for (i = 0; i < 256; i++) {
+        if (index(name[i], "unused") != 0) {
+            continue;
+        }
+        printf("        set(%s);\n", uppername[i]);
+    }
+
+    next;
+}
+
+{ print; }
+
+function readBytecodes(i, parts) {
+    for (i = 0; i < 256; i++) {
+        $0 = "";
+        while (($0 == "") || (index($0, "#") != 0)) {
+            if ((getline <bytecodeFile) != 1) {
+                print "trouble reading bytecode file";
+                exit 1;
+            }
+        }
+        split($0, parts);
+        hex[i] = parts[1];
+        format[i] = parts[2];
+        hasres[i] = (parts[3] == "n") ? "false" : "true";
+        name[i] = parts[4];
+        uppername[i] = toupper(parts[4]);
+        gsub("[---/]", "_", uppername[i]);
+        split(name[i], parts, "/");
+        family[i] = toupper(parts[1]);
+        gsub("-", "_", family[i]);
+    }
+}
+' "$file" > "$tmpfile"
+
+cp "$tmpfile" "$file"
+rm "$tmpfile"
diff --git a/dx/etc/run-opcode-gen b/dx/etc/run-opcode-gen
new file mode 100755
index 0000000..061c048
--- /dev/null
+++ b/dx/etc/run-opcode-gen
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+# Run this from this directory.
+
+./opcode-gen ../src/com/android/dx/dex/code/DalvOps.java
+./opcode-gen ../src/com/android/dx/dex/code/Dops.java
diff --git a/dx/src/Android.mk b/dx/src/Android.mk
new file mode 100644
index 0000000..80d1b85
--- /dev/null
+++ b/dx/src/Android.mk
@@ -0,0 +1,34 @@
+# Copyright 2006 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
+# dx java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= dx
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+endif # TARGET_BUILD_APPS
+
+# the documentation
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files) $(call all-subdir-html-files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= dx
+LOCAL_DROIDDOC_OPTIONS := -hidden
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_IS_HOST_MODULE := true
+
+include $(BUILD_DROIDDOC)
diff --git a/dx/src/com/android/dx/Version.java b/dx/src/com/android/dx/Version.java
new file mode 100644
index 0000000..b22173d
--- /dev/null
+++ b/dx/src/com/android/dx/Version.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package com.android.dx;
+
+/**
+ * Version number for dx.
+ */
+public class Version {
+    /** {@code non-null;} version string */
+    public static final String VERSION = "1.5";
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java b/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java
new file mode 100644
index 0000000..fe0b3ab
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Attribute class for {@code AnnotationDefault} attributes.
+ */
+public final class AttAnnotationDefault extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "AnnotationDefault";
+
+    /** {@code non-null;} the annotation default value */
+    private final Constant value;
+
+    /** {@code >= 0;} attribute data length in the original classfile (not
+     * including the attribute header) */
+    private final int byteLength;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param value {@code non-null;} the annotation default value
+     * @param byteLength {@code >= 0;} attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttAnnotationDefault(Constant value, int byteLength) {
+        super(ATTRIBUTE_NAME);
+
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        this.value = value;
+        this.byteLength = byteLength;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        // Add six for the standard attribute header.
+        return byteLength + 6;
+    }
+
+    /**
+     * Gets the annotation default value.
+     *
+     * @return {@code non-null;} the value
+     */
+    public Constant getValue() {
+        return value;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttCode.java b/dx/src/com/android/dx/cf/attrib/AttCode.java
new file mode 100644
index 0000000..8d34c69
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttCode.java
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard {@code Code} attributes.
+ */
+public final class AttCode extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Code";
+
+    /** {@code >= 0;} the stack size */
+    private final int maxStack;
+
+    /** {@code >= 0;} the number of locals */
+    private final int maxLocals;
+
+    /** {@code non-null;} array containing the bytecode per se */
+    private final BytecodeArray code;
+
+    /** {@code non-null;} the exception table */
+    private final ByteCatchList catches;
+
+    /** {@code non-null;} the associated list of attributes */
+    private final AttributeList attributes;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param maxStack {@code >= 0;} the stack size
+     * @param maxLocals {@code >= 0;} the number of locals
+     * @param code {@code non-null;} array containing the bytecode per se
+     * @param catches {@code non-null;} the exception table
+     * @param attributes {@code non-null;} the associated list of attributes
+     */
+    public AttCode(int maxStack, int maxLocals, BytecodeArray code,
+                   ByteCatchList catches, AttributeList attributes) {
+        super(ATTRIBUTE_NAME);
+
+        if (maxStack < 0) {
+            throw new IllegalArgumentException("maxStack < 0");
+        }
+
+        if (maxLocals < 0) {
+            throw new IllegalArgumentException("maxLocals < 0");
+        }
+
+        if (code == null) {
+            throw new NullPointerException("code == null");
+        }
+
+        try {
+            if (catches.isMutable()) {
+                throw new MutabilityException("catches.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("catches == null");
+        }
+
+        try {
+            if (attributes.isMutable()) {
+                throw new MutabilityException("attributes.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("attributes == null");
+        }
+
+        this.maxStack = maxStack;
+        this.maxLocals = maxLocals;
+        this.code = code;
+        this.catches = catches;
+        this.attributes = attributes;
+    }
+
+    public int byteLength() {
+        return 10 + code.byteLength() + catches.byteLength() +
+            attributes.byteLength();
+    }
+
+    /**
+     * Gets the maximum stack size.
+     *
+     * @return {@code >= 0;} the maximum stack size
+     */
+    public int getMaxStack() {
+        return maxStack;
+    }
+
+    /**
+     * Gets the number of locals.
+     *
+     * @return {@code >= 0;} the number of locals
+     */
+    public int getMaxLocals() {
+        return maxLocals;
+    }
+
+    /**
+     * Gets the bytecode array.
+     *
+     * @return {@code non-null;} the bytecode array
+     */
+    public BytecodeArray getCode() {
+        return code;
+    }
+
+    /**
+     * Gets the exception table.
+     *
+     * @return {@code non-null;} the exception table
+     */
+    public ByteCatchList getCatches() {
+        return catches;
+    }
+
+    /**
+     * Gets the associated attribute list.
+     *
+     * @return {@code non-null;} the attribute list
+     */
+    public AttributeList getAttributes() {
+        return attributes;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttConstantValue.java b/dx/src/com/android/dx/cf/attrib/AttConstantValue.java
new file mode 100644
index 0000000..aa6d1b3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttConstantValue.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Attribute class for standard {@code ConstantValue} attributes.
+ */
+public final class AttConstantValue extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "ConstantValue";
+
+    /** {@code non-null;} the constant value */
+    private final TypedConstant constantValue;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param constantValue {@code non-null;} the constant value, which must
+     * be an instance of one of: {@code CstString},
+     * {@code CstInteger}, {@code CstLong},
+     * {@code CstFloat}, or {@code CstDouble}
+     */
+    public AttConstantValue(TypedConstant constantValue) {
+        super(ATTRIBUTE_NAME);
+
+        if (!((constantValue instanceof CstString) ||
+               (constantValue instanceof CstInteger) ||
+               (constantValue instanceof CstLong) ||
+               (constantValue instanceof CstFloat) ||
+               (constantValue instanceof CstDouble))) {
+            if (constantValue == null) {
+                throw new NullPointerException("constantValue == null");
+            }
+            throw new IllegalArgumentException("bad type for constantValue");
+        }
+
+        this.constantValue = constantValue;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8;
+    }
+
+    /**
+     * Gets the constant value of this instance. The returned value
+     * is an instance of one of: {@code CstString},
+     * {@code CstInteger}, {@code CstLong},
+     * {@code CstFloat}, or {@code CstDouble}.
+     *
+     * @return {@code non-null;} the constant value
+     */
+    public TypedConstant getConstantValue() {
+        return constantValue;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttDeprecated.java b/dx/src/com/android/dx/cf/attrib/AttDeprecated.java
new file mode 100644
index 0000000..d440aae
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttDeprecated.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+/**
+ * Attribute class for standard {@code Deprecated} attributes.
+ */
+public final class AttDeprecated extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Deprecated";
+
+    /**
+     * Constructs an instance.
+     */
+    public AttDeprecated() {
+        super(ATTRIBUTE_NAME);
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 6;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java b/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java
new file mode 100644
index 0000000..6717e15
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Attribute class for standards-track {@code EnclosingMethod}
+ * attributes.
+ */
+public final class AttEnclosingMethod extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "EnclosingMethod";
+
+    /** {@code non-null;} the innermost enclosing class */
+    private final CstType type;
+
+    /** {@code null-ok;} the name-and-type of the innermost enclosing method, if any */
+    private final CstNat method;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param type {@code non-null;} the innermost enclosing class
+     * @param method {@code null-ok;} the name-and-type of the innermost enclosing
+     * method, if any
+     */
+    public AttEnclosingMethod(CstType type, CstNat method) {
+        super(ATTRIBUTE_NAME);
+
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        this.type = type;
+        this.method = method;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 10;
+    }
+
+    /**
+     * Gets the innermost enclosing class.
+     *
+     * @return {@code non-null;} the innermost enclosing class
+     */
+    public CstType getEnclosingClass() {
+        return type;
+    }
+
+    /**
+     * Gets the name-and-type of the innermost enclosing method, if
+     * any.
+     *
+     * @return {@code null-ok;} the name-and-type of the innermost enclosing
+     * method, if any
+     */
+    public CstNat getMethod() {
+        return method;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttExceptions.java b/dx/src/com/android/dx/cf/attrib/AttExceptions.java
new file mode 100644
index 0000000..a17e009
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttExceptions.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard {@code Exceptions} attributes.
+ */
+public final class AttExceptions extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Exceptions";
+
+    /** {@code non-null;} list of exception classes */
+    private final TypeList exceptions;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param exceptions {@code non-null;} list of classes, presumed but not
+     * verified to be subclasses of {@code Throwable}
+     */
+    public AttExceptions(TypeList exceptions) {
+        super(ATTRIBUTE_NAME);
+
+        try {
+            if (exceptions.isMutable()) {
+                throw new MutabilityException("exceptions.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("exceptions == null");
+        }
+
+        this.exceptions = exceptions;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8 + exceptions.size() * 2;
+    }
+
+    /**
+     * Gets the list of classes associated with this instance. In
+     * general, these classes are not pre-verified to be subclasses of
+     * {@code Throwable}.
+     *
+     * @return {@code non-null;} the list of classes
+     */
+    public TypeList getExceptions() {
+        return exceptions;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java b/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java
new file mode 100644
index 0000000..77a4b08
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard {@code InnerClasses} attributes.
+ */
+public final class AttInnerClasses extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "InnerClasses";
+
+    /** {@code non-null;} list of inner class entries */
+    private final InnerClassList innerClasses;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param innerClasses {@code non-null;} list of inner class entries
+     */
+    public AttInnerClasses(InnerClassList innerClasses) {
+        super(ATTRIBUTE_NAME);
+
+        try {
+            if (innerClasses.isMutable()) {
+                throw new MutabilityException("innerClasses.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("innerClasses == null");
+        }
+
+        this.innerClasses = innerClasses;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8 + innerClasses.size() * 8;
+    }
+
+    /**
+     * Gets the list of "inner class" entries associated with this instance.
+     *
+     * @return {@code non-null;} the list
+     */
+    public InnerClassList getInnerClasses() {
+        return innerClasses;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java b/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java
new file mode 100644
index 0000000..5eac8cb
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LineNumberList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard {@code LineNumberTable} attributes.
+ */
+public final class AttLineNumberTable extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "LineNumberTable";
+
+    /** {@code non-null;} list of line number entries */
+    private final LineNumberList lineNumbers;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param lineNumbers {@code non-null;} list of line number entries
+     */
+    public AttLineNumberTable(LineNumberList lineNumbers) {
+        super(ATTRIBUTE_NAME);
+
+        try {
+            if (lineNumbers.isMutable()) {
+                throw new MutabilityException("lineNumbers.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("lineNumbers == null");
+        }
+
+        this.lineNumbers = lineNumbers;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8 + 4 * lineNumbers.size();
+    }
+
+    /**
+     * Gets the list of "line number" entries associated with this instance.
+     *
+     * @return {@code non-null;} the list
+     */
+    public LineNumberList getLineNumbers() {
+        return lineNumbers;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java
new file mode 100644
index 0000000..1d2b4aa
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+
+/**
+ * Attribute class for standard {@code LocalVariableTable} attributes.
+ */
+public final class AttLocalVariableTable extends BaseLocalVariables {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "LocalVariableTable";
+
+    /**
+     * Constructs an instance.
+     *
+     * @param localVariables {@code non-null;} list of local variable entries
+     */
+    public AttLocalVariableTable(LocalVariableList localVariables) {
+        super(ATTRIBUTE_NAME, localVariables);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java
new file mode 100644
index 0000000..2520bf6
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+
+/**
+ * Attribute class for standard {@code LocalVariableTypeTable} attributes.
+ */
+public final class AttLocalVariableTypeTable extends BaseLocalVariables {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "LocalVariableTypeTable";
+
+    /**
+     * Constructs an instance.
+     *
+     * @param localVariables {@code non-null;} list of local variable entries
+     */
+    public AttLocalVariableTypeTable(LocalVariableList localVariables) {
+        super(ATTRIBUTE_NAME, localVariables);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java
new file mode 100644
index 0000000..d3afe27
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+
+/**
+ * Attribute class for standard {@code RuntimeInvisibleAnnotations}
+ * attributes.
+ */
+public final class AttRuntimeInvisibleAnnotations extends BaseAnnotations {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "RuntimeInvisibleAnnotations";
+
+    /**
+     * Constructs an instance.
+     *
+     * @param annotations {@code non-null;} the list of annotations
+     * @param byteLength {@code >= 0;} attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttRuntimeInvisibleAnnotations(Annotations annotations,
+            int byteLength) {
+        super(ATTRIBUTE_NAME, annotations, byteLength);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java
new file mode 100644
index 0000000..c9c5136
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+
+/**
+ * Attribute class for standard
+ * {@code RuntimeInvisibleParameterAnnotations} attributes.
+ */
+public final class AttRuntimeInvisibleParameterAnnotations
+        extends BaseParameterAnnotations {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME =
+        "RuntimeInvisibleParameterAnnotations";
+
+    /**
+     * Constructs an instance.
+     *
+     * @param parameterAnnotations {@code non-null;} the parameter annotations
+     * @param byteLength {@code >= 0;} attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttRuntimeInvisibleParameterAnnotations(
+            AnnotationsList parameterAnnotations, int byteLength) {
+        super(ATTRIBUTE_NAME, parameterAnnotations, byteLength);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java
new file mode 100644
index 0000000..a6a640d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+
+/**
+ * Attribute class for standard {@code RuntimeVisibleAnnotations}
+ * attributes.
+ */
+public final class AttRuntimeVisibleAnnotations extends BaseAnnotations {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "RuntimeVisibleAnnotations";
+
+    /**
+     * Constructs an instance.
+     *
+     * @param annotations {@code non-null;} the list of annotations
+     * @param byteLength {@code >= 0;} attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttRuntimeVisibleAnnotations(Annotations annotations,
+            int byteLength) {
+        super(ATTRIBUTE_NAME, annotations, byteLength);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java
new file mode 100644
index 0000000..177eb4c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+
+/**
+ * Attribute class for standard {@code RuntimeVisibleParameterAnnotations}
+ * attributes.
+ */
+public final class AttRuntimeVisibleParameterAnnotations
+        extends BaseParameterAnnotations {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME =
+        "RuntimeVisibleParameterAnnotations";
+
+    /**
+     * Constructs an instance.
+     *
+     * @param annotations {@code non-null;} the parameter annotations
+     * @param byteLength {@code >= 0;} attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public AttRuntimeVisibleParameterAnnotations(
+            AnnotationsList annotations, int byteLength) {
+        super(ATTRIBUTE_NAME, annotations, byteLength);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSignature.java b/dx/src/com/android/dx/cf/attrib/AttSignature.java
new file mode 100644
index 0000000..f6023f3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSignature.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Attribute class for standards-track {@code Signature} attributes.
+ */
+public final class AttSignature extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Signature";
+
+    /** {@code non-null;} the signature string */
+    private final CstUtf8 signature;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param signature {@code non-null;} the signature string
+     */
+    public AttSignature(CstUtf8 signature) {
+        super(ATTRIBUTE_NAME);
+
+        if (signature == null) {
+            throw new NullPointerException("signature == null");
+        }
+
+        this.signature = signature;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8;
+    }
+
+    /**
+     * Gets the signature string.
+     *
+     * @return {@code non-null;} the signature string
+     */
+    public CstUtf8 getSignature() {
+        return signature;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSourceFile.java b/dx/src/com/android/dx/cf/attrib/AttSourceFile.java
new file mode 100644
index 0000000..b84ff4d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSourceFile.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Attribute class for standard {@code SourceFile} attributes.
+ */
+public final class AttSourceFile extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "SourceFile";
+
+    /** {@code non-null;} name of the source file */
+    private final CstUtf8 sourceFile;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param sourceFile {@code non-null;} the name of the source file
+     */
+    public AttSourceFile(CstUtf8 sourceFile) {
+        super(ATTRIBUTE_NAME);
+
+        if (sourceFile == null) {
+            throw new NullPointerException("sourceFile == null");
+        }
+
+        this.sourceFile = sourceFile;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 8;
+    }
+
+    /**
+     * Gets the source file name of this instance.
+     *
+     * @return {@code non-null;} the source file
+     */
+    public CstUtf8 getSourceFile() {
+        return sourceFile;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSynthetic.java b/dx/src/com/android/dx/cf/attrib/AttSynthetic.java
new file mode 100644
index 0000000..e3841eb
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSynthetic.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+/**
+ * Attribute class for standard {@code Synthetic} attributes.
+ */
+public final class AttSynthetic extends BaseAttribute {
+    /** {@code non-null;} attribute name for attributes of this type */
+    public static final String ATTRIBUTE_NAME = "Synthetic";
+
+    /**
+     * Constructs an instance.
+     */
+    public AttSynthetic() {
+        super(ATTRIBUTE_NAME);
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return 6;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java b/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java
new file mode 100644
index 0000000..bc138af
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base class for annotations attributes.
+ */
+public abstract class BaseAnnotations extends BaseAttribute {
+    /** {@code non-null;} list of annotations */
+    private final Annotations annotations;
+
+    /** {@code >= 0;} attribute data length in the original classfile (not
+     * including the attribute header) */
+    private final int byteLength;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param attributeName {@code non-null;} the name of the attribute
+     * @param annotations {@code non-null;} the list of annotations
+     * @param byteLength {@code >= 0;} attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public BaseAnnotations(String attributeName, Annotations annotations,
+            int byteLength) {
+        super(attributeName);
+
+        try {
+            if (annotations.isMutable()) {
+                throw new MutabilityException("annotations.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("annotations == null");
+        }
+
+        this.annotations = annotations;
+        this.byteLength = byteLength;
+    }
+
+    /** {@inheritDoc} */
+    public final int byteLength() {
+        // Add six for the standard attribute header.
+        return byteLength + 6;
+    }
+
+    /**
+     * Gets the list of annotations associated with this instance.
+     *
+     * @return {@code non-null;} the list
+     */
+    public final Annotations getAnnotations() {
+        return annotations;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseAttribute.java b/dx/src/com/android/dx/cf/attrib/BaseAttribute.java
new file mode 100644
index 0000000..9961725
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseAttribute.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.iface.Attribute;
+
+/**
+ * Base implementation of {@link Attribute}, which directly stores
+ * the attribute name but leaves the rest up to subclasses.
+ */
+public abstract class BaseAttribute implements Attribute {
+    /** {@code non-null;} attribute name */
+    private final String name;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param name {@code non-null;} attribute name
+     */
+    public BaseAttribute(String name) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        this.name = name;
+    }
+
+    /** {@inheritDoc} */
+    public String getName() {
+        return name;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java b/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java
new file mode 100644
index 0000000..27cd6fb
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base attribute class for standard {@code LocalVariableTable}
+ * and {@code LocalVariableTypeTable} attributes.
+ */
+public abstract class BaseLocalVariables extends BaseAttribute {
+    /** {@code non-null;} list of local variable entries */
+    private final LocalVariableList localVariables;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param name {@code non-null;} attribute name
+     * @param localVariables {@code non-null;} list of local variable entries
+     */
+    public BaseLocalVariables(String name,
+            LocalVariableList localVariables) {
+        super(name);
+
+        try {
+            if (localVariables.isMutable()) {
+                throw new MutabilityException("localVariables.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("localVariables == null");
+        }
+
+        this.localVariables = localVariables;
+    }
+
+    /** {@inheritDoc} */
+    public final int byteLength() {
+        return 8 + localVariables.size() * 10;
+    }
+
+    /**
+     * Gets the list of "local variable" entries associated with this instance.
+     *
+     * @return {@code non-null;} the list
+     */
+    public final LocalVariableList getLocalVariables() {
+        return localVariables;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java
new file mode 100644
index 0000000..791f8cd
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base class for parameter annotation list attributes.
+ */
+public abstract class BaseParameterAnnotations extends BaseAttribute {
+    /** {@code non-null;} list of annotations */
+    private final AnnotationsList parameterAnnotations;
+
+    /** {@code >= 0;} attribute data length in the original classfile (not
+     * including the attribute header) */
+    private final int byteLength;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param attributeName {@code non-null;} the name of the attribute
+     * @param parameterAnnotations {@code non-null;} the annotations
+     * @param byteLength {@code >= 0;} attribute data length in the original
+     * classfile (not including the attribute header)
+     */
+    public BaseParameterAnnotations(String attributeName,
+            AnnotationsList parameterAnnotations, int byteLength) {
+        super(attributeName);
+
+        try {
+            if (parameterAnnotations.isMutable()) {
+                throw new MutabilityException(
+                        "parameterAnnotations.isMutable()");
+            }
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("parameterAnnotations == null");
+        }
+
+        this.parameterAnnotations = parameterAnnotations;
+        this.byteLength = byteLength;
+    }
+
+    /** {@inheritDoc} */
+    public final int byteLength() {
+        // Add six for the standard attribute header.
+        return byteLength + 6;
+    }
+
+    /**
+     * Gets the list of annotation lists associated with this instance.
+     *
+     * @return {@code non-null;} the list
+     */
+    public final AnnotationsList getParameterAnnotations() {
+        return parameterAnnotations;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/InnerClassList.java b/dx/src/com/android/dx/cf/attrib/InnerClassList.java
new file mode 100644
index 0000000..96e1b60
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/InnerClassList.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "inner class" entries, which are the contents of
+ * {@code InnerClasses} attributes.
+ */
+public final class InnerClassList extends FixedSizeList {
+    /**
+     * Constructs an instance.
+     *
+     * @param count the number of elements to be in the list of inner classes
+     */
+    public InnerClassList(int count) {
+        super(count);
+    }
+
+    /**
+     * Gets the indicated item.
+     *
+     * @param n {@code >= 0;} which item
+     * @return {@code null-ok;} the indicated item
+     */
+    public Item get(int n) {
+        return (Item) get0(n);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n {@code >= 0, < size();} which class
+     * @param innerClass {@code non-null;} class this item refers to
+     * @param outerClass {@code null-ok;} outer class that this class is a
+     * member of, if any
+     * @param innerName {@code null-ok;} original simple name of this class,
+     * if not anonymous
+     * @param accessFlags original declared access flags
+     */
+    public void set(int n, CstType innerClass, CstType outerClass,
+                    CstUtf8 innerName, int accessFlags) {
+        set0(n, new Item(innerClass, outerClass, innerName, accessFlags));
+    }
+
+    /**
+     * Item in an inner classes list.
+     */
+    public static class Item {
+        /** {@code non-null;} class this item refers to */
+        private final CstType innerClass;
+
+        /** {@code null-ok;} outer class that this class is a member of, if any */
+        private final CstType outerClass;
+
+        /** {@code null-ok;} original simple name of this class, if not anonymous */
+        private final CstUtf8 innerName;
+
+        /** original declared access flags */
+        private final int accessFlags;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param innerClass {@code non-null;} class this item refers to
+         * @param outerClass {@code null-ok;} outer class that this class is a
+         * member of, if any
+         * @param innerName {@code null-ok;} original simple name of this
+         * class, if not anonymous
+         * @param accessFlags original declared access flags
+         */
+        public Item(CstType innerClass, CstType outerClass,
+                    CstUtf8 innerName, int accessFlags) {
+            if (innerClass == null) {
+                throw new NullPointerException("innerClass == null");
+            }
+
+            this.innerClass = innerClass;
+            this.outerClass = outerClass;
+            this.innerName = innerName;
+            this.accessFlags = accessFlags;
+        }
+
+        /**
+         * Gets the class this item refers to.
+         *
+         * @return {@code non-null;} the class
+         */
+        public CstType getInnerClass() {
+            return innerClass;
+        }
+
+        /**
+         * Gets the outer class that this item's class is a member of, if any.
+         *
+         * @return {@code null-ok;} the class
+         */
+        public CstType getOuterClass() {
+            return outerClass;
+        }
+
+        /**
+         * Gets the original name of this item's class, if not anonymous.
+         *
+         * @return {@code null-ok;} the name
+         */
+        public CstUtf8 getInnerName() {
+            return innerName;
+        }
+
+        /**
+         * Gets the original declared access flags.
+         *
+         * @return the access flags
+         */
+        public int getAccessFlags() {
+            return accessFlags;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/RawAttribute.java b/dx/src/com/android/dx/cf/attrib/RawAttribute.java
new file mode 100644
index 0000000..e905dd1
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/RawAttribute.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.util.ByteArray;
+
+/**
+ * Raw attribute, for holding onto attributes that are unrecognized.
+ */
+public final class RawAttribute extends BaseAttribute {
+    /** {@code non-null;} attribute data */
+    private final ByteArray data;
+
+    /**
+     * {@code null-ok;} constant pool to use for resolution of cpis in {@link
+     * #data}
+     */
+    private final ConstantPool pool;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param name {@code non-null;} attribute name
+     * @param data {@code non-null;} attribute data
+     * @param pool {@code null-ok;} constant pool to use for cpi resolution
+     */
+    public RawAttribute(String name, ByteArray data, ConstantPool pool) {
+        super(name);
+
+        if (data == null) {
+            throw new NullPointerException("data == null");
+        }
+
+        this.data = data;
+        this.pool = pool;
+    }
+
+    /**
+     * Constructs an instance from a sub-array of a {@link ByteArray}.
+     *
+     * @param name {@code non-null;} attribute name
+     * @param data {@code non-null;} array containing the attribute data
+     * @param offset offset in {@code data} to the attribute data
+     * @param length length of the attribute data, in bytes
+     * @param pool {@code null-ok;} constant pool to use for cpi resolution
+     */
+    public RawAttribute(String name, ByteArray data, int offset,
+                        int length, ConstantPool pool) {
+        this(name, data.slice(offset, offset + length), pool);
+    }
+
+    /**
+     * Get the raw data of the attribute.
+     *
+     * @return {@code non-null;} the data
+     */
+    public ByteArray getData() {
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        return data.size() + 6;
+    }
+
+    /**
+     * Gets the constant pool to use for cpi resolution, if any. It
+     * presumably came from the class file that this attribute came
+     * from.
+     *
+     * @return {@code null-ok;} the constant pool
+     */
+    public ConstantPool getPool() {
+        return pool;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/package.html b/dx/src/com/android/dx/cf/attrib/package.html
new file mode 100644
index 0000000..8125079
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/package.html
@@ -0,0 +1,11 @@
+<body>
+<p>Implementation of containers and utilities for all the standard Java
+attribute types.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/code/BaseMachine.java b/dx/src/com/android/dx/cf/code/BaseMachine.java
new file mode 100644
index 0000000..aae6056
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BaseMachine.java
@@ -0,0 +1,545 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import java.util.ArrayList;
+
+/**
+ * Base implementation of {@link Machine}.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public abstract class BaseMachine implements Machine {
+    /* {@code non-null;} the prototype for the associated method */
+    private final Prototype prototype;
+
+    /** {@code non-null;} primary arguments */
+    private TypeBearer[] args;
+
+    /** {@code >= 0;} number of primary arguments */
+    private int argCount;
+
+    /** {@code null-ok;} type of the operation, if salient */
+    private Type auxType;
+
+    /** auxiliary {@code int} argument */
+    private int auxInt;
+
+    /** {@code null-ok;} auxiliary constant argument */
+    private Constant auxCst;
+
+    /** auxiliary branch target argument */
+    private int auxTarget;
+
+    /** {@code null-ok;} auxiliary switch cases argument */
+    private SwitchList auxCases;
+
+    /** {@code null-ok;} auxiliary initial value list for newarray */
+    private ArrayList<Constant> auxInitValues;
+
+    /** {@code >= -1;} last local accessed */
+    private int localIndex;
+
+    /** {@code null-ok;} local target spec, if salient and calculated */
+    private RegisterSpec localTarget;
+
+    /** {@code non-null;} results */
+    private TypeBearer[] results;
+
+    /**
+     * {@code >= -1;} count of the results, or {@code -1} if no results
+     * have been set
+     */
+    private int resultCount;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param prototype {@code non-null;} the prototype for the associated method
+     */
+    public BaseMachine(Prototype prototype) {
+        if (prototype == null) {
+            throw new NullPointerException("prototype == null");
+        }
+
+        this.prototype = prototype;
+        args = new TypeBearer[10];
+        results = new TypeBearer[6];
+        clearArgs();
+    }
+
+    /** {@inheritDoc} */
+    public Prototype getPrototype() {
+        return prototype;
+    }
+
+    /** {@inheritDoc} */
+    public final void clearArgs() {
+        argCount = 0;
+        auxType = null;
+        auxInt = 0;
+        auxCst = null;
+        auxTarget = 0;
+        auxCases = null;
+        auxInitValues = null;
+        localIndex = -1;
+        localTarget = null;
+        resultCount = -1;
+    }
+
+    /** {@inheritDoc} */
+    public final void popArgs(Frame frame, int count) {
+        ExecutionStack stack = frame.getStack();
+
+        clearArgs();
+
+        if (count > args.length) {
+            // Grow args, and add a little extra room to grow even more.
+            args = new TypeBearer[count + 10];
+        }
+
+        for (int i = count - 1; i >= 0; i--) {
+            args[i] = stack.pop();
+        }
+
+        argCount = count;
+    }
+
+    /** {@inheritDoc} */
+    public void popArgs(Frame frame, Prototype prototype) {
+        StdTypeList types = prototype.getParameterTypes();
+        int size = types.size();
+
+        // Use the above method to do the actual popping...
+        popArgs(frame, size);
+
+        // ...and then verify the popped types.
+
+        for (int i = 0; i < size; i++) {
+            if (! Merger.isPossiblyAssignableFrom(types.getType(i), args[i])) {
+                throw new SimException("at stack depth " + (size - 1 - i) +
+                        ", expected type " + types.getType(i).toHuman() +
+                        " but found " + args[i].getType().toHuman());
+            }
+        }
+    }
+
+    public final void popArgs(Frame frame, Type type) {
+        // Use the above method to do the actual popping...
+        popArgs(frame, 1);
+
+        // ...and then verify the popped type.
+        if (! Merger.isPossiblyAssignableFrom(type, args[0])) {
+            throw new SimException("expected type " + type.toHuman() +
+                    " but found " + args[0].getType().toHuman());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final void popArgs(Frame frame, Type type1, Type type2) {
+        // Use the above method to do the actual popping...
+        popArgs(frame, 2);
+
+        // ...and then verify the popped types.
+
+        if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
+            throw new SimException("expected type " + type1.toHuman() +
+                    " but found " + args[0].getType().toHuman());
+        }
+
+        if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
+            throw new SimException("expected type " + type2.toHuman() +
+                    " but found " + args[1].getType().toHuman());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final void popArgs(Frame frame, Type type1, Type type2,
+            Type type3) {
+        // Use the above method to do the actual popping...
+        popArgs(frame, 3);
+
+        // ...and then verify the popped types.
+
+        if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
+            throw new SimException("expected type " + type1.toHuman() +
+                    " but found " + args[0].getType().toHuman());
+        }
+
+        if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
+            throw new SimException("expected type " + type2.toHuman() +
+                    " but found " + args[1].getType().toHuman());
+        }
+
+        if (! Merger.isPossiblyAssignableFrom(type3, args[2])) {
+            throw new SimException("expected type " + type2.toHuman() +
+                    " but found " + args[2].getType().toHuman());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final void localArg(Frame frame, int idx) {
+        clearArgs();
+        args[0] = frame.getLocals().get(idx);
+        argCount = 1;
+        localIndex = idx;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxType(Type type) {
+        auxType = type;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxIntArg(int value) {
+        auxInt = value;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxCstArg(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        auxCst = cst;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxTargetArg(int target) {
+        auxTarget = target;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxSwitchArg(SwitchList cases) {
+        if (cases == null) {
+            throw new NullPointerException("cases == null");
+        }
+
+        auxCases = cases;
+    }
+
+    /** {@inheritDoc} */
+    public final void auxInitValues(ArrayList<Constant> initValues) {
+        auxInitValues = initValues;
+    }
+
+    /** {@inheritDoc} */
+    public final void localTarget(int idx, Type type, LocalItem local) {
+        localTarget = RegisterSpec.makeLocalOptional(idx, type, local);
+    }
+
+    /**
+     * Gets the number of primary arguments.
+     *
+     * @return {@code >= 0;} the number of primary arguments
+     */
+    protected final int argCount() {
+        return argCount;
+    }
+
+    /**
+     * Gets the width of the arguments (where a category-2 value counts as
+     * two).
+     *
+     * @return {@code >= 0;} the argument width
+     */
+    protected final int argWidth() {
+        int result = 0;
+
+        for (int i = 0; i < argCount; i++) {
+            result += args[i].getType().getCategory();
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the {@code n}th primary argument.
+     *
+     * @param n {@code >= 0, < argCount();} which argument
+     * @return {@code non-null;} the indicated argument
+     */
+    protected final TypeBearer arg(int n) {
+        if (n >= argCount) {
+            throw new IllegalArgumentException("n >= argCount");
+        }
+
+        try {
+            return args[n];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("n < 0");
+        }
+    }
+
+    /**
+     * Gets the type auxiliary argument.
+     *
+     * @return {@code null-ok;} the salient type
+     */
+    protected final Type getAuxType() {
+        return auxType;
+    }
+
+    /**
+     * Gets the {@code int} auxiliary argument.
+     *
+     * @return the argument value
+     */
+    protected final int getAuxInt() {
+        return auxInt;
+    }
+
+    /**
+     * Gets the constant auxiliary argument.
+     *
+     * @return {@code null-ok;} the argument value
+     */
+    protected final Constant getAuxCst() {
+        return auxCst;
+    }
+
+    /**
+     * Gets the branch target auxiliary argument.
+     *
+     * @return the argument value
+     */
+    protected final int getAuxTarget() {
+        return auxTarget;
+    }
+
+    /**
+     * Gets the switch cases auxiliary argument.
+     *
+     * @return {@code null-ok;} the argument value
+     */
+    protected final SwitchList getAuxCases() {
+        return auxCases;
+    }
+
+    /**
+     * Gets the init values auxiliary argument.
+     *
+     * @return {@code null-ok;} the argument value
+     */
+    protected final ArrayList<Constant> getInitValues() {
+        return auxInitValues;
+    }
+    /**
+     * Gets the last local index accessed.
+     *
+     * @return {@code >= -1;} the salient local index or {@code -1} if none
+     * was set since the last time {@link #clearArgs} was called
+     */
+    protected final int getLocalIndex() {
+        return localIndex;
+    }
+
+    /**
+     * Gets the target local register spec of the current operation, if any.
+     * The local target spec is the combination of the values indicated
+     * by a previous call to {@link #localTarget} with the type of what
+     * should be the sole result set by a call to {@link #setResult} (or
+     * the combination {@link #clearResult} then {@link #addResult}.
+     *
+     * @return {@code null-ok;} the salient register spec or {@code null} if no
+     * local target was set since the last time {@link #clearArgs} was
+     * called
+     */
+    protected final RegisterSpec getLocalTarget() {
+        if (localTarget == null) {
+            return null;
+        }
+
+        if (resultCount != 1) {
+            throw new SimException("local target with " +
+                    ((resultCount == 0) ? "no" : "multiple") + " results");
+        }
+
+        TypeBearer result = results[0];
+        Type resultType = result.getType();
+        Type localType = localTarget.getType();
+
+        if (resultType == localType) {
+            return localTarget;
+        }
+
+        if (! Merger.isPossiblyAssignableFrom(localType, resultType)) {
+            // The result and local types are inconsistent. Complain!
+            throwLocalMismatch(resultType, localType);
+            return null;
+        }
+
+        if (localType == Type.OBJECT) {
+            /*
+             * The result type is more specific than the local type,
+             * so use that instead.
+             */
+            localTarget = localTarget.withType(result);
+        }
+
+        return localTarget;
+    }
+
+    /**
+     * Clears the results.
+     */
+    protected final void clearResult() {
+        resultCount = 0;
+    }
+
+    /**
+     * Sets the results list to be the given single value.
+     *
+     * <p><b>Note:</b> If there is more than one result value, the
+     * others may be added by using {@link #addResult}.</p>
+     *
+     * @param result {@code non-null;} result value
+     */
+    protected final void setResult(TypeBearer result) {
+        if (result == null) {
+            throw new NullPointerException("result == null");
+        }
+
+        results[0] = result;
+        resultCount = 1;
+    }
+
+    /**
+     * Adds an additional element to the list of results.
+     *
+     * @see #setResult
+     *
+     * @param result {@code non-null;} result value
+     */
+    protected final void addResult(TypeBearer result) {
+        if (result == null) {
+            throw new NullPointerException("result == null");
+        }
+
+        results[resultCount] = result;
+        resultCount++;
+    }
+
+    /**
+     * Gets the count of results. This throws an exception if results were
+     * never set. (Explicitly clearing the results counts as setting them.)
+     *
+     * @return {@code >= 0;} the count
+     */
+    protected final int resultCount() {
+        if (resultCount < 0) {
+            throw new SimException("results never set");
+        }
+
+        return resultCount;
+    }
+
+    /**
+     * Gets the width of the results (where a category-2 value counts as
+     * two).
+     *
+     * @return {@code >= 0;} the result width
+     */
+    protected final int resultWidth() {
+        int width = 0;
+
+        for (int i = 0; i < resultCount; i++) {
+            width += results[i].getType().getCategory();
+        }
+
+        return width;
+    }
+
+    /**
+     * Gets the {@code n}th result value.
+     *
+     * @param n {@code >= 0, < resultCount();} which result
+     * @return {@code non-null;} the indicated result value
+     */
+    protected final TypeBearer result(int n) {
+        if (n >= resultCount) {
+            throw new IllegalArgumentException("n >= resultCount");
+        }
+
+        try {
+            return results[n];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("n < 0");
+        }
+    }
+
+    /**
+     * Stores the results of the latest operation into the given frame. If
+     * there is a local target (see {@link #localTarget}), then the sole
+     * result is stored to that target; otherwise any results are pushed
+     * onto the stack.
+     *
+     * @param frame {@code non-null;} frame to operate on
+     */
+    protected final void storeResults(Frame frame) {
+        if (resultCount < 0) {
+            throw new SimException("results never set");
+        }
+
+        if (resultCount == 0) {
+            // Nothing to do.
+            return;
+        }
+
+        if (localTarget != null) {
+            /*
+             * Note: getLocalTarget() doesn't necessarily return
+             * localTarget directly.
+             */
+            frame.getLocals().set(getLocalTarget());
+        } else {
+            ExecutionStack stack = frame.getStack();
+            for (int i = 0; i < resultCount; i++) {
+                stack.push(results[i]);
+            }
+        }
+    }
+
+    /**
+     * Throws an exception that indicates a mismatch in local variable
+     * types.
+     *
+     * @param found {@code non-null;} the encountered type
+     * @param local {@code non-null;} the local variable's claimed type
+     */
+    public static void throwLocalMismatch(TypeBearer found,
+            TypeBearer local) {
+        throw new SimException("local variable type mismatch: " +
+                "attempt to set or access a value of type " +
+                found.toHuman() +
+                " using a local variable of type " +
+                local.toHuman() +
+                ". This is symptomatic of .class transformation tools " +
+                "that ignore local variable information.");
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/BasicBlocker.java b/dx/src/com/android/dx/cf/code/BasicBlocker.java
new file mode 100644
index 0000000..8fb9560
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BasicBlocker.java
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+import java.util.ArrayList;
+
+/**
+ * Utility that identifies basic blocks in bytecode.
+ */
+public final class BasicBlocker implements BytecodeArray.Visitor {
+    /** {@code non-null;} method being converted */
+    private final ConcreteMethod method;
+
+    /**
+     * {@code non-null;} work set; bits indicate offsets in need of
+     * examination
+     */
+    private final int[] workSet;
+
+    /**
+     * {@code non-null;} live set; bits indicate potentially-live
+     * opcodes; contrawise, a bit that isn't on is either in the
+     * middle of an instruction or is a definitely-dead opcode
+     */
+    private final int[] liveSet;
+
+    /**
+     * {@code non-null;} block start set; bits indicate the starts of
+     * basic blocks, including the opcodes that start blocks of
+     * definitely-dead code
+     */
+    private final int[] blockSet;
+
+    /**
+     * {@code non-null, sparse;} for each instruction offset to a branch of
+     * some sort, the list of targets for that instruction
+     */
+    private final IntList[] targetLists;
+
+    /**
+     * {@code non-null, sparse;} for each instruction offset to a throwing
+     * instruction, the list of exception handlers for that instruction
+     */
+    private final ByteCatchList[] catchLists;
+
+    /** offset of the previously parsed bytecode */
+    private int previousOffset;
+
+    /**
+     * Identifies and enumerates the basic blocks in the given method,
+     * returning a list of them. The returned list notably omits any
+     * definitely-dead code that is identified in the process.
+     *
+     * @param method {@code non-null;} method to convert
+     * @return {@code non-null;} list of basic blocks
+     */
+    public static ByteBlockList identifyBlocks(ConcreteMethod method) {
+        BasicBlocker bb = new BasicBlocker(method);
+
+        bb.doit();
+        return bb.getBlockList();
+    }
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable; use
+     * {@link #identifyBlocks}.
+     *
+     * @param method {@code non-null;} method to convert
+     */
+    private BasicBlocker(ConcreteMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        this.method = method;
+
+        /*
+         * The "+1" below is so the idx-past-end is also valid,
+         * avoiding a special case, but without preventing
+         * flow-of-control falling past the end of the method from
+         * getting properly reported.
+         */
+        int sz = method.getCode().size() + 1;
+
+        workSet = Bits.makeBitSet(sz);
+        liveSet = Bits.makeBitSet(sz);
+        blockSet = Bits.makeBitSet(sz);
+        targetLists = new IntList[sz];
+        catchLists = new ByteCatchList[sz];
+        previousOffset = -1;
+    }
+
+    /*
+     * Note: These methods are defined implementation of the interface
+     * BytecodeArray.Visitor; since the class isn't publicly
+     * instantiable, no external code ever gets a chance to actually
+     * call these methods.
+     */
+
+    /** {@inheritDoc} */
+    public void visitInvalid(int opcode, int offset, int length) {
+        visitCommon(offset, length, true);
+    }
+
+    /** {@inheritDoc} */
+    public void visitNoArgs(int opcode, int offset, int length, Type type) {
+        switch (opcode) {
+            case ByteOps.IRETURN:
+            case ByteOps.RETURN: {
+                visitCommon(offset, length, false);
+                targetLists[offset] = IntList.EMPTY;
+                break;
+            }
+            case ByteOps.ATHROW: {
+                visitCommon(offset, length, false);
+                visitThrowing(offset, length, false);
+                break;
+            }
+            case ByteOps.IALOAD:
+            case ByteOps.LALOAD:
+            case ByteOps.FALOAD:
+            case ByteOps.DALOAD:
+            case ByteOps.AALOAD:
+            case ByteOps.BALOAD:
+            case ByteOps.CALOAD:
+            case ByteOps.SALOAD:
+            case ByteOps.IASTORE:
+            case ByteOps.LASTORE:
+            case ByteOps.FASTORE:
+            case ByteOps.DASTORE:
+            case ByteOps.AASTORE:
+            case ByteOps.BASTORE:
+            case ByteOps.CASTORE:
+            case ByteOps.SASTORE:
+            case ByteOps.ARRAYLENGTH:
+            case ByteOps.MONITORENTER:
+            case ByteOps.MONITOREXIT: {
+                /*
+                 * These instructions can all throw, so they have to end
+                 * the block they appear in (since throws are branches).
+                 */
+                visitCommon(offset, length, true);
+                visitThrowing(offset, length, true);
+                break;
+            }
+            case ByteOps.IDIV:
+            case ByteOps.IREM: {
+                /*
+                 * The int and long versions of division and remainder may
+                 * throw, but not the other types.
+                 */
+                visitCommon(offset, length, true);
+                if ((type == Type.INT) || (type == Type.LONG)) {
+                    visitThrowing(offset, length, true);
+                }
+                break;
+            }
+            default: {
+                visitCommon(offset, length, true);
+                break;
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void visitLocal(int opcode, int offset, int length,
+            int idx, Type type, int value) {
+        if (opcode == ByteOps.RET) {
+            visitCommon(offset, length, false);
+            targetLists[offset] = IntList.EMPTY;
+        } else {
+            visitCommon(offset, length, true);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void visitConstant(int opcode, int offset, int length,
+            Constant cst, int value) {
+        visitCommon(offset, length, true);
+
+        if ((cst instanceof CstMemberRef) || (cst instanceof CstType) ||
+            (cst instanceof CstString)) {
+            /*
+             * Instructions with these sorts of constants have the
+             * possibility of throwing, so this instruction needs to
+             * end its block (since it can throw, and possible-throws
+             * are branch points).
+             */
+            visitThrowing(offset, length, true);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void visitBranch(int opcode, int offset, int length,
+            int target) {
+        switch (opcode) {
+            case ByteOps.GOTO: {
+                visitCommon(offset, length, false);
+                targetLists[offset] = IntList.makeImmutable(target);
+                break;
+            }
+            case ByteOps.JSR: {
+                /*
+                 * Each jsr is quarantined into a separate block (containing
+                 * only the jsr instruction) but is otherwise treated
+                 * as a conditional branch. (That is to say, both its
+                 * target and next instruction begin new blocks.)
+                 */
+                addWorkIfNecessary(offset, true);
+                // Fall through to next case...
+            }
+            default: {
+                int next = offset + length;
+                visitCommon(offset, length, true);
+                addWorkIfNecessary(next, true);
+                targetLists[offset] = IntList.makeImmutable(next, target);
+                break;
+            }
+        }
+
+        addWorkIfNecessary(target, true);
+    }
+
+    /** {@inheritDoc} */
+    public void visitSwitch(int opcode, int offset, int length,
+            SwitchList cases, int padding) {
+        visitCommon(offset, length, false);
+        addWorkIfNecessary(cases.getDefaultTarget(), true);
+
+        int sz = cases.size();
+        for (int i = 0; i < sz; i++) {
+            addWorkIfNecessary(cases.getTarget(i), true);
+        }
+
+        targetLists[offset] = cases.getTargets();
+    }
+
+    /** {@inheritDoc} */
+    public void visitNewarray(int offset, int length, CstType type,
+            ArrayList<Constant> intVals) {
+        visitCommon(offset, length, true);
+        visitThrowing(offset, length, true);
+    }
+
+    /**
+     * Extracts the list of basic blocks from the bit sets.
+     *
+     * @return {@code non-null;} the list of basic blocks
+     */
+    private ByteBlockList getBlockList() {
+        BytecodeArray bytes = method.getCode();
+        ByteBlock[] bbs = new ByteBlock[bytes.size()];
+        int count = 0;
+
+        for (int at = 0, next; /*at*/; at = next) {
+            next = Bits.findFirst(blockSet, at + 1);
+            if (next < 0) {
+                break;
+            }
+
+            if (Bits.get(liveSet, at)) {
+                /*
+                 * Search backward for the branch or throwing
+                 * instruction at the end of this block, if any. If
+                 * there isn't any, then "next" is the sole target.
+                 */
+                IntList targets = null;
+                int targetsAt = -1;
+                ByteCatchList blockCatches;
+
+                for (int i = next - 1; i >= at; i--) {
+                    targets = targetLists[i];
+                    if (targets != null) {
+                        targetsAt = i;
+                        break;
+                    }
+                }
+
+                if (targets == null) {
+                    targets = IntList.makeImmutable(next);
+                    blockCatches = ByteCatchList.EMPTY;
+                } else {
+                    blockCatches = catchLists[targetsAt];
+                    if (blockCatches == null) {
+                        blockCatches = ByteCatchList.EMPTY;
+                    }
+                }
+
+                bbs[count] =
+                    new ByteBlock(at, at, next, targets, blockCatches);
+                count++;
+            }
+        }
+
+        ByteBlockList result = new ByteBlockList(count);
+        for (int i = 0; i < count; i++) {
+            result.set(i, bbs[i]);
+        }
+
+        return result;
+    }
+
+    /**
+     * Does basic block identification.
+     */
+    private void doit() {
+        BytecodeArray bytes = method.getCode();
+        ByteCatchList catches = method.getCatches();
+        int catchSz = catches.size();
+
+        /*
+         * Start by setting offset 0 as the start of a block and in need
+         * of work...
+         */
+        Bits.set(workSet, 0);
+        Bits.set(blockSet, 0);
+
+        /*
+         * And then process the work set, add new work based on
+         * exception ranges that are active, and iterate until there's
+         * nothing left to work on.
+         */
+        while (!Bits.isEmpty(workSet)) {
+            try {
+                bytes.processWorkSet(workSet, this);
+            } catch (IllegalArgumentException ex) {
+                // Translate the exception.
+                throw new SimException("flow of control falls off " +
+                                       "end of method",
+                                       ex);
+            }
+
+            for (int i = 0; i < catchSz; i++) {
+                ByteCatchList.Item item = catches.get(i);
+                int start = item.getStartPc();
+                int end = item.getEndPc();
+                if (Bits.anyInRange(liveSet, start, end)) {
+                    Bits.set(blockSet, start);
+                    Bits.set(blockSet, end);
+                    addWorkIfNecessary(item.getHandlerPc(), true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets a bit in the work set, but only if the instruction in question
+     * isn't yet known to be possibly-live.
+     *
+     * @param offset offset to the instruction in question
+     * @param blockStart {@code true} iff this instruction starts a
+     * basic block
+     */
+    private void addWorkIfNecessary(int offset, boolean blockStart) {
+        if (!Bits.get(liveSet, offset)) {
+            Bits.set(workSet, offset);
+        }
+
+        if (blockStart) {
+            Bits.set(blockSet, offset);
+        }
+    }
+
+    /**
+     * Helper method used by all the visitor methods.
+     *
+     * @param offset offset to the instruction
+     * @param length length of the instruction, in bytes
+     * @param nextIsLive {@code true} iff the instruction after
+     * the indicated one is possibly-live (because this one isn't an
+     * unconditional branch, a return, or a switch)
+     */
+    private void visitCommon(int offset, int length, boolean nextIsLive) {
+        Bits.set(liveSet, offset);
+
+        if (nextIsLive) {
+            /*
+             * If the next instruction is flowed to by this one, just
+             * add it to the work set, and then a subsequent visit*()
+             * will deal with it as appropriate.
+             */
+            addWorkIfNecessary(offset + length, false);
+        } else {
+            /*
+             * If the next instruction isn't flowed to by this one,
+             * then mark it as a start of a block but *don't* add it
+             * to the work set, so that in the final phase we can know
+             * dead code blocks as those marked as blocks but not also marked
+             * live.
+             */
+            Bits.set(blockSet, offset + length);
+        }
+    }
+
+    /**
+     * Helper method used by all the visitor methods that deal with
+     * opcodes that possibly throw. This method should be called after calling
+     * {@link #visitCommon}.
+     *
+     * @param offset offset to the instruction
+     * @param length length of the instruction, in bytes
+     * @param nextIsLive {@code true} iff the instruction after
+     * the indicated one is possibly-live (because this one isn't an
+     * unconditional throw)
+     */
+    private void visitThrowing(int offset, int length, boolean nextIsLive) {
+        int next = offset + length;
+
+        if (nextIsLive) {
+            addWorkIfNecessary(next, true);
+        }
+
+        ByteCatchList catches = method.getCatches().listFor(offset);
+        catchLists[offset] = catches;
+        targetLists[offset] = catches.toTargetList(nextIsLive ? next : -1);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setPreviousOffset(int offset) {
+        previousOffset = offset;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getPreviousOffset() {
+        return previousOffset;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteBlock.java b/dx/src/com/android/dx/cf/code/ByteBlock.java
new file mode 100644
index 0000000..73bbbab
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteBlock.java
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledItem;
+
+/**
+ * Representation of a basic block in a bytecode array.
+ */
+public final class ByteBlock implements LabeledItem {
+    /** {@code >= 0;} label for this block */
+    private final int label;
+
+    /** {@code >= 0;} bytecode offset (inclusive) of the start of the block */
+    private final int start;
+
+    /** {@code > start;} bytecode offset (exclusive) of the end of the block */
+    private final int end;
+
+    /** {@code non-null;} list of successors that this block may branch to */
+    private final IntList successors;
+
+    /** {@code non-null;} list of exceptions caught and their handler targets */
+    private final ByteCatchList catches;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param label {@code >= 0;} target label for this block
+     * @param start {@code >= 0;} bytecode offset (inclusive) of the start
+     * of the block
+     * @param end {@code > start;} bytecode offset (exclusive) of the end
+     * of the block
+     * @param successors {@code non-null;} list of successors that this block may
+     * branch to
+     * @param catches {@code non-null;} list of exceptions caught and their
+     * handler targets
+     */
+    public ByteBlock(int label, int start, int end, IntList successors,
+                     ByteCatchList catches) {
+        if (label < 0) {
+            throw new IllegalArgumentException("label < 0");
+        }
+
+        if (start < 0) {
+            throw new IllegalArgumentException("start < 0");
+        }
+
+        if (end <= start) {
+            throw new IllegalArgumentException("end <= start");
+        }
+
+        if (successors == null) {
+            throw new NullPointerException("targets == null");
+        }
+
+        int sz = successors.size();
+        for (int i = 0; i < sz; i++) {
+            if (successors.get(i) < 0) {
+                throw new IllegalArgumentException("successors[" + i +
+                                                   "] == " +
+                                                   successors.get(i));
+            }
+        }
+
+        if (catches == null) {
+            throw new NullPointerException("catches == null");
+        }
+
+        this.label = label;
+        this.start = start;
+        this.end = end;
+        this.successors = successors;
+        this.catches = catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return '{' + Hex.u2(label) + ": " + Hex.u2(start) + ".." +
+            Hex.u2(end) + '}';
+    }
+
+    /**
+     * Gets the label of this block.
+     *
+     * @return {@code >= 0;} the label
+     */
+    public int getLabel() {
+        return label;
+    }
+
+    /**
+     * Gets the bytecode offset (inclusive) of the start of this block.
+     *
+     * @return {@code >= 0;} the start offset
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * Gets the bytecode offset (exclusive) of the end of this block.
+     *
+     * @return {@code > getStart();} the end offset
+     */
+    public int getEnd() {
+        return end;
+    }
+
+    /**
+     * Gets the list of successors that this block may branch to
+     * non-exceptionally.
+     *
+     * @return {@code non-null;} the successor list
+     */
+    public IntList getSuccessors() {
+        return successors;
+    }
+
+    /**
+     * Gets the list of exceptions caught and their handler targets.
+     *
+     * @return {@code non-null;} the catch list
+     */
+    public ByteCatchList getCatches() {
+        return catches;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteBlockList.java b/dx/src/com/android/dx/cf/code/ByteBlockList.java
new file mode 100644
index 0000000..412dfc3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteBlockList.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.LabeledList;
+
+/**
+ * List of {@link ByteBlock} instances.
+ */
+public final class ByteBlockList extends LabeledList {
+
+    /**
+     * Constructs an instance.
+     *
+     * @param size {@code >= 0;} the number of elements to be in the list
+     */
+    public ByteBlockList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @return {@code non-null;} the indicated element
+     */
+    public ByteBlock get(int n) {
+        return (ByteBlock) get0(n);
+    }
+
+    /**
+     * Gets the block with the given label.
+     *
+     * @param label the label to look for
+     * @return {@code non-null;} the block with the given label
+     */
+    public ByteBlock labelToBlock(int label) {
+        int idx = indexOfLabel(label);
+
+        if (idx < 0) {
+            throw new IllegalArgumentException("no such label: "
+                    + Hex.u2(label));
+        }
+
+        return get(idx);
+    }
+
+    /**
+     * Sets the element at the given index.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param bb {@code null-ok;} the value to store
+     */
+    public void set(int n, ByteBlock bb) {
+        super.set(n, bb);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteCatchList.java b/dx/src/com/android/dx/cf/code/ByteCatchList.java
new file mode 100644
index 0000000..36c37af
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteCatchList.java
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.IntList;
+
+/**
+ * List of catch entries, that is, the elements of an "exception table,"
+ * which is part of a standard {@code Code} attribute.
+ */
+public final class ByteCatchList extends FixedSizeList {
+    /** {@code non-null;} convenient zero-entry instance */
+    public static final ByteCatchList EMPTY = new ByteCatchList(0);
+
+    /**
+     * Constructs an instance.
+     *
+     * @param count the number of elements to be in the table
+     */
+    public ByteCatchList(int count) {
+        super(count);
+    }
+
+    /**
+     * Gets the total length of this structure in bytes, when included in
+     * a {@code Code} attribute. The returned value includes the
+     * two bytes for {@code exception_table_length}.
+     *
+     * @return {@code >= 2;} the total length, in bytes
+     */
+    public int byteLength() {
+        return 2 + size() * 8;
+    }
+
+    /**
+     * Gets the indicated item.
+     *
+     * @param n {@code >= 0;} which item
+     * @return {@code null-ok;} the indicated item
+     */
+    public Item get(int n) {
+        return (Item) get0(n);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n {@code >= 0, < size();} which entry to set
+     * @param item {@code non-null;} the item
+     */
+    public void set(int n, Item item) {
+        if (item == null) {
+            throw new NullPointerException("item == null");
+        }
+
+        set0(n, item);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n {@code >= 0, < size();} which entry to set
+     * @param startPc {@code >= 0;} the start pc (inclusive) of the handler's range
+     * @param endPc {@code >= startPc;} the end pc (exclusive) of the
+     * handler's range
+     * @param handlerPc {@code >= 0;} the pc of the exception handler
+     * @param exceptionClass {@code null-ok;} the exception class or
+     * {@code null} to catch all exceptions with this handler
+     */
+    public void set(int n, int startPc, int endPc, int handlerPc,
+            CstType exceptionClass) {
+        set0(n, new Item(startPc, endPc, handlerPc, exceptionClass));
+    }
+
+    /**
+     * Gets the list of items active at the given address. The result is
+     * automatically made immutable.
+     *
+     * @param pc which address
+     * @return {@code non-null;} list of exception handlers active at
+     * {@code pc}
+     */
+    public ByteCatchList listFor(int pc) {
+        int sz = size();
+        Item[] resultArr = new Item[sz];
+        int resultSz = 0;
+
+        for (int i = 0; i < sz; i++) {
+            Item one = get(i);
+            if (one.covers(pc) && typeNotFound(one, resultArr, resultSz)) {
+                resultArr[resultSz] = one;
+                resultSz++;
+            }
+        }
+
+        if (resultSz == 0) {
+            return EMPTY;
+        }
+
+        ByteCatchList result = new ByteCatchList(resultSz);
+        for (int i = 0; i < resultSz; i++) {
+            result.set(i, resultArr[i]);
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Helper method for {@link #listFor}, which tells whether a match
+     * is <i>not</i> found for the exception type of the given item in
+     * the given array. A match is considered to be either an exact type
+     * match or the class {@code Object} which represents a catch-all.
+     *
+     * @param item {@code non-null;} item with the exception type to look for
+     * @param arr {@code non-null;} array to search in
+     * @param count {@code non-null;} maximum number of elements in the array to check
+     * @return {@code true} iff the exception type is <i>not</i> found
+     */
+    private static boolean typeNotFound(Item item, Item[] arr, int count) {
+        CstType type = item.getExceptionClass();
+
+        for (int i = 0; i < count; i++) {
+            CstType one = arr[i].getExceptionClass();
+            if ((one == type) || (one == CstType.OBJECT)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns a target list corresponding to this instance. The result
+     * is a list of all the exception handler addresses, with the given
+     * {@code noException} address appended if appropriate. The
+     * result is automatically made immutable.
+     *
+     * @param noException {@code >= -1;} the no-exception address to append, or
+     * {@code -1} not to append anything
+     * @return {@code non-null;} list of exception targets, with
+     * {@code noException} appended if necessary
+     */
+    public IntList toTargetList(int noException) {
+        if (noException < -1) {
+            throw new IllegalArgumentException("noException < -1");
+        }
+
+        boolean hasDefault = (noException >= 0);
+        int sz = size();
+
+        if (sz == 0) {
+            if (hasDefault) {
+                /*
+                 * The list is empty, but there is a no-exception
+                 * address; so, the result is just that address.
+                 */
+                return IntList.makeImmutable(noException);
+            }
+            /*
+             * The list is empty and there isn't even a no-exception
+             * address.
+             */
+            return IntList.EMPTY;
+        }
+
+        IntList result = new IntList(sz + (hasDefault ? 1 : 0));
+
+        for (int i = 0; i < sz; i++) {
+            result.add(get(i).getHandlerPc());
+        }
+
+        if (hasDefault) {
+            result.add(noException);
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Returns a rop-style catches list equivalent to this one.
+     *
+     * @return {@code non-null;} the converted instance
+     */
+    public TypeList toRopCatchList() {
+        int sz = size();
+        if (sz == 0) {
+            return StdTypeList.EMPTY;
+        }
+
+        StdTypeList result = new StdTypeList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            result.set(i, get(i).getExceptionClass().getClassType());
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Item in an exception handler list.
+     */
+    public static class Item {
+        /** {@code >= 0;} the start pc (inclusive) of the handler's range */
+        private final int startPc;
+
+        /** {@code >= startPc;} the end pc (exclusive) of the handler's range */
+        private final int endPc;
+
+        /** {@code >= 0;} the pc of the exception handler */
+        private final int handlerPc;
+
+        /** {@code null-ok;} the exception class or {@code null} to catch all
+         * exceptions with this handler */
+        private final CstType exceptionClass;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param startPc {@code >= 0;} the start pc (inclusive) of the
+         * handler's range
+         * @param endPc {@code >= startPc;} the end pc (exclusive) of the
+         * handler's range
+         * @param handlerPc {@code >= 0;} the pc of the exception handler
+         * @param exceptionClass {@code null-ok;} the exception class or
+         * {@code null} to catch all exceptions with this handler
+         */
+        public Item(int startPc, int endPc, int handlerPc,
+                CstType exceptionClass) {
+            if (startPc < 0) {
+                throw new IllegalArgumentException("startPc < 0");
+            }
+
+            if (endPc < startPc) {
+                throw new IllegalArgumentException("endPc < startPc");
+            }
+
+            if (handlerPc < 0) {
+                throw new IllegalArgumentException("handlerPc < 0");
+            }
+
+            this.startPc = startPc;
+            this.endPc = endPc;
+            this.handlerPc = handlerPc;
+            this.exceptionClass = exceptionClass;
+        }
+
+        /**
+         * Gets the start pc (inclusive) of the handler's range.
+         *
+         * @return {@code >= 0;} the start pc (inclusive) of the handler's range.
+         */
+        public int getStartPc() {
+            return startPc;
+        }
+
+        /**
+         * Gets the end pc (exclusive) of the handler's range.
+         *
+         * @return {@code >= startPc;} the end pc (exclusive) of the
+         * handler's range.
+         */
+        public int getEndPc() {
+            return endPc;
+        }
+
+        /**
+         * Gets the pc of the exception handler.
+         *
+         * @return {@code >= 0;} the pc of the exception handler
+         */
+        public int getHandlerPc() {
+            return handlerPc;
+        }
+
+        /**
+         * Gets the class of exception handled.
+         *
+         * @return {@code non-null;} the exception class; {@link CstType#OBJECT}
+         * if this entry handles all possible exceptions
+         */
+        public CstType getExceptionClass() {
+            return (exceptionClass != null) ?
+                exceptionClass : CstType.OBJECT;
+        }
+
+        /**
+         * Returns whether the given address is in the range of this item.
+         *
+         * @param pc the address
+         * @return {@code true} iff this item covers {@code pc}
+         */
+        public boolean covers(int pc) {
+            return (pc >= startPc) && (pc < endPc);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteOps.java b/dx/src/com/android/dx/cf/code/ByteOps.java
new file mode 100644
index 0000000..1376008
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteOps.java
@@ -0,0 +1,649 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * Constants and utility methods for dealing with bytecode arrays at an
+ * opcode level.
+ */
+public class ByteOps {
+    // one constant per opcode
+    public static final int NOP = 0x00;
+    public static final int ACONST_NULL = 0x01;
+    public static final int ICONST_M1 = 0x02;
+    public static final int ICONST_0 = 0x03;
+    public static final int ICONST_1 = 0x04;
+    public static final int ICONST_2 = 0x05;
+    public static final int ICONST_3 = 0x06;
+    public static final int ICONST_4 = 0x07;
+    public static final int ICONST_5 = 0x08;
+    public static final int LCONST_0 = 0x09;
+    public static final int LCONST_1 = 0x0a;
+    public static final int FCONST_0 = 0x0b;
+    public static final int FCONST_1 = 0x0c;
+    public static final int FCONST_2 = 0x0d;
+    public static final int DCONST_0 = 0x0e;
+    public static final int DCONST_1 = 0x0f;
+    public static final int BIPUSH = 0x10;
+    public static final int SIPUSH = 0x11;
+    public static final int LDC = 0x12;
+    public static final int LDC_W = 0x13;
+    public static final int LDC2_W = 0x14;
+    public static final int ILOAD = 0x15;
+    public static final int LLOAD = 0x16;
+    public static final int FLOAD = 0x17;
+    public static final int DLOAD = 0x18;
+    public static final int ALOAD = 0x19;
+    public static final int ILOAD_0 = 0x1a;
+    public static final int ILOAD_1 = 0x1b;
+    public static final int ILOAD_2 = 0x1c;
+    public static final int ILOAD_3 = 0x1d;
+    public static final int LLOAD_0 = 0x1e;
+    public static final int LLOAD_1 = 0x1f;
+    public static final int LLOAD_2 = 0x20;
+    public static final int LLOAD_3 = 0x21;
+    public static final int FLOAD_0 = 0x22;
+    public static final int FLOAD_1 = 0x23;
+    public static final int FLOAD_2 = 0x24;
+    public static final int FLOAD_3 = 0x25;
+    public static final int DLOAD_0 = 0x26;
+    public static final int DLOAD_1 = 0x27;
+    public static final int DLOAD_2 = 0x28;
+    public static final int DLOAD_3 = 0x29;
+    public static final int ALOAD_0 = 0x2a;
+    public static final int ALOAD_1 = 0x2b;
+    public static final int ALOAD_2 = 0x2c;
+    public static final int ALOAD_3 = 0x2d;
+    public static final int IALOAD = 0x2e;
+    public static final int LALOAD = 0x2f;
+    public static final int FALOAD = 0x30;
+    public static final int DALOAD = 0x31;
+    public static final int AALOAD = 0x32;
+    public static final int BALOAD = 0x33;
+    public static final int CALOAD = 0x34;
+    public static final int SALOAD = 0x35;
+    public static final int ISTORE = 0x36;
+    public static final int LSTORE = 0x37;
+    public static final int FSTORE = 0x38;
+    public static final int DSTORE = 0x39;
+    public static final int ASTORE = 0x3a;
+    public static final int ISTORE_0 = 0x3b;
+    public static final int ISTORE_1 = 0x3c;
+    public static final int ISTORE_2 = 0x3d;
+    public static final int ISTORE_3 = 0x3e;
+    public static final int LSTORE_0 = 0x3f;
+    public static final int LSTORE_1 = 0x40;
+    public static final int LSTORE_2 = 0x41;
+    public static final int LSTORE_3 = 0x42;
+    public static final int FSTORE_0 = 0x43;
+    public static final int FSTORE_1 = 0x44;
+    public static final int FSTORE_2 = 0x45;
+    public static final int FSTORE_3 = 0x46;
+    public static final int DSTORE_0 = 0x47;
+    public static final int DSTORE_1 = 0x48;
+    public static final int DSTORE_2 = 0x49;
+    public static final int DSTORE_3 = 0x4a;
+    public static final int ASTORE_0 = 0x4b;
+    public static final int ASTORE_1 = 0x4c;
+    public static final int ASTORE_2 = 0x4d;
+    public static final int ASTORE_3 = 0x4e;
+    public static final int IASTORE = 0x4f;
+    public static final int LASTORE = 0x50;
+    public static final int FASTORE = 0x51;
+    public static final int DASTORE = 0x52;
+    public static final int AASTORE = 0x53;
+    public static final int BASTORE = 0x54;
+    public static final int CASTORE = 0x55;
+    public static final int SASTORE = 0x56;
+    public static final int POP = 0x57;
+    public static final int POP2 = 0x58;
+    public static final int DUP = 0x59;
+    public static final int DUP_X1 = 0x5a;
+    public static final int DUP_X2 = 0x5b;
+    public static final int DUP2 = 0x5c;
+    public static final int DUP2_X1 = 0x5d;
+    public static final int DUP2_X2 = 0x5e;
+    public static final int SWAP = 0x5f;
+    public static final int IADD = 0x60;
+    public static final int LADD = 0x61;
+    public static final int FADD = 0x62;
+    public static final int DADD = 0x63;
+    public static final int ISUB = 0x64;
+    public static final int LSUB = 0x65;
+    public static final int FSUB = 0x66;
+    public static final int DSUB = 0x67;
+    public static final int IMUL = 0x68;
+    public static final int LMUL = 0x69;
+    public static final int FMUL = 0x6a;
+    public static final int DMUL = 0x6b;
+    public static final int IDIV = 0x6c;
+    public static final int LDIV = 0x6d;
+    public static final int FDIV = 0x6e;
+    public static final int DDIV = 0x6f;
+    public static final int IREM = 0x70;
+    public static final int LREM = 0x71;
+    public static final int FREM = 0x72;
+    public static final int DREM = 0x73;
+    public static final int INEG = 0x74;
+    public static final int LNEG = 0x75;
+    public static final int FNEG = 0x76;
+    public static final int DNEG = 0x77;
+    public static final int ISHL = 0x78;
+    public static final int LSHL = 0x79;
+    public static final int ISHR = 0x7a;
+    public static final int LSHR = 0x7b;
+    public static final int IUSHR = 0x7c;
+    public static final int LUSHR = 0x7d;
+    public static final int IAND = 0x7e;
+    public static final int LAND = 0x7f;
+    public static final int IOR = 0x80;
+    public static final int LOR = 0x81;
+    public static final int IXOR = 0x82;
+    public static final int LXOR = 0x83;
+    public static final int IINC = 0x84;
+    public static final int I2L = 0x85;
+    public static final int I2F = 0x86;
+    public static final int I2D = 0x87;
+    public static final int L2I = 0x88;
+    public static final int L2F = 0x89;
+    public static final int L2D = 0x8a;
+    public static final int F2I = 0x8b;
+    public static final int F2L = 0x8c;
+    public static final int F2D = 0x8d;
+    public static final int D2I = 0x8e;
+    public static final int D2L = 0x8f;
+    public static final int D2F = 0x90;
+    public static final int I2B = 0x91;
+    public static final int I2C = 0x92;
+    public static final int I2S = 0x93;
+    public static final int LCMP = 0x94;
+    public static final int FCMPL = 0x95;
+    public static final int FCMPG = 0x96;
+    public static final int DCMPL = 0x97;
+    public static final int DCMPG = 0x98;
+    public static final int IFEQ = 0x99;
+    public static final int IFNE = 0x9a;
+    public static final int IFLT = 0x9b;
+    public static final int IFGE = 0x9c;
+    public static final int IFGT = 0x9d;
+    public static final int IFLE = 0x9e;
+    public static final int IF_ICMPEQ = 0x9f;
+    public static final int IF_ICMPNE = 0xa0;
+    public static final int IF_ICMPLT = 0xa1;
+    public static final int IF_ICMPGE = 0xa2;
+    public static final int IF_ICMPGT = 0xa3;
+    public static final int IF_ICMPLE = 0xa4;
+    public static final int IF_ACMPEQ = 0xa5;
+    public static final int IF_ACMPNE = 0xa6;
+    public static final int GOTO = 0xa7;
+    public static final int JSR = 0xa8;
+    public static final int RET = 0xa9;
+    public static final int TABLESWITCH = 0xaa;
+    public static final int LOOKUPSWITCH = 0xab;
+    public static final int IRETURN = 0xac;
+    public static final int LRETURN = 0xad;
+    public static final int FRETURN = 0xae;
+    public static final int DRETURN = 0xaf;
+    public static final int ARETURN = 0xb0;
+    public static final int RETURN = 0xb1;
+    public static final int GETSTATIC = 0xb2;
+    public static final int PUTSTATIC = 0xb3;
+    public static final int GETFIELD = 0xb4;
+    public static final int PUTFIELD = 0xb5;
+    public static final int INVOKEVIRTUAL = 0xb6;
+    public static final int INVOKESPECIAL = 0xb7;
+    public static final int INVOKESTATIC = 0xb8;
+    public static final int INVOKEINTERFACE = 0xb9;
+    public static final int NEW = 0xbb;
+    public static final int NEWARRAY = 0xbc;
+    public static final int ANEWARRAY = 0xbd;
+    public static final int ARRAYLENGTH = 0xbe;
+    public static final int ATHROW = 0xbf;
+    public static final int CHECKCAST = 0xc0;
+    public static final int INSTANCEOF = 0xc1;
+    public static final int MONITORENTER = 0xc2;
+    public static final int MONITOREXIT = 0xc3;
+    public static final int WIDE = 0xc4;
+    public static final int MULTIANEWARRAY = 0xc5;
+    public static final int IFNULL = 0xc6;
+    public static final int IFNONNULL = 0xc7;
+    public static final int GOTO_W = 0xc8;
+    public static final int JSR_W = 0xc9;
+
+    // a constant for each valid argument to "newarray"
+
+    public static final int NEWARRAY_BOOLEAN = 4;
+    public static final int NEWARRAY_CHAR = 5;
+    public static final int NEWARRAY_FLOAT = 6;
+    public static final int NEWARRAY_DOUBLE = 7;
+    public static final int NEWARRAY_BYTE = 8;
+    public static final int NEWARRAY_SHORT = 9;
+    public static final int NEWARRAY_INT = 10;
+    public static final int NEWARRAY_LONG = 11;
+
+    // a constant for each possible instruction format
+
+    /** invalid */
+    public static final int FMT_INVALID = 0;
+
+    /** "-": {@code op} */
+    public static final int FMT_NO_ARGS = 1;
+
+    /** "0": {@code op}; implies {@code max_locals >= 1} */
+    public static final int FMT_NO_ARGS_LOCALS_1 = 2;
+
+    /** "1": {@code op}; implies {@code max_locals >= 2} */
+    public static final int FMT_NO_ARGS_LOCALS_2 = 3;
+
+    /** "2": {@code op}; implies {@code max_locals >= 3} */
+    public static final int FMT_NO_ARGS_LOCALS_3 = 4;
+
+    /** "3": {@code op}; implies {@code max_locals >= 4} */
+    public static final int FMT_NO_ARGS_LOCALS_4 = 5;
+
+    /** "4": {@code op}; implies {@code max_locals >= 5} */
+    public static final int FMT_NO_ARGS_LOCALS_5 = 6;
+
+    /** "b": {@code op target target} */
+    public static final int FMT_BRANCH = 7;
+
+    /** "c": {@code op target target target target} */
+    public static final int FMT_WIDE_BRANCH = 8;
+
+    /** "p": {@code op #cpi #cpi}; constant restricted as specified */
+    public static final int FMT_CPI = 9;
+
+    /**
+     * "l": {@code op local}; category-1 local; implies
+     * {@code max_locals} is at least two more than the given
+     * local number
+     */
+    public static final int FMT_LOCAL_1 = 10;
+
+    /**
+     * "m": {@code op local}; category-2 local; implies
+     * {@code max_locals} is at least two more than the given
+     * local number
+     */
+    public static final int FMT_LOCAL_2 = 11;
+
+    /**
+     * "y": {@code op #byte} ({@code bipush} and
+     * {@code newarray})
+     */
+    public static final int FMT_LITERAL_BYTE = 12;
+
+    /** "I": {@code invokeinterface cpi cpi count 0} */
+    public static final int FMT_INVOKEINTERFACE = 13;
+
+    /** "L": {@code ldc #cpi}; constant restricted as specified */
+    public static final int FMT_LDC = 14;
+
+    /** "S": {@code sipush #byte #byte} */
+    public static final int FMT_SIPUSH = 15;
+
+    /** "T": {@code tableswitch ...} */
+    public static final int FMT_TABLESWITCH = 16;
+
+    /** "U": {@code lookupswitch ...} */
+    public static final int FMT_LOOKUPSWITCH = 17;
+
+    /** "M": {@code multianewarray cpi cpi dims} */
+    public static final int FMT_MULTIANEWARRAY = 18;
+
+    /** "W": {@code wide ...} */
+    public static final int FMT_WIDE = 19;
+
+    /** mask for the bits representing the opcode format */
+    public static final int FMT_MASK = 0x1f;
+
+    /** "I": flag bit for valid cp type for {@code Integer} */
+    public static final int CPOK_Integer = 0x20;
+
+    /** "F": flag bit for valid cp type for {@code Float} */
+    public static final int CPOK_Float = 0x40;
+
+    /** "J": flag bit for valid cp type for {@code Long} */
+    public static final int CPOK_Long = 0x80;
+
+    /** "D": flag bit for valid cp type for {@code Double} */
+    public static final int CPOK_Double = 0x100;
+
+    /** "c": flag bit for valid cp type for {@code Class} */
+    public static final int CPOK_Class = 0x200;
+
+    /** "s": flag bit for valid cp type for {@code String} */
+    public static final int CPOK_String = 0x400;
+
+    /** "f": flag bit for valid cp type for {@code Fieldref} */
+    public static final int CPOK_Fieldref = 0x800;
+
+    /** "m": flag bit for valid cp type for {@code Methodref} */
+    public static final int CPOK_Methodref = 0x1000;
+
+    /** "i": flag bit for valid cp type for {@code InterfaceMethodref} */
+    public static final int CPOK_InterfaceMethodref = 0x2000;
+
+    /**
+     * {@code non-null;} map from opcodes to format or'ed with allowed constant
+     * pool types
+     */
+    private static final int[] OPCODE_INFO = new int[256];
+
+    /** {@code non-null;} map from opcodes to their names */
+    private static final String[] OPCODE_NAMES = new String[256];
+
+    /** {@code non-null;} bigass string describing all the opcodes */
+    private static final String OPCODE_DETAILS =
+        "00 - nop;" +
+        "01 - aconst_null;" +
+        "02 - iconst_m1;" +
+        "03 - iconst_0;" +
+        "04 - iconst_1;" +
+        "05 - iconst_2;" +
+        "06 - iconst_3;" +
+        "07 - iconst_4;" +
+        "08 - iconst_5;" +
+        "09 - lconst_0;" +
+        "0a - lconst_1;" +
+        "0b - fconst_0;" +
+        "0c - fconst_1;" +
+        "0d - fconst_2;" +
+        "0e - dconst_0;" +
+        "0f - dconst_1;" +
+        "10 y bipush;" +
+        "11 S sipush;" +
+        "12 L:IFcs ldc;" +
+        "13 p:IFcs ldc_w;" +
+        "14 p:DJ ldc2_w;" +
+        "15 l iload;" +
+        "16 m lload;" +
+        "17 l fload;" +
+        "18 m dload;" +
+        "19 l aload;" +
+        "1a 0 iload_0;" +
+        "1b 1 iload_1;" +
+        "1c 2 iload_2;" +
+        "1d 3 iload_3;" +
+        "1e 1 lload_0;" +
+        "1f 2 lload_1;" +
+        "20 3 lload_2;" +
+        "21 4 lload_3;" +
+        "22 0 fload_0;" +
+        "23 1 fload_1;" +
+        "24 2 fload_2;" +
+        "25 3 fload_3;" +
+        "26 1 dload_0;" +
+        "27 2 dload_1;" +
+        "28 3 dload_2;" +
+        "29 4 dload_3;" +
+        "2a 0 aload_0;" +
+        "2b 1 aload_1;" +
+        "2c 2 aload_2;" +
+        "2d 3 aload_3;" +
+        "2e - iaload;" +
+        "2f - laload;" +
+        "30 - faload;" +
+        "31 - daload;" +
+        "32 - aaload;" +
+        "33 - baload;" +
+        "34 - caload;" +
+        "35 - saload;" +
+        "36 - istore;" +
+        "37 - lstore;" +
+        "38 - fstore;" +
+        "39 - dstore;" +
+        "3a - astore;" +
+        "3b 0 istore_0;" +
+        "3c 1 istore_1;" +
+        "3d 2 istore_2;" +
+        "3e 3 istore_3;" +
+        "3f 1 lstore_0;" +
+        "40 2 lstore_1;" +
+        "41 3 lstore_2;" +
+        "42 4 lstore_3;" +
+        "43 0 fstore_0;" +
+        "44 1 fstore_1;" +
+        "45 2 fstore_2;" +
+        "46 3 fstore_3;" +
+        "47 1 dstore_0;" +
+        "48 2 dstore_1;" +
+        "49 3 dstore_2;" +
+        "4a 4 dstore_3;" +
+        "4b 0 astore_0;" +
+        "4c 1 astore_1;" +
+        "4d 2 astore_2;" +
+        "4e 3 astore_3;" +
+        "4f - iastore;" +
+        "50 - lastore;" +
+        "51 - fastore;" +
+        "52 - dastore;" +
+        "53 - aastore;" +
+        "54 - bastore;" +
+        "55 - castore;" +
+        "56 - sastore;" +
+        "57 - pop;" +
+        "58 - pop2;" +
+        "59 - dup;" +
+        "5a - dup_x1;" +
+        "5b - dup_x2;" +
+        "5c - dup2;" +
+        "5d - dup2_x1;" +
+        "5e - dup2_x2;" +
+        "5f - swap;" +
+        "60 - iadd;" +
+        "61 - ladd;" +
+        "62 - fadd;" +
+        "63 - dadd;" +
+        "64 - isub;" +
+        "65 - lsub;" +
+        "66 - fsub;" +
+        "67 - dsub;" +
+        "68 - imul;" +
+        "69 - lmul;" +
+        "6a - fmul;" +
+        "6b - dmul;" +
+        "6c - idiv;" +
+        "6d - ldiv;" +
+        "6e - fdiv;" +
+        "6f - ddiv;" +
+        "70 - irem;" +
+        "71 - lrem;" +
+        "72 - frem;" +
+        "73 - drem;" +
+        "74 - ineg;" +
+        "75 - lneg;" +
+        "76 - fneg;" +
+        "77 - dneg;" +
+        "78 - ishl;" +
+        "79 - lshl;" +
+        "7a - ishr;" +
+        "7b - lshr;" +
+        "7c - iushr;" +
+        "7d - lushr;" +
+        "7e - iand;" +
+        "7f - land;" +
+        "80 - ior;" +
+        "81 - lor;" +
+        "82 - ixor;" +
+        "83 - lxor;" +
+        "84 l iinc;" +
+        "85 - i2l;" +
+        "86 - i2f;" +
+        "87 - i2d;" +
+        "88 - l2i;" +
+        "89 - l2f;" +
+        "8a - l2d;" +
+        "8b - f2i;" +
+        "8c - f2l;" +
+        "8d - f2d;" +
+        "8e - d2i;" +
+        "8f - d2l;" +
+        "90 - d2f;" +
+        "91 - i2b;" +
+        "92 - i2c;" +
+        "93 - i2s;" +
+        "94 - lcmp;" +
+        "95 - fcmpl;" +
+        "96 - fcmpg;" +
+        "97 - dcmpl;" +
+        "98 - dcmpg;" +
+        "99 b ifeq;" +
+        "9a b ifne;" +
+        "9b b iflt;" +
+        "9c b ifge;" +
+        "9d b ifgt;" +
+        "9e b ifle;" +
+        "9f b if_icmpeq;" +
+        "a0 b if_icmpne;" +
+        "a1 b if_icmplt;" +
+        "a2 b if_icmpge;" +
+        "a3 b if_icmpgt;" +
+        "a4 b if_icmple;" +
+        "a5 b if_acmpeq;" +
+        "a6 b if_acmpne;" +
+        "a7 b goto;" +
+        "a8 b jsr;" +
+        "a9 l ret;" +
+        "aa T tableswitch;" +
+        "ab U lookupswitch;" +
+        "ac - ireturn;" +
+        "ad - lreturn;" +
+        "ae - freturn;" +
+        "af - dreturn;" +
+        "b0 - areturn;" +
+        "b1 - return;" +
+        "b2 p:f getstatic;" +
+        "b3 p:f putstatic;" +
+        "b4 p:f getfield;" +
+        "b5 p:f putfield;" +
+        "b6 p:m invokevirtual;" +
+        "b7 p:m invokespecial;" +
+        "b8 p:m invokestatic;" +
+        "b9 I:i invokeinterface;" +
+        "bb p:c new;" +
+        "bc y newarray;" +
+        "bd p:c anewarray;" +
+        "be - arraylength;" +
+        "bf - athrow;" +
+        "c0 p:c checkcast;" +
+        "c1 p:c instanceof;" +
+        "c2 - monitorenter;" +
+        "c3 - monitorexit;" +
+        "c4 W wide;" +
+        "c5 M:c multianewarray;" +
+        "c6 b ifnull;" +
+        "c7 b ifnonnull;" +
+        "c8 c goto_w;" +
+        "c9 c jsr_w;";
+
+    static {
+        // Set up OPCODE_INFO and OPCODE_NAMES.
+        String s = OPCODE_DETAILS;
+        int len = s.length();
+
+        for (int i = 0; i < len; /*i*/) {
+            int idx = (Character.digit(s.charAt(i), 16) << 4) |
+                Character.digit(s.charAt(i + 1), 16);
+            int info;
+            switch (s.charAt(i + 3)) {
+                case '-': info = FMT_NO_ARGS; break;
+                case '0': info = FMT_NO_ARGS_LOCALS_1; break;
+                case '1': info = FMT_NO_ARGS_LOCALS_2; break;
+                case '2': info = FMT_NO_ARGS_LOCALS_3; break;
+                case '3': info = FMT_NO_ARGS_LOCALS_4; break;
+                case '4': info = FMT_NO_ARGS_LOCALS_5; break;
+                case 'b': info = FMT_BRANCH; break;
+                case 'c': info = FMT_WIDE_BRANCH; break;
+                case 'p': info = FMT_CPI; break;
+                case 'l': info = FMT_LOCAL_1; break;
+                case 'm': info = FMT_LOCAL_2; break;
+                case 'y': info = FMT_LITERAL_BYTE; break;
+                case 'I': info = FMT_INVOKEINTERFACE; break;
+                case 'L': info = FMT_LDC; break;
+                case 'S': info = FMT_SIPUSH; break;
+                case 'T': info = FMT_TABLESWITCH; break;
+                case 'U': info = FMT_LOOKUPSWITCH; break;
+                case 'M': info = FMT_MULTIANEWARRAY; break;
+                case 'W': info = FMT_WIDE; break;
+                default: info = FMT_INVALID; break;
+            }
+
+            i += 5;
+            if (s.charAt(i - 1) == ':') {
+                inner:
+                for (;;) {
+                    switch (s.charAt(i)) {
+                        case 'I': info |= CPOK_Integer; break;
+                        case 'F': info |= CPOK_Float; break;
+                        case 'J': info |= CPOK_Long; break;
+                        case 'D': info |= CPOK_Double; break;
+                        case 'c': info |= CPOK_Class; break;
+                        case 's': info |= CPOK_String; break;
+                        case 'f': info |= CPOK_Fieldref; break;
+                        case 'm': info |= CPOK_Methodref; break;
+                        case 'i': info |= CPOK_InterfaceMethodref; break;
+                        default: break inner;
+                    }
+                    i++;
+                }
+                i++;
+            }
+
+            int endAt = s.indexOf(';', i);
+            OPCODE_INFO[idx] = info;
+            OPCODE_NAMES[idx] = s.substring(i, endAt);
+            i = endAt + 1;
+        }
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private ByteOps() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the name of the given opcode.
+     *
+     * @param opcode {@code >= 0, <= 255;} the opcode
+     * @return {@code non-null;} its name
+     */
+    public static String opName(int opcode) {
+        String result = OPCODE_NAMES[opcode];
+
+        if (result == null) {
+            result = "unused_" + Hex.u1(opcode);
+            OPCODE_NAMES[opcode] = result;
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the format and allowed cp types of the given opcode.
+     *
+     * @param opcode {@code >= 0, <= 255;} the opcode
+     * @return its format and allowed cp types
+     */
+    public static int opInfo(int opcode) {
+        return OPCODE_INFO[opcode];
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/BytecodeArray.java b/dx/src/com/android/dx/cf/code/BytecodeArray.java
new file mode 100644
index 0000000..80e94bf
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BytecodeArray.java
@@ -0,0 +1,1393 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import java.util.ArrayList;
+
+/**
+ * Bytecode array, which is part of a standard {@code Code} attribute.
+ */
+public final class BytecodeArray {
+    /** convenient no-op implementation of {@link Visitor} */
+    public static final Visitor EMPTY_VISITOR = new BaseVisitor();
+
+    /** {@code non-null;} underlying bytes */
+    private final ByteArray bytes;
+
+    /** {@code non-null;} constant pool to use when resolving constant pool indices */
+    private final ConstantPool pool;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes {@code non-null;} underlying bytes
+     * @param pool {@code non-null;} constant pool to use when resolving constant
+     * pool indices
+     */
+    public BytecodeArray(ByteArray bytes, ConstantPool pool) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        if (pool == null) {
+            throw new NullPointerException("pool == null");
+        }
+
+        this.bytes = bytes;
+        this.pool = pool;
+    }
+
+    /**
+     * Gets the underlying byte array.
+     *
+     * @return {@code non-null;} the byte array
+     */
+    public ByteArray getBytes() {
+        return bytes;
+    }
+
+    /**
+     * Gets the size of the bytecode array, per se.
+     *
+     * @return {@code >= 0;} the length of the bytecode array
+     */
+    public int size() {
+        return bytes.size();
+    }
+
+    /**
+     * Gets the total length of this structure in bytes, when included in
+     * a {@code Code} attribute. The returned value includes the
+     * array size plus four bytes for {@code code_length}.
+     *
+     * @return {@code >= 4;} the total length, in bytes
+     */
+    public int byteLength() {
+        return 4 + bytes.size();
+    }
+
+    /**
+     * Parses each instruction in the array, in order.
+     *
+     * @param visitor {@code null-ok;} visitor to call back to for each instruction
+     */
+    public void forEach(Visitor visitor) {
+        int sz = bytes.size();
+        int at = 0;
+
+        while (at < sz) {
+            /*
+             * Don't record the previous offset here, so that we get to see the
+             * raw code that initializes the array
+             */
+            at += parseInstruction(at, visitor);
+        }
+    }
+
+    /**
+     * Finds the offset to each instruction in the bytecode array. The
+     * result is a bit set with the offset of each opcode-per-se flipped on.
+     *
+     * @see Bits
+     * @return {@code non-null;} appropriately constructed bit set
+     */
+    public int[] getInstructionOffsets() {
+        int sz = bytes.size();
+        int[] result = Bits.makeBitSet(sz);
+        int at = 0;
+
+        while (at < sz) {
+            Bits.set(result, at, true);
+            int length = parseInstruction(at, null);
+            at += length;
+        }
+
+        return result;
+    }
+
+    /**
+     * Processes the given "work set" by repeatedly finding the lowest bit
+     * in the set, clearing it, and parsing and visiting the instruction at
+     * the indicated offset (that is, the bit index), repeating until the
+     * work set is empty. It is expected that the visitor will regularly
+     * set new bits in the work set during the process.
+     *
+     * @param workSet {@code non-null;} the work set to process
+     * @param visitor {@code non-null;} visitor to call back to for each instruction
+     */
+    public void processWorkSet(int[] workSet, Visitor visitor) {
+        if (visitor == null) {
+            throw new NullPointerException("visitor == null");
+        }
+
+        for (;;) {
+            int offset = Bits.findFirst(workSet, 0);
+            if (offset < 0) {
+                break;
+            }
+            Bits.clear(workSet, offset);
+            parseInstruction(offset, visitor);
+            visitor.setPreviousOffset(offset);
+        }
+    }
+
+    /**
+     * Parses the instruction at the indicated offset. Indicate the
+     * result by calling the visitor if supplied and by returning the
+     * number of bytes consumed by the instruction.
+     *
+     * <p>In order to simplify further processing, the opcodes passed
+     * to the visitor are canonicalized, altering the opcode to a more
+     * universal one and making formerly implicit arguments
+     * explicit. In particular:</p>
+     *
+     * <ul>
+     * <li>The opcodes to push literal constants of primitive types all become
+     *   {@code ldc}.
+     *   E.g., {@code fconst_0}, {@code sipush}, and
+     *   {@code lconst_0} qualify for this treatment.</li>
+     * <li>{@code aconst_null} becomes {@code ldc} of a
+     *   "known null."</li>
+     * <li>Shorthand local variable accessors become the corresponding
+     *   longhand. E.g. {@code aload_2} becomes {@code aload}.</li>
+     * <li>{@code goto_w} and {@code jsr_w} become {@code goto}
+     *   and {@code jsr} (respectively).</li>
+     * <li>{@code ldc_w} becomes {@code ldc}.</li>
+     * <li>{@code tableswitch} becomes {@code lookupswitch}.
+     * <li>Arithmetic, array, and value-returning ops are collapsed
+     *   to the {@code int} variant opcode, with the {@code type}
+     *   argument set to indicate the actual type. E.g.,
+     *   {@code fadd} becomes {@code iadd}, but
+     *   {@code type} is passed as {@code Type.FLOAT} in that
+     *   case. Similarly, {@code areturn} becomes
+     *   {@code ireturn}. (However, {@code return} remains
+     *   unchanged.</li>
+     * <li>Local variable access ops are collapsed to the {@code int}
+     *   variant opcode, with the {@code type} argument set to indicate
+     *   the actual type. E.g., {@code aload} becomes {@code iload},
+     *   but {@code type} is passed as {@code Type.OBJECT} in
+     *   that case.</li>
+     * <li>Numeric conversion ops ({@code i2l}, etc.) are left alone
+     *   to avoid too much confustion, but their {@code type} is
+     *   the pushed type. E.g., {@code i2b} gets type
+     *   {@code Type.INT}, and {@code f2d} gets type
+     *   {@code Type.DOUBLE}. Other unaltered opcodes also get
+     *   their pushed type. E.g., {@code arraylength} gets type
+     *   {@code Type.INT}.</li>
+     * </ul>
+     *
+     * @param offset {@code >= 0, < bytes.size();} offset to the start of the
+     * instruction
+     * @param visitor {@code null-ok;} visitor to call back to
+     * @return the length of the instruction, in bytes
+     */
+    public int parseInstruction(int offset, Visitor visitor) {
+        if (visitor == null) {
+            visitor = EMPTY_VISITOR;
+        }
+
+        try {
+            int opcode = bytes.getUnsignedByte(offset);
+            int info = ByteOps.opInfo(opcode);
+            int fmt = info & ByteOps.FMT_MASK;
+
+            switch (opcode) {
+                case ByteOps.NOP: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+                    return 1;
+                }
+                case ByteOps.ACONST_NULL: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstKnownNull.THE_ONE, 0);
+                    return 1;
+                }
+                case ByteOps.ICONST_M1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_M1, -1);
+                    return 1;
+                }
+                case ByteOps.ICONST_0: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_0, 0);
+                    return 1;
+                }
+                case ByteOps.ICONST_1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_1, 1);
+                    return 1;
+                }
+                case ByteOps.ICONST_2: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_2, 2);
+                    return 1;
+                }
+                case ByteOps.ICONST_3: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_3, 3);
+                    return 1;
+                }
+                case ByteOps.ICONST_4: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_4, 4);
+                    return 1;
+                }
+                case ByteOps.ICONST_5:  {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstInteger.VALUE_5, 5);
+                    return 1;
+                }
+                case ByteOps.LCONST_0: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstLong.VALUE_0, 0);
+                    return 1;
+                }
+                case ByteOps.LCONST_1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstLong.VALUE_1, 0);
+                    return 1;
+                }
+                case ByteOps.FCONST_0: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstFloat.VALUE_0, 0);
+                    return 1;
+                }
+                case ByteOps.FCONST_1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstFloat.VALUE_1, 0);
+                    return 1;
+                }
+                case ByteOps.FCONST_2:  {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstFloat.VALUE_2, 0);
+                    return 1;
+                }
+                case ByteOps.DCONST_0: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstDouble.VALUE_0, 0);
+                    return 1;
+                }
+                case ByteOps.DCONST_1: {
+                    visitor.visitConstant(ByteOps.LDC, offset, 1,
+                                          CstDouble.VALUE_1, 0);
+                    return 1;
+                }
+                case ByteOps.BIPUSH: {
+                    int value = bytes.getByte(offset + 1);
+                    visitor.visitConstant(ByteOps.LDC, offset, 2,
+                                          CstInteger.make(value), value);
+                    return 2;
+                }
+                case ByteOps.SIPUSH: {
+                    int value = bytes.getShort(offset + 1);
+                    visitor.visitConstant(ByteOps.LDC, offset, 3,
+                                          CstInteger.make(value), value);
+                    return 3;
+                }
+                case ByteOps.LDC: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    Constant cst = pool.get(idx);
+                    int value = (cst instanceof CstInteger) ?
+                        ((CstInteger) cst).getValue() : 0;
+                    visitor.visitConstant(ByteOps.LDC, offset, 2, cst, value);
+                    return 2;
+                }
+                case ByteOps.LDC_W: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    Constant cst = pool.get(idx);
+                    int value = (cst instanceof CstInteger) ?
+                        ((CstInteger) cst).getValue() : 0;
+                    visitor.visitConstant(ByteOps.LDC, offset, 3, cst, value);
+                    return 3;
+                }
+                case ByteOps.LDC2_W: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    Constant cst = pool.get(idx);
+                    visitor.visitConstant(ByteOps.LDC2_W, offset, 3, cst, 0);
+                    return 3;
+                }
+                case ByteOps.ILOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.INT, 0);
+                    return 2;
+                }
+                case ByteOps.LLOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.LONG, 0);
+                    return 2;
+                }
+                case ByteOps.FLOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.FLOAT, 0);
+                    return 2;
+                }
+                case ByteOps.DLOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.DOUBLE, 0);
+                    return 2;
+                }
+                case ByteOps.ALOAD: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+                                       Type.OBJECT, 0);
+                    return 2;
+                }
+                case ByteOps.ILOAD_0:
+                case ByteOps.ILOAD_1:
+                case ByteOps.ILOAD_2:
+                case ByteOps.ILOAD_3: {
+                    int idx = opcode - ByteOps.ILOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.INT, 0);
+                    return 1;
+                }
+                case ByteOps.LLOAD_0:
+                case ByteOps.LLOAD_1:
+                case ByteOps.LLOAD_2:
+                case ByteOps.LLOAD_3: {
+                    int idx = opcode - ByteOps.LLOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.LONG, 0);
+                    return 1;
+                }
+                case ByteOps.FLOAD_0:
+                case ByteOps.FLOAD_1:
+                case ByteOps.FLOAD_2:
+                case ByteOps.FLOAD_3: {
+                    int idx = opcode - ByteOps.FLOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.FLOAT, 0);
+                    return 1;
+                }
+                case ByteOps.DLOAD_0:
+                case ByteOps.DLOAD_1:
+                case ByteOps.DLOAD_2:
+                case ByteOps.DLOAD_3: {
+                    int idx = opcode - ByteOps.DLOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.DOUBLE, 0);
+                    return 1;
+                }
+                case ByteOps.ALOAD_0:
+                case ByteOps.ALOAD_1:
+                case ByteOps.ALOAD_2:
+                case ByteOps.ALOAD_3: {
+                    int idx = opcode - ByteOps.ALOAD_0;
+                    visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+                                       Type.OBJECT, 0);
+                    return 1;
+                }
+                case ByteOps.IALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.LALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.LONG);
+                    return 1;
+                }
+                case ByteOps.FALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+                                        Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.DALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+                                        Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.AALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+                                        Type.OBJECT);
+                    return 1;
+                }
+                case ByteOps.BALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.BYTE);
+                    return 1;
+                }
+                case ByteOps.CALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.CHAR);
+                    return 1;
+                }
+                case ByteOps.SALOAD: {
+                    visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+                                        Type.SHORT);
+                    return 1;
+                }
+                case ByteOps.ISTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.INT, 0);
+                    return 2;
+                }
+                case ByteOps.LSTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.LONG, 0);
+                    return 2;
+                }
+                case ByteOps.FSTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.FLOAT, 0);
+                    return 2;
+                }
+                case ByteOps.DSTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.DOUBLE, 0);
+                    return 2;
+                }
+                case ByteOps.ASTORE: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+                                       Type.OBJECT, 0);
+                    return 2;
+                }
+                case ByteOps.ISTORE_0:
+                case ByteOps.ISTORE_1:
+                case ByteOps.ISTORE_2:
+                case ByteOps.ISTORE_3: {
+                    int idx = opcode - ByteOps.ISTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.INT, 0);
+                    return 1;
+                }
+                case ByteOps.LSTORE_0:
+                case ByteOps.LSTORE_1:
+                case ByteOps.LSTORE_2:
+                case ByteOps.LSTORE_3: {
+                    int idx = opcode - ByteOps.LSTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.LONG, 0);
+                    return 1;
+                }
+                case ByteOps.FSTORE_0:
+                case ByteOps.FSTORE_1:
+                case ByteOps.FSTORE_2:
+                case ByteOps.FSTORE_3: {
+                    int idx = opcode - ByteOps.FSTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.FLOAT, 0);
+                    return 1;
+                }
+                case ByteOps.DSTORE_0:
+                case ByteOps.DSTORE_1:
+                case ByteOps.DSTORE_2:
+                case ByteOps.DSTORE_3: {
+                    int idx = opcode - ByteOps.DSTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.DOUBLE, 0);
+                    return 1;
+                }
+                case ByteOps.ASTORE_0:
+                case ByteOps.ASTORE_1:
+                case ByteOps.ASTORE_2:
+                case ByteOps.ASTORE_3: {
+                    int idx = opcode - ByteOps.ASTORE_0;
+                    visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+                                       Type.OBJECT, 0);
+                    return 1;
+                }
+                case ByteOps.IASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.LASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.LONG);
+                    return 1;
+                }
+                case ByteOps.FASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.DASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.AASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.OBJECT);
+                    return 1;
+                }
+                case ByteOps.BASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.BYTE);
+                    return 1;
+                }
+                case ByteOps.CASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.CHAR);
+                    return 1;
+                }
+                case ByteOps.SASTORE: {
+                    visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+                                        Type.SHORT);
+                    return 1;
+                }
+                case ByteOps.POP:
+                case ByteOps.POP2:
+                case ByteOps.DUP:
+                case ByteOps.DUP_X1:
+                case ByteOps.DUP_X2:
+                case ByteOps.DUP2:
+                case ByteOps.DUP2_X1:
+                case ByteOps.DUP2_X2:
+                case ByteOps.SWAP: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+                    return 1;
+                }
+                case ByteOps.IADD:
+                case ByteOps.ISUB:
+                case ByteOps.IMUL:
+                case ByteOps.IDIV:
+                case ByteOps.IREM:
+                case ByteOps.INEG:
+                case ByteOps.ISHL:
+                case ByteOps.ISHR:
+                case ByteOps.IUSHR:
+                case ByteOps.IAND:
+                case ByteOps.IOR:
+                case ByteOps.IXOR: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.LADD:
+                case ByteOps.LSUB:
+                case ByteOps.LMUL:
+                case ByteOps.LDIV:
+                case ByteOps.LREM:
+                case ByteOps.LNEG:
+                case ByteOps.LSHL:
+                case ByteOps.LSHR:
+                case ByteOps.LUSHR:
+                case ByteOps.LAND:
+                case ByteOps.LOR:
+                case ByteOps.LXOR: {
+                    /*
+                     * It's "opcode - 1" because, conveniently enough, all
+                     * these long ops are one past the int variants.
+                     */
+                    visitor.visitNoArgs(opcode - 1, offset, 1, Type.LONG);
+                    return 1;
+                }
+                case ByteOps.FADD:
+                case ByteOps.FSUB:
+                case ByteOps.FMUL:
+                case ByteOps.FDIV:
+                case ByteOps.FREM:
+                case ByteOps.FNEG: {
+                    /*
+                     * It's "opcode - 2" because, conveniently enough, all
+                     * these float ops are two past the int variants.
+                     */
+                    visitor.visitNoArgs(opcode - 2, offset, 1, Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.DADD:
+                case ByteOps.DSUB:
+                case ByteOps.DMUL:
+                case ByteOps.DDIV:
+                case ByteOps.DREM:
+                case ByteOps.DNEG: {
+                    /*
+                     * It's "opcode - 3" because, conveniently enough, all
+                     * these double ops are three past the int variants.
+                     */
+                    visitor.visitNoArgs(opcode - 3, offset, 1, Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.IINC: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    int value = bytes.getByte(offset + 2);
+                    visitor.visitLocal(opcode, offset, 3, idx,
+                                       Type.INT, value);
+                    return 3;
+                }
+                case ByteOps.I2L:
+                case ByteOps.F2L:
+                case ByteOps.D2L: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.LONG);
+                    return 1;
+                }
+                case ByteOps.I2F:
+                case ByteOps.L2F:
+                case ByteOps.D2F: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.I2D:
+                case ByteOps.L2D:
+                case ByteOps.F2D: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.L2I:
+                case ByteOps.F2I:
+                case ByteOps.D2I:
+                case ByteOps.I2B:
+                case ByteOps.I2C:
+                case ByteOps.I2S:
+                case ByteOps.LCMP:
+                case ByteOps.FCMPL:
+                case ByteOps.FCMPG:
+                case ByteOps.DCMPL:
+                case ByteOps.DCMPG:
+                case ByteOps.ARRAYLENGTH: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.IFEQ:
+                case ByteOps.IFNE:
+                case ByteOps.IFLT:
+                case ByteOps.IFGE:
+                case ByteOps.IFGT:
+                case ByteOps.IFLE:
+                case ByteOps.IF_ICMPEQ:
+                case ByteOps.IF_ICMPNE:
+                case ByteOps.IF_ICMPLT:
+                case ByteOps.IF_ICMPGE:
+                case ByteOps.IF_ICMPGT:
+                case ByteOps.IF_ICMPLE:
+                case ByteOps.IF_ACMPEQ:
+                case ByteOps.IF_ACMPNE:
+                case ByteOps.GOTO:
+                case ByteOps.JSR:
+                case ByteOps.IFNULL:
+                case ByteOps.IFNONNULL: {
+                    int target = offset + bytes.getShort(offset + 1);
+                    visitor.visitBranch(opcode, offset, 3, target);
+                    return 3;
+                }
+                case ByteOps.RET: {
+                    int idx = bytes.getUnsignedByte(offset + 1);
+                    visitor.visitLocal(opcode, offset, 2, idx,
+                                       Type.RETURN_ADDRESS, 0);
+                    return 2;
+                }
+                case ByteOps.TABLESWITCH: {
+                    return parseTableswitch(offset, visitor);
+                }
+                case ByteOps.LOOKUPSWITCH: {
+                    return parseLookupswitch(offset, visitor);
+                }
+                case ByteOps.IRETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1, Type.INT);
+                    return 1;
+                }
+                case ByteOps.LRETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+                                        Type.LONG);
+                    return 1;
+                }
+                case ByteOps.FRETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+                                        Type.FLOAT);
+                    return 1;
+                }
+                case ByteOps.DRETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+                                        Type.DOUBLE);
+                    return 1;
+                }
+                case ByteOps.ARETURN: {
+                    visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+                                        Type.OBJECT);
+                    return 1;
+                }
+                case ByteOps.RETURN:
+                case ByteOps.ATHROW:
+                case ByteOps.MONITORENTER:
+                case ByteOps.MONITOREXIT: {
+                    visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+                    return 1;
+                }
+                case ByteOps.GETSTATIC:
+                case ByteOps.PUTSTATIC:
+                case ByteOps.GETFIELD:
+                case ByteOps.PUTFIELD:
+                case ByteOps.INVOKEVIRTUAL:
+                case ByteOps.INVOKESPECIAL:
+                case ByteOps.INVOKESTATIC:
+                case ByteOps.NEW:
+                case ByteOps.ANEWARRAY:
+                case ByteOps.CHECKCAST:
+                case ByteOps.INSTANCEOF: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    Constant cst = pool.get(idx);
+                    visitor.visitConstant(opcode, offset, 3, cst, 0);
+                    return 3;
+                }
+                case ByteOps.INVOKEINTERFACE: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    int count = bytes.getUnsignedByte(offset + 3);
+                    int expectZero = bytes.getUnsignedByte(offset + 4);
+                    Constant cst = pool.get(idx);
+                    visitor.visitConstant(opcode, offset, 5, cst,
+                                          count | (expectZero << 8));
+                    return 5;
+                }
+                case ByteOps.NEWARRAY: {
+                    return parseNewarray(offset, visitor);
+                }
+                case ByteOps.WIDE: {
+                    return parseWide(offset, visitor);
+                }
+                case ByteOps.MULTIANEWARRAY: {
+                    int idx = bytes.getUnsignedShort(offset + 1);
+                    int dimensions = bytes.getUnsignedByte(offset + 3);
+                    Constant cst = pool.get(idx);
+                    visitor.visitConstant(opcode, offset, 4, cst, dimensions);
+                    return 4;
+                }
+                case ByteOps.GOTO_W:
+                case ByteOps.JSR_W: {
+                    int target = offset + bytes.getInt(offset + 1);
+                    int newop =
+                        (opcode == ByteOps.GOTO_W) ? ByteOps.GOTO :
+                        ByteOps.JSR;
+                    visitor.visitBranch(newop, offset, 5, target);
+                    return 5;
+                }
+                default: {
+                    visitor.visitInvalid(opcode, offset, 1);
+                    return 1;
+                }
+            }
+        } catch (SimException ex) {
+            ex.addContext("...at bytecode offset " + Hex.u4(offset));
+            throw ex;
+        } catch (RuntimeException ex) {
+            SimException se = new SimException(ex);
+            se.addContext("...at bytecode offset " + Hex.u4(offset));
+            throw se;
+        }
+    }
+
+    /**
+     * Helper to deal with {@code tableswitch}.
+     *
+     * @param offset the offset to the {@code tableswitch} opcode itself
+     * @param visitor {@code non-null;} visitor to use
+     * @return instruction length, in bytes
+     */
+    private int parseTableswitch(int offset, Visitor visitor) {
+        int at = (offset + 4) & ~3; // "at" skips the padding.
+
+        // Collect the padding.
+        int padding = 0;
+        for (int i = offset + 1; i < at; i++) {
+            padding = (padding << 8) | bytes.getUnsignedByte(i);
+        }
+
+        int defaultTarget = offset + bytes.getInt(at);
+        int low = bytes.getInt(at + 4);
+        int high = bytes.getInt(at + 8);
+        int count = high - low + 1;
+        at += 12;
+
+        if (low > high) {
+            throw new SimException("low / high inversion");
+        }
+
+        SwitchList cases = new SwitchList(count);
+        for (int i = 0; i < count; i++) {
+            int target = offset + bytes.getInt(at);
+            at += 4;
+            cases.add(low + i, target);
+        }
+        cases.setDefaultTarget(defaultTarget);
+        cases.removeSuperfluousDefaults();
+        cases.setImmutable();
+
+        int length = at - offset;
+        visitor.visitSwitch(ByteOps.LOOKUPSWITCH, offset, length, cases,
+                            padding);
+
+        return length;
+    }
+
+    /**
+     * Helper to deal with {@code lookupswitch}.
+     *
+     * @param offset the offset to the {@code lookupswitch} opcode itself
+     * @param visitor {@code non-null;} visitor to use
+     * @return instruction length, in bytes
+     */
+    private int parseLookupswitch(int offset, Visitor visitor) {
+        int at = (offset + 4) & ~3; // "at" skips the padding.
+
+        // Collect the padding.
+        int padding = 0;
+        for (int i = offset + 1; i < at; i++) {
+            padding = (padding << 8) | bytes.getUnsignedByte(i);
+        }
+
+        int defaultTarget = offset + bytes.getInt(at);
+        int npairs = bytes.getInt(at + 4);
+        at += 8;
+
+        SwitchList cases = new SwitchList(npairs);
+        for (int i = 0; i < npairs; i++) {
+            int match = bytes.getInt(at);
+            int target = offset + bytes.getInt(at + 4);
+            at += 8;
+            cases.add(match, target);
+        }
+        cases.setDefaultTarget(defaultTarget);
+        cases.removeSuperfluousDefaults();
+        cases.setImmutable();
+
+        int length = at - offset;
+        visitor.visitSwitch(ByteOps.LOOKUPSWITCH, offset, length, cases,
+                            padding);
+
+        return length;
+    }
+
+    /**
+     * Helper to deal with {@code newarray}.
+     *
+     * @param offset the offset to the {@code newarray} opcode itself
+     * @param visitor {@code non-null;} visitor to use
+     * @return instruction length, in bytes
+     */
+    private int parseNewarray(int offset, Visitor visitor) {
+        int value = bytes.getUnsignedByte(offset + 1);
+        CstType type;
+        switch (value) {
+            case ByteOps.NEWARRAY_BOOLEAN: {
+                type = CstType.BOOLEAN_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_CHAR: {
+                type = CstType.CHAR_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_DOUBLE: {
+                type = CstType.DOUBLE_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_FLOAT: {
+                type = CstType.FLOAT_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_BYTE: {
+                type = CstType.BYTE_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_SHORT: {
+                type = CstType.SHORT_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_INT: {
+                type = CstType.INT_ARRAY;
+                break;
+            }
+            case ByteOps.NEWARRAY_LONG: {
+                type = CstType.LONG_ARRAY;
+                break;
+            }
+            default: {
+                throw new SimException("bad newarray code " +
+                        Hex.u1(value));
+            }
+        }
+
+        // Revisit the previous bytecode to find out the length of the array
+        int previousOffset = visitor.getPreviousOffset();
+        ConstantParserVisitor constantVisitor = new ConstantParserVisitor();
+        int arrayLength = 0;
+
+        /*
+         * For visitors that don't record the previous offset, -1 will be
+         * seen here
+         */
+        if (previousOffset >= 0) {
+            parseInstruction(previousOffset, constantVisitor);
+            if (constantVisitor.cst instanceof CstInteger &&
+                    constantVisitor.length + previousOffset == offset) {
+                arrayLength = constantVisitor.value;
+
+            }
+        }
+
+        /*
+         * Try to match the array initialization idiom. For example, if the
+         * subsequent code is initializing an int array, we are expecting the
+         * following pattern repeatedly:
+         *  dup
+         *  push index
+         *  push value
+         *  *astore
+         *
+         * where the index value will be incrimented sequentially from 0 up.
+         */
+        int nInit = 0;
+        int curOffset = offset+2;
+        int lastOffset = curOffset;
+        ArrayList<Constant> initVals = new ArrayList<Constant>();
+
+        if (arrayLength != 0) {
+            while (true) {
+                boolean punt = false;
+
+                // First check if the next bytecode is dup
+                int nextByte = bytes.getUnsignedByte(curOffset++);
+                if (nextByte != ByteOps.DUP)
+                    break;
+
+                // Next check if the expected array index is pushed to the stack
+                parseInstruction(curOffset, constantVisitor);
+                if (constantVisitor.length == 0 ||
+                        !(constantVisitor.cst instanceof CstInteger) ||
+                        constantVisitor.value != nInit)
+                    break;
+
+                // Next, fetch the init value and record it
+                curOffset += constantVisitor.length;
+
+                // Next find out what kind of constant is pushed onto the stack
+                parseInstruction(curOffset, constantVisitor);
+                if (constantVisitor.length == 0 ||
+                        !(constantVisitor.cst instanceof CstLiteralBits))
+                    break;
+
+                curOffset += constantVisitor.length;
+                initVals.add(constantVisitor.cst);
+
+                nextByte = bytes.getUnsignedByte(curOffset++);
+                // Now, check if the value is stored to the array properly
+                switch (value) {
+                    case ByteOps.NEWARRAY_BYTE:
+                    case ByteOps.NEWARRAY_BOOLEAN: {
+                        if (nextByte != ByteOps.BASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_CHAR: {
+                        if (nextByte != ByteOps.CASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_DOUBLE: {
+                        if (nextByte != ByteOps.DASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_FLOAT: {
+                        if (nextByte != ByteOps.FASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_SHORT: {
+                        if (nextByte != ByteOps.SASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_INT: {
+                        if (nextByte != ByteOps.IASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    case ByteOps.NEWARRAY_LONG: {
+                        if (nextByte != ByteOps.LASTORE) {
+                            punt = true;
+                        }
+                        break;
+                    }
+                    default:
+                        punt = true;
+                        break;
+                }
+                if (punt) {
+                    break;
+                }
+                lastOffset = curOffset;
+                nInit++;
+            }
+        }
+
+        /*
+         * For singleton arrays it is still more economical to
+         * generate the aput.
+         */
+        if (nInit < 2 || nInit != arrayLength) {
+            visitor.visitNewarray(offset, 2, type, null);
+            return 2;
+        } else {
+            visitor.visitNewarray(offset, lastOffset - offset, type, initVals);
+            return lastOffset - offset;
+        }
+     }
+
+
+    /**
+     * Helper to deal with {@code wide}.
+     *
+     * @param offset the offset to the {@code wide} opcode itself
+     * @param visitor {@code non-null;} visitor to use
+     * @return instruction length, in bytes
+     */
+    private int parseWide(int offset, Visitor visitor) {
+        int opcode = bytes.getUnsignedByte(offset + 1);
+        int idx = bytes.getUnsignedShort(offset + 2);
+        switch (opcode) {
+            case ByteOps.ILOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.INT, 0);
+                return 4;
+            }
+            case ByteOps.LLOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.LONG, 0);
+                return 4;
+            }
+            case ByteOps.FLOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.FLOAT, 0);
+                return 4;
+            }
+            case ByteOps.DLOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.DOUBLE, 0);
+                return 4;
+            }
+            case ByteOps.ALOAD: {
+                visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+                                   Type.OBJECT, 0);
+                return 4;
+            }
+            case ByteOps.ISTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.INT, 0);
+                return 4;
+            }
+            case ByteOps.LSTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.LONG, 0);
+                return 4;
+            }
+            case ByteOps.FSTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.FLOAT, 0);
+                return 4;
+            }
+            case ByteOps.DSTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.DOUBLE, 0);
+                return 4;
+            }
+            case ByteOps.ASTORE: {
+                visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+                                   Type.OBJECT, 0);
+                return 4;
+            }
+            case ByteOps.RET: {
+                visitor.visitLocal(opcode, offset, 4, idx,
+                                   Type.RETURN_ADDRESS, 0);
+                return 4;
+            }
+            case ByteOps.IINC: {
+                int value = bytes.getShort(offset + 4);
+                visitor.visitLocal(opcode, offset, 6, idx,
+                                   Type.INT, value);
+                return 6;
+            }
+            default: {
+                visitor.visitInvalid(ByteOps.WIDE, offset, 1);
+                return 1;
+            }
+        }
+    }
+
+    /**
+     * Instruction visitor interface.
+     */
+    public interface Visitor {
+        /**
+         * Visits an invalid instruction.
+         *
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         */
+        public void visitInvalid(int opcode, int offset, int length);
+
+        /**
+         * Visits an instruction which has no inline arguments
+         * (implicit or explicit).
+         *
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param type {@code non-null;} type the instruction operates on
+         */
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type);
+
+        /**
+         * Visits an instruction which has a local variable index argument.
+         *
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param idx the local variable index
+         * @param type {@code non-null;} the type of the accessed value
+         * @param value additional literal integer argument, if salient (i.e.,
+         * for {@code iinc})
+         */
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value);
+
+        /**
+         * Visits an instruction which has a (possibly synthetic)
+         * constant argument, and possibly also an
+         * additional literal integer argument. In the case of
+         * {@code multianewarray}, the argument is the count of
+         * dimensions. In the case of {@code invokeinterface},
+         * the argument is the parameter count or'ed with the
+         * should-be-zero value left-shifted by 8. In the case of entries
+         * of type {@code int}, the {@code value} field always
+         * holds the raw value (for convenience of clients).
+         *
+         * <p><b>Note:</b> In order to avoid giving it a barely-useful
+         * visitor all its own, {@code newarray} also uses this
+         * form, passing {@code value} as the array type code and
+         * {@code cst} as a {@link CstType} instance
+         * corresponding to the array type.</p>
+         *
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param cst {@code non-null;} the constant
+         * @param value additional literal integer argument, if salient
+         * (ignore if not)
+         */
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value);
+
+        /**
+         * Visits an instruction which has a branch target argument.
+         *
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param target the absolute (not relative) branch target
+         */
+        public void visitBranch(int opcode, int offset, int length,
+                int target);
+
+        /**
+         * Visits a switch instruction.
+         *
+         * @param opcode the opcode
+         * @param offset offset to the instruction
+         * @param length length of the instruction, in bytes
+         * @param cases {@code non-null;} list of (value, target) pairs, plus the
+         * default target
+         * @param padding the bytes found in the padding area (if any),
+         * packed
+         */
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding);
+
+        /**
+         * Visits a newarray instruction.
+         *
+         * @param offset   offset to the instruction
+         * @param length   length of the instruction, in bytes
+         * @param type {@code non-null;} the type of the array
+         * @param initVals {@code non-null;} list of bytecode offsets for init values
+         */
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initVals);
+
+        /**
+         * Set previous bytecode offset
+         * @param offset    offset of the previous fully parsed bytecode
+         */
+        public void setPreviousOffset(int offset);
+
+        /**
+         * Get previous bytecode offset
+         * @return return the recored offset of the previous bytecode
+         */
+        public int getPreviousOffset();
+    }
+
+    /**
+     * Base implementation of {@link Visitor}, which has empty method
+     * bodies for all methods.
+     */
+    public static class BaseVisitor implements Visitor {
+
+        /** offset of the previously parsed bytecode */
+        private int previousOffset;
+
+        BaseVisitor() {
+            previousOffset = -1;
+        }
+
+        /** {@inheritDoc} */
+        public void visitInvalid(int opcode, int offset, int length) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitBranch(int opcode, int offset, int length,
+                int target) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initValues) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void setPreviousOffset(int offset) {
+            previousOffset = offset;
+        }
+
+        /** {@inheritDoc} */
+        public int getPreviousOffset() {
+            return previousOffset;
+        }
+    }
+
+    /**
+     * Base implementation of {@link Visitor}, which has empty method
+     * bodies for all methods.
+     */
+    class ConstantParserVisitor extends BaseVisitor {
+        Constant cst;
+        int length;
+        int value;
+
+        /** Empty constructor */
+        ConstantParserVisitor() {
+        }
+
+        private void clear() {
+            length = 0;
+        }
+
+        /** {@inheritDoc} */
+        public void visitInvalid(int opcode, int offset, int length) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value) {
+            this.cst = cst;
+            this.length = length;
+            this.value = value;
+        }
+
+        /** {@inheritDoc} */
+        public void visitBranch(int opcode, int offset, int length,
+                int target) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initVals) {
+            clear();
+        }
+
+        /** {@inheritDoc} */
+        public void setPreviousOffset(int offset) {
+            // Intentionally left empty
+        }
+
+        /** {@inheritDoc} */
+        public int getPreviousOffset() {
+            // Intentionally left empty
+            return -1;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ConcreteMethod.java b/dx/src/com/android/dx/cf/code/ConcreteMethod.java
new file mode 100644
index 0000000..da6cff7
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ConcreteMethod.java
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.attrib.AttLineNumberTable;
+import com.android.dx.cf.attrib.AttLocalVariableTable;
+import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.ClassFile;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Container for all the giblets that make up a concrete Java bytecode method.
+ * It implements {@link Method}, so it provides all the original access
+ * (by delegation), but it also constructs and keeps useful versions of
+ * stuff extracted from the method's {@code Code} attribute.
+ */
+public final class ConcreteMethod implements Method {
+    /** {@code non-null;} method being wrapped */
+    private final Method method;
+
+    /**
+     * {@code null-ok;} the class's {@code SourceFile} attribute value,
+     * if any
+     */
+    private final CstUtf8 sourceFile;
+
+    /**
+     * whether the class that this method is part of is defined with
+     * {@code ACC_SUPER}
+     */
+    private final boolean accSuper;
+
+    /** {@code non-null;} the code attribute */
+    private final AttCode attCode;
+
+    /** {@code non-null;} line number list */
+    private final LineNumberList lineNumbers;
+
+    /** {@code non-null;} local variable list */
+    private final LocalVariableList localVariables;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method {@code non-null;} the method to be based on
+     * @param cf {@code non-null;} the class file that contains this method
+     * @param keepLines whether to keep the line number information
+     * (if any)
+     * @param keepLocals whether to keep the local variable
+     * information (if any)
+     */
+    public ConcreteMethod(Method method, ClassFile cf, boolean keepLines,
+                          boolean keepLocals) {
+        this.method = method;
+        this.accSuper = (cf.getAccessFlags() & AccessFlags.ACC_SUPER) != 0;
+        this.sourceFile = cf.getSourceFile();
+
+        AttributeList attribs = method.getAttributes();
+        this.attCode = (AttCode) attribs.findFirst(AttCode.ATTRIBUTE_NAME);
+
+        AttributeList codeAttribs = attCode.getAttributes();
+
+        /*
+         * Combine all LineNumberTable attributes into one, with the
+         * combined result saved into the instance. The following code
+         * isn't particularly efficient for doing merges, but as far
+         * as I know, this situation rarely occurs "in the
+         * wild," so there's not much point in optimizing for it.
+         */
+        LineNumberList lineNumbers = LineNumberList.EMPTY;
+        if (keepLines) {
+            for (AttLineNumberTable lnt = (AttLineNumberTable)
+                     codeAttribs.findFirst(AttLineNumberTable.ATTRIBUTE_NAME);
+                 lnt != null;
+                 lnt = (AttLineNumberTable) codeAttribs.findNext(lnt)) {
+                lineNumbers = LineNumberList.concat(lineNumbers,
+                        lnt.getLineNumbers());
+            }
+        }
+        this.lineNumbers = lineNumbers;
+
+        LocalVariableList localVariables = LocalVariableList.EMPTY;
+        if (keepLocals) {
+            /*
+             * Do likewise (and with the same caveat) for
+             * LocalVariableTable and LocalVariableTypeTable attributes.
+             * This combines both of these kinds of attribute into a
+             * single LocalVariableList.
+             */
+            for (AttLocalVariableTable lvt = (AttLocalVariableTable)
+                     codeAttribs.findFirst(
+                             AttLocalVariableTable.ATTRIBUTE_NAME);
+                 lvt != null;
+                 lvt = (AttLocalVariableTable) codeAttribs.findNext(lvt)) {
+                localVariables =
+                    LocalVariableList.concat(localVariables,
+                            lvt.getLocalVariables());
+            }
+
+            LocalVariableList typeList = LocalVariableList.EMPTY;
+            for (AttLocalVariableTypeTable lvtt = (AttLocalVariableTypeTable)
+                     codeAttribs.findFirst(
+                             AttLocalVariableTypeTable.ATTRIBUTE_NAME);
+                 lvtt != null;
+                 lvtt =
+                     (AttLocalVariableTypeTable) codeAttribs.findNext(lvtt)) {
+                typeList =
+                    LocalVariableList.concat(typeList,
+                            lvtt.getLocalVariables());
+            }
+
+            if (typeList.size() != 0) {
+                localVariables =
+                    LocalVariableList.mergeDescriptorsAndSignatures(
+                            localVariables, typeList);
+            }
+        }
+        this.localVariables = localVariables;
+    }
+
+    /** {@inheritDoc} */
+    public CstNat getNat() {
+        return method.getNat();
+    }
+
+    /** {@inheritDoc} */
+    public CstUtf8 getName() {
+        return method.getName();
+    }
+
+    /** {@inheritDoc} */
+    public CstUtf8 getDescriptor() {
+        return method.getDescriptor();
+    }
+
+    /** {@inheritDoc} */
+    public int getAccessFlags() {
+        return method.getAccessFlags();
+    }
+
+    /** {@inheritDoc} */
+    public AttributeList getAttributes() {
+        return method.getAttributes();
+    }
+
+    /** {@inheritDoc} */
+    public CstType getDefiningClass() {
+        return method.getDefiningClass();
+    }
+
+    /** {@inheritDoc} */
+    public Prototype getEffectiveDescriptor() {
+        return method.getEffectiveDescriptor();
+    }
+
+    /**
+     * Gets whether the class that this method is part of is defined with
+     * {@code ACC_SUPER}.
+     *
+     * @return the {@code ACC_SUPER} value
+     */
+    public boolean getAccSuper() {
+        return accSuper;
+    }
+
+    /**
+     * Gets the maximum stack size.
+     *
+     * @return {@code >= 0;} the maximum stack size
+     */
+    public int getMaxStack() {
+        return attCode.getMaxStack();
+    }
+
+    /**
+     * Gets the number of locals.
+     *
+     * @return {@code >= 0;} the number of locals
+     */
+    public int getMaxLocals() {
+        return attCode.getMaxLocals();
+    }
+
+    /**
+     * Gets the bytecode array.
+     *
+     * @return {@code non-null;} the bytecode array
+     */
+    public BytecodeArray getCode() {
+        return attCode.getCode();
+    }
+
+    /**
+     * Gets the exception table.
+     *
+     * @return {@code non-null;} the exception table
+     */
+    public ByteCatchList getCatches() {
+        return attCode.getCatches();
+    }
+
+    /**
+     * Gets the line number list.
+     *
+     * @return {@code non-null;} the line number list
+     */
+    public LineNumberList getLineNumbers() {
+        return lineNumbers;
+    }
+
+    /**
+     * Gets the local variable list.
+     *
+     * @return {@code non-null;} the local variable list
+     */
+    public LocalVariableList getLocalVariables() {
+        return localVariables;
+    }
+
+    /**
+     * Returns a {@link SourcePosition} instance corresponding to the
+     * given bytecode offset.
+     *
+     * @param offset {@code >= 0;} the bytecode offset
+     * @return {@code non-null;} an appropriate instance
+     */
+    public SourcePosition makeSourcePosistion(int offset) {
+        return new SourcePosition(sourceFile, offset,
+                                  lineNumbers.pcToLine(offset));
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ExecutionStack.java b/dx/src/com/android/dx/cf/code/ExecutionStack.java
new file mode 100644
index 0000000..8f5b528
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ExecutionStack.java
@@ -0,0 +1,305 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Representation of a Java method execution stack.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public final class ExecutionStack extends MutabilityControl {
+    /** {@code non-null;} array of stack contents */
+    private final TypeBearer[] stack;
+
+    /**
+     * {@code >= 0;} stack pointer (points one past the end) / current stack
+     * size
+     */
+    private int stackPtr;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param maxStack {@code >= 0;} the maximum size of the stack for this
+     * instance
+     */
+    public ExecutionStack(int maxStack) {
+        super(maxStack != 0);
+        stack = new TypeBearer[maxStack];
+        stackPtr = 0;
+    }
+
+    /**
+     * Makes and returns a mutable copy of this instance.
+     *
+     * @return {@code non-null;} the copy
+     */
+    public ExecutionStack copy() {
+        ExecutionStack result = new ExecutionStack(stack.length);
+
+        System.arraycopy(stack, 0, result.stack, 0, stack.length);
+        result.stackPtr = stackPtr;
+
+        return result;
+    }
+
+    /**
+     * Annotates (adds context to) the given exception with information
+     * about this instance.
+     *
+     * @param ex {@code non-null;} the exception to annotate
+     */
+    public void annotate(ExceptionWithContext ex) {
+        int limit = stackPtr - 1;
+
+        for (int i = 0; i <= limit; i++) {
+            String idx = (i == limit) ? "top0" : Hex.u2(limit - i);
+
+            ex.addContext("stack[" + idx + "]: " +
+                          stackElementString(stack[i]));
+        }
+    }
+
+    /**
+     * Replaces all the occurrences of the given uninitialized type in
+     * this stack with its initialized equivalent.
+     *
+     * @param type {@code non-null;} type to replace
+     */
+    public void makeInitialized(Type type) {
+        if (stackPtr == 0) {
+            // We have to check for this before checking for immutability.
+            return;
+        }
+
+        throwIfImmutable();
+
+        Type initializedType = type.getInitializedType();
+
+        for (int i = 0; i < stackPtr; i++) {
+            if (stack[i] == type) {
+                stack[i] = initializedType;
+            }
+        }
+    }
+
+    /**
+     * Gets the maximum stack size for this instance.
+     *
+     * @return {@code >= 0;} the max stack size
+     */
+    public int getMaxStack() {
+        return stack.length;
+    }
+
+    /**
+     * Gets the current stack size.
+     *
+     * @return {@code >= 0, < getMaxStack();} the current stack size
+     */
+    public int size() {
+        return stackPtr;
+    }
+
+    /**
+     * Clears the stack. (That is, this method pops everything off.)
+     */
+    public void clear() {
+        throwIfImmutable();
+
+        for (int i = 0; i < stackPtr; i++) {
+            stack[i] = null;
+        }
+
+        stackPtr = 0;
+    }
+
+    /**
+     * Pushes a value of the given type onto the stack.
+     *
+     * @param type {@code non-null;} type of the value
+     * @throws SimException thrown if there is insufficient room on the
+     * stack for the value
+     */
+    public void push(TypeBearer type) {
+        throwIfImmutable();
+
+        int category;
+
+        try {
+            type = type.getFrameType();
+            category = type.getType().getCategory();
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("type == null");
+        }
+
+        if ((stackPtr + category) > stack.length) {
+            throwSimException("overflow");
+            return;
+        }
+
+        if (category == 2) {
+            stack[stackPtr] = null;
+            stackPtr++;
+        }
+
+        stack[stackPtr] = type;
+        stackPtr++;
+    }
+
+    /**
+     * Peeks at the {@code n}th element down from the top of the stack.
+     * {@code n == 0} means to peek at the top of the stack. Note that
+     * this will return {@code null} if the indicated element is the
+     * deeper half of a category-2 value.
+     *
+     * @param n {@code >= 0;} which element to peek at
+     * @return {@code null-ok;} the type of value stored at that element
+     * @throws SimException thrown if {@code n >= size()}
+     */
+    public TypeBearer peek(int n) {
+        if (n < 0) {
+            throw new IllegalArgumentException("n < 0");
+        }
+
+        if (n >= stackPtr) {
+            return throwSimException("underflow");
+        }
+
+        return stack[stackPtr - n - 1];
+    }
+
+    /**
+     * Peeks at the {@code n}th element down from the top of the
+     * stack, returning the type per se, as opposed to the
+     * <i>type-bearer</i>.  This method is just a convenient shorthand
+     * for {@code peek(n).getType()}.
+     *
+     * @see #peek
+     */
+    public Type peekType(int n) {
+        return peek(n).getType();
+    }
+
+    /**
+     * Pops the top element off of the stack.
+     *
+     * @return {@code non-null;} the type formerly on the top of the stack
+     * @throws SimException thrown if the stack is empty
+     */
+    public TypeBearer pop() {
+        throwIfImmutable();
+
+        TypeBearer result = peek(0);
+
+        stack[stackPtr - 1] = null;
+        stackPtr -= result.getType().getCategory();
+
+        return result;
+    }
+
+    /**
+     * Changes an element already on a stack. This method is useful in limited
+     * contexts, particularly when merging two instances. As such, it places
+     * the following restriction on its behavior: You may only replace
+     * values with other values of the same category.
+     *
+     * @param n {@code >= 0;} which element to change, where {@code 0} is
+     * the top element of the stack
+     * @param type {@code non-null;} type of the new value
+     * @throws SimException thrown if {@code n >= size()} or
+     * the action is otherwise prohibited
+     */
+    public void change(int n, TypeBearer type) {
+        throwIfImmutable();
+
+        try {
+            type = type.getFrameType();
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("type == null");
+        }
+
+        int idx = stackPtr - n - 1;
+        TypeBearer orig = stack[idx];
+
+        if ((orig == null) ||
+            (orig.getType().getCategory() != type.getType().getCategory())) {
+            throwSimException("incompatible substitution: " +
+                              stackElementString(orig) + " -> " +
+                              stackElementString(type));
+        }
+
+        stack[idx] = type;
+    }
+
+    /**
+     * Merges this stack with another stack. A new instance is returned if
+     * this merge results in a change. If no change results, this instance is
+     * returned.  See {@link Merger#mergeStack(ExecutionStack,ExecutionStack)
+     * Merger.mergeStack()}
+     *
+     * @param other {@code non-null;} a stack to merge with
+     * @return {@code non-null;} the result of the merge
+     */
+    public ExecutionStack merge(ExecutionStack other) {
+        try {
+            return Merger.mergeStack(this, other);
+        } catch (SimException ex) {
+            ex.addContext("underlay stack:");
+            this.annotate(ex);
+            ex.addContext("overlay stack:");
+            other.annotate(ex);
+            throw ex;
+        }
+    }
+
+    /**
+     * Gets the string form for a stack element. This is the same as
+     * {@code toString()} except that {@code null} is converted
+     * to {@code "<invalid>"}.
+     *
+     * @param type {@code null-ok;} the stack element
+     * @return {@code non-null;} the string form
+     */
+    private static String stackElementString(TypeBearer type) {
+        if (type == null) {
+            return "<invalid>";
+        }
+
+        return type.toString();
+    }
+
+    /**
+     * Throws a properly-formatted exception.
+     *
+     * @param msg {@code non-null;} useful message
+     * @return never (keeps compiler happy)
+     */
+    private static TypeBearer throwSimException(String msg) {
+        throw new SimException("stack: " + msg);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/Frame.java b/dx/src/com/android/dx/cf/code/Frame.java
new file mode 100644
index 0000000..002a4fb
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Frame.java
@@ -0,0 +1,415 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.IntList;
+
+/**
+ * Representation of a Java method execution frame. A frame consists
+ * of a set of locals and a value stack, and it can be told to act on
+ * them to load and store values between them and an "arguments /
+ * results" area.
+ */
+public final class Frame {
+    /** {@code non-null;} the locals */
+    private final LocalsArray locals;
+
+    /** {@code non-null;} the stack */
+    private final ExecutionStack stack;
+
+    /** {@code null-ok;} stack of labels of subroutines that this block is nested in */
+    private final IntList subroutines;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param locals {@code non-null;} the locals array to use
+     * @param stack {@code non-null;} the execution stack to use
+     */
+    private Frame(LocalsArray locals, ExecutionStack stack) {
+        this(locals, stack, IntList.EMPTY);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param locals {@code non-null;} the locals array to use
+     * @param stack {@code non-null;} the execution stack to use
+     * @param subroutines {@code non-null;} list of subroutine start labels for
+     * subroutines this frame is nested in
+     */
+    private Frame(LocalsArray locals,
+            ExecutionStack stack, IntList subroutines) {
+        if (locals == null) {
+            throw new NullPointerException("locals == null");
+        }
+
+        if (stack == null) {
+            throw new NullPointerException("stack == null");
+        }
+
+        subroutines.throwIfMutable();
+
+        this.locals = locals;
+        this.stack = stack;
+        this.subroutines = subroutines;
+    }
+
+    /**
+     * Constructs an instance. The locals array initially consists of
+     * all-uninitialized values (represented as {@code null}s) and
+     * the stack starts out empty.
+     *
+     * @param maxLocals {@code >= 0;} the maximum number of locals this instance
+     * can refer to
+     * @param maxStack {@code >= 0;} the maximum size of the stack for this
+     * instance
+     */
+    public Frame(int maxLocals, int maxStack) {
+        this(new OneLocalsArray(maxLocals), new ExecutionStack(maxStack));
+    }
+
+    /**
+     * Makes and returns a mutable copy of this instance. The copy
+     * contains copies of the locals and stack (that is, it doesn't
+     * share them with the original).
+     *
+     * @return {@code non-null;} the copy
+     */
+    public Frame copy() {
+        return new Frame(locals.copy(), stack.copy(), subroutines);
+    }
+
+    /**
+     * Makes this instance immutable.
+     */
+    public void setImmutable() {
+        locals.setImmutable();
+        stack.setImmutable();
+        // "subroutines" is always immutable
+    }
+
+    /**
+     * Replaces all the occurrences of the given uninitialized type in
+     * this frame with its initialized equivalent.
+     *
+     * @param type {@code non-null;} type to replace
+     */
+    public void makeInitialized(Type type) {
+        locals.makeInitialized(type);
+        stack.makeInitialized(type);
+    }
+
+    /**
+     * Gets the locals array for this instance.
+     *
+     * @return {@code non-null;} the locals array
+     */
+    public LocalsArray getLocals() {
+        return locals;
+    }
+
+    /**
+     * Gets the execution stack for this instance.
+     *
+     * @return {@code non-null;} the execution stack
+     */
+    public ExecutionStack getStack() {
+        return stack;
+    }
+
+    /**
+     * Returns the largest subroutine nesting this block may be in. An
+     * empty list is returned if this block is not in any subroutine.
+     * Subroutines are identified by the label of their start block. The
+     * list is ordered such that the deepest nesting (the actual subroutine
+     * this block is in) is the last label in the list.
+     *
+     * @return {@code non-null;} list as noted above
+     */
+    public IntList getSubroutines() {
+        return subroutines;
+    }
+
+    /**
+     * Initialize this frame with the method's parameters. Used for the first
+     * frame.
+     *
+     * @param params Type list of method parameters.
+     */
+    public void initializeWithParameters(StdTypeList params) {
+        int at = 0;
+        int sz = params.size();
+
+        for (int i = 0; i < sz; i++) {
+             Type one = params.get(i);
+             locals.set(at, one);
+             at += one.getCategory();
+        }
+    }
+
+    /**
+     * Returns a Frame instance representing the frame state that should
+     * be used when returning from a subroutine. The stack state of all
+     * subroutine invocations is identical, but the locals state may differ.
+     *
+     * @param startLabel {@code >=0;} The label of the returning subroutine's
+     * start block
+     * @param subLabel {@code >=0;} A calling label of a subroutine
+     * @return {@code null-ok;} an appropriatly-constructed instance, or null
+     * if label is not in the set
+     */
+    public Frame subFrameForLabel(int startLabel, int subLabel) {
+        LocalsArray subLocals = null;
+
+        if (locals instanceof LocalsArraySet) {
+            subLocals = ((LocalsArraySet)locals).subArrayForLabel(subLabel);
+        }
+
+        IntList newSubroutines;
+        try {
+            newSubroutines = subroutines.mutableCopy();
+
+            if (newSubroutines.pop() != startLabel) {
+                throw new RuntimeException("returning from invalid subroutine");
+            }
+            newSubroutines.setImmutable();
+        } catch (IndexOutOfBoundsException ex) {
+            throw new RuntimeException("returning from invalid subroutine");
+        } catch (NullPointerException ex) {
+            throw new NullPointerException("can't return from non-subroutine");
+        }
+
+        return (subLocals == null) ? null
+                : new Frame(subLocals, stack, newSubroutines);
+    }
+
+    /**
+     * Merges two frames. If the merged result is the same as this frame,
+     * then this instance is returned.
+     *
+     * @param other {@code non-null;} another frame
+     * @return {@code non-null;} the result of merging the two frames
+     */
+    public Frame mergeWith(Frame other) {
+        LocalsArray resultLocals;
+        ExecutionStack resultStack;
+        IntList resultSubroutines;
+
+        resultLocals = getLocals().merge(other.getLocals());
+        resultStack = getStack().merge(other.getStack());
+        resultSubroutines = mergeSubroutineLists(other.subroutines);
+
+        resultLocals = adjustLocalsForSubroutines(
+                resultLocals, resultSubroutines);
+
+        if ((resultLocals == getLocals())
+                && (resultStack == getStack())
+                && subroutines == resultSubroutines) {
+            return this;
+        }
+
+        return new Frame(resultLocals, resultStack, resultSubroutines);
+    }
+
+    /**
+     * Merges this frame's subroutine lists with another. The result
+     * is the deepest common nesting (effectively, the common prefix of the
+     * two lists).
+     *
+     * @param otherSubroutines label list of subroutine start blocks, from
+     * least-nested to most-nested.
+     * @return {@code non-null;} merged subroutine nest list as described above
+     */
+    private IntList mergeSubroutineLists(IntList otherSubroutines) {
+        if (subroutines.equals(otherSubroutines)) {
+            return subroutines;
+        }
+
+        IntList resultSubroutines = new IntList();
+
+        int szSubroutines = subroutines.size();
+        int szOthers = otherSubroutines.size();
+        for (int i = 0; i < szSubroutines && i < szOthers
+                && (subroutines.get(i) == otherSubroutines.get(i)); i++) {
+            resultSubroutines.add(i);
+        }
+
+        resultSubroutines.setImmutable();
+
+        return resultSubroutines;
+    }
+
+    /**
+     * Adjusts a locals array to account for a merged subroutines list.
+     * If a frame merge results in, effectively, a subroutine return through
+     * a throw then the current locals will be a LocalsArraySet that will
+     * need to be trimmed of all OneLocalsArray elements that relevent to
+     * the subroutine that is returning.
+     *
+     * @param locals {@code non-null;} LocalsArray from before a merge
+     * @param subroutines {@code non-null;} a label list of subroutine start blocks
+     * representing the subroutine nesting of the block being merged into.
+     * @return {@code non-null;} locals set appropriate for merge
+     */
+    private static LocalsArray adjustLocalsForSubroutines(
+            LocalsArray locals, IntList subroutines) {
+        if (! (locals instanceof LocalsArraySet)) {
+            // nothing to see here
+            return locals;
+        }
+
+        LocalsArraySet laSet = (LocalsArraySet)locals;
+
+        if (subroutines.size() == 0) {
+            /*
+             * We've merged from a subroutine context to a non-subroutine
+             * context, likely via a throw. Our successor will only need
+             * to consider the primary locals state, not the state of
+             * all possible subroutine paths.
+             */
+
+            return laSet.getPrimary();
+        }
+
+        /*
+         * It's unclear to me if the locals set needs to be trimmed here.
+         * If it does, then I believe it is all of the calling blocks
+         * in the subroutine at the end of "subroutines" passed into
+         * this method that should be removed.
+         */
+        return laSet;
+    }
+
+    /**
+     * Merges this frame with the frame of a subroutine caller at
+     * {@code predLabel}. Only called on the frame at the first
+     * block of a subroutine.
+     *
+     * @param other {@code non-null;} another frame
+     * @param subLabel label of subroutine start block
+     * @param predLabel label of calling block
+     * @return {@code non-null;} the result of merging the two frames
+     */
+    public Frame mergeWithSubroutineCaller(Frame other, int subLabel,
+            int predLabel) {
+        LocalsArray resultLocals;
+        ExecutionStack resultStack;
+
+        resultLocals = getLocals().mergeWithSubroutineCaller(
+                other.getLocals(), predLabel);
+        resultStack = getStack().merge(other.getStack());
+
+        IntList newOtherSubroutines = other.subroutines.mutableCopy();
+        newOtherSubroutines.add(subLabel);
+        newOtherSubroutines.setImmutable();
+
+        if ((resultLocals == getLocals())
+                && (resultStack == getStack())
+                && subroutines.equals(newOtherSubroutines)) {
+            return this;
+        }
+
+        IntList resultSubroutines;
+
+        if (subroutines.equals(newOtherSubroutines)) {
+            resultSubroutines = subroutines;
+        } else {
+            /*
+             * The new subroutines list should be the deepest of the two
+             * lists being merged, but the postfix of the resultant list
+             * must be equal to the shorter list.
+             */
+            IntList nonResultSubroutines;
+
+            if (subroutines.size() > newOtherSubroutines.size()) {
+                resultSubroutines = subroutines;
+                nonResultSubroutines = newOtherSubroutines;
+            } else {
+                resultSubroutines = newOtherSubroutines;
+                nonResultSubroutines = subroutines;
+            }
+
+            int szResult = resultSubroutines.size();
+            int szNonResult = nonResultSubroutines.size();
+
+            for (int i = szNonResult - 1; i >=0; i-- ) {
+                if (nonResultSubroutines.get(i)
+                        != resultSubroutines.get(
+                        i + (szResult - szNonResult))) {
+                    throw new
+                            RuntimeException("Incompatible merged subroutines");
+                }
+            }
+
+        }
+
+        return new Frame(resultLocals, resultStack, resultSubroutines);
+    }
+
+    /**
+     * Makes a frame for a subroutine start block, given that this is the
+     * ending frame of one of the subroutine's calling blocks. Subroutine
+     * calls may be nested and thus may have nested locals state, so we
+     * start with an initial state as seen by the subroutine, but keep track
+     * of the individual locals states that will be expected when the individual
+     * subroutine calls return.
+     *
+     * @param subLabel label of subroutine start block
+     * @param callerLabel {@code >=0;} label of the caller block where this frame
+     * came from.
+     * @return a new instance to begin a called subroutine.
+     */
+    public Frame makeNewSubroutineStartFrame(int subLabel, int callerLabel) {
+        IntList newSubroutines = subroutines.mutableCopy();
+        newSubroutines.add(subLabel);
+        Frame newFrame = new Frame(locals.getPrimary(), stack,
+                IntList.makeImmutable(subLabel));
+        return newFrame.mergeWithSubroutineCaller(this, subLabel, callerLabel);
+    }
+
+    /**
+     * Makes a new frame for an exception handler block invoked from this
+     * frame.
+     *
+     * @param exceptionClass exception that the handler block will handle
+     * @return new frame
+     */
+    public Frame makeExceptionHandlerStartFrame(CstType exceptionClass) {
+        ExecutionStack newStack = getStack().copy();
+
+        newStack.clear();
+        newStack.push(exceptionClass);
+
+        return new Frame(getLocals(), newStack, subroutines);
+    }
+
+    /**
+     * Annotates (adds context to) the given exception with information
+     * about this frame.
+     *
+     * @param ex {@code non-null;} the exception to annotate
+     */
+    public void annotate(ExceptionWithContext ex) {
+        locals.annotate(ex);
+        stack.annotate(ex);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/LineNumberList.java b/dx/src/com/android/dx/cf/code/LineNumberList.java
new file mode 100644
index 0000000..f54f8b5
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LineNumberList.java
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "line number" entries, which are the contents of
+ * {@code LineNumberTable} attributes.
+ */
+public final class LineNumberList extends FixedSizeList {
+    /** {@code non-null;} zero-size instance */
+    public static final LineNumberList EMPTY = new LineNumberList(0);
+
+    /**
+     * Returns an instance which is the concatenation of the two given
+     * instances.
+     *
+     * @param list1 {@code non-null;} first instance
+     * @param list2 {@code non-null;} second instance
+     * @return {@code non-null;} combined instance
+     */
+    public static LineNumberList concat(LineNumberList list1,
+                                        LineNumberList list2) {
+        if (list1 == EMPTY) {
+            // easy case
+            return list2;
+        }
+
+        int sz1 = list1.size();
+        int sz2 = list2.size();
+        LineNumberList result = new LineNumberList(sz1 + sz2);
+
+        for (int i = 0; i < sz1; i++) {
+            result.set(i, list1.get(i));
+        }
+
+        for (int i = 0; i < sz2; i++) {
+            result.set(sz1 + i, list2.get(i));
+        }
+
+        return result;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param count the number of elements to be in the list
+     */
+    public LineNumberList(int count) {
+        super(count);
+    }
+
+    /**
+     * Gets the indicated item.
+     *
+     * @param n {@code >= 0;} which item
+     * @return {@code null-ok;} the indicated item
+     */
+    public Item get(int n) {
+        return (Item) get0(n);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param item {@code non-null;} the item
+     */
+    public void set(int n, Item item) {
+        if (item == null) {
+            throw new NullPointerException("item == null");
+        }
+
+        set0(n, item);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param startPc {@code >= 0;} start pc of this item
+     * @param lineNumber {@code >= 0;} corresponding line number
+     */
+    public void set(int n, int startPc, int lineNumber) {
+        set0(n, new Item(startPc, lineNumber));
+    }
+
+    /**
+     * Gets the line number associated with the given address.
+     *
+     * @param pc {@code >= 0;} the address to look up
+     * @return {@code >= -1;} the associated line number, or {@code -1} if
+     * none is known
+     */
+    public int pcToLine(int pc) {
+        /*
+         * Line number entries don't have to appear in any particular
+         * order, so we have to do a linear search. TODO: If
+         * this turns out to be a bottleneck, consider sorting the
+         * list prior to use.
+         */
+        int sz = size();
+        int bestPc = -1;
+        int bestLine = -1;
+
+        for (int i = 0; i < sz; i++) {
+            Item one = get(i);
+            int onePc = one.getStartPc();
+            if ((onePc <= pc) && (onePc > bestPc)) {
+                bestPc = onePc;
+                bestLine = one.getLineNumber();
+                if (bestPc == pc) {
+                    // We can't do better than this
+                    break;
+                }
+            }
+        }
+
+        return bestLine;
+    }
+
+    /**
+     * Item in a line number table.
+     */
+    public static class Item {
+        /** {@code >= 0;} start pc of this item */
+        private final int startPc;
+
+        /** {@code >= 0;} corresponding line number */
+        private final int lineNumber;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param startPc {@code >= 0;} start pc of this item
+         * @param lineNumber {@code >= 0;} corresponding line number
+         */
+        public Item(int startPc, int lineNumber) {
+            if (startPc < 0) {
+                throw new IllegalArgumentException("startPc < 0");
+            }
+
+            if (lineNumber < 0) {
+                throw new IllegalArgumentException("lineNumber < 0");
+            }
+
+            this.startPc = startPc;
+            this.lineNumber = lineNumber;
+        }
+
+        /**
+         * Gets the start pc of this item.
+         *
+         * @return the start pc
+         */
+        public int getStartPc() {
+            return startPc;
+        }
+
+        /**
+         * Gets the line number of this item.
+         *
+         * @return the line number
+         */
+        public int getLineNumber() {
+            return lineNumber;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalVariableList.java b/dx/src/com/android/dx/cf/code/LocalVariableList.java
new file mode 100644
index 0000000..dbf8ba2
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalVariableList.java
@@ -0,0 +1,373 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "local variable" entries, which are the contents of
+ * {@code LocalVariableTable} and {@code LocalVariableTypeTable}
+ * attributes, as well as combinations of the two.
+ */
+public final class LocalVariableList extends FixedSizeList {
+    /** {@code non-null;} zero-size instance */
+    public static final LocalVariableList EMPTY = new LocalVariableList(0);
+
+    /**
+     * Returns an instance which is the concatenation of the two given
+     * instances. The result is immutable.
+     *
+     * @param list1 {@code non-null;} first instance
+     * @param list2 {@code non-null;} second instance
+     * @return {@code non-null;} combined instance
+     */
+    public static LocalVariableList concat(LocalVariableList list1,
+                                           LocalVariableList list2) {
+        if (list1 == EMPTY) {
+            // easy case
+            return list2;
+        }
+
+        int sz1 = list1.size();
+        int sz2 = list2.size();
+        LocalVariableList result = new LocalVariableList(sz1 + sz2);
+
+        for (int i = 0; i < sz1; i++) {
+            result.set(i, list1.get(i));
+        }
+
+        for (int i = 0; i < sz2; i++) {
+            result.set(sz1 + i, list2.get(i));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Returns an instance which is the result of merging the two
+     * given instances, where one instance should have only type
+     * descriptors and the other only type signatures. The merged
+     * result is identical to the one with descriptors, except that
+     * any element whose {name, index, start, length} matches an
+     * element in the signature list gets augmented with the
+     * corresponding signature. The result is immutable.
+     *
+     * @param descriptorList {@code non-null;} list with descriptors
+     * @param signatureList {@code non-null;} list with signatures
+     * @return {@code non-null;} the merged result
+     */
+    public static LocalVariableList mergeDescriptorsAndSignatures(
+            LocalVariableList descriptorList,
+            LocalVariableList signatureList) {
+        int descriptorSize = descriptorList.size();
+        LocalVariableList result = new LocalVariableList(descriptorSize);
+
+        for (int i = 0; i < descriptorSize; i++) {
+            Item item = descriptorList.get(i);
+            Item signatureItem = signatureList.itemToLocal(item);
+            if (signatureItem != null) {
+                CstUtf8 signature = signatureItem.getSignature();
+                item = item.withSignature(signature);
+            }
+            result.set(i, item);
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param count the number of elements to be in the list
+     */
+    public LocalVariableList(int count) {
+        super(count);
+    }
+
+    /**
+     * Gets the indicated item.
+     *
+     * @param n {@code >= 0;} which item
+     * @return {@code null-ok;} the indicated item
+     */
+    public Item get(int n) {
+        return (Item) get0(n);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param item {@code non-null;} the item
+     */
+    public void set(int n, Item item) {
+        if (item == null) {
+            throw new NullPointerException("item == null");
+        }
+
+        set0(n, item);
+    }
+
+    /**
+     * Sets the item at the given index.
+     *
+     * <p><b>Note:</b> At least one of {@code descriptor} or
+     * {@code signature} must be passed as non-null.</p>
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param startPc {@code >= 0;} the start pc of this variable's scope
+     * @param length {@code >= 0;} the length (in bytecodes) of this variable's
+     * scope
+     * @param name {@code non-null;} the variable's name
+     * @param descriptor {@code null-ok;} the variable's type descriptor
+     * @param signature {@code null-ok;} the variable's type signature
+     * @param index {@code >= 0;} the variable's local index
+     */
+    public void set(int n, int startPc, int length, CstUtf8 name,
+            CstUtf8 descriptor, CstUtf8 signature, int index) {
+        set0(n, new Item(startPc, length, name, descriptor, signature, index));
+    }
+
+    /**
+     * Gets the local variable information in this instance which matches
+     * the given {@link com.android.dx.cf.code.LocalVariableList.Item}
+     * in all respects but the type descriptor and signature, if any.
+     *
+     * @param item {@code non-null;} local variable information to match
+     * @return {@code null-ok;} the corresponding local variable information stored
+     * in this instance, or {@code null} if there is no matching
+     * information
+     */
+    public Item itemToLocal(Item item) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            Item one = (Item) get0(i);
+
+            if ((one != null) && one.matchesAllButType(item)) {
+                return one;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the local variable information associated with a given address
+     * and local index, if any. <b>Note:</b> In standard classfiles, a
+     * variable's start point is listed as the address of the instruction
+     * <i>just past</i> the one that sets the variable.
+     *
+     * @param pc {@code >= 0;} the address to look up
+     * @param index {@code >= 0;} the local variable index
+     * @return {@code null-ok;} the associated local variable information, or
+     * {@code null} if none is known
+     */
+    public Item pcAndIndexToLocal(int pc, int index) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            Item one = (Item) get0(i);
+
+            if ((one != null) && one.matchesPcAndIndex(pc, index)) {
+                return one;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Item in a local variable table.
+     */
+    public static class Item {
+        /** {@code >= 0;} the start pc of this variable's scope */
+        private final int startPc;
+
+        /** {@code >= 0;} the length (in bytecodes) of this variable's scope */
+        private final int length;
+
+        /** {@code non-null;} the variable's name */
+        private final CstUtf8 name;
+
+        /** {@code null-ok;} the variable's type descriptor */
+        private final CstUtf8 descriptor;
+
+        /** {@code null-ok;} the variable's type signature */
+        private final CstUtf8 signature;
+
+        /** {@code >= 0;} the variable's local index */
+        private final int index;
+
+        /**
+         * Constructs an instance.
+         *
+         * <p><b>Note:</b> At least one of {@code descriptor} or
+         * {@code signature} must be passed as non-null.</p>
+         *
+         * @param startPc {@code >= 0;} the start pc of this variable's scope
+         * @param length {@code >= 0;} the length (in bytecodes) of this variable's
+         * scope
+         * @param name {@code non-null;} the variable's name
+         * @param descriptor {@code null-ok;} the variable's type descriptor
+         * @param signature {@code null-ok;} the variable's type signature
+         * @param index {@code >= 0;} the variable's local index
+         */
+        public Item(int startPc, int length, CstUtf8 name,
+                CstUtf8 descriptor, CstUtf8 signature, int index) {
+            if (startPc < 0) {
+                throw new IllegalArgumentException("startPc < 0");
+            }
+
+            if (length < 0) {
+                throw new IllegalArgumentException("length < 0");
+            }
+
+            if (name == null) {
+                throw new NullPointerException("name == null");
+            }
+
+            if ((descriptor == null) && (signature == null)) {
+                throw new NullPointerException(
+                        "(descriptor == null) && (signature == null)");
+            }
+
+            if (index < 0) {
+                throw new IllegalArgumentException("index < 0");
+            }
+
+            this.startPc = startPc;
+            this.length = length;
+            this.name = name;
+            this.descriptor = descriptor;
+            this.signature = signature;
+            this.index = index;
+        }
+
+        /**
+         * Gets the start pc of this variable's scope.
+         *
+         * @return {@code >= 0;} the start pc of this variable's scope
+         */
+        public int getStartPc() {
+            return startPc;
+        }
+
+        /**
+         * Gets the length (in bytecodes) of this variable's scope.
+         *
+         * @return {@code >= 0;} the length (in bytecodes) of this variable's scope
+         */
+        public int getLength() {
+            return length;
+        }
+
+        /**
+         * Gets the variable's type descriptor.
+         *
+         * @return {@code null-ok;} the variable's type descriptor
+         */
+        public CstUtf8 getDescriptor() {
+            return descriptor;
+        }
+
+        /**
+         * Gets the variable's LocalItem, a (name, signature) tuple
+         *
+         * @return {@code null-ok;} the variable's type descriptor
+         */
+        public LocalItem getLocalItem() {
+            return LocalItem.make(name, signature);
+        }
+
+        /**
+         * Gets the variable's type signature. Private because if you need this,
+         * you want getLocalItem() instead.
+         *
+         * @return {@code null-ok;} the variable's type signature
+         */
+        private CstUtf8 getSignature() {
+            return signature;
+        }
+
+        /**
+         * Gets the variable's local index.
+         *
+         * @return {@code >= 0;} the variable's local index
+         */
+        public int getIndex() {
+            return index;
+        }
+
+        /**
+         * Gets the variable's type descriptor. This is a convenient shorthand
+         * for {@code Type.intern(getDescriptor().getString())}.
+         *
+         * @return {@code non-null;} the variable's type
+         */
+        public Type getType() {
+            return Type.intern(descriptor.getString());
+        }
+
+        /**
+         * Constructs and returns an instance which is identical to this
+         * one, except that the signature is changed to the given value.
+         *
+         * @param newSignature {@code non-null;} the new signature
+         * @return {@code non-null;} an appropriately-constructed instance
+         */
+        public Item withSignature(CstUtf8 newSignature) {
+            return new Item(startPc, length, name, descriptor, newSignature,
+                    index);
+        }
+
+        /**
+         * Gets whether this instance matches (describes) the given
+         * address and index.
+         *
+         * @param pc {@code >= 0;} the address in question
+         * @param index {@code >= 0;} the local variable index in question
+         * @return {@code true} iff this instance matches {@code pc}
+         * and {@code index}
+         */
+        public boolean matchesPcAndIndex(int pc, int index) {
+            return (index == this.index) &&
+                (pc >= startPc) &&
+                (pc < (startPc + length));
+        }
+
+        /**
+         * Gets whether this instance matches (describes) the given
+         * other instance exactly in all fields except type descriptor and
+         * type signature.
+         *
+         * @param other {@code non-null;} the instance to compare to
+         * @return {@code true} iff this instance matches
+         */
+        public boolean matchesAllButType(Item other) {
+            return (startPc == other.startPc)
+                && (length == other.length)
+                && (index == other.index)
+                && name.equals(other.name);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalsArray.java b/dx/src/com/android/dx/cf/code/LocalsArray.java
new file mode 100644
index 0000000..75af047
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalsArray.java
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Representation of an array of local variables, with Java semantics.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public abstract class LocalsArray extends MutabilityControl implements ToHuman {
+
+    /**
+     * Constructs an instance, explicitly indicating the mutability.
+     *
+     * @param mutable {@code true} if this instance is mutable
+     */
+    protected LocalsArray(boolean mutable) {
+        super(mutable);
+    }
+
+    /**
+     * Makes and returns a mutable copy of this instance.
+     *
+     * @return {@code non-null;} the copy
+     */
+    public abstract LocalsArray copy();
+
+    /**
+     * Annotates (adds context to) the given exception with information
+     * about this instance.
+     *
+     * @param ex {@code non-null;} the exception to annotate
+     */
+    public abstract void annotate(ExceptionWithContext ex);
+
+    /**
+     * Replaces all the occurrences of the given uninitialized type in
+     * this array with its initialized equivalent.
+     *
+     * @param type {@code non-null;} type to replace
+     */
+    public abstract void makeInitialized(Type type);
+
+    /**
+     * Gets the maximum number of locals this instance can refer to.
+     *
+     * @return the max locals
+     */
+    public abstract int getMaxLocals();
+
+    /**
+     * Sets the type stored at the given local index. If the given type
+     * is category-2, then (a) the index must be at least two less than
+     * {@link #getMaxLocals} and (b) the next index gets invalidated
+     * by the operation. In case of either category, if the <i>previous</i>
+     * local contains a category-2 value, then it too is invalidated by
+     * this operation.
+     *
+     * @param idx {@code >= 0, < getMaxLocals();} which local
+     * @param type {@code non-null;} new type for the local at {@code idx}
+     */
+    public abstract void set(int idx, TypeBearer type);
+
+    /**
+     * Sets the type for the local indicated by the given register spec
+     * to that register spec (which includes type and optional name
+     * information). This is identical to calling
+     * {@code set(spec.getReg(), spec)}.
+     *
+     * @param spec {@code non-null;} register spec to use as the basis for the update
+     */
+    public abstract void set(RegisterSpec spec);
+
+    /**
+     * Invalidates the local at the given index.
+     *
+     * @param idx {@code >= 0, < getMaxLocals();} which local
+     */
+    public abstract void invalidate(int idx);
+
+    /**
+     * Gets the type stored at the given local index, or {@code null}
+     * if the given local is uninitialized / invalid.
+     *
+     * @param idx {@code >= 0, < getMaxLocals();} which local
+     * @return {@code null-ok;} the type of value stored in that local
+     */
+    public abstract TypeBearer getOrNull(int idx);
+
+    /**
+     * Gets the type stored at the given local index, only succeeding if
+     * the given local contains a valid type (though it is allowed to
+     * be an uninitialized instance).
+     *
+     * @param idx {@code >= 0, < getMaxLocals();} which local
+     * @return {@code non-null;} the type of value stored in that local
+     * @throws SimException thrown if {@code idx} is valid, but
+     * the contents are invalid
+     */
+    public abstract TypeBearer get(int idx);
+
+    /**
+     * Gets the type stored at the given local index, which is expected
+     * to be an initialized category-1 value.
+     *
+     * @param idx {@code >= 0, < getMaxLocals();} which local
+     * @return {@code non-null;} the type of value stored in that local
+     * @throws SimException thrown if {@code idx} is valid, but
+     * one of the following holds: (a) the local is invalid; (b) the local
+     * contains an uninitialized instance; (c) the local contains a
+     * category-2 value
+     */
+    public abstract TypeBearer getCategory1(int idx);
+
+    /**
+     * Gets the type stored at the given local index, which is expected
+     * to be a category-2 value.
+     *
+     * @param idx {@code >= 0, < getMaxLocals();} which local
+     * @return {@code non-null;} the type of value stored in that local
+     * @throws SimException thrown if {@code idx} is valid, but
+     * one of the following holds: (a) the local is invalid; (b) the local
+     * contains a category-1 value
+     */
+    public abstract TypeBearer getCategory2(int idx);
+
+    /**
+     * Merges this instance with {@code other}. If the merged result is
+     * the same as this instance, then this is returned (not a copy).
+     *
+     * @param other {@code non-null;} another LocalsArray
+     * @return {@code non-null;} the merge result, a new instance or this
+     */
+    public abstract LocalsArray merge(LocalsArray other);
+
+    /**
+     * Merges this instance with a {@code LocalsSet} from a subroutine
+     * caller. To be used when merging in the first block of a subroutine.
+     *
+     * @param other {@code other non-null;} another LocalsArray. The final locals
+     * state of a subroutine caller.
+     * @param predLabel the label of the subroutine caller block.
+     * @return {@code non-null;} the merge result, a new instance or this
+     */
+    public abstract LocalsArraySet mergeWithSubroutineCaller
+            (LocalsArray other, int predLabel);
+
+    /**
+     * Gets the locals set appropriate for the current execution context.
+     * That is, if this is a {@code OneLocalsArray} instance, then return
+     * {@code this}, otherwise return {@code LocalsArraySet}'s
+     * primary.
+     *
+     * @return locals for this execution context.
+     */
+    protected abstract OneLocalsArray getPrimary();
+
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalsArraySet.java b/dx/src/com/android/dx/cf/code/LocalsArraySet.java
new file mode 100644
index 0000000..5d03055
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalsArraySet.java
@@ -0,0 +1,462 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.ArrayList;
+
+/**
+ * Representation of a set of local variable arrays, with Java semantics.
+ * This peculiar case is to support in-method subroutines, which can
+ * have different locals sets for each caller.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link
+ * com.android.dx.rop.type.TypeBearer}.</p>
+ */
+public class LocalsArraySet extends LocalsArray {
+
+    /**
+     * The primary LocalsArray represents the locals as seen from
+     * the subroutine itself, which is the merged representation of all the
+     * individual locals states.
+     */
+    private final OneLocalsArray primary;
+
+    /**
+     * Indexed by label of caller block: the locals specific to each caller's
+     * invocation of the subroutine.
+     */
+    private final ArrayList<LocalsArray> secondaries;
+
+    /**
+     * Constructs an instance. The locals array initially consists of
+     * all-uninitialized values (represented as {@code null}s).
+     *
+     * @param maxLocals {@code >= 0;} the maximum number of locals this instance
+     * can refer to
+     */
+    public LocalsArraySet(int maxLocals) {
+        super(maxLocals != 0);
+        primary = new OneLocalsArray(maxLocals);
+        secondaries = new ArrayList();
+    }
+
+    /**
+     * Constructs an instance with the specified primary and secondaries set.
+     *
+     * @param primary {@code non-null;} primary locals to use
+     * @param secondaries {@code non-null;} secondaries set, indexed by subroutine
+     * caller label.
+     */
+    public LocalsArraySet(OneLocalsArray primary,
+            ArrayList<LocalsArray> secondaries) {
+        super(primary.getMaxLocals() > 0);
+
+        this.primary = primary;
+        this.secondaries = secondaries;
+    }
+
+    /**
+     * Constructs an instance which is a copy of another.
+     *
+     * @param toCopy {@code non-null;} instance to copy.
+     */
+    private LocalsArraySet(LocalsArraySet toCopy) {
+        super(toCopy.getMaxLocals() > 0);
+
+        primary = toCopy.primary.copy();
+        secondaries = new ArrayList(toCopy.secondaries.size());
+
+        int sz = toCopy.secondaries.size();
+        for (int i = 0; i < sz; i++) {
+            LocalsArray la = toCopy.secondaries.get(i);
+
+            if (la == null) {
+                secondaries.add(null);
+            } else {
+                secondaries.add(la.copy());
+            }
+        }
+    }
+
+
+    /** @inheritDoc */
+    @Override
+    public void setImmutable() {
+        primary.setImmutable();
+
+        for (LocalsArray la : secondaries) {
+            if (la != null) {
+                la.setImmutable();
+            }
+        }
+        super.setImmutable();
+    }
+
+    /** @inheritDoc */
+    @Override
+    public LocalsArray copy() {
+        return new LocalsArraySet(this);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void annotate(ExceptionWithContext ex) {
+        ex.addContext("(locals array set; primary)");
+        primary.annotate(ex);
+
+        int sz = secondaries.size();
+        for (int label = 0; label < sz; label++) {
+            LocalsArray la = secondaries.get(label);
+
+            if (la != null) {
+                ex.addContext("(locals array set: primary for caller "
+                        + Hex.u2(label) + ')');
+
+                la.getPrimary().annotate(ex);
+            }
+        }
+    }
+
+    /** {@inheritDoc*/
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("(locals array set; primary)\n");
+
+        sb.append(getPrimary().toHuman());
+        sb.append('\n');
+
+        int sz = secondaries.size();
+        for (int label = 0; label < sz; label++) {
+            LocalsArray la = secondaries.get(label);
+
+            if (la != null) {
+                sb.append("(locals array set: primary for caller "
+                        + Hex.u2(label) + ")\n");
+
+                sb.append(la.getPrimary().toHuman());
+                sb.append('\n');
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void makeInitialized(Type type) {
+        int len = primary.getMaxLocals();
+
+        if (len == 0) {
+            // We have to check for this before checking for immutability.
+            return;
+        }
+
+        throwIfImmutable();
+
+        primary.makeInitialized(type);
+
+        for (LocalsArray la : secondaries) {
+            if (la != null) {
+                la.makeInitialized(type);
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public int getMaxLocals() {
+        return primary.getMaxLocals();
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void set(int idx, TypeBearer type) {
+        throwIfImmutable();
+
+        primary.set(idx, type);
+
+        for (LocalsArray la : secondaries) {
+            if (la != null) {
+                la.set(idx, type);
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void set(RegisterSpec spec) {
+        set(spec.getReg(), spec);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void invalidate(int idx) {
+        throwIfImmutable();
+
+        primary.invalidate(idx);
+
+        for (LocalsArray la : secondaries) {
+            if (la != null) {
+                la.invalidate(idx);
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public TypeBearer getOrNull(int idx) {
+        return primary.getOrNull(idx);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public TypeBearer get(int idx) {
+        return primary.get(idx);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public TypeBearer getCategory1(int idx) {
+        return primary.getCategory1(idx);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public TypeBearer getCategory2(int idx) {
+        return primary.getCategory2(idx);
+    }
+
+    /**
+     * Merges this set with another {@code LocalsArraySet} instance.
+     *
+     * @param other {@code non-null;} to merge
+     * @return {@code non-null;} this instance if merge was a no-op, or
+     * new merged instance.
+     */
+    private LocalsArraySet mergeWithSet(LocalsArraySet other) {
+        OneLocalsArray newPrimary;
+        ArrayList<LocalsArray> newSecondaries;
+        boolean secondariesChanged = false;
+
+        newPrimary = primary.merge(other.getPrimary());
+
+        int sz1 = secondaries.size();
+        int sz2 = other.secondaries.size();
+        int sz = Math.max(sz1, sz2);
+        newSecondaries = new ArrayList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            LocalsArray la1 = (i < sz1 ? secondaries.get(i) : null);
+            LocalsArray la2 = (i < sz2 ? other.secondaries.get(i) : null);
+            LocalsArray resultla = null;
+
+            if (la1 == la2) {
+                resultla = la1;
+            } else if (la1 == null) {
+                resultla = la2;
+            } else if (la2 == null) {
+                resultla = la1;
+            } else {
+                try {
+                    resultla = la1.merge(la2);
+                } catch (SimException ex) {
+                    ex.addContext(
+                            "Merging locals set for caller block " + Hex.u2(i));
+                }
+            }
+
+            secondariesChanged = secondariesChanged || (la1 != resultla);
+
+            newSecondaries.add(resultla);
+        }
+
+        if ((primary == newPrimary) && ! secondariesChanged ) {
+            return this;
+        }
+
+        return new LocalsArraySet(newPrimary, newSecondaries);
+    }
+
+    /**
+     * Merges this set with a {@code OneLocalsArray} instance.
+     *
+     * @param other {@code non-null;} to merge
+     * @return {@code non-null;} this instance if merge was a no-op, or
+     * new merged instance.
+     */
+    private LocalsArraySet mergeWithOne(OneLocalsArray other) {
+        OneLocalsArray newPrimary;
+        ArrayList<LocalsArray> newSecondaries;
+        boolean secondariesChanged = false;
+
+        newPrimary = primary.merge(other.getPrimary());
+        newSecondaries = new ArrayList(secondaries.size());
+
+        int sz = secondaries.size();
+        for (int i = 0; i < sz; i++) {
+            LocalsArray la = secondaries.get(i);
+            LocalsArray resultla = null;
+
+            if (la != null) {
+                try {
+                    resultla = la.merge(other);
+                } catch (SimException ex) {
+                    ex.addContext("Merging one locals against caller block "
+                                    + Hex.u2(i));
+                }
+            }
+
+            secondariesChanged = secondariesChanged || (la != resultla);
+
+            newSecondaries.add(resultla);
+        }
+
+        if ((primary == newPrimary) && ! secondariesChanged ) {
+            return this;
+        }
+
+        return new LocalsArraySet(newPrimary, newSecondaries);
+    }
+
+    /** @inheritDoc */
+    @Override
+    public LocalsArraySet merge(LocalsArray other) {
+        LocalsArraySet result;
+
+        try {
+            if (other instanceof LocalsArraySet) {
+                result = mergeWithSet((LocalsArraySet) other);
+            } else {
+                result = mergeWithOne((OneLocalsArray) other);
+            }
+        } catch (SimException ex) {
+            ex.addContext("underlay locals:");
+            annotate(ex);
+            ex.addContext("overlay locals:");
+            other.annotate(ex);
+            throw ex;
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Gets the {@code LocalsArray} instance for a specified subroutine
+     * caller label, or null if label has no locals associated with it.
+     *
+     * @param label {@code >= 0;} subroutine caller label
+     * @return {@code null-ok;} locals if available.
+     */
+    private LocalsArray getSecondaryForLabel(int label) {
+        if (label >= secondaries.size()) {
+            return null;
+        }
+
+        return secondaries.get(label);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public LocalsArraySet mergeWithSubroutineCaller
+            (LocalsArray other, int predLabel) {
+
+        LocalsArray mine = getSecondaryForLabel(predLabel);
+        LocalsArray newSecondary;
+        OneLocalsArray newPrimary;
+
+        newPrimary = primary.merge(other.getPrimary());
+
+        if (mine == other) {
+            newSecondary = mine;
+        } else if (mine == null) {
+            newSecondary = other;
+        } else {
+            newSecondary = mine.merge(other);
+        }
+
+        if ((newSecondary == mine) && (newPrimary == primary)) {
+            return this;
+        } else {
+            /*
+             * We're going to re-build a primary as a merge of all the
+             * secondaries.
+             */
+            newPrimary = null;
+
+            int szSecondaries = secondaries.size();
+            int sz = Math.max(predLabel + 1, szSecondaries);
+            ArrayList<LocalsArray> newSecondaries = new ArrayList(sz);
+            for (int i = 0; i < sz; i++) {
+                LocalsArray la = null;
+
+                if (i == predLabel) {
+                    /*
+                     * This LocalsArray always replaces any existing one,
+                     * since this is the result of a refined iteration.
+                     */
+                    la = newSecondary;
+                } else if (i < szSecondaries) {
+                    la = secondaries.get(i);
+                }
+
+                if (la != null) {
+                    if (newPrimary == null) {
+                        newPrimary = la.getPrimary();
+                    } else {
+                        newPrimary = newPrimary.merge(la.getPrimary());
+                    }
+                }
+
+                newSecondaries.add(la);
+            }
+
+            LocalsArraySet result
+                    = new LocalsArraySet(newPrimary, newSecondaries);
+            result.setImmutable();
+            return result;
+        }
+    }
+
+    /**
+     * Returns a LocalsArray instance representing the locals state that should
+     * be used when returning to a subroutine caller.
+     *
+     * @param subLabel {@code >= 0;} A calling label of a subroutine
+     * @return {@code null-ok;} an instance for this subroutine, or null if subroutine
+     * is not in this set.
+     */
+    public LocalsArray subArrayForLabel(int subLabel) {
+        LocalsArray result = getSecondaryForLabel(subLabel);
+        return result;
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    protected OneLocalsArray getPrimary() {
+        return primary;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/Machine.java b/dx/src/com/android/dx/cf/code/Machine.java
new file mode 100644
index 0000000..72ba3b4
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Machine.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.code.LocalItem;
+import java.util.ArrayList;
+
+/**
+ * Interface for machines capable of executing bytecode by acting
+ * upon a {@link Frame}. A machine conceptually contains four arbitrary-value
+ * argument slots, slots for several literal-value arguments, and slots for
+ * branch target information.
+ */
+public interface Machine {
+    /**
+     * Gets the effective prototype of the method that this instance is
+     * being used for. The <i>effective</i> prototype includes an initial
+     * {@code this} argument for instance methods.
+     *
+     * @return {@code non-null;} the method prototype
+     */
+    public Prototype getPrototype();
+
+    /**
+     * Clears the regular and auxiliary arguments area.
+     */
+    public void clearArgs();
+
+    /**
+     * Pops the given number of values from the stack (of either category),
+     * and store them in the arguments area, indicating that there are now
+     * that many arguments. Also, clear the auxiliary arguments.
+     *
+     * @param frame {@code non-null;} frame to operate on
+     * @param count {@code >= 0;} number of values to pop
+     */
+    public void popArgs(Frame frame, int count);
+
+    /**
+     * Pops values from the stack of the types indicated by the given
+     * {@code Prototype} (popped in reverse of the argument
+     * order, so the first prototype argument type is for the deepest
+     * element of the stack), and store them in the arguments area,
+     * indicating that there are now that many arguments. Also, clear
+     * the auxiliary arguments.
+     *
+     * @param frame {@code non-null;} frame to operate on
+     * @param prototype {@code non-null;} prototype indicating arguments to pop
+     */
+    public void popArgs(Frame frame, Prototype prototype);
+
+    /**
+     * Pops a value from the stack of the indicated type, and store it
+     * in the arguments area, indicating that there are now that many
+     * arguments. Also, clear the auxiliary arguments.
+     *
+     * @param frame {@code non-null;} frame to operate on
+     * @param type {@code non-null;} type of the argument
+     */
+    public void popArgs(Frame frame, Type type);
+
+    /**
+     * Pops values from the stack of the indicated types (popped in
+     * reverse argument order, so the first indicated type is for the
+     * deepest element of the stack), and store them in the arguments
+     * area, indicating that there are now that many arguments. Also,
+     * clear the auxiliary arguments.
+     *
+     * @param frame {@code non-null;} frame to operate on
+     * @param type1 {@code non-null;} type of the first argument
+     * @param type2 {@code non-null;} type of the second argument
+     */
+    public void popArgs(Frame frame, Type type1, Type type2);
+
+    /**
+     * Pops values from the stack of the indicated types (popped in
+     * reverse argument order, so the first indicated type is for the
+     * deepest element of the stack), and store them in the arguments
+     * area, indicating that there are now that many arguments. Also,
+     * clear the auxiliary arguments.
+     *
+     * @param frame {@code non-null;} frame to operate on
+     * @param type1 {@code non-null;} type of the first argument
+     * @param type2 {@code non-null;} type of the second argument
+     * @param type3 {@code non-null;} type of the third argument
+     */
+    public void popArgs(Frame frame, Type type1, Type type2, Type type3);
+
+    /**
+     * Loads the local variable with the given index as the sole argument in
+     * the arguments area. Also, clear the auxiliary arguments.
+     *
+     * @param frame {@code non-null;} frame to operate on
+     * @param idx {@code >= 0;} the local variable index
+     */
+    public void localArg(Frame frame, int idx);
+
+    /**
+     * Indicates that the salient type of this operation is as
+     * given. This differentiates between, for example, the various
+     * arithmetic opcodes, which, by the time they hit a
+     * {@code Machine} are collapsed to the {@code int}
+     * variant. (See {@link BytecodeArray#parseInstruction} for
+     * details.)
+     *
+     * @param type {@code non-null;} the salient type of the upcoming operation
+     */
+    public void auxType(Type type);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack)
+     * argument of type {@code int}, with the given value.
+     *
+     * <p><b>Note:</b> Perhaps unintuitively, the stack manipulation
+     * ops (e.g., {@code dup} and {@code swap}) use this to
+     * indicate the result stack pattern with a straightforward hex
+     * encoding of the push order starting with least-significant
+     * nibbles getting pushed first). For example, an all-category-1
+     * {@code dup2_x1} sets this to {@code 0x12312}, and the
+     * other form of that op sets this to
+     * {@code 0x121}.</p>
+     *
+     * <p><b>Also Note:</b> For {@code switch*} instructions, this is
+     * used to indicate the padding value (which is only useful for
+     * verification).</p>
+     *
+     * @param value the argument value
+     */
+    public void auxIntArg(int value);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack) object
+     * argument, with the value based on the given constant.
+     *
+     * <p><b>Note:</b> Some opcodes use both {@code int} and
+     * constant auxiliary arguments.</p>
+     *
+     * @param cst {@code non-null;} the constant containing / referencing
+     * the value
+     */
+    public void auxCstArg(Constant cst);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack) argument
+     * indicating a branch target.
+     *
+     * @param target the argument value
+     */
+    public void auxTargetArg(int target);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack) argument
+     * consisting of a {@code switch*} table.
+     *
+     * <p><b>Note:</b> This is generally used in conjunction with
+     * {@link #auxIntArg} (which holds the padding).</p>
+     *
+     * @param cases {@code non-null;} the list of key-target pairs, plus the default
+     * target
+     */
+    public void auxSwitchArg(SwitchList cases);
+
+    /**
+     * Indicates that there is an auxiliary (inline, not stack) argument
+     * consisting of a list of initial values for a newly created array.
+     *
+     * @param initValues {@code non-null;} the list of constant values to initialize
+     * the array
+     */
+    public void auxInitValues(ArrayList<Constant> initValues);
+
+    /**
+     * Indicates that the target of this operation is the given local.
+     *
+     * @param idx {@code >= 0;} the local variable index
+     * @param type {@code non-null;} the type of the local
+     * @param local {@code null-ok;} the name and signature of the local, if known
+     */
+    public void localTarget(int idx, Type type, LocalItem local);
+
+    /**
+     * "Runs" the indicated opcode in an appropriate way, using the arguments
+     * area as appropriate, and modifying the given frame in response.
+     *
+     * @param frame {@code non-null;} frame to operate on
+     * @param offset {@code >= 0;} byte offset in the method to the opcode being
+     * run
+     * @param opcode {@code >= 0;} the opcode to run
+     */
+    public void run(Frame frame, int offset, int opcode);
+}
diff --git a/dx/src/com/android/dx/cf/code/Merger.java b/dx/src/com/android/dx/cf/code/Merger.java
new file mode 100644
index 0000000..51c31c3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Merger.java
@@ -0,0 +1,305 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * Utility methods to merge various frame information.
+ */
+public final class Merger {
+    /**
+     * This class is uninstantiable.
+     */
+    private Merger() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Merges two locals arrays. If the merged result is the same as the first
+     * argument, then return the first argument (not a copy).
+     *
+     * @param locals1 {@code non-null;} a locals array
+     * @param locals2 {@code non-null;} another locals array
+     * @return {@code non-null;} the result of merging the two locals arrays
+     */
+    public static OneLocalsArray mergeLocals(OneLocalsArray locals1,
+                                          OneLocalsArray locals2) {
+        if (locals1 == locals2) {
+            // Easy out.
+            return locals1;
+        }
+
+        int sz = locals1.getMaxLocals();
+        OneLocalsArray result = null;
+
+        if (locals2.getMaxLocals() != sz) {
+            throw new SimException("mismatched maxLocals values");
+        }
+
+        for (int i = 0; i < sz; i++) {
+            TypeBearer tb1 = locals1.getOrNull(i);
+            TypeBearer tb2 = locals2.getOrNull(i);
+            TypeBearer resultType = mergeType(tb1, tb2);
+            if (resultType != tb1) {
+                /*
+                 * We only need to do anything when the result differs
+                 * from what is in the first array, since that's what the
+                 * result gets initialized to.
+                 */
+                if (result == null) {
+                    result = locals1.copy();
+                }
+
+                if (resultType == null) {
+                    result.invalidate(i);
+                } else {
+                    result.set(i, resultType);
+                }
+            }
+        }
+
+        if (result == null) {
+            return locals1;
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Merges two stacks. If the merged result is the same as the first
+     * argument, then return the first argument (not a copy).
+     *
+     * @param stack1 {@code non-null;} a stack
+     * @param stack2 {@code non-null;} another stack
+     * @return {@code non-null;} the result of merging the two stacks
+     */
+    public static ExecutionStack mergeStack(ExecutionStack stack1,
+                                            ExecutionStack stack2) {
+        if (stack1 == stack2) {
+            // Easy out.
+            return stack1;
+        }
+
+        int sz = stack1.size();
+        ExecutionStack result = null;
+
+        if (stack2.size() != sz) {
+            throw new SimException("mismatched stack depths");
+        }
+
+        for (int i = 0; i < sz; i++) {
+            TypeBearer tb1 = stack1.peek(i);
+            TypeBearer tb2 = stack2.peek(i);
+            TypeBearer resultType = mergeType(tb1, tb2);
+            if (resultType != tb1) {
+                /*
+                 * We only need to do anything when the result differs
+                 * from what is in the first stack, since that's what the
+                 * result gets initialized to.
+                 */
+                if (result == null) {
+                    result = stack1.copy();
+                }
+
+                try {
+                    if (resultType == null) {
+                        throw new SimException("incompatible: " + tb1 + ", " +
+                                               tb2);
+                    } else {
+                        result.change(i, resultType);
+                    }
+                } catch (SimException ex) {
+                    ex.addContext("...while merging stack[" + Hex.u2(i) + "]");
+                    throw ex;
+                }
+            }
+        }
+
+        if (result == null) {
+            return stack1;
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Merges two frame types.
+     *
+     * @param ft1 {@code non-null;} a frame type
+     * @param ft2 {@code non-null;} another frame type
+     * @return {@code non-null;} the result of merging the two types
+     */
+    public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
+        if ((ft1 == null) || ft1.equals(ft2)) {
+            return ft1;
+        } else if (ft2 == null) {
+            return null;
+        } else {
+            Type type1 = ft1.getType();
+            Type type2 = ft2.getType();
+
+            if (type1 == type2) {
+                return type1;
+            } else if (type1.isReference() && type2.isReference()) {
+                if (type1 == Type.KNOWN_NULL) {
+                    /*
+                     * A known-null merges with any other reference type to
+                     * be that reference type.
+                     */
+                    return type2;
+                } else if (type2 == Type.KNOWN_NULL) {
+                    /*
+                     * The same as above, but this time it's type2 that's
+                     * the known-null.
+                     */
+                    return type1;
+                } else if (type1.isArray() && type2.isArray()) {
+                    TypeBearer componentUnion =
+                        mergeType(type1.getComponentType(),
+                                type2.getComponentType());
+                    if (componentUnion == null) {
+                        /*
+                         * At least one of the types is a primitive type,
+                         * so the merged result is just Object.
+                         */
+                        return Type.OBJECT;
+                    }
+                    return ((Type) componentUnion).getArrayType();
+                } else {
+                    /*
+                     * All other unequal reference types get merged to be
+                     * Object in this phase. This is fine here, but it
+                     * won't be the right thing to do in the verifier.
+                     */
+                    return Type.OBJECT;
+                }
+            } else if (type1.isIntlike() && type2.isIntlike()) {
+                /*
+                 * Merging two non-identical int-like types results in
+                 * the type int.
+                 */
+                return Type.INT;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Returns whether the given supertype is possibly assignable from
+     * the given subtype. This takes into account primitiveness,
+     * int-likeness, known-nullness, and array dimensions, but does
+     * not assume anything about class hierarchy other than that the
+     * type {@code Object} is the supertype of all reference
+     * types and all arrays are assignable to
+     * {@code Serializable} and {@code Cloneable}.
+     *
+     * @param supertypeBearer {@code non-null;} the supertype
+     * @param subtypeBearer {@code non-null;} the subtype
+     */
+    public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer,
+            TypeBearer subtypeBearer) {
+        Type supertype = supertypeBearer.getType();
+        Type subtype = subtypeBearer.getType();
+
+        if (supertype.equals(subtype)) {
+            // Easy out.
+            return true;
+        }
+
+        int superBt = supertype.getBasicType();
+        int subBt = subtype.getBasicType();
+
+        // Treat return types as Object for the purposes of this method.
+
+        if (superBt == Type.BT_ADDR) {
+            supertype = Type.OBJECT;
+            superBt = Type.BT_OBJECT;
+        }
+
+        if (subBt == Type.BT_ADDR) {
+            subtype = Type.OBJECT;
+            subBt = Type.BT_OBJECT;
+        }
+
+        if ((superBt != Type.BT_OBJECT) || (subBt != Type.BT_OBJECT)) {
+            /*
+             * No two distinct primitive types are assignable in this sense,
+             * unless they are both int-like.
+             */
+            return supertype.isIntlike() && subtype.isIntlike();
+        }
+
+        // At this point, we know both types are reference types.
+
+        if (supertype == Type.KNOWN_NULL) {
+            /*
+             * A known-null supertype is only assignable from another
+             * known-null (handled in the easy out at the top of the
+             * method).
+             */
+            return false;
+        } else if (subtype == Type.KNOWN_NULL) {
+            /*
+             * A known-null subtype is in fact assignable to any
+             * reference type.
+             */
+            return true;
+        } else if (supertype == Type.OBJECT) {
+            /*
+             * Object is assignable from any reference type.
+             */
+            return true;
+        } else if (supertype.isArray()) {
+            // The supertype is an array type.
+            if (! subtype.isArray()) {
+                // The subtype isn't an array, and so can't be assignable.
+                return false;
+            }
+
+            /*
+             * Strip off as many matched component types from both
+             * types as possible, and check the assignability of the
+             * results.
+             */
+            do {
+                supertype = supertype.getComponentType();
+                subtype = subtype.getComponentType();
+            } while (supertype.isArray() && subtype.isArray());
+
+            return isPossiblyAssignableFrom(supertype, subtype);
+        } else if (subtype.isArray()) {
+            /*
+             * Other than Object (handled above), array types are
+             * assignable only to Serializable and Cloneable.
+             */
+            return (supertype == Type.SERIALIZABLE) ||
+                (supertype == Type.CLONEABLE);
+        } else {
+            /*
+             * All other unequal reference types are considered at
+             * least possibly assignable.
+             */
+            return true;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/OneLocalsArray.java b/dx/src/com/android/dx/cf/code/OneLocalsArray.java
new file mode 100644
index 0000000..cafd177
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/OneLocalsArray.java
@@ -0,0 +1,246 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Representation of an array of local variables, with Java semantics.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link
+ * com.android.dx.rop.type.TypeBearer}.</p>
+ */
+public class OneLocalsArray extends LocalsArray {
+    /** {@code non-null;} actual array */
+    private final TypeBearer[] locals;
+
+    /**
+     * Constructs an instance. The locals array initially consists of
+     * all-uninitialized values (represented as {@code null}s).
+     *
+     * @param maxLocals {@code >= 0;} the maximum number of locals this instance
+     * can refer to
+     */
+    public OneLocalsArray(int maxLocals) {
+        super(maxLocals != 0);
+        locals = new TypeBearer[maxLocals];
+    }
+
+    /** @inheritDoc */
+    public OneLocalsArray copy() {
+        OneLocalsArray result = new OneLocalsArray(locals.length);
+
+        System.arraycopy(locals, 0, result.locals, 0, locals.length);
+
+        return result;
+    }
+
+    /** @inheritDoc */
+    public void annotate(ExceptionWithContext ex) {
+        for (int i = 0; i < locals.length; i++) {
+            TypeBearer type = locals[i];
+            String s = (type == null) ? "<invalid>" : type.toString();
+            ex.addContext("locals[" + Hex.u2(i) + "]: " + s);
+        }
+    }
+
+    /** {@inheritDoc*/
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        for (int i = 0; i < locals.length; i++) {
+            TypeBearer type = locals[i];
+            String s = (type == null) ? "<invalid>" : type.toString();
+            sb.append("locals[" + Hex.u2(i) + "]: " + s + "\n");
+        }
+
+        return sb.toString();
+    }
+
+    /** @inheritDoc */
+    public void makeInitialized(Type type) {
+        int len = locals.length;
+
+        if (len == 0) {
+            // We have to check for this before checking for immutability.
+            return;
+        }
+
+        throwIfImmutable();
+
+        Type initializedType = type.getInitializedType();
+
+        for (int i = 0; i < len; i++) {
+            if (locals[i] == type) {
+                locals[i] = initializedType;
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    public int getMaxLocals() {
+        return locals.length;
+    }
+
+    /** @inheritDoc */
+    public void set(int idx, TypeBearer type) {
+        throwIfImmutable();
+
+        try {
+            type = type.getFrameType();
+        } catch (NullPointerException ex) {
+            // Elucidate the exception
+            throw new NullPointerException("type == null");
+        }
+
+        if (idx < 0) {
+            throw new IndexOutOfBoundsException("idx < 0");
+        }
+
+        // Make highest possible out-of-bounds check happen first.
+        if (type.getType().isCategory2()) {
+            locals[idx + 1] = null;
+        }
+
+        locals[idx] = type;
+
+        if (idx != 0) {
+            TypeBearer prev = locals[idx - 1];
+            if ((prev != null) && prev.getType().isCategory2()) {
+                locals[idx - 1] = null;
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    public void set(RegisterSpec spec) {
+        set(spec.getReg(), spec);
+    }
+
+    /** @inheritDoc */
+    public void invalidate(int idx) {
+        throwIfImmutable();
+        locals[idx] = null;
+    }
+
+    /** @inheritDoc */
+    public TypeBearer getOrNull(int idx) {
+        return locals[idx];
+    }
+
+    /** @inheritDoc */
+    public TypeBearer get(int idx) {
+        TypeBearer result = locals[idx];
+
+        if (result == null) {
+            return throwSimException(idx, "invalid");
+        }
+
+        return result;
+    }
+
+    /** @inheritDoc */
+    public TypeBearer getCategory1(int idx) {
+        TypeBearer result = get(idx);
+        Type type = result.getType();
+
+        if (type.isUninitialized()) {
+            return throwSimException(idx, "uninitialized instance");
+        }
+
+        if (type.isCategory2()) {
+            return throwSimException(idx, "category-2");
+        }
+
+        return result;
+    }
+
+    /** @inheritDoc */
+    public TypeBearer getCategory2(int idx) {
+        TypeBearer result = get(idx);
+
+        if (result.getType().isCategory1()) {
+            return throwSimException(idx, "category-1");
+        }
+
+        return result;
+    }
+
+    /** @inheritDoc */
+    @Override
+    public LocalsArray merge(LocalsArray other) {
+        if (other instanceof OneLocalsArray) {
+            return merge((OneLocalsArray)other);
+        } else { //LocalsArraySet
+            // LocalsArraySet knows how to merge me.
+            return other.merge(this);
+        }
+    }
+
+    /**
+     * Merges this OneLocalsArray instance with another OneLocalsArray
+     * instance. A more-refined version of {@link #merge(LocalsArray) merge}
+     * which is called by that method when appropriate.
+     *
+     * @param other locals array with which to merge
+     * @return this instance if merge was a no-op, or a new instance if
+     * the merge resulted in a change.
+     */
+    public OneLocalsArray merge(OneLocalsArray other) {
+        try {
+            return Merger.mergeLocals(this, other);
+        } catch (SimException ex) {
+            ex.addContext("underlay locals:");
+            annotate(ex);
+            ex.addContext("overlay locals:");
+            other.annotate(ex);
+            throw ex;
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public LocalsArraySet mergeWithSubroutineCaller
+            (LocalsArray other, int predLabel) {
+
+        LocalsArraySet result = new LocalsArraySet(getMaxLocals());
+        return result.mergeWithSubroutineCaller(other, predLabel);
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    protected OneLocalsArray getPrimary() {
+        return this;
+    }
+
+    /**
+     * Throws a properly-formatted exception.
+     *
+     * @param idx the salient local index
+     * @param msg {@code non-null;} useful message
+     * @return never (keeps compiler happy)
+     */
+    private static TypeBearer throwSimException(int idx, String msg) {
+        throw new SimException("local " + Hex.u2(idx) + ": " + msg);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ReturnAddress.java b/dx/src/com/android/dx/cf/code/ReturnAddress.java
new file mode 100644
index 0000000..ee36450
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ReturnAddress.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a subroutine return address. In Java verification,
+ * somewhat counterintuitively, the salient bit of information you need to
+ * know about a return address is the <i>start address</i> of the subroutine
+ * being returned from, not the address being returned <i>to</i>, so that's
+ * what instances of this class hang onto.
+ */
+public final class ReturnAddress implements TypeBearer {
+    /** {@code >= 0;} the start address of the subroutine being returned from */
+    private final int subroutineAddress;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param subroutineAddress {@code >= 0;} the start address of the
+     * subroutine being returned from
+     */
+    public ReturnAddress(int subroutineAddress) {
+        if (subroutineAddress < 0) {
+            throw new IllegalArgumentException("subroutineAddress < 0");
+        }
+
+        this.subroutineAddress = subroutineAddress;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return ("<addr:" + Hex.u2(subroutineAddress) + ">");
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toString();
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.RETURN_ADDRESS;
+    }
+
+    /** {@inheritDoc} */
+    public TypeBearer getFrameType() {
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    public int getBasicType() {
+        return Type.RETURN_ADDRESS.getBasicType();
+    }
+
+    /** {@inheritDoc} */
+    public int getBasicFrameType() {
+        return Type.RETURN_ADDRESS.getBasicFrameType();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isConstant() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof ReturnAddress)) {
+            return false;
+        }
+
+        return subroutineAddress == ((ReturnAddress) other).subroutineAddress;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return subroutineAddress;
+    }
+
+    /**
+     * Gets the subroutine address.
+     *
+     * @return {@code >= 0;} the subroutine address
+     */
+    public int getSubroutineAddress() {
+        return subroutineAddress;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/Ropper.java b/dx/src/com/android/dx/cf/code/Ropper.java
new file mode 100644
index 0000000..8217166
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Ropper.java
@@ -0,0 +1,1675 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Bits;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+
+/**
+ * Utility that converts a basic block list into a list of register-oriented
+ * blocks.
+ */
+public final class Ropper {
+    /** label offset for the parameter assignment block */
+    private static final int PARAM_ASSIGNMENT = -1;
+
+    /** label offset for the return block */
+    private static final int RETURN = -2;
+
+    /** label offset for the synchronized method final return block */
+    private static final int SYNCH_RETURN = -3;
+
+    /** label offset for the first synchronized method setup block */
+    private static final int SYNCH_SETUP_1 = -4;
+
+    /** label offset for the second synchronized method setup block */
+    private static final int SYNCH_SETUP_2 = -5;
+
+    /**
+     * label offset for the first synchronized method exception
+     * handler block
+     */
+    private static final int SYNCH_CATCH_1 = -6;
+
+    /**
+     * label offset for the second synchronized method exception
+     * handler block
+     */
+    private static final int SYNCH_CATCH_2 = -7;
+
+    /** number of special label offsets */
+    private static final int SPECIAL_LABEL_COUNT = 7;
+
+    /** {@code non-null;} method being converted */
+    private final ConcreteMethod method;
+
+    /** {@code non-null;} original block list */
+    private final ByteBlockList blocks;
+
+    /** max locals of the method */
+    private final int maxLocals;
+
+    /** max label (exclusive) of any original bytecode block */
+    private final int maxLabel;
+
+    /** {@code non-null;} simulation machine to use */
+    private final RopperMachine machine;
+
+    /** {@code non-null;} simulator to use */
+    private final Simulator sim;
+
+    /**
+     * {@code non-null;} sparse array mapping block labels to initial frame
+     * contents, if known
+     */
+    private final Frame[] startFrames;
+
+    /** {@code non-null;} output block list in-progress */
+    private final ArrayList<BasicBlock> result;
+
+    /**
+     * {@code non-null;} list of subroutine-nest labels
+     * (See {@link Frame#getSubroutines} associated with each result block.
+     * Parallel to {@link Ropper#result}.
+     */
+    private final ArrayList<IntList> resultSubroutines;
+
+    /**
+     * {@code non-null;} for each block (by label) that is used as an exception
+     * handler, the type of exception it catches
+     */
+    private final Type[] catchTypes;
+
+    /**
+     * whether an exception-handler block for a synchronized method was
+     * ever required
+     */
+    private boolean synchNeedsExceptionHandler;
+
+    /**
+     * {@code non-null;} list of subroutines indexed by label of start
+     * address */
+    private final Subroutine[] subroutines;
+
+    /** true if {@code subroutines} is non-empty */
+    private boolean hasSubroutines;
+
+    /**
+     * Keeps track of subroutines that exist in java form and are inlined in
+     * Rop form.
+     */
+    private class Subroutine {
+        /** list of all blocks that jsr to this subroutine */
+        private BitSet callerBlocks;
+        /** List of all blocks that return from this subroutine */
+        private BitSet retBlocks;
+        /** first block in this subroutine */
+        private int startBlock;
+
+        /**
+         * Constructs instance.
+         *
+         * @param startBlock First block of the subroutine.
+         */
+        Subroutine(int startBlock) {
+            this.startBlock = startBlock;
+            retBlocks = new BitSet(maxLabel);
+            callerBlocks = new BitSet(maxLabel);
+            hasSubroutines = true;
+        }
+
+        /**
+         * Constructs instance.
+         *
+         * @param startBlock First block of the subroutine.
+         * @param retBlock one of the ret blocks (final blocks) of this
+         * subroutine.
+         */
+        Subroutine(int startBlock, int retBlock) {
+            this(startBlock);
+            addRetBlock(retBlock);
+        }
+
+        /**
+         * @return {@code >= 0;} the label of the subroutine's start block.
+         */
+        int getStartBlock() {
+            return startBlock;
+        }
+
+        /**
+         * Adds a label to the list of ret blocks (final blocks) for this
+         * subroutine.
+         *
+         * @param retBlock ret block label
+         */
+        void addRetBlock(int retBlock) {
+            retBlocks.set(retBlock);
+        }
+
+        /**
+         * Adds a label to the list of caller blocks for this subroutine.
+         *
+         * @param label a block that invokes this subroutine.
+         */
+        void addCallerBlock(int label) {
+            callerBlocks.set(label);
+        }
+
+        /**
+         * Generates a list of subroutine successors. Note: successor blocks
+         * could be listed more than once. This is ok, because this successor
+         * list (and the block it's associated with) will be copied and inlined
+         * before we leave the ropper. Redundent successors will result in
+         * redundent (no-op) merges.
+         *
+         * @return all currently known successors
+         * (return destinations) for that subroutine
+         */
+        IntList getSuccessors() {
+            IntList successors = new IntList(callerBlocks.size());
+
+            /*
+             * For each subroutine caller, get it's target. If the
+             * target is us, add the ret target (subroutine successor)
+             * to our list
+             */
+
+            for (int label = callerBlocks.nextSetBit(0); label >= 0;
+                 label = callerBlocks.nextSetBit(label+1)) {
+                BasicBlock subCaller = labelToBlock(label);
+                successors.add(subCaller.getSuccessors().get(0));
+            }
+
+            successors.setImmutable();
+
+            return successors;
+        }
+
+        /**
+         * Merges the specified frame into this subroutine's successors,
+         * setting {@code workSet} as appropriate. To be called with
+         * the frame of a subroutine ret block.
+         *
+         * @param frame {@code non-null;} frame from ret block to merge
+         * @param workSet {@code non-null;} workset to update
+         */
+        void mergeToSuccessors(Frame frame, int[] workSet) {
+            for (int label = callerBlocks.nextSetBit(0); label >= 0;
+                 label = callerBlocks.nextSetBit(label+1)) {
+                BasicBlock subCaller = labelToBlock(label);
+                int succLabel = subCaller.getSuccessors().get(0);
+
+                Frame subFrame = frame.subFrameForLabel(startBlock, label);
+
+                if (subFrame != null) {
+                    mergeAndWorkAsNecessary(succLabel, -1, null,
+                            subFrame, workSet);
+                } else {
+                    Bits.set(workSet, label);
+                }
+            }
+        }
+    }
+
+    /**
+     * Converts a {@link ConcreteMethod} to a {@link RopMethod}.
+     *
+     * @param method {@code non-null;} method to convert
+     * @param advice {@code non-null;} translation advice to use
+     * @return {@code non-null;} the converted instance
+     */
+    public static RopMethod convert(ConcreteMethod method,
+            TranslationAdvice advice) {
+        try {
+            Ropper r = new Ropper(method, advice);
+            r.doit();
+            return r.getRopMethod();
+        } catch (SimException ex) {
+            ex.addContext("...while working on method " +
+                          method.getNat().toHuman());
+            throw ex;
+        }
+    }
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable; use
+     * {@link #convert}.
+     *
+     * @param method {@code non-null;} method to convert
+     * @param advice {@code non-null;} translation advice to use
+     */
+    private Ropper(ConcreteMethod method, TranslationAdvice advice) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        if (advice == null) {
+            throw new NullPointerException("advice == null");
+        }
+
+        this.method = method;
+        this.blocks = BasicBlocker.identifyBlocks(method);
+        this.maxLabel = blocks.getMaxLabel();
+        this.maxLocals = method.getMaxLocals();
+        this.machine = new RopperMachine(this, method, advice);
+        this.sim = new Simulator(machine, method);
+        this.startFrames = new Frame[maxLabel];
+        this.subroutines = new Subroutine[maxLabel];
+
+        /*
+         * The "* 2 + 10" below is to conservatively believe that every
+         * block is an exception handler target and should also
+         * take care of enough other possible extra overhead such that
+         * the underlying array is unlikely to need resizing.
+         */
+        this.result = new ArrayList<BasicBlock>(blocks.size() * 2 + 10);
+        this.resultSubroutines =
+            new ArrayList<IntList>(blocks.size() * 2 + 10);
+
+        this.catchTypes = new Type[maxLabel];
+        this.synchNeedsExceptionHandler = false;
+
+        /*
+         * Set up the first stack frame with the right limits, but leave it
+         * empty here (to be filled in outside of the constructor).
+         */
+        startFrames[0] = new Frame(maxLocals, method.getMaxStack());
+    }
+
+    /**
+     * Gets the first (lowest) register number to use as the temporary
+     * area when unwinding stack manipulation ops.
+     *
+     * @return {@code >= 0;} the first register to use
+     */
+    /*package*/ int getFirstTempStackReg() {
+        /*
+         * We use the register that is just past the deepest possible
+         * stack element, plus one if the method is synchronized to
+         * avoid overlapping with the synch register. We don't need to
+         * do anything else special at this level, since later passes
+         * will merely notice the highest register used by explicit
+         * inspection.
+         */
+        int regCount = getNormalRegCount();
+        return isSynchronized() ? regCount + 1 : regCount;
+    }
+
+    /**
+     * Gets the label for the exception handler setup block corresponding
+     * to the given label.
+     *
+     * @param label {@code >= 0;} the original label
+     * @return {@code >= 0;} the corresponding exception handler setup label
+     */
+    private int getExceptionSetupLabel(int label) {
+        return maxLabel + label;
+    }
+
+    /**
+     * Gets the label for the given special-purpose block. The given label
+     * should be one of the static constants defined by this class.
+     *
+     * @param label {@code < 0;} the special label constant
+     * @return {@code >= 0;} the actual label value to use
+     */
+    private int getSpecialLabel(int label) {
+        /*
+         * The label is bitwise-complemented so that mistakes where
+         * LABEL is used instead of getSpecialLabel(LABEL) cause a
+         * failure at block construction time, since negative labels
+         * are illegal. We multiply maxLabel by 2 since 0..maxLabel
+         * (exclusive) are the original blocks and
+         * maxLabel..(maxLabel*2) are reserved for exception handler
+         * setup blocks (see getExceptionSetupLabel(), above).
+         */
+        return (maxLabel * 2) + ~label;
+    }
+
+    /**
+     * Gets the minimum label for unreserved use.
+     *
+     * @return {@code >= 0;} the minimum label
+     */
+    private int getMinimumUnreservedLabel() {
+        /*
+         * The labels below ((maxLabel * 2) + SPECIAL_LABEL_COUNT) are
+         * reserved for particular uses.
+         */
+
+        return (maxLabel * 2) + SPECIAL_LABEL_COUNT;
+    }
+
+    /**
+     * Gets an arbitrary unreserved and available label.
+     *
+     * @return {@code >= 0;} the label
+     */
+    private int getAvailableLabel() {
+        int candidate = getMinimumUnreservedLabel();
+
+        for (BasicBlock bb : result) {
+            int label = bb.getLabel();
+            if (label >= candidate) {
+                candidate = label + 1;
+            }
+        }
+
+        return candidate;
+    }
+
+    /**
+     * Gets whether the method being translated is synchronized.
+     *
+     * @return whether the method being translated is synchronized
+     */
+    private boolean isSynchronized() {
+        int accessFlags = method.getAccessFlags();
+        return (accessFlags & AccessFlags.ACC_SYNCHRONIZED) != 0;
+    }
+
+    /**
+     * Gets whether the method being translated is static.
+     *
+     * @return whether the method being translated is static
+     */
+    private boolean isStatic() {
+        int accessFlags = method.getAccessFlags();
+        return (accessFlags & AccessFlags.ACC_STATIC) != 0;
+    }
+
+    /**
+     * Gets the total number of registers used for "normal" purposes (i.e.,
+     * for the straightforward translation from the original Java).
+     *
+     * @return {@code >= 0;} the total number of registers used
+     */
+    private int getNormalRegCount() {
+        return maxLocals + method.getMaxStack();
+    }
+
+    /**
+     * Gets the register spec to use to hold the object to synchronize on,
+     * for a synchronized method.
+     *
+     * @return {@code non-null;} the register spec
+     */
+    private RegisterSpec getSynchReg() {
+        /*
+         * We use the register that is just past the deepest possible
+         * stack element, with a minimum of v1 since v0 is what's
+         * always used to hold the caught exception when unwinding. We
+         * don't need to do anything else special at this level, since
+         * later passes will merely notice the highest register used
+         * by explicit inspection.
+         */
+        int reg = getNormalRegCount();
+        return RegisterSpec.make((reg < 1) ? 1 : reg, Type.OBJECT);
+    }
+
+    /**
+     * Searches {@link #result} for a block with the given label. Returns its
+     * index if found, or returns {@code -1} if there is no such block.
+     *
+     * @param label the label to look for
+     * @return {@code >= -1;} the index for the block with the given label or
+     * {@code -1} if there is no such block
+     */
+    private int labelToResultIndex(int label) {
+        int sz = result.size();
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = result.get(i);
+            if (one.getLabel() == label) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Searches {@link #result} for a block with the given label. Returns it if
+     * found, or throws an exception if there is no such block.
+     *
+     * @param label the label to look for
+     * @return {@code non-null;} the block with the given label
+     */
+    private BasicBlock labelToBlock(int label) {
+        int idx = labelToResultIndex(label);
+
+        if (idx < 0) {
+            throw new IllegalArgumentException("no such label " +
+                    Hex.u2(label));
+        }
+
+        return result.get(idx);
+    }
+
+    /**
+     * Adds a block to the output result.
+     *
+     * @param block {@code non-null;} the block to add
+     * @param subroutines {@code non-null;} subroutine label list
+     * as described in {@link Frame#getSubroutines}
+     */
+    private void addBlock(BasicBlock block, IntList subroutines) {
+        if (block == null) {
+            throw new NullPointerException("block == null");
+        }
+
+        result.add(block);
+        subroutines.throwIfMutable();
+        resultSubroutines.add(subroutines);
+    }
+
+    /**
+     * Adds or replace a block in the output result. If this is a
+     * replacement, then any extra blocks that got added with the
+     * original get removed as a result of calling this method.
+     *
+     * @param block {@code non-null;} the block to add or replace
+     * @param subroutines {@code non-null;} subroutine label list
+     * as described in {@link Frame#getSubroutines}
+     * @return {@code true} if the block was replaced or
+     * {@code false} if it was added for the first time
+     */
+    private boolean addOrReplaceBlock(BasicBlock block, IntList subroutines) {
+        if (block == null) {
+            throw new NullPointerException("block == null");
+        }
+
+        int idx = labelToResultIndex(block.getLabel());
+        boolean ret;
+
+        if (idx < 0) {
+            ret = false;
+        } else {
+            /*
+             * We are replacing a pre-existing block, so find any
+             * blocks that got added as part of the original and
+             * remove those too. Such blocks are (possibly indirect)
+             * successors of this block which are out of the range of
+             * normally-translated blocks.
+             */
+            removeBlockAndSpecialSuccessors(idx);
+            ret = true;
+        }
+
+        result.add(block);
+        subroutines.throwIfMutable();
+        resultSubroutines.add(subroutines);
+        return ret;
+    }
+
+    /**
+     * Adds or replaces a block in the output result. Do not delete
+     * any successors.
+     *
+     * @param block {@code non-null;} the block to add or replace
+     * @param subroutines {@code non-null;} subroutine label list
+     * as described in {@link Frame#getSubroutines}
+     * @return {@code true} if the block was replaced or
+     * {@code false} if it was added for the first time
+     */
+    private boolean addOrReplaceBlockNoDelete(BasicBlock block,
+            IntList subroutines) {
+        if (block == null) {
+            throw new NullPointerException("block == null");
+        }
+
+        int idx = labelToResultIndex(block.getLabel());
+        boolean ret;
+
+        if (idx < 0) {
+            ret = false;
+        } else {
+            result.remove(idx);
+            resultSubroutines.remove(idx);
+            ret = true;
+        }
+
+        result.add(block);
+        subroutines.throwIfMutable();
+        resultSubroutines.add(subroutines);
+        return ret;
+    }
+
+    /**
+     * Helper for {@link #addOrReplaceBlock} which recursively removes
+     * the given block and all blocks that are (direct and indirect)
+     * successors of it whose labels indicate that they are not in the
+     * normally-translated range.
+     *
+     * @param idx {@code non-null;} block to remove (etc.)
+     */
+    private void removeBlockAndSpecialSuccessors(int idx) {
+        int minLabel = getMinimumUnreservedLabel();
+        BasicBlock block = result.get(idx);
+        IntList successors = block.getSuccessors();
+        int sz = successors.size();
+
+        result.remove(idx);
+        resultSubroutines.remove(idx);
+
+        for (int i = 0; i < sz; i++) {
+            int label = successors.get(i);
+            if (label >= minLabel) {
+                idx = labelToResultIndex(label);
+                if (idx < 0) {
+                    throw new RuntimeException("Invalid label "
+                            + Hex.u2(label));
+                }
+                removeBlockAndSpecialSuccessors(idx);
+            }
+        }
+    }
+
+    /**
+     * Extracts the resulting {@link RopMethod} from the instance.
+     *
+     * @return {@code non-null;} the method object
+     */
+    private RopMethod getRopMethod() {
+
+        // Construct the final list of blocks.
+
+        int sz = result.size();
+        BasicBlockList bbl = new BasicBlockList(sz);
+        for (int i = 0; i < sz; i++) {
+            bbl.set(i, result.get(i));
+        }
+        bbl.setImmutable();
+
+        // Construct the method object to wrap it all up.
+
+        /*
+         * Note: The parameter assignment block is always the first
+         * that should be executed, hence the second argument to the
+         * constructor.
+         */
+        return new RopMethod(bbl, getSpecialLabel(PARAM_ASSIGNMENT));
+    }
+
+    /**
+     * Does the conversion.
+     */
+    private void doit() {
+        int[] workSet = Bits.makeBitSet(maxLabel);
+
+        Bits.set(workSet, 0);
+        addSetupBlocks();
+        setFirstFrame();
+
+        for (;;) {
+            int offset = Bits.findFirst(workSet, 0);
+            if (offset < 0) {
+                break;
+            }
+            Bits.clear(workSet, offset);
+            ByteBlock block = blocks.labelToBlock(offset);
+            Frame frame = startFrames[offset];
+            try {
+                processBlock(block, frame, workSet);
+            } catch (SimException ex) {
+                ex.addContext("...while working on block " + Hex.u2(offset));
+                throw ex;
+            }
+        }
+
+        addReturnBlock();
+        addSynchExceptionHandlerBlock();
+        addExceptionSetupBlocks();
+
+        if (hasSubroutines) {
+            // Subroutines are very rare, so skip this step if it's n/a
+            inlineSubroutines();
+        }
+    }
+
+    /**
+     * Sets up the first frame to contain all the incoming parameters in
+     * locals.
+     */
+    private void setFirstFrame() {
+        Prototype desc = method.getEffectiveDescriptor();
+        startFrames[0].initializeWithParameters(desc.getParameterTypes());
+        startFrames[0].setImmutable();
+    }
+
+    /**
+     * Processes the given block.
+     *
+     * @param block {@code non-null;} block to process
+     * @param frame {@code non-null;} start frame for the block
+     * @param workSet {@code non-null;} bits representing work to do,
+     * which this method may add to
+     */
+    private void processBlock(ByteBlock block, Frame frame, int[] workSet) {
+        // Prepare the list of caught exceptions for this block.
+        ByteCatchList catches = block.getCatches();
+        machine.startBlock(catches.toRopCatchList());
+
+        /*
+         * Using a copy of the given frame, simulate each instruction,
+         * calling into machine for each.
+         */
+        frame = frame.copy();
+        sim.simulate(block, frame);
+        frame.setImmutable();
+
+        int extraBlockCount = machine.getExtraBlockCount();
+        ArrayList<Insn> insns = machine.getInsns();
+        int insnSz = insns.size();
+
+        /*
+         * Merge the frame into each possible non-exceptional
+         * successor.
+         */
+
+        int catchSz = catches.size();
+        IntList successors = block.getSuccessors();
+
+        int startSuccessorIndex;
+
+        Subroutine calledSubroutine = null;
+        if (machine.hasJsr()) {
+            /*
+             * If this frame ends in a JSR, only merge our frame with
+             * the subroutine start, not the subroutine's return target.
+             */
+            startSuccessorIndex = 1;
+
+            int subroutineLabel = successors.get(1);
+
+            if (subroutines[subroutineLabel] == null) {
+                subroutines[subroutineLabel] =
+                    new Subroutine (subroutineLabel);
+            }
+
+            subroutines[subroutineLabel].addCallerBlock(block.getLabel());
+
+            calledSubroutine = subroutines[subroutineLabel];
+        } else if (machine.hasRet()) {
+            /*
+             * This block ends in a ret, which means it's the final block
+             * in some subroutine. Ultimately, this block will be copied
+             * and inlined for each call and then disposed of.
+             */
+
+            ReturnAddress ra = machine.getReturnAddress();
+            int subroutineLabel = ra.getSubroutineAddress();
+
+            if (subroutines[subroutineLabel] == null) {
+                subroutines[subroutineLabel]
+                        = new Subroutine (subroutineLabel, block.getLabel());
+            } else {
+                subroutines[subroutineLabel].addRetBlock(block.getLabel());
+            }
+
+            successors = subroutines[subroutineLabel].getSuccessors();
+            subroutines[subroutineLabel]
+                    .mergeToSuccessors(frame, workSet);
+            // Skip processing below since we just did it.
+            startSuccessorIndex = successors.size();
+        } else if (machine.wereCatchesUsed()) {
+            /*
+             * If there are catches, then the first successors
+             * (which will either be all of them or all but the last one)
+             * are catch targets.
+             */
+            startSuccessorIndex = catchSz;
+        } else {
+            startSuccessorIndex = 0;
+        }
+
+        int succSz = successors.size();
+        for (int i = startSuccessorIndex; i < succSz;
+             i++) {
+            int succ = successors.get(i);
+            try {
+                mergeAndWorkAsNecessary(succ, block.getLabel(),
+                        calledSubroutine, frame, workSet);
+            } catch (SimException ex) {
+                ex.addContext("...while merging to block " + Hex.u2(succ));
+                throw ex;
+            }
+        }
+
+        if ((succSz == 0) && machine.returns()) {
+            /*
+             * The block originally contained a return, but it has
+             * been made to instead end with a goto, and we need to
+             * tell it at this point that its sole successor is the
+             * return block. This has to happen after the merge loop
+             * above, since, at this point, the return block doesn't
+             * actually exist; it gets synthesized at the end of
+             * processing the original blocks.
+             */
+            successors = IntList.makeImmutable(getSpecialLabel(RETURN));
+            succSz = 1;
+        }
+
+        int primarySucc;
+
+        if (succSz == 0) {
+            primarySucc = -1;
+        } else {
+            primarySucc = machine.getPrimarySuccessorIndex();
+            if (primarySucc >= 0) {
+                primarySucc = successors.get(primarySucc);
+            }
+        }
+
+        /*
+         * This variable is true only when the method is synchronized and
+         * the block being processed can possibly throw an exception.
+         */
+        boolean synch = isSynchronized() && machine.canThrow();
+
+        if (synch || (catchSz != 0)) {
+            /*
+             * Deal with exception handlers: Merge an exception-catch
+             * frame into each possible exception handler, and
+             * construct a new set of successors to point at the
+             * exception handler setup blocks (which get synthesized
+             * at the very end of processing).
+             */
+            boolean catchesAny = false;
+            IntList newSucc = new IntList(succSz);
+            for (int i = 0; i < catchSz; i++) {
+                ByteCatchList.Item one = catches.get(i);
+                CstType exceptionClass = one.getExceptionClass();
+                int targ = one.getHandlerPc();
+
+                catchesAny |= (exceptionClass == CstType.OBJECT);
+
+                Frame f = frame.makeExceptionHandlerStartFrame(exceptionClass);
+
+                try {
+                    mergeAndWorkAsNecessary(targ, block.getLabel(),
+                            null, f, workSet);
+                } catch (SimException ex) {
+                    ex.addContext("...while merging exception to block " +
+                                  Hex.u2(targ));
+                    throw ex;
+                }
+
+                /*
+                 * Set up the exception handler type, by setting it if
+                 * the given handler has yet to be encountered, or by
+                 * conservatively unioning if it has.
+                 */
+                Type already = catchTypes[targ];
+                if (already == null) {
+                    catchTypes[targ] = exceptionClass.getClassType();
+                } else if (already != exceptionClass.getClassType()) {
+                    catchTypes[targ] = Type.OBJECT;
+                }
+
+                /*
+                 * The synthesized exception setup block will have the
+                 * label getExceptionSetupLabel(targ).
+                 */
+                newSucc.add(getExceptionSetupLabel(targ));
+            }
+
+            if (synch && !catchesAny) {
+                /*
+                 * The method is synchronized and this block doesn't
+                 * already have a catch-all handler, so add one to the
+                 * end, both in the successors and in the throwing
+                 * instruction(s) at the end of the block (which is where
+                 * the caught classes live).
+                 */
+                newSucc.add(getSpecialLabel(SYNCH_CATCH_1));
+                synchNeedsExceptionHandler = true;
+
+                for (int i = insnSz - extraBlockCount - 1; i < insnSz; i++) {
+                    Insn insn = insns.get(i);
+                    if (insn.canThrow()) {
+                        insn = insn.withAddedCatch(Type.OBJECT);
+                        insns.set(i, insn);
+                    }
+                }
+            }
+
+            if (primarySucc >= 0) {
+                newSucc.add(primarySucc);
+            }
+
+            newSucc.setImmutable();
+            successors = newSucc;
+        }
+
+        // Construct the final resulting block(s), and store it (them).
+
+        int primarySuccListIndex = successors.indexOf(primarySucc);
+
+        /*
+         * If there are any extra blocks, work backwards through the
+         * list of instructions, adding single-instruction blocks, and
+         * resetting the successors variables as appropriate.
+         */
+        for (/*extraBlockCount*/; extraBlockCount > 0; extraBlockCount--) {
+            /*
+             * Some of the blocks that the RopperMachine wants added
+             * are for move-result insns, and these need goto insns as well.
+             */
+            Insn extraInsn = insns.get(--insnSz);
+            boolean needsGoto
+                    = extraInsn.getOpcode().getBranchingness()
+                        == Rop.BRANCH_NONE;
+            InsnList il = new InsnList(needsGoto ? 2 : 1);
+            IntList extraBlockSuccessors = successors;
+
+            il.set(0, extraInsn);
+
+            if (needsGoto) {
+                il.set(1, new PlainInsn(Rops.GOTO,
+                        extraInsn.getPosition(), null,
+                        RegisterSpecList.EMPTY));
+                /*
+                 * Obviously, this block won't be throwing an exception
+                 * so it should only have one successor.
+                 */
+                extraBlockSuccessors = IntList.makeImmutable(primarySucc);
+            }
+            il.setImmutable();
+
+            int label = getAvailableLabel();
+            BasicBlock bb = new BasicBlock(label, il, extraBlockSuccessors,
+                    primarySucc);
+            // All of these extra blocks will be in the same subroutine
+            addBlock(bb, frame.getSubroutines());
+
+            successors = successors.mutableCopy();
+            successors.set(primarySuccListIndex, label);
+            successors.setImmutable();
+            primarySucc = label;
+        }
+
+        Insn lastInsn = (insnSz == 0) ? null : insns.get(insnSz - 1);
+
+        /*
+         * Add a goto to the end of the block if it doesn't already
+         * end with a branch, to maintain the invariant that all
+         * blocks end with a branch of some sort or other. Note that
+         * it is possible for there to be blocks for which no
+         * instructions were ever output (e.g., only consist of pop*
+         * in the original Java bytecode).
+         */
+        if ((lastInsn == null) ||
+            (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE)) {
+            SourcePosition pos = (lastInsn == null) ? SourcePosition.NO_INFO :
+                lastInsn.getPosition();
+            insns.add(new PlainInsn(Rops.GOTO, pos, null,
+                                    RegisterSpecList.EMPTY));
+            insnSz++;
+        }
+
+        /*
+         * Construct a block for the remaining instructions (which in
+         * the usual case is all of them).
+         */
+
+        InsnList il = new InsnList(insnSz);
+        for (int i = 0; i < insnSz; i++) {
+            il.set(i, insns.get(i));
+        }
+        il.setImmutable();
+
+        BasicBlock bb =
+            new BasicBlock(block.getLabel(), il, successors, primarySucc);
+        addOrReplaceBlock(bb, frame.getSubroutines());
+    }
+
+    /**
+     * Helper for {@link #processBlock}, which merges frames and
+     * adds to the work set, as necessary.
+     *
+     * @param label {@code >= 0;} label to work on
+     * @param pred  predecessor label; must be {@code >= 0} when
+     * {@code label} is a subroutine start block and calledSubroutine
+     * is non-null. Otherwise, may be -1.
+     * @param calledSubroutine {@code null-ok;} a Subroutine instance if
+     * {@code label} is the first block in a subroutine.
+     * @param frame {@code non-null;} new frame for the labelled block
+     * @param workSet {@code non-null;} bits representing work to do,
+     * which this method may add to
+     */
+    private void mergeAndWorkAsNecessary(int label, int pred,
+            Subroutine calledSubroutine, Frame frame, int[] workSet) {
+        Frame existing = startFrames[label];
+        Frame merged;
+
+        if (existing != null) {
+            /*
+             * Some other block also continues at this label. Merge
+             * the frames, and re-set the bit in the work set if there
+             * was a change.
+             */
+            if (calledSubroutine != null) {
+                merged = existing.mergeWithSubroutineCaller(frame,
+                        calledSubroutine.getStartBlock(), pred);
+            } else {
+                merged = existing.mergeWith(frame);
+            }
+            if (merged != existing) {
+                startFrames[label] = merged;
+                Bits.set(workSet, label);
+            }
+        } else {
+            // This is the first time this label has been encountered.
+            if (calledSubroutine != null) {
+                startFrames[label]
+                        = frame.makeNewSubroutineStartFrame(label, pred);
+            } else {
+                startFrames[label] = frame;
+            }
+            Bits.set(workSet, label);
+        }
+    }
+
+    /**
+     * Constructs and adds the blocks that perform setup for the rest of
+     * the method. This includes a first block which merely contains
+     * assignments from parameters to the same-numbered registers and
+     * a possible second block which deals with synchronization.
+     */
+    private void addSetupBlocks() {
+        LocalVariableList localVariables = method.getLocalVariables();
+        SourcePosition pos = method.makeSourcePosistion(0);
+        Prototype desc = method.getEffectiveDescriptor();
+        StdTypeList params = desc.getParameterTypes();
+        int sz = params.size();
+        InsnList insns = new InsnList(sz + 1);
+        int at = 0;
+
+        for (int i = 0; i < sz; i++) {
+            Type one = params.get(i);
+            LocalVariableList.Item local =
+                localVariables.pcAndIndexToLocal(0, at);
+            RegisterSpec result = (local == null) ?
+                RegisterSpec.make(at, one) :
+                RegisterSpec.makeLocalOptional(at, one, local.getLocalItem());
+
+            Insn insn = new PlainCstInsn(Rops.opMoveParam(one), pos, result,
+                                         RegisterSpecList.EMPTY,
+                                         CstInteger.make(at));
+            insns.set(i, insn);
+            at += one.getCategory();
+        }
+
+        insns.set(sz, new PlainInsn(Rops.GOTO, pos, null,
+                                    RegisterSpecList.EMPTY));
+        insns.setImmutable();
+
+        boolean synch = isSynchronized();
+        int label = synch ? getSpecialLabel(SYNCH_SETUP_1) : 0;
+        BasicBlock bb =
+            new BasicBlock(getSpecialLabel(PARAM_ASSIGNMENT), insns,
+                           IntList.makeImmutable(label), label);
+        addBlock(bb, IntList.EMPTY);
+
+        if (synch) {
+            RegisterSpec synchReg = getSynchReg();
+            Insn insn;
+            if (isStatic()) {
+                insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
+                                           RegisterSpecList.EMPTY,
+                                           StdTypeList.EMPTY,
+                                           method.getDefiningClass());
+                insns = new InsnList(1);
+                insns.set(0, insn);
+            } else {
+                insns = new InsnList(2);
+                insn = new PlainCstInsn(Rops.MOVE_PARAM_OBJECT, pos,
+                                        synchReg, RegisterSpecList.EMPTY,
+                                        CstInteger.VALUE_0);
+                insns.set(0, insn);
+                insns.set(1, new PlainInsn(Rops.GOTO, pos, null,
+                                           RegisterSpecList.EMPTY));
+            }
+
+            int label2 = getSpecialLabel(SYNCH_SETUP_2);
+            insns.setImmutable();
+            bb = new BasicBlock(label, insns,
+                                IntList.makeImmutable(label2), label2);
+            addBlock(bb, IntList.EMPTY);
+
+            insns = new InsnList(isStatic() ? 2 : 1);
+
+            if (isStatic()) {
+                insns.set(0, new PlainInsn(Rops.opMoveResultPseudo(synchReg),
+                        pos, synchReg, RegisterSpecList.EMPTY));
+            }
+
+            insn = new ThrowingInsn(Rops.MONITOR_ENTER, pos,
+                                    RegisterSpecList.make(synchReg),
+                                    StdTypeList.EMPTY);
+            insns.set(isStatic() ? 1 :0, insn);
+            insns.setImmutable();
+            bb = new BasicBlock(label2, insns, IntList.makeImmutable(0), 0);
+            addBlock(bb, IntList.EMPTY);
+        }
+    }
+
+    /**
+     * Constructs and adds the return block, if necessary. The return
+     * block merely contains an appropriate {@code return}
+     * instruction.
+     */
+    private void addReturnBlock() {
+        Rop returnOp = machine.getReturnOp();
+
+        if (returnOp == null) {
+            /*
+             * The method being converted never returns normally, so there's
+             * no need for a return block.
+             */
+            return;
+        }
+
+        SourcePosition returnPos = machine.getReturnPosition();
+        int label = getSpecialLabel(RETURN);
+
+        if (isSynchronized()) {
+            InsnList insns = new InsnList(1);
+            Insn insn = new ThrowingInsn(Rops.MONITOR_EXIT, returnPos,
+                                         RegisterSpecList.make(getSynchReg()),
+                                         StdTypeList.EMPTY);
+            insns.set(0, insn);
+            insns.setImmutable();
+
+            int nextLabel = getSpecialLabel(SYNCH_RETURN);
+            BasicBlock bb =
+                new BasicBlock(label, insns,
+                               IntList.makeImmutable(nextLabel), nextLabel);
+            addBlock(bb, IntList.EMPTY);
+
+            label = nextLabel;
+        }
+
+        InsnList insns = new InsnList(1);
+        TypeList sourceTypes = returnOp.getSources();
+        RegisterSpecList sources;
+
+        if (sourceTypes.size() == 0) {
+            sources = RegisterSpecList.EMPTY;
+        } else {
+            RegisterSpec source = RegisterSpec.make(0, sourceTypes.getType(0));
+            sources = RegisterSpecList.make(source);
+        }
+
+        Insn insn = new PlainInsn(returnOp, returnPos, null, sources);
+        insns.set(0, insn);
+        insns.setImmutable();
+
+        BasicBlock bb = new BasicBlock(label, insns, IntList.EMPTY, -1);
+        addBlock(bb, IntList.EMPTY);
+    }
+
+    /**
+     * Constructs and adds, if necessary, the catch-all exception handler
+     * block to deal with unwinding the lock taken on entry to a synchronized
+     * method.
+     */
+    private void addSynchExceptionHandlerBlock() {
+        if (!synchNeedsExceptionHandler) {
+            /*
+             * The method being converted either isn't synchronized or
+             * can't possibly throw exceptions in its main body, so
+             * there's no need for a synchronized method exception
+             * handler.
+             */
+            return;
+        }
+
+        SourcePosition pos = method.makeSourcePosistion(0);
+        RegisterSpec exReg = RegisterSpec.make(0, Type.THROWABLE);
+        BasicBlock bb;
+        Insn insn;
+
+        InsnList insns = new InsnList(2);
+        insn = new PlainInsn(Rops.opMoveException(Type.THROWABLE), pos,
+                             exReg, RegisterSpecList.EMPTY);
+        insns.set(0, insn);
+        insn = new ThrowingInsn(Rops.MONITOR_EXIT, pos,
+                                RegisterSpecList.make(getSynchReg()),
+                                StdTypeList.EMPTY);
+        insns.set(1, insn);
+        insns.setImmutable();
+
+        int label2 = getSpecialLabel(SYNCH_CATCH_2);
+        bb = new BasicBlock(getSpecialLabel(SYNCH_CATCH_1), insns,
+                            IntList.makeImmutable(label2), label2);
+        addBlock(bb, IntList.EMPTY);
+
+        insns = new InsnList(1);
+        insn = new ThrowingInsn(Rops.THROW, pos,
+                                RegisterSpecList.make(exReg),
+                                StdTypeList.EMPTY);
+        insns.set(0, insn);
+        insns.setImmutable();
+
+        bb = new BasicBlock(label2, insns, IntList.EMPTY, -1);
+        addBlock(bb, IntList.EMPTY);
+    }
+
+    /**
+     * Creates the exception handler setup blocks. "maxLocals"
+     * below is because that's the register number corresponding
+     * to the sole element on a one-deep stack (which is the
+     * situation at the start of an exception handler block).
+     */
+    private void addExceptionSetupBlocks() {
+
+        int len = catchTypes.length;
+        for (int i = 0; i < len; i++) {
+            Type one = catchTypes[i];
+            if (one != null) {
+                Insn proto = labelToBlock(i).getFirstInsn();
+                SourcePosition pos = proto.getPosition();
+                InsnList il = new InsnList(2);
+
+                Insn insn = new PlainInsn(Rops.opMoveException(one),
+                                          pos,
+                                          RegisterSpec.make(maxLocals, one),
+                                          RegisterSpecList.EMPTY);
+                il.set(0, insn);
+
+                insn = new PlainInsn(Rops.GOTO, pos, null,
+                                     RegisterSpecList.EMPTY);
+                il.set(1, insn);
+                il.setImmutable();
+
+                BasicBlock bb = new BasicBlock(getExceptionSetupLabel(i),
+                                               il,
+                                               IntList.makeImmutable(i),
+                                               i);
+                addBlock(bb, startFrames[i].getSubroutines());
+            }
+        }
+    }
+
+    /**
+     * Checks to see if the basic block is a subroutine caller block.
+     *
+     * @param bb {@code non-null;} the basic block in question
+     * @return true if this block calls a subroutine
+     */
+    private boolean isSubroutineCaller(BasicBlock bb) {
+        IntList successors = bb.getSuccessors();
+        if (successors.size() < 2) return false;
+
+        int subLabel = successors.get(1);
+
+        return (subLabel < subroutines.length)
+                && (subroutines[subLabel] != null);
+    }
+
+    /**
+     * Inlines any subroutine calls.
+     */
+    private void inlineSubroutines() {
+        final IntList reachableSubroutineCallerLabels = new IntList(4);
+
+        /*
+         * Compile a list of all subroutine calls reachable
+         * through the normal (non-subroutine) flow.  We do this first, since
+         * we'll be affecting the call flow as we go.
+         *
+         * Start at label 0 --  the param assignment block has nothing for us
+         */
+        forEachNonSubBlockDepthFirst(0, new BasicBlock.Visitor() {
+            public void visitBlock(BasicBlock b) {
+                if (isSubroutineCaller(b)) {
+                    reachableSubroutineCallerLabels.add(b.getLabel());
+                }
+            }
+        });
+
+        /*
+         * Convert the resultSubroutines list, indexed by block index,
+         * to a label-to-subroutines mapping used by the inliner.
+         */
+        int largestAllocedLabel = getAvailableLabel();
+        ArrayList<IntList> labelToSubroutines
+                = new ArrayList<IntList>(largestAllocedLabel);
+        for (int i = 0; i < largestAllocedLabel; i++) {
+            labelToSubroutines.add(null);
+        }
+
+        for (int i = 0; i < result.size(); i++) {
+            BasicBlock b = result.get(i);
+            if (b == null) {
+                continue;
+            }
+            IntList subroutineList = resultSubroutines.get(i);
+            labelToSubroutines.set(b.getLabel(), subroutineList);
+        }
+
+        /*
+         * Inline all reachable subroutines.
+         * Inner subroutines will be inlined as they are encountered.
+         */
+        int sz = reachableSubroutineCallerLabels.size();
+        for (int i = 0 ; i < sz ; i++) {
+            int label = reachableSubroutineCallerLabels.get(i);
+            new SubroutineInliner(
+                    new LabelAllocator(getAvailableLabel()),
+                    labelToSubroutines)
+                    .inlineSubroutineCalledFrom(labelToBlock(label));
+        }
+
+        // Now find the blocks that aren't reachable and remove them
+        deleteUnreachableBlocks();
+    }
+
+    /**
+     * Deletes all blocks that cannot be reached. This is run to delete
+     * original subroutine blocks after subroutine inlining.
+     */
+    private void deleteUnreachableBlocks() {
+        final IntList reachableLabels = new IntList(result.size());
+
+        // subroutine inlining is done now and we won't update this list here
+        resultSubroutines.clear();
+
+        forEachNonSubBlockDepthFirst(getSpecialLabel(PARAM_ASSIGNMENT),
+                new BasicBlock.Visitor() {
+
+            public void visitBlock(BasicBlock b) {
+                reachableLabels.add(b.getLabel());
+            }
+        });
+
+        reachableLabels.sort();
+
+        for (int i = result.size() - 1 ; i >= 0 ; i--) {
+            if (reachableLabels.indexOf(result.get(i).getLabel()) < 0) {
+                result.remove(i);
+                // unnecessary here really, since subroutine inlining is done
+                //resultSubroutines.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Allocates labels, without requiring previously allocated labels
+     * to have been added to the blocks list.
+     */
+    private static class LabelAllocator {
+        int nextAvailableLabel;
+
+        /**
+         * @param startLabel available label to start allocating from
+         */
+        LabelAllocator(int startLabel) {
+            nextAvailableLabel = startLabel;
+        }
+
+        /**
+         * @return next available label
+         */
+        int getNextLabel() {
+            return nextAvailableLabel++;
+        }
+    }
+
+    /**
+     * Inlines a subroutine. Start by calling
+     * {@link #inlineSubroutineCalledFrom}.
+     */
+    private class SubroutineInliner {
+        /**
+         * maps original label to the label that will be used by the
+         * inlined version
+         */
+        private final HashMap<Integer, Integer> origLabelToCopiedLabel;
+
+        /** set of original labels that need to be copied */
+        private final BitSet workList;
+
+        /** the label of the original start block for this subroutine */
+        private int subroutineStart;
+
+        /** the label of the ultimate return block */
+        private int subroutineSuccessor;
+
+        /** used for generating new labels for copied blocks */
+        private final LabelAllocator labelAllocator;
+
+        /**
+         * A mapping, indexed by label, to subroutine nesting list.
+         * The subroutine nest list is as returned by
+         * {@link Frame#getSubroutines}.
+         */
+        private final ArrayList<IntList> labelToSubroutines;
+
+        SubroutineInliner(final LabelAllocator labelAllocator,
+                ArrayList<IntList> labelToSubroutines) {
+            origLabelToCopiedLabel = new HashMap<Integer, Integer>();
+
+            workList = new BitSet(maxLabel);
+
+            this.labelAllocator = labelAllocator;
+            this.labelToSubroutines = labelToSubroutines;
+        }
+
+        /**
+         * Inlines a subroutine.
+         *
+         * @param b block where {@code jsr} occurred in the original bytecode
+         */
+        void inlineSubroutineCalledFrom(final BasicBlock b) {
+            /*
+             * The 0th successor of a subroutine caller block is where
+             * the subroutine should return to. The 1st successor is
+             * the start block of the subroutine.
+             */
+            subroutineSuccessor = b.getSuccessors().get(0);
+            subroutineStart = b.getSuccessors().get(1);
+
+            /*
+             * This allocates an initial label and adds the first
+             * block to the worklist.
+             */
+            int newSubStartLabel = mapOrAllocateLabel(subroutineStart);
+
+            for (int label = workList.nextSetBit(0); label >= 0;
+                 label = workList.nextSetBit(0)) {
+                workList.clear(label);
+                int newLabel = origLabelToCopiedLabel.get(label);
+
+                copyBlock(label, newLabel);
+
+                if (isSubroutineCaller(labelToBlock(label))) {
+                    new SubroutineInliner(labelAllocator, labelToSubroutines)
+                        .inlineSubroutineCalledFrom(labelToBlock(newLabel));
+                }
+            }
+
+            /*
+             * Replace the original caller block, since we now have a
+             * new successor
+             */
+
+            addOrReplaceBlockNoDelete(
+                new BasicBlock(b.getLabel(), b.getInsns(),
+                    IntList.makeImmutable (newSubStartLabel),
+                            newSubStartLabel),
+                labelToSubroutines.get(b.getLabel()));
+       }
+
+        /**
+         * Copies a basic block, mapping its successors along the way.
+         *
+         * @param origLabel original block label
+         * @param newLabel label that the new block should have
+         */
+       private void copyBlock(int origLabel, int newLabel) {
+
+            BasicBlock origBlock = labelToBlock(origLabel);
+
+            final IntList origSuccessors = origBlock.getSuccessors();
+            IntList successors;
+            int primarySuccessor = -1;
+            Subroutine subroutine;
+
+            if (isSubroutineCaller(origBlock)) {
+                /*
+                 * A subroutine call inside a subroutine call.
+                 * Set up so we can recurse. The caller block should have
+                 * it's first successor be a copied block that will be
+                 * the subroutine's return point. It's second successor will
+                 * be copied when we recurse, and remains as the original
+                 * label of the start of the inner subroutine.
+                 */
+
+                successors = IntList.makeImmutable(
+                        mapOrAllocateLabel(origSuccessors.get(0)),
+                        origSuccessors.get(1));
+                // primary successor will be set when this block is replaced
+            } else if (null
+                    != (subroutine = subroutineFromRetBlock(origLabel))) {
+                /*
+                 * this is a ret block -- its successor
+                 * should be subroutineSuccessor
+                 */
+
+                // Sanity check
+                if (subroutine.startBlock != subroutineStart) {
+                    throw new RuntimeException (
+                            "ret instruction returns to label "
+                            + Hex.u2 (subroutine.startBlock)
+                            + " expected: " + Hex.u2(subroutineStart));
+                }
+
+                successors = IntList.makeImmutable(subroutineSuccessor);
+                primarySuccessor = subroutineSuccessor;
+            } else {
+                // Map all the successor labels
+
+                int origPrimary = origBlock.getPrimarySuccessor();
+                int sz = origSuccessors.size();
+
+                successors = new IntList(sz);
+
+                for (int i = 0 ; i < sz ; i++) {
+                    int origSuccLabel = origSuccessors.get(i);
+                    int newSuccLabel =  mapOrAllocateLabel(origSuccLabel);
+
+                    successors.add(newSuccLabel);
+
+                    if (origPrimary == origSuccLabel) {
+                        primarySuccessor = newSuccLabel;
+                    }
+                }
+
+                successors.setImmutable();
+            }
+
+            addBlock (
+                new BasicBlock(newLabel,
+                    filterMoveReturnAddressInsns(origBlock.getInsns()),
+                    successors, primarySuccessor),
+                    labelToSubroutines.get(newLabel));
+        }
+
+        /**
+         * Checks to see if a specified label is involved in a specified
+         * subroutine.
+         *
+         * @param label {@code >= 0;} a basic block label
+         * @param subroutineStart {@code >= 0;} a subroutine as identified
+         * by the label of its start block
+         * @return true if the block is dominated by the subroutine call
+         */
+        private boolean involvedInSubroutine(int label, int subroutineStart) {
+            IntList subroutinesList = labelToSubroutines.get(label);
+            return (subroutinesList.size() > 0
+                    && subroutinesList.top() == subroutineStart);
+        }
+
+        /**
+         * Maps the label of a pre-copied block to the label of the inlined
+         * block, allocating a new label and adding it to the worklist
+         * if necessary.  If the origLabel is a "special" label, it
+         * is returned exactly and not scheduled for duplication: copying
+         * never proceeds past a special label, which likely is the function
+         * return block or an immediate predecessor.
+         *
+         * @param origLabel label of original, pre-copied block
+         * @return label for new, inlined block
+         */
+        private int mapOrAllocateLabel(int origLabel) {
+            int resultLabel;
+            Integer mappedLabel = origLabelToCopiedLabel.get(origLabel);
+
+            if (mappedLabel != null) {
+                resultLabel = mappedLabel;
+            } else if (!involvedInSubroutine(origLabel,subroutineStart)) {
+                /*
+                 * A subroutine has ended by some means other than a "ret"
+                 * (which really means a throw caught later).
+                 */
+                resultLabel = origLabel;
+            } else {
+                resultLabel = labelAllocator.getNextLabel();
+                workList.set(origLabel);
+                origLabelToCopiedLabel.put(origLabel, resultLabel);
+
+                // The new label has the same frame as the original label
+                while (labelToSubroutines.size() <= resultLabel) {
+                    labelToSubroutines.add(null);
+                }
+                labelToSubroutines.set(resultLabel,
+                        labelToSubroutines.get(origLabel));
+            }
+
+            return resultLabel;
+        }
+    }
+
+    /**
+     * Finds a {@code Subroutine} that is returned from by a {@code ret} in
+     * a given block.
+     *
+     * @param label A block that originally contained a {@code ret} instruction
+     * @return {@code null-ok;} found subroutine or {@code null} if none
+     * was found
+     */
+    private Subroutine subroutineFromRetBlock(int label) {
+        for (int i = subroutines.length - 1 ; i >= 0 ; i--) {
+            if (subroutines[i] != null) {
+                Subroutine subroutine = subroutines[i];
+
+                if (subroutine.retBlocks.get(label)) {
+                    return subroutine;
+                }
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Removes all {@code move-return-address} instructions, returning a new
+     * {@code InsnList} if necessary. The {@code move-return-address}
+     * insns are dead code after subroutines have been inlined.
+     *
+     * @param insns {@code InsnList} that may contain
+     * {@code move-return-address} insns
+     * @return {@code InsnList} with {@code move-return-address} removed
+     */
+    private InsnList filterMoveReturnAddressInsns(InsnList insns) {
+        int sz;
+        int newSz = 0;
+
+        // First see if we need to filter, and if so what the new size will be
+        sz = insns.size();
+        for (int i = 0; i < sz; i++) {
+            if (insns.get(i).getOpcode() != Rops.MOVE_RETURN_ADDRESS) {
+                newSz++;
+            }
+        }
+
+        if (newSz == sz) {
+            return insns;
+        }
+
+        // Make a new list without the MOVE_RETURN_ADDRESS insns
+        InsnList newInsns = new InsnList(newSz);
+
+        int newIndex = 0;
+        for (int i = 0; i < sz; i++) {
+            Insn insn = insns.get(i);
+            if (insn.getOpcode() != Rops.MOVE_RETURN_ADDRESS) {
+                newInsns.set(newIndex++, insn);
+            }
+        }
+
+        newInsns.setImmutable();
+        return newInsns;
+    }
+
+    /**
+     * Visits each non-subroutine block once in depth-first successor order.
+     *
+     * @param firstLabel label of start block
+     * @param v callback interface
+     */
+    private void forEachNonSubBlockDepthFirst(int firstLabel,
+            BasicBlock.Visitor v) {
+        forEachNonSubBlockDepthFirst0(labelToBlock(firstLabel),
+                v, new BitSet(maxLabel));
+    }
+
+    /**
+     * Visits each block once in depth-first successor order, ignoring
+     * {@code jsr} targets. Worker for {@link #forEachNonSubBlockDepthFirst}.
+     *
+     * @param next next block to visit
+     * @param v callback interface
+     * @param visited set of blocks already visited
+     */
+    private void forEachNonSubBlockDepthFirst0(
+            BasicBlock next, BasicBlock.Visitor v, BitSet visited) {
+        v.visitBlock(next);
+        visited.set(next.getLabel());
+
+        IntList successors = next.getSuccessors();
+        int sz = successors.size();
+
+        for (int i = 0; i < sz; i++) {
+            int succ = successors.get(i);
+
+            if (visited.get(succ)) {
+                continue;
+            }
+
+            if (isSubroutineCaller(next) && i > 0) {
+                // ignore jsr targets
+                continue;
+            }
+
+            /*
+             * Ignore missing labels: they're successors of
+             * subroutines that never invoke a ret.
+             */
+            int idx = labelToResultIndex(succ);
+            if (idx >= 0) {
+                forEachNonSubBlockDepthFirst0(result.get(idx), v, visited);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/RopperMachine.java b/dx/src/com/android/dx/cf/code/RopperMachine.java
new file mode 100644
index 0000000..ebff24b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/RopperMachine.java
@@ -0,0 +1,941 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.FillArrayDataInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.PlainCstInsn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+
+/**
+ * Machine implementation for use by {@link Ropper}.
+ */
+/*package*/ final class RopperMachine extends ValueAwareMachine {
+    /** {@code non-null;} array reflection class */
+    private static final CstType ARRAY_REFLECT_TYPE =
+        new CstType(Type.internClassName("java/lang/reflect/Array"));
+
+    /**
+     * {@code non-null;} method constant for use in converting
+     * {@code multianewarray} instructions
+     */
+    private static final CstMethodRef MULTIANEWARRAY_METHOD =
+        new CstMethodRef(ARRAY_REFLECT_TYPE,
+                         new CstNat(new CstUtf8("newInstance"),
+                                    new CstUtf8("(Ljava/lang/Class;[I)" +
+                                                "Ljava/lang/Object;")));
+
+    /** {@code non-null;} {@link Ropper} controlling this instance */
+    private final Ropper ropper;
+
+    /** {@code non-null;} method being converted */
+    private final ConcreteMethod method;
+
+    /** {@code non-null;} translation advice */
+    private final TranslationAdvice advice;
+
+    /** max locals of the method */
+    private final int maxLocals;
+
+    /** {@code non-null;} instructions for the rop basic block in-progress */
+    private final ArrayList<Insn> insns;
+
+    /** {@code non-null;} catches for the block currently being processed */
+    private TypeList catches;
+
+    /** whether the catches have been used in an instruction */
+    private boolean catchesUsed;
+
+    /** whether the block contains a {@code return} */
+    private boolean returns;
+
+    /** primary successor index */
+    private int primarySuccessorIndex;
+
+    /** {@code >= 0;} number of extra basic blocks required */
+    private int extraBlockCount;
+
+    /** true if last processed block ends with a jsr or jsr_W*/
+    private boolean hasJsr;
+
+    /** true if an exception can be thrown by the last block processed */
+    private boolean blockCanThrow;
+
+    /**
+     * If non-null, the ReturnAddress that was used by the terminating ret
+     * instruction. If null, there was no ret instruction encountered.
+     */
+
+    private ReturnAddress returnAddress;
+
+    /**
+     * {@code null-ok;} the appropriate {@code return} op or {@code null}
+     * if it is not yet known
+     */
+    private Rop returnOp;
+
+    /**
+     * {@code null-ok;} the source position for the return block or {@code null}
+     * if it is not yet known
+     */
+    private SourcePosition returnPosition;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param ropper {@code non-null;} ropper controlling this instance
+     * @param method {@code non-null;} method being converted
+     * @param advice {@code non-null;} translation advice to use
+     */
+    public RopperMachine(Ropper ropper, ConcreteMethod method,
+            TranslationAdvice advice) {
+        super(method.getEffectiveDescriptor());
+
+        if (ropper == null) {
+            throw new NullPointerException("ropper == null");
+        }
+
+        if (advice == null) {
+            throw new NullPointerException("advice == null");
+        }
+
+        this.ropper = ropper;
+        this.method = method;
+        this.advice = advice;
+        this.maxLocals = method.getMaxLocals();
+        this.insns = new ArrayList<Insn>(25);
+        this.catches = null;
+        this.catchesUsed = false;
+        this.returns = false;
+        this.primarySuccessorIndex = -1;
+        this.extraBlockCount = 0;
+        this.blockCanThrow = false;
+        this.returnOp = null;
+        this.returnPosition = null;
+    }
+
+    /**
+     * Gets the instructions array. It is shared and gets modified by
+     * subsequent calls to this instance.
+     *
+     * @return {@code non-null;} the instructions array
+     */
+    public ArrayList<Insn> getInsns() {
+        return insns;
+    }
+
+    /**
+     * Gets the return opcode encountered, if any.
+     *
+     * @return {@code null-ok;} the return opcode
+     */
+    public Rop getReturnOp() {
+        return returnOp;
+    }
+
+    /**
+     * Gets the return position, if known.
+     *
+     * @return {@code null-ok;} the return position
+     */
+    public SourcePosition getReturnPosition() {
+        return returnPosition;
+    }
+
+    /**
+     * Gets ready to start working on a new block. This will clear the
+     * {@link #insns} list, set {@link #catches}, reset whether it has
+     * been used, reset whether the block contains a
+     * {@code return}, and reset {@link #primarySuccessorIndex}.
+     */
+    public void startBlock(TypeList catches) {
+        this.catches = catches;
+
+        insns.clear();
+        catchesUsed = false;
+        returns = false;
+        primarySuccessorIndex = 0;
+        extraBlockCount = 0;
+        blockCanThrow = false;
+        hasJsr = false;
+        returnAddress = null;
+    }
+
+    /**
+     * Gets whether {@link #catches} was used. This indicates that the
+     * last instruction in the block is one of the ones that can throw.
+     *
+     * @return whether {@code catches} has been used
+     */
+    public boolean wereCatchesUsed() {
+        return catchesUsed;
+    }
+
+    /**
+     * Gets whether the block just processed ended with a
+     * {@code return}.
+     *
+     * @return whether the block returns
+     */
+    public boolean returns() {
+        return returns;
+    }
+
+    /**
+     * Gets the primary successor index. This is the index into the
+     * successors list where the primary may be found or
+     * {@code -1} if there are successors but no primary
+     * successor. This may return something other than
+     * {@code -1} in the case of an instruction with no
+     * successors at all (primary or otherwise).
+     *
+     * @return {@code >= -1;} the primary successor index
+     */
+    public int getPrimarySuccessorIndex() {
+        return primarySuccessorIndex;
+    }
+
+    /**
+     * Gets how many extra blocks will be needed to represent the
+     * block currently being translated. Each extra block should consist
+     * of one instruction from the end of the original block.
+     *
+     * @return {@code >= 0;} the number of extra blocks needed
+     */
+    public int getExtraBlockCount() {
+        return extraBlockCount;
+    }
+
+    /**
+     * @return true if at least one of the insn processed since the last
+     * call to startBlock() can throw.
+     */
+    public boolean canThrow() {
+        return blockCanThrow;
+    }
+
+    /**
+     * @return true if a JSR has ben encountered since the last call to
+     * startBlock()
+     */
+    public boolean hasJsr() {
+        return hasJsr;
+    }
+
+    /**
+     * @return {@code true} if a {@code ret} has ben encountered since
+     * the last call to {@code startBlock()}
+     */
+    public boolean hasRet() {
+        return returnAddress != null;
+    }
+
+    /**
+     * @return {@code null-ok;} return address of a {@code ret}
+     * instruction if encountered since last call to startBlock().
+     * {@code null} if no ret instruction encountered.
+     */
+    public ReturnAddress getReturnAddress() {
+        return returnAddress;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void run(Frame frame, int offset, int opcode) {
+        /*
+         * This is the stack pointer after the opcode's arguments have been
+         * popped.
+         */
+        int stackPointer = maxLocals + frame.getStack().size();
+
+        // The sources have to be retrieved before super.run() gets called.
+        RegisterSpecList sources = getSources(opcode, stackPointer);
+        int sourceCount = sources.size();
+
+        super.run(frame, offset, opcode);
+
+        SourcePosition pos = method.makeSourcePosistion(offset);
+        RegisterSpec localTarget = getLocalTarget();
+        int destCount = resultCount();
+        RegisterSpec dest;
+
+        if (destCount == 0) {
+            dest = null;
+            switch (opcode) {
+                case ByteOps.POP:
+                case ByteOps.POP2: {
+                    // These simply don't appear in the rop form.
+                    return;
+                }
+            }
+        } else if (localTarget != null) {
+            dest = localTarget;
+        } else if (destCount == 1) {
+            dest = RegisterSpec.make(stackPointer, result(0));
+        } else {
+            /*
+             * This clause only ever applies to the stack manipulation
+             * ops that have results (that is, dup* and swap but not
+             * pop*).
+             *
+             * What we do is first move all the source registers into
+             * the "temporary stack" area defined for the method, and
+             * then move stuff back down onto the main "stack" in the
+             * arrangement specified by the stack op pattern.
+             *
+             * Note: This code ends up emitting a lot of what will
+             * turn out to be superfluous moves (e.g., moving back and
+             * forth to the same local when doing a dup); however,
+             * that makes this code a bit easier (and goodness knows
+             * it doesn't need any extra complexity), and all the SSA
+             * stuff is going to want to deal with this sort of
+             * superfluous assignment anyway, so it should be a wash
+             * in the end.
+             */
+            int scratchAt = ropper.getFirstTempStackReg();
+            RegisterSpec[] scratchRegs = new RegisterSpec[sourceCount];
+
+            for (int i = 0; i < sourceCount; i++) {
+                RegisterSpec src = sources.get(i);
+                TypeBearer type = src.getTypeBearer();
+                RegisterSpec scratch = src.withReg(scratchAt);
+                insns.add(new PlainInsn(Rops.opMove(type), pos, scratch, src));
+                scratchRegs[i] = scratch;
+                scratchAt += src.getCategory();
+            }
+
+            for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
+                int which = (pattern & 0x0f) - 1;
+                RegisterSpec scratch = scratchRegs[which];
+                TypeBearer type = scratch.getTypeBearer();
+                insns.add(new PlainInsn(Rops.opMove(type), pos,
+                                        scratch.withReg(stackPointer),
+                                        scratch));
+                stackPointer += type.getType().getCategory();
+            }
+            return;
+        }
+
+        TypeBearer destType = (dest != null) ? dest : Type.VOID;
+        Constant cst = getAuxCst();
+        int ropOpcode;
+        Rop rop;
+        Insn insn;
+
+        if (opcode == ByteOps.MULTIANEWARRAY) {
+            blockCanThrow = true;
+
+            // Add the extra instructions for handling multianewarray.
+
+            extraBlockCount = 6;
+
+            /*
+             * Add an array constructor for the int[] containing all the
+             * dimensions.
+             */
+            RegisterSpec dimsReg =
+                RegisterSpec.make(dest.getNextReg(), Type.INT_ARRAY);
+            rop = Rops.opFilledNewArray(Type.INT_ARRAY, sourceCount);
+            insn = new ThrowingCstInsn(rop, pos, sources, catches,
+                    CstType.INT_ARRAY);
+            insns.add(insn);
+
+            // Add a move-result for the new-filled-array
+            rop = Rops.opMoveResult(Type.INT_ARRAY);
+            insn = new PlainInsn(rop, pos, dimsReg, RegisterSpecList.EMPTY);
+            insns.add(insn);
+
+            /*
+             * Add a const-class instruction for the specified array
+             * class.
+             */
+
+            /*
+             * Remove as many dimensions from the originally specified
+             * class as are given in the explicit list of dimensions,
+             * so as to pass the right component class to the standard
+             * Java library array constructor.
+             */
+            Type componentType = ((CstType) cst).getClassType();
+            for (int i = 0; i < sourceCount; i++) {
+                componentType = componentType.getComponentType();
+            }
+
+            RegisterSpec classReg =
+                RegisterSpec.make(dest.getReg(), Type.CLASS);
+
+            if (componentType.isPrimitive()) {
+                /*
+                 * The component type is primitive (e.g., int as opposed
+                 * to Integer), so we have to fetch the corresponding
+                 * TYPE class.
+                 */
+                CstFieldRef typeField =
+                    CstFieldRef.forPrimitiveType(componentType);
+                insn = new ThrowingCstInsn(Rops.GET_STATIC_OBJECT, pos,
+                                           RegisterSpecList.EMPTY,
+                                           catches, typeField);
+            } else {
+                /*
+                 * The component type is an object type, so just make a
+                 * normal class reference.
+                 */
+                insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
+                                           RegisterSpecList.EMPTY, catches,
+                                           new CstType(componentType));
+            }
+
+            insns.add(insn);
+
+            // Add a move-result-pseudo for the get-static or const
+            rop = Rops.opMoveResultPseudo(classReg.getType());
+            insn = new PlainInsn(rop, pos, classReg, RegisterSpecList.EMPTY);
+            insns.add(insn);
+
+            /*
+             * Add a call to the "multianewarray method," that is,
+             * Array.newInstance(class, dims). Note: The result type
+             * of newInstance() is Object, which is why the last
+             * instruction in this sequence is a cast to the right
+             * type for the original instruction.
+             */
+
+            RegisterSpec objectReg =
+                RegisterSpec.make(dest.getReg(), Type.OBJECT);
+
+            insn = new ThrowingCstInsn(
+                    Rops.opInvokeStatic(MULTIANEWARRAY_METHOD.getPrototype()),
+                    pos, RegisterSpecList.make(classReg, dimsReg),
+                    catches, MULTIANEWARRAY_METHOD);
+            insns.add(insn);
+
+            // Add a move-result.
+            rop = Rops.opMoveResult(MULTIANEWARRAY_METHOD.getPrototype()
+                    .getReturnType());
+            insn = new PlainInsn(rop, pos, objectReg, RegisterSpecList.EMPTY);
+            insns.add(insn);
+
+            /*
+             * And finally, set up for the remainder of this method to
+             * add an appropriate cast.
+             */
+
+            opcode = ByteOps.CHECKCAST;
+            sources = RegisterSpecList.make(objectReg);
+        } else if (opcode == ByteOps.JSR) {
+            // JSR has no Rop instruction
+            hasJsr = true;
+            return;
+        } else if (opcode == ByteOps.RET) {
+            try {
+                returnAddress = (ReturnAddress)arg(0);
+            } catch (ClassCastException ex) {
+                throw new RuntimeException(
+                        "Argument to RET was not a ReturnAddress", ex);
+            }
+            // RET has no Rop instruction.
+            return;
+        }
+
+        ropOpcode = jopToRopOpcode(opcode, cst);
+        rop = Rops.ropFor(ropOpcode, destType, sources, cst);
+
+        Insn moveResult = null;
+        if (dest != null && rop.isCallLike()) {
+            /*
+             * We're going to want to have a move-result in the next
+             * basic block.
+             */
+            extraBlockCount++;
+
+            moveResult = new PlainInsn(
+                    Rops.opMoveResult(((CstMethodRef) cst).getPrototype()
+                    .getReturnType()), pos, dest, RegisterSpecList.EMPTY);
+
+            dest = null;
+        } else if (dest != null && rop.canThrow()) {
+            /*
+             * We're going to want to have a move-result-pseudo in the
+             * next basic block.
+             */
+            extraBlockCount++;
+
+            moveResult = new PlainInsn(
+                    Rops.opMoveResultPseudo(dest.getTypeBearer()),
+                    pos, dest, RegisterSpecList.EMPTY);
+
+            dest = null;
+        }
+        if (ropOpcode == RegOps.NEW_ARRAY) {
+            /*
+             * In the original bytecode, this was either a primitive
+             * array constructor "newarray" or an object array
+             * constructor "anewarray". In the former case, there is
+             * no explicit constant, and in the latter, the constant
+             * is for the element type and not the array type. The rop
+             * instruction form for both of these is supposed to be
+             * the resulting array type, so we initialize / alter
+             * "cst" here, accordingly. Conveniently enough, the rop
+             * opcode already gets constructed with the proper array
+             * type.
+             */
+            cst = CstType.intern(rop.getResult());
+        } else if ((cst == null) && (sourceCount == 2)) {
+            TypeBearer lastType = sources.get(1).getTypeBearer();
+
+            if (lastType.isConstant()
+                    && advice.hasConstantOperation(rop,
+                    sources.get(0), sources.get(1))) {
+                /*
+                 * The target architecture has an instruction that can
+                 * build in the constant found in the second argument,
+                 * so pull it out of the sources and just use it as a
+                 * constant here.
+                 */
+                cst = (Constant) lastType;
+                sources = sources.withoutLast();
+                rop = Rops.ropFor(ropOpcode, destType, sources, cst);
+            }
+        }
+
+        SwitchList cases = getAuxCases();
+        ArrayList<Constant> initValues = getInitValues();
+        boolean canThrow = rop.canThrow();
+
+        blockCanThrow |= canThrow;
+
+        if (cases != null) {
+            if (cases.size() == 0) {
+                // It's a default-only switch statement. It can happen!
+                insn = new PlainInsn(Rops.GOTO, pos, null,
+                                     RegisterSpecList.EMPTY);
+                primarySuccessorIndex = 0;
+            } else {
+                IntList values = cases.getValues();
+                insn = new SwitchInsn(rop, pos, dest, sources, values);
+                primarySuccessorIndex = values.size();
+            }
+        } else if (ropOpcode == RegOps.RETURN) {
+            /*
+             * Returns get turned into the combination of a move (if
+             * non-void and if the return doesn't already mention
+             * register 0) and a goto (to the return block).
+             */
+            if (sources.size() != 0) {
+                RegisterSpec source = sources.get(0);
+                TypeBearer type = source.getTypeBearer();
+                if (source.getReg() != 0) {
+                    insns.add(new PlainInsn(Rops.opMove(type), pos,
+                                            RegisterSpec.make(0, type),
+                                            source));
+                }
+            }
+            insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY);
+            primarySuccessorIndex = 0;
+            updateReturnOp(rop, pos);
+            returns = true;
+        } else if (cst != null) {
+            if (canThrow) {
+                insn =
+                    new ThrowingCstInsn(rop, pos, sources, catches, cst);
+                catchesUsed = true;
+                primarySuccessorIndex = catches.size();
+            } else {
+                insn = new PlainCstInsn(rop, pos, dest, sources, cst);
+            }
+        } else if (canThrow) {
+            insn = new ThrowingInsn(rop, pos, sources, catches);
+            catchesUsed = true;
+            if (opcode == ByteOps.ATHROW) {
+                /*
+                 * The op athrow is the only one where it's possible
+                 * to have non-empty successors and yet not have a
+                 * primary successor.
+                 */
+                primarySuccessorIndex = -1;
+            } else {
+                primarySuccessorIndex = catches.size();
+            }
+        } else {
+            insn = new PlainInsn(rop, pos, dest, sources);
+        }
+
+        insns.add(insn);
+
+        if (moveResult != null) {
+            insns.add(moveResult);
+        }
+
+        /*
+         * If initValues is non-null, it means that the parser has
+         * seen a group of compatible constant initialization
+         * bytecodes that are applied to the current newarray. The
+         * action we take here is to convert these initialization
+         * bytecodes into a single fill-array-data ROP which lays out
+         * all the constant values in a table.
+         */
+        if (initValues != null) {
+            extraBlockCount++;
+            insn = new FillArrayDataInsn(Rops.FILL_ARRAY_DATA, pos,
+                    RegisterSpecList.make(moveResult.getResult()), initValues,
+                    cst);
+            insns.add(insn);
+        }
+    }
+
+    /**
+     * Helper for {@link #run}, which gets the list of sources for the.
+     * instruction.
+     *
+     * @param opcode the opcode being translated
+     * @param stackPointer {@code >= 0;} the stack pointer after the
+     * instruction's arguments have been popped
+     * @return {@code non-null;} the sources
+     */
+    private RegisterSpecList getSources(int opcode, int stackPointer) {
+        int count = argCount();
+
+        if (count == 0) {
+            // We get an easy out if there aren't any sources.
+            return RegisterSpecList.EMPTY;
+        }
+
+        int localIndex = getLocalIndex();
+        RegisterSpecList sources;
+
+        if (localIndex >= 0) {
+            // The instruction is operating on a local variable.
+            sources = new RegisterSpecList(1);
+            sources.set(0, RegisterSpec.make(localIndex, arg(0)));
+        } else {
+            sources = new RegisterSpecList(count);
+            int regAt = stackPointer;
+            for (int i = 0; i < count; i++) {
+                RegisterSpec spec = RegisterSpec.make(regAt, arg(i));
+                sources.set(i, spec);
+                regAt += spec.getCategory();
+            }
+
+            switch (opcode) {
+                case ByteOps.IASTORE: {
+                    /*
+                     * The Java argument order for array stores is
+                     * (array, index, value), but the rop argument
+                     * order is (value, array, index). The following
+                     * code gets the right arguments in the right
+                     * places.
+                     */
+                    if (count != 3) {
+                        throw new RuntimeException("shouldn't happen");
+                    }
+                    RegisterSpec array = sources.get(0);
+                    RegisterSpec index = sources.get(1);
+                    RegisterSpec value = sources.get(2);
+                    sources.set(0, value);
+                    sources.set(1, array);
+                    sources.set(2, index);
+                    break;
+                }
+                case ByteOps.PUTFIELD: {
+                    /*
+                     * Similar to above: The Java argument order for
+                     * putfield is (object, value), but the rop
+                     * argument order is (value, object).
+                     */
+                    if (count != 2) {
+                        throw new RuntimeException("shouldn't happen");
+                    }
+                    RegisterSpec obj = sources.get(0);
+                    RegisterSpec value = sources.get(1);
+                    sources.set(0, value);
+                    sources.set(1, obj);
+                    break;
+                }
+            }
+        }
+
+        sources.setImmutable();
+        return sources;
+    }
+
+    /**
+     * Sets or updates the information about the return block.
+     *
+     * @param op {@code non-null;} the opcode to use
+     * @param pos {@code non-null;} the position to use
+     */
+    private void updateReturnOp(Rop op, SourcePosition pos) {
+        if (op == null) {
+            throw new NullPointerException("op == null");
+        }
+
+        if (pos == null) {
+            throw new NullPointerException("pos == null");
+        }
+
+        if (returnOp == null) {
+            returnOp = op;
+            returnPosition = pos;
+        } else {
+            if (returnOp != op) {
+                throw new SimException("return op mismatch: " + op + ", " +
+                                       returnOp);
+            }
+
+            if (pos.getLine() > returnPosition.getLine()) {
+                // Pick the largest line number to be the "canonical" return.
+                returnPosition = pos;
+            }
+        }
+    }
+
+    /**
+     * Gets the register opcode for the given Java opcode.
+     *
+     * @param jop {@code >= 0;} the Java opcode
+     * @param cst {@code null-ok;} the constant argument, if any
+     * @return {@code >= 0;} the corresponding register opcode
+     */
+    private int jopToRopOpcode(int jop, Constant cst) {
+        switch (jop) {
+            case ByteOps.POP:
+            case ByteOps.POP2:
+            case ByteOps.DUP:
+            case ByteOps.DUP_X1:
+            case ByteOps.DUP_X2:
+            case ByteOps.DUP2:
+            case ByteOps.DUP2_X1:
+            case ByteOps.DUP2_X2:
+            case ByteOps.SWAP:
+            case ByteOps.JSR:
+            case ByteOps.RET:
+            case ByteOps.MULTIANEWARRAY: {
+                // These need to be taken care of specially.
+                break;
+            }
+            case ByteOps.NOP: {
+                return RegOps.NOP;
+            }
+            case ByteOps.LDC:
+            case ByteOps.LDC2_W: {
+                return RegOps.CONST;
+            }
+            case ByteOps.ILOAD:
+            case ByteOps.ISTORE: {
+                return RegOps.MOVE;
+            }
+            case ByteOps.IALOAD: {
+                return RegOps.AGET;
+            }
+            case ByteOps.IASTORE: {
+                return RegOps.APUT;
+            }
+            case ByteOps.IADD:
+            case ByteOps.IINC: {
+                return RegOps.ADD;
+            }
+            case ByteOps.ISUB: {
+                return RegOps.SUB;
+            }
+            case ByteOps.IMUL: {
+                return RegOps.MUL;
+            }
+            case ByteOps.IDIV: {
+                return RegOps.DIV;
+            }
+            case ByteOps.IREM: {
+                return RegOps.REM;
+            }
+            case ByteOps.INEG: {
+                return RegOps.NEG;
+            }
+            case ByteOps.ISHL: {
+                return RegOps.SHL;
+            }
+            case ByteOps.ISHR: {
+                return RegOps.SHR;
+            }
+            case ByteOps.IUSHR: {
+                return RegOps.USHR;
+            }
+            case ByteOps.IAND: {
+                return RegOps.AND;
+            }
+            case ByteOps.IOR: {
+                return RegOps.OR;
+            }
+            case ByteOps.IXOR: {
+                return RegOps.XOR;
+            }
+            case ByteOps.I2L:
+            case ByteOps.I2F:
+            case ByteOps.I2D:
+            case ByteOps.L2I:
+            case ByteOps.L2F:
+            case ByteOps.L2D:
+            case ByteOps.F2I:
+            case ByteOps.F2L:
+            case ByteOps.F2D:
+            case ByteOps.D2I:
+            case ByteOps.D2L:
+            case ByteOps.D2F: {
+                return RegOps.CONV;
+            }
+            case ByteOps.I2B: {
+                return RegOps.TO_BYTE;
+            }
+            case ByteOps.I2C: {
+                return RegOps.TO_CHAR;
+            }
+            case ByteOps.I2S: {
+                return RegOps.TO_SHORT;
+            }
+            case ByteOps.LCMP:
+            case ByteOps.FCMPL:
+            case ByteOps.DCMPL: {
+                return RegOps.CMPL;
+            }
+            case ByteOps.FCMPG:
+            case ByteOps.DCMPG: {
+                return RegOps.CMPG;
+            }
+            case ByteOps.IFEQ:
+            case ByteOps.IF_ICMPEQ:
+            case ByteOps.IF_ACMPEQ:
+            case ByteOps.IFNULL: {
+                return RegOps.IF_EQ;
+            }
+            case ByteOps.IFNE:
+            case ByteOps.IF_ICMPNE:
+            case ByteOps.IF_ACMPNE:
+            case ByteOps.IFNONNULL: {
+                return RegOps.IF_NE;
+            }
+            case ByteOps.IFLT:
+            case ByteOps.IF_ICMPLT: {
+                return RegOps.IF_LT;
+            }
+            case ByteOps.IFGE:
+            case ByteOps.IF_ICMPGE: {
+                return RegOps.IF_GE;
+            }
+            case ByteOps.IFGT:
+            case ByteOps.IF_ICMPGT: {
+                return RegOps.IF_GT;
+            }
+            case ByteOps.IFLE:
+            case ByteOps.IF_ICMPLE: {
+                return RegOps.IF_LE;
+            }
+            case ByteOps.GOTO: {
+                return RegOps.GOTO;
+            }
+            case ByteOps.LOOKUPSWITCH: {
+                return RegOps.SWITCH;
+            }
+            case ByteOps.IRETURN:
+            case ByteOps.RETURN: {
+                return RegOps.RETURN;
+            }
+            case ByteOps.GETSTATIC: {
+                return RegOps.GET_STATIC;
+            }
+            case ByteOps.PUTSTATIC: {
+                return RegOps.PUT_STATIC;
+            }
+            case ByteOps.GETFIELD: {
+                return RegOps.GET_FIELD;
+            }
+            case ByteOps.PUTFIELD: {
+                return RegOps.PUT_FIELD;
+            }
+            case ByteOps.INVOKEVIRTUAL: {
+                return RegOps.INVOKE_VIRTUAL;
+            }
+            case ByteOps.INVOKESPECIAL: {
+                /*
+                 * Determine whether the opcode should be
+                 * INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6
+                 * on "invokespecial" as well as section 4.8.2 (7th
+                 * bullet point) for the gory details.
+                 */
+                CstMethodRef ref = (CstMethodRef) cst;
+                if (ref.isInstanceInit() ||
+                    (ref.getDefiningClass() == method.getDefiningClass()) ||
+                    !method.getAccSuper()) {
+                    return RegOps.INVOKE_DIRECT;
+                }
+                return RegOps.INVOKE_SUPER;
+            }
+            case ByteOps.INVOKESTATIC: {
+                return RegOps.INVOKE_STATIC;
+            }
+            case ByteOps.INVOKEINTERFACE: {
+                return RegOps.INVOKE_INTERFACE;
+            }
+            case ByteOps.NEW: {
+                return RegOps.NEW_INSTANCE;
+            }
+            case ByteOps.NEWARRAY:
+            case ByteOps.ANEWARRAY: {
+                return RegOps.NEW_ARRAY;
+            }
+            case ByteOps.ARRAYLENGTH: {
+                return RegOps.ARRAY_LENGTH;
+            }
+            case ByteOps.ATHROW: {
+                return RegOps.THROW;
+            }
+            case ByteOps.CHECKCAST: {
+                return RegOps.CHECK_CAST;
+            }
+            case ByteOps.INSTANCEOF: {
+                return RegOps.INSTANCE_OF;
+            }
+            case ByteOps.MONITORENTER: {
+                return RegOps.MONITOR_ENTER;
+            }
+            case ByteOps.MONITOREXIT: {
+                return RegOps.MONITOR_EXIT;
+            }
+        }
+
+        throw new RuntimeException("shouldn't happen");
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/SimException.java b/dx/src/com/android/dx/cf/code/SimException.java
new file mode 100644
index 0000000..220f281
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/SimException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Exception from simulation.
+ */
+public class SimException
+        extends ExceptionWithContext {
+    public SimException(String message) {
+        super(message);
+    }
+
+    public SimException(Throwable cause) {
+        super(cause);
+    }
+
+    public SimException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/Simulator.java b/dx/src/com/android/dx/cf/code/Simulator.java
new file mode 100644
index 0000000..f96699e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Simulator.java
@@ -0,0 +1,720 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstInterfaceMethodRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Class which knows how to simulate the effects of executing bytecode.
+ *
+ * <p><b>Note:</b> This class is not thread-safe. If multiple threads
+ * need to use a single instance, they must synchronize access explicitly
+ * between themselves.</p>
+ */
+public class Simulator {
+    /**
+     * {@code non-null;} canned error message for local variable
+     * table mismatches
+     */
+    private static final String LOCAL_MISMATCH_ERROR =
+        "This is symptomatic of .class transformation tools that ignore " +
+        "local variable information.";
+
+    /** {@code non-null;} machine to use when simulating */
+    private final Machine machine;
+
+    /** {@code non-null;} array of bytecode */
+    private final BytecodeArray code;
+
+    /** {@code non-null;} local variable information */
+    private final LocalVariableList localVariables;
+
+    /** {@code non-null;} visitor instance to use */
+    private final SimVisitor visitor;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param machine {@code non-null;} machine to use when simulating
+     * @param method {@code non-null;} method data to use
+     */
+    public Simulator(Machine machine, ConcreteMethod method) {
+        if (machine == null) {
+            throw new NullPointerException("machine == null");
+        }
+
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        this.machine = machine;
+        this.code = method.getCode();
+        this.localVariables = method.getLocalVariables();
+        this.visitor = new SimVisitor();
+    }
+
+    /**
+     * Simulates the effect of executing the given basic block. This modifies
+     * the passed-in frame to represent the end result.
+     *
+     * @param bb {@code non-null;} the basic block
+     * @param frame {@code non-null;} frame to operate on
+     */
+    public void simulate(ByteBlock bb, Frame frame) {
+        int end = bb.getEnd();
+
+        visitor.setFrame(frame);
+
+        try {
+            for (int off = bb.getStart(); off < end; /*off*/) {
+                int length = code.parseInstruction(off, visitor);
+                visitor.setPreviousOffset(off);
+                off += length;
+            }
+        } catch (SimException ex) {
+            frame.annotate(ex);
+            throw ex;
+        }
+    }
+
+    /**
+     * Simulates the effect of the instruction at the given offset, by
+     * making appropriate calls on the given frame.
+     *
+     * @param offset {@code >= 0;} offset of the instruction to simulate
+     * @param frame {@code non-null;} frame to operate on
+     * @return the length of the instruction, in bytes
+     */
+    public int simulate(int offset, Frame frame) {
+        visitor.setFrame(frame);
+        return code.parseInstruction(offset, visitor);
+    }
+
+    /**
+     * Constructs an "illegal top-of-stack" exception, for the stack
+     * manipulation opcodes.
+     */
+    private static SimException illegalTos() {
+        return new SimException("stack mismatch: illegal " +
+                "top-of-stack for opcode");
+    }
+
+    /**
+     * Bytecode visitor used during simulation.
+     */
+    private class SimVisitor implements BytecodeArray.Visitor {
+        /**
+         * {@code non-null;} machine instance to use (just to avoid excessive
+         * cross-object field access)
+         */
+        private final Machine machine;
+
+        /**
+         * {@code null-ok;} frame to use; set with each call to
+         * {@link Simulator#simulate}
+         */
+        private Frame frame;
+
+        /** offset of the previous bytecode */
+        private int previousOffset;
+
+        /**
+         * Constructs an instance.
+         */
+        public SimVisitor() {
+            this.machine = Simulator.this.machine;
+            this.frame = null;
+        }
+
+        /**
+         * Sets the frame to act on.
+         *
+         * @param frame {@code non-null;} the frame
+         */
+        public void setFrame(Frame frame) {
+            if (frame == null) {
+                throw new NullPointerException("frame == null");
+            }
+
+            this.frame = frame;
+        }
+
+        /** {@inheritDoc} */
+        public void visitInvalid(int opcode, int offset, int length) {
+            throw new SimException("invalid opcode " + Hex.u1(opcode));
+        }
+
+        /** {@inheritDoc} */
+        public void visitNoArgs(int opcode, int offset, int length,
+                Type type) {
+            switch (opcode) {
+                case ByteOps.NOP: {
+                    machine.clearArgs();
+                    break;
+                }
+                case ByteOps.INEG: {
+                    machine.popArgs(frame, type);
+                    break;
+                }
+                case ByteOps.I2L:
+                case ByteOps.I2F:
+                case ByteOps.I2D:
+                case ByteOps.I2B:
+                case ByteOps.I2C:
+                case ByteOps.I2S: {
+                    machine.popArgs(frame, Type.INT);
+                    break;
+                }
+                case ByteOps.L2I:
+                case ByteOps.L2F:
+                case ByteOps.L2D: {
+                    machine.popArgs(frame, Type.LONG);
+                    break;
+                }
+                case ByteOps.F2I:
+                case ByteOps.F2L:
+                case ByteOps.F2D: {
+                    machine.popArgs(frame, Type.FLOAT);
+                    break;
+                }
+                case ByteOps.D2I:
+                case ByteOps.D2L:
+                case ByteOps.D2F: {
+                    machine.popArgs(frame, Type.DOUBLE);
+                    break;
+                }
+                case ByteOps.RETURN: {
+                    machine.clearArgs();
+                    checkReturnType(Type.VOID);
+                    break;
+                }
+                case ByteOps.IRETURN: {
+                    Type checkType = type;
+                    if (type == Type.OBJECT) {
+                        /*
+                         * For an object return, use the best-known
+                         * type of the popped value.
+                         */
+                        checkType = frame.getStack().peekType(0);
+                    }
+                    machine.popArgs(frame, type);
+                    checkReturnType(checkType);
+                    break;
+                }
+                case ByteOps.POP: {
+                    Type peekType = frame.getStack().peekType(0);
+                    if (peekType.isCategory2()) {
+                        throw illegalTos();
+                    }
+                    machine.popArgs(frame, 1);
+                    break;
+                }
+                case ByteOps.ARRAYLENGTH: {
+                    Type arrayType = frame.getStack().peekType(0);
+                    if (!arrayType.isArrayOrKnownNull()) {
+                        throw new SimException("type mismatch: expected " +
+                                "array type but encountered " +
+                                arrayType.toHuman());
+                    }
+                    machine.popArgs(frame, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.ATHROW:
+                case ByteOps.MONITORENTER:
+                case ByteOps.MONITOREXIT: {
+                    machine.popArgs(frame, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.IALOAD: {
+                    /*
+                     * Change the type (which is to be pushed) to
+                     * reflect the actual component type of the array
+                     * being popped, unless it turns out to be a
+                     * known-null, in which case we just use the type
+                     * implied by the original instruction.
+                     */
+                    Type foundArrayType = frame.getStack().peekType(1);
+                    Type requireArrayType;
+
+                    if (foundArrayType != Type.KNOWN_NULL) {
+                        requireArrayType = foundArrayType;
+                        type = foundArrayType.getComponentType();
+                    } else {
+                        requireArrayType = type.getArrayType();
+                    }
+
+                    machine.popArgs(frame, requireArrayType, Type.INT);
+                    break;
+                }
+                case ByteOps.IADD:
+                case ByteOps.ISUB:
+                case ByteOps.IMUL:
+                case ByteOps.IDIV:
+                case ByteOps.IREM:
+                case ByteOps.IAND:
+                case ByteOps.IOR:
+                case ByteOps.IXOR: {
+                    machine.popArgs(frame, type, type);
+                    break;
+                }
+                case ByteOps.ISHL:
+                case ByteOps.ISHR:
+                case ByteOps.IUSHR: {
+                    machine.popArgs(frame, type, Type.INT);
+                    break;
+                }
+                case ByteOps.LCMP: {
+                    machine.popArgs(frame, Type.LONG, Type.LONG);
+                    break;
+                }
+                case ByteOps.FCMPL:
+                case ByteOps.FCMPG: {
+                    machine.popArgs(frame, Type.FLOAT, Type.FLOAT);
+                    break;
+                }
+                case ByteOps.DCMPL:
+                case ByteOps.DCMPG: {
+                    machine.popArgs(frame, Type.DOUBLE, Type.DOUBLE);
+                    break;
+                }
+                case ByteOps.IASTORE: {
+                    /*
+                     * Change the type (which is the type of the
+                     * element) to reflect the actual component type
+                     * of the array being popped, unless it turns out
+                     * to be a known-null, in which case we just use
+                     * the type implied by the original instruction.
+                     * The category 1 vs. 2 thing here is that, if the
+                     * element type is category 2, we have to skip over
+                     * one extra stack slot to find the array.
+                     */
+                    Type foundArrayType =
+                        frame.getStack().peekType(type.isCategory1() ? 2 : 3);
+                    Type requireArrayType;
+
+                    if (foundArrayType != Type.KNOWN_NULL) {
+                        requireArrayType = foundArrayType;
+                        type = foundArrayType.getComponentType();
+                    } else {
+                        requireArrayType = type.getArrayType();
+                    }
+
+                    machine.popArgs(frame, requireArrayType, Type.INT, type);
+                    break;
+                }
+                case ByteOps.POP2:
+                case ByteOps.DUP2: {
+                    ExecutionStack stack = frame.getStack();
+                    int pattern;
+
+                    if (stack.peekType(0).isCategory2()) {
+                        // "form 2" in vmspec-2
+                        machine.popArgs(frame, 1);
+                        pattern = 0x11;
+                    } else if (stack.peekType(1).isCategory1()) {
+                        // "form 1"
+                        machine.popArgs(frame, 2);
+                        pattern = 0x2121;
+                    } else {
+                        throw illegalTos();
+                    }
+
+                    if (opcode == ByteOps.DUP2) {
+                        machine.auxIntArg(pattern);
+                    }
+                    break;
+                }
+                case ByteOps.DUP: {
+                    Type peekType = frame.getStack().peekType(0);
+
+                    if (peekType.isCategory2()) {
+                        throw illegalTos();
+                    }
+
+                    machine.popArgs(frame, 1);
+                    machine.auxIntArg(0x11);
+                    break;
+                }
+                case ByteOps.DUP_X1: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (! (stack.peekType(0).isCategory1() &&
+                           stack.peekType(1).isCategory1())) {
+                        throw illegalTos();
+                    }
+
+                    machine.popArgs(frame, 2);
+                    machine.auxIntArg(0x212);
+                    break;
+                }
+                case ByteOps.DUP_X2: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (stack.peekType(0).isCategory2()) {
+                        throw illegalTos();
+                    }
+
+                    if (stack.peekType(1).isCategory2()) {
+                        // "form 2" in vmspec-2
+                        machine.popArgs(frame, 2);
+                        machine.auxIntArg(0x212);
+                    } else if (stack.peekType(2).isCategory1()) {
+                        // "form 1"
+                        machine.popArgs(frame, 3);
+                        machine.auxIntArg(0x3213);
+                    } else {
+                        throw illegalTos();
+                    }
+                    break;
+                }
+                case ByteOps.DUP2_X1: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (stack.peekType(0).isCategory2()) {
+                        // "form 2" in vmspec-2
+                        if (stack.peekType(2).isCategory2()) {
+                            throw illegalTos();
+                        }
+                        machine.popArgs(frame, 2);
+                        machine.auxIntArg(0x212);
+                    } else {
+                        // "form 1"
+                        if (stack.peekType(1).isCategory2() ||
+                            stack.peekType(2).isCategory2()) {
+                            throw illegalTos();
+                        }
+                        machine.popArgs(frame, 3);
+                        machine.auxIntArg(0x32132);
+                    }
+                    break;
+                }
+                case ByteOps.DUP2_X2: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (stack.peekType(0).isCategory2()) {
+                        if (stack.peekType(2).isCategory2()) {
+                            // "form 4" in vmspec-2
+                            machine.popArgs(frame, 2);
+                            machine.auxIntArg(0x212);
+                        } else if (stack.peekType(3).isCategory1()) {
+                            // "form 2"
+                            machine.popArgs(frame, 3);
+                            machine.auxIntArg(0x3213);
+                        } else {
+                            throw illegalTos();
+                        }
+                    } else if (stack.peekType(1).isCategory1()) {
+                        if (stack.peekType(2).isCategory2()) {
+                            // "form 3"
+                            machine.popArgs(frame, 3);
+                            machine.auxIntArg(0x32132);
+                        } else if (stack.peekType(3).isCategory1()) {
+                            // "form 1"
+                            machine.popArgs(frame, 4);
+                            machine.auxIntArg(0x432143);
+                        } else {
+                            throw illegalTos();
+                        }
+                    } else {
+                        throw illegalTos();
+                    }
+                    break;
+                }
+                case ByteOps.SWAP: {
+                    ExecutionStack stack = frame.getStack();
+
+                    if (! (stack.peekType(0).isCategory1() &&
+                           stack.peekType(1).isCategory1())) {
+                        throw illegalTos();
+                    }
+
+                    machine.popArgs(frame, 2);
+                    machine.auxIntArg(0x12);
+                    break;
+                }
+                default: {
+                    visitInvalid(opcode, offset, length);
+                    return;
+                }
+            }
+
+            machine.auxType(type);
+            machine.run(frame, offset, opcode);
+        }
+
+        /**
+         * Checks whether the prototype is compatible with returning the
+         * given type, and throws if not.
+         *
+         * @param encountered {@code non-null;} the encountered return type
+         */
+        private void checkReturnType(Type encountered) {
+            Type returnType = machine.getPrototype().getReturnType();
+
+            /*
+             * Check to see if the prototype's return type is
+             * possibly assignable from the type we encountered. This
+             * takes care of all the salient cases (types are the same,
+             * they're compatible primitive types, etc.).
+             */
+            if (! Merger.isPossiblyAssignableFrom(returnType, encountered)) {
+                throw new SimException("return type mismatch: prototype " +
+                        "indicates " + returnType.toHuman() +
+                        ", but encountered type " + encountered.toHuman());
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitLocal(int opcode, int offset, int length,
+                int idx, Type type, int value) {
+            /*
+             * Note that the "type" parameter is always the simplest
+             * type based on the original opcode, e.g., "int" for
+             * "iload" (per se) and "Object" for "aload". So, when
+             * possible, we replace the type with the one indicated in
+             * the local variable table, though we still need to check
+             * to make sure it's valid for the opcode.
+             *
+             * The reason we use (offset + length) for the localOffset
+             * for a store is because it is only after the store that
+             * the local type becomes valid. On the other hand, the
+             * type associated with a load is valid at the start of
+             * the instruction.
+             */
+            int localOffset =
+                (opcode == ByteOps.ISTORE) ? (offset + length) : offset;
+            LocalVariableList.Item local =
+                localVariables.pcAndIndexToLocal(localOffset, idx);
+            Type localType;
+
+            if (local != null) {
+                localType = local.getType();
+                if (localType.getBasicFrameType() !=
+                        type.getBasicFrameType()) {
+                    BaseMachine.throwLocalMismatch(type, localType);
+                    return;
+                }
+            } else {
+                localType = type;
+            }
+
+            switch (opcode) {
+                case ByteOps.ILOAD:
+                case ByteOps.RET: {
+                    machine.localArg(frame, idx);
+                    machine.auxType(type);
+                    break;
+                }
+                case ByteOps.ISTORE: {
+                    LocalItem item
+                            = (local == null) ? null : local.getLocalItem();
+                    machine.popArgs(frame, type);
+                    machine.auxType(type);
+                    machine.localTarget(idx, localType, item);
+                    break;
+                }
+                case ByteOps.IINC: {
+                    LocalItem item
+                            = (local == null) ? null : local.getLocalItem();
+                    machine.localArg(frame, idx);
+                    machine.localTarget(idx, localType, item);
+                    machine.auxType(type);
+                    machine.auxIntArg(value);
+                    machine.auxCstArg(CstInteger.make(value));
+                    break;
+                }
+                default: {
+                    visitInvalid(opcode, offset, length);
+                    return;
+                }
+            }
+
+            machine.run(frame, offset, opcode);
+        }
+
+        /** {@inheritDoc} */
+        public void visitConstant(int opcode, int offset, int length,
+                Constant cst, int value) {
+            switch (opcode) {
+                case ByteOps.ANEWARRAY: {
+                    machine.popArgs(frame, Type.INT);
+                    break;
+                }
+                case ByteOps.PUTSTATIC: {
+                    Type fieldType = ((CstFieldRef) cst).getType();
+                    machine.popArgs(frame, fieldType);
+                    break;
+                }
+                case ByteOps.GETFIELD:
+                case ByteOps.CHECKCAST:
+                case ByteOps.INSTANCEOF: {
+                    machine.popArgs(frame, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.PUTFIELD: {
+                    Type fieldType = ((CstFieldRef) cst).getType();
+                    machine.popArgs(frame, Type.OBJECT, fieldType);
+                    break;
+                }
+                case ByteOps.INVOKEINTERFACE: {
+                    /*
+                     * Convert the interface method ref into a normal
+                     * method ref.
+                     */
+                    cst = ((CstInterfaceMethodRef) cst).toMethodRef();
+                    // and fall through...
+                }
+                case ByteOps.INVOKEVIRTUAL:
+                case ByteOps.INVOKESPECIAL: {
+                    /*
+                     * Get the instance prototype, and use it to direct
+                     * the machine.
+                     */
+                    Prototype prototype =
+                        ((CstMethodRef) cst).getPrototype(false);
+                    machine.popArgs(frame, prototype);
+                    break;
+                }
+                case ByteOps.INVOKESTATIC: {
+                    /*
+                     * Get the static prototype, and use it to direct
+                     * the machine.
+                     */
+                    Prototype prototype =
+                        ((CstMethodRef) cst).getPrototype(true);
+                    machine.popArgs(frame, prototype);
+                    break;
+                }
+                case ByteOps.MULTIANEWARRAY: {
+                    /*
+                     * The "value" here is the count of dimensions to
+                     * create. Make a prototype of that many "int"
+                     * types, and tell the machine to pop them. This
+                     * isn't the most efficient way in the world to do
+                     * this, but then again, multianewarray is pretty
+                     * darn rare and so not worth much effort
+                     * optimizing for.
+                     */
+                    Prototype prototype =
+                        Prototype.internInts(Type.VOID, value);
+                    machine.popArgs(frame, prototype);
+                    break;
+                }
+                default: {
+                    machine.clearArgs();
+                    break;
+                }
+            }
+
+            machine.auxIntArg(value);
+            machine.auxCstArg(cst);
+            machine.run(frame, offset, opcode);
+        }
+
+        /** {@inheritDoc} */
+        public void visitBranch(int opcode, int offset, int length,
+                int target) {
+            switch (opcode) {
+                case ByteOps.IFEQ:
+                case ByteOps.IFNE:
+                case ByteOps.IFLT:
+                case ByteOps.IFGE:
+                case ByteOps.IFGT:
+                case ByteOps.IFLE: {
+                    machine.popArgs(frame, Type.INT);
+                    break;
+                }
+                case ByteOps.IFNULL:
+                case ByteOps.IFNONNULL: {
+                    machine.popArgs(frame, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.IF_ICMPEQ:
+                case ByteOps.IF_ICMPNE:
+                case ByteOps.IF_ICMPLT:
+                case ByteOps.IF_ICMPGE:
+                case ByteOps.IF_ICMPGT:
+                case ByteOps.IF_ICMPLE: {
+                    machine.popArgs(frame, Type.INT, Type.INT);
+                    break;
+                }
+                case ByteOps.IF_ACMPEQ:
+                case ByteOps.IF_ACMPNE: {
+                    machine.popArgs(frame, Type.OBJECT, Type.OBJECT);
+                    break;
+                }
+                case ByteOps.GOTO:
+                case ByteOps.JSR:
+                case ByteOps.GOTO_W:
+                case ByteOps.JSR_W: {
+                    machine.clearArgs();
+                    break;
+                }
+                default: {
+                    visitInvalid(opcode, offset, length);
+                    return;
+                }
+            }
+
+            machine.auxTargetArg(target);
+            machine.run(frame, offset, opcode);
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitch(int opcode, int offset, int length,
+                SwitchList cases, int padding) {
+            machine.popArgs(frame, Type.INT);
+            machine.auxIntArg(padding);
+            machine.auxSwitchArg(cases);
+            machine.run(frame, offset, opcode);
+        }
+
+        /** {@inheritDoc} */
+        public void visitNewarray(int offset, int length, CstType type,
+                ArrayList<Constant> initValues) {
+            machine.popArgs(frame, Type.INT);
+            machine.auxInitValues(initValues);
+            machine.auxCstArg(type);
+            machine.run(frame, offset, ByteOps.NEWARRAY);
+        }
+
+        /** {@inheritDoc} */
+        public void setPreviousOffset(int offset) {
+            previousOffset = offset;
+        }
+
+        /** {@inheritDoc} */
+        public int getPreviousOffset() {
+            return previousOffset;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/SwitchList.java b/dx/src/com/android/dx/cf/code/SwitchList.java
new file mode 100644
index 0000000..621d728
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/SwitchList.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.IntList;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * List of (value, target) mappings representing the choices of a
+ * {@code tableswitch} or {@code lookupswitch} instruction. It
+ * also holds the default target for the switch.
+ */
+public final class SwitchList extends MutabilityControl {
+    /** {@code non-null;} list of test values */
+    private final IntList values;
+
+    /**
+     * {@code non-null;} list of targets corresponding to the test values; there
+     * is always one extra element in the target list, to hold the
+     * default target
+     */
+    private final IntList targets;
+
+    /** ultimate size of the list */
+    private int size;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param size {@code >= 0;} the number of elements to be in the table
+     */
+    public SwitchList(int size) {
+        super(true);
+        this.values = new IntList(size);
+        this.targets = new IntList(size + 1);
+        this.size = size;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setImmutable() {
+        values.setImmutable();
+        targets.setImmutable();
+        super.setImmutable();
+    }
+
+    /**
+     * Gets the size of the list.
+     *
+     * @return {@code >= 0;} the list size
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Gets the indicated test value.
+     *
+     * @param n {@code >= 0;}, &lt; size(); which index
+     * @return the test value
+     */
+    public int getValue(int n) {
+        return values.get(n);
+    }
+
+    /**
+     * Gets the indicated target. Asking for the target at {@code size()}
+     * returns the default target.
+     *
+     * @param n {@code >= 0, <= size();} which index
+     * @return {@code >= 0;} the target
+     */
+    public int getTarget(int n) {
+        return targets.get(n);
+    }
+
+    /**
+     * Gets the default target. This is just a shorthand for
+     * {@code getTarget(size())}.
+     *
+     * @return {@code >= 0;} the default target
+     */
+    public int getDefaultTarget() {
+        return targets.get(size);
+    }
+
+    /**
+     * Gets the list of all targets. This includes one extra element at the
+     * end of the list, which holds the default target.
+     *
+     * @return {@code non-null;} the target list
+     */
+    public IntList getTargets() {
+        return targets;
+    }
+
+    /**
+     * Gets the list of all case values.
+     *
+     * @return {@code non-null;} the case value list
+     */
+    public IntList getValues() {
+        return values;
+    }
+
+    /**
+     * Sets the default target. It is only valid to call this method
+     * when all the non-default elements have been set.
+     *
+     * @param target {@code >= 0;} the absolute (not relative) default target
+     * address
+     */
+    public void setDefaultTarget(int target) {
+        throwIfImmutable();
+
+        if (target < 0) {
+            throw new IllegalArgumentException("target < 0");
+        }
+
+        if (targets.size() != size) {
+            throw new RuntimeException("non-default elements not all set");
+        }
+
+        targets.add(target);
+    }
+
+    /**
+     * Adds the given item.
+     *
+     * @param value the test value
+     * @param target {@code >= 0;} the absolute (not relative) target address
+     */
+    public void add(int value, int target) {
+        throwIfImmutable();
+
+        if (target < 0) {
+            throw new IllegalArgumentException("target < 0");
+        }
+
+        values.add(value);
+        targets.add(target);
+    }
+
+    /**
+     * Shrinks this instance if possible, removing test elements that
+     * refer to the default target. This is only valid after the instance
+     * is fully populated, including the default target (naturally).
+     */
+    public void removeSuperfluousDefaults() {
+        throwIfImmutable();
+
+        int sz = size;
+
+        if (sz != (targets.size() - 1)) {
+            throw new IllegalArgumentException("incomplete instance");
+        }
+
+        int defaultTarget = targets.get(sz);
+        int at = 0;
+
+        for (int i = 0; i < sz; i++) {
+            int target = targets.get(i);
+            if (target != defaultTarget) {
+                if (i != at) {
+                    targets.set(at, target);
+                    values.set(at, values.get(i));
+                }
+                at++;
+            }
+        }
+
+        if (at != sz) {
+            values.shrink(at);
+            targets.set(at, defaultTarget);
+            targets.shrink(at + 1);
+            size = at;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/ValueAwareMachine.java b/dx/src/com/android/dx/cf/code/ValueAwareMachine.java
new file mode 100644
index 0000000..de75db5
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ValueAwareMachine.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * {@link Machine} which keeps track of known values but does not do
+ * smart/realistic reference type calculations.
+ */
+public class ValueAwareMachine extends BaseMachine {
+    /**
+     * Constructs an instance.
+     *
+     * @param prototype {@code non-null;} the prototype for the associated
+     * method
+     */
+    public ValueAwareMachine(Prototype prototype) {
+        super(prototype);
+    }
+
+    /** {@inheritDoc} */
+    public void run(Frame frame, int offset, int opcode) {
+        switch (opcode) {
+            case ByteOps.NOP:
+            case ByteOps.IASTORE:
+            case ByteOps.POP:
+            case ByteOps.POP2:
+            case ByteOps.IFEQ:
+            case ByteOps.IFNE:
+            case ByteOps.IFLT:
+            case ByteOps.IFGE:
+            case ByteOps.IFGT:
+            case ByteOps.IFLE:
+            case ByteOps.IF_ICMPEQ:
+            case ByteOps.IF_ICMPNE:
+            case ByteOps.IF_ICMPLT:
+            case ByteOps.IF_ICMPGE:
+            case ByteOps.IF_ICMPGT:
+            case ByteOps.IF_ICMPLE:
+            case ByteOps.IF_ACMPEQ:
+            case ByteOps.IF_ACMPNE:
+            case ByteOps.GOTO:
+            case ByteOps.RET:
+            case ByteOps.LOOKUPSWITCH:
+            case ByteOps.IRETURN:
+            case ByteOps.RETURN:
+            case ByteOps.PUTSTATIC:
+            case ByteOps.PUTFIELD:
+            case ByteOps.ATHROW:
+            case ByteOps.MONITORENTER:
+            case ByteOps.MONITOREXIT:
+            case ByteOps.IFNULL:
+            case ByteOps.IFNONNULL: {
+                // Nothing to do for these ops in this class.
+                clearResult();
+                break;
+            }
+            case ByteOps.LDC:
+            case ByteOps.LDC2_W: {
+                setResult((TypeBearer) getAuxCst());
+                break;
+            }
+            case ByteOps.ILOAD:
+            case ByteOps.ISTORE: {
+                setResult(arg(0));
+                break;
+            }
+            case ByteOps.IALOAD:
+            case ByteOps.IADD:
+            case ByteOps.ISUB:
+            case ByteOps.IMUL:
+            case ByteOps.IDIV:
+            case ByteOps.IREM:
+            case ByteOps.INEG:
+            case ByteOps.ISHL:
+            case ByteOps.ISHR:
+            case ByteOps.IUSHR:
+            case ByteOps.IAND:
+            case ByteOps.IOR:
+            case ByteOps.IXOR:
+            case ByteOps.IINC:
+            case ByteOps.I2L:
+            case ByteOps.I2F:
+            case ByteOps.I2D:
+            case ByteOps.L2I:
+            case ByteOps.L2F:
+            case ByteOps.L2D:
+            case ByteOps.F2I:
+            case ByteOps.F2L:
+            case ByteOps.F2D:
+            case ByteOps.D2I:
+            case ByteOps.D2L:
+            case ByteOps.D2F:
+            case ByteOps.I2B:
+            case ByteOps.I2C:
+            case ByteOps.I2S:
+            case ByteOps.LCMP:
+            case ByteOps.FCMPL:
+            case ByteOps.FCMPG:
+            case ByteOps.DCMPL:
+            case ByteOps.DCMPG:
+            case ByteOps.ARRAYLENGTH: {
+                setResult(getAuxType());
+                break;
+            }
+            case ByteOps.DUP:
+            case ByteOps.DUP_X1:
+            case ByteOps.DUP_X2:
+            case ByteOps.DUP2:
+            case ByteOps.DUP2_X1:
+            case ByteOps.DUP2_X2:
+            case ByteOps.SWAP: {
+                clearResult();
+                for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
+                    int which = (pattern & 0x0f) - 1;
+                    addResult(arg(which));
+                }
+                break;
+            }
+
+            case ByteOps.JSR: {
+                setResult(new ReturnAddress(getAuxTarget()));
+                break;
+            }
+            case ByteOps.GETSTATIC:
+            case ByteOps.GETFIELD:
+            case ByteOps.INVOKEVIRTUAL:
+            case ByteOps.INVOKESTATIC:
+            case ByteOps.INVOKEINTERFACE: {
+                Type type = ((TypeBearer) getAuxCst()).getType();
+                if (type == Type.VOID) {
+                    clearResult();
+                } else {
+                    setResult(type);
+                }
+                break;
+            }
+            case ByteOps.INVOKESPECIAL: {
+                Type thisType = arg(0).getType();
+                if (thisType.isUninitialized()) {
+                    frame.makeInitialized(thisType);
+                }
+                Type type = ((TypeBearer) getAuxCst()).getType();
+                if (type == Type.VOID) {
+                    clearResult();
+                } else {
+                    setResult(type);
+                }
+                break;
+            }
+            case ByteOps.NEW: {
+                Type type = ((CstType) getAuxCst()).getClassType();
+                setResult(type.asUninitialized(offset));
+                break;
+            }
+            case ByteOps.NEWARRAY:
+            case ByteOps.CHECKCAST:
+            case ByteOps.MULTIANEWARRAY: {
+                Type type = ((CstType) getAuxCst()).getClassType();
+                setResult(type);
+                break;
+            }
+            case ByteOps.ANEWARRAY: {
+                Type type = ((CstType) getAuxCst()).getClassType();
+                setResult(type.getArrayType());
+                break;
+            }
+            case ByteOps.INSTANCEOF: {
+                setResult(Type.INT);
+                break;
+            }
+            default: {
+                throw new RuntimeException("shouldn't happen: " +
+                                           Hex.u1(opcode));
+            }
+        }
+
+        storeResults(frame);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/code/package.html b/dx/src/com/android/dx/cf/code/package.html
new file mode 100644
index 0000000..abd4e9b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/package.html
@@ -0,0 +1,10 @@
+<body>
+<p>Implementation of classes having to do with Java simulation, such as
+is needed for verification or stack-to-register conversion.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
new file mode 100644
index 0000000..5eecfa6
--- /dev/null
+++ b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
@@ -0,0 +1,335 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.cst;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstInterfaceMethodRef;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.StdConstantPool;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import static com.android.dx.cf.cst.ConstantTags.*;
+
+/**
+ * Parser for a constant pool embedded in a class file.
+ */
+public final class ConstantPoolParser {
+    /** {@code non-null;} the bytes of the constant pool */
+    private final ByteArray bytes;
+
+    /** {@code non-null;} actual parsed constant pool contents */
+    private final StdConstantPool pool;
+
+    /** {@code non-null;} byte offsets to each cst */
+    private final int[] offsets;
+
+    /**
+     * -1 || &gt;= 10; the end offset of this constant pool in the
+     * {@code byte[]} which it came from or {@code -1} if not
+     * yet parsed
+     */
+    private int endOffset;
+
+    /** {@code null-ok;} parse observer, if any */
+    private ParseObserver observer;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes {@code non-null;} the bytes of the file
+     */
+    public ConstantPoolParser(ByteArray bytes) {
+        int size = bytes.getUnsignedShort(8); // constant_pool_count
+
+        this.bytes = bytes;
+        this.pool = new StdConstantPool(size);
+        this.offsets = new int[size];
+        this.endOffset = -1;
+    }
+
+    /**
+     * Sets the parse observer for this instance.
+     *
+     * @param observer {@code null-ok;} the observer
+     */
+    public void setObserver(ParseObserver observer) {
+        this.observer = observer;
+    }
+
+    /**
+     * Gets the end offset of this constant pool in the {@code byte[]}
+     * which it came from.
+     *
+     * @return {@code >= 10;} the end offset
+     */
+    public int getEndOffset() {
+        parseIfNecessary();
+        return endOffset;
+    }
+
+    /**
+     * Gets the actual constant pool.
+     *
+     * @return {@code non-null;} the constant pool
+     */
+    public StdConstantPool getPool() {
+        parseIfNecessary();
+        return pool;
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run successfully.
+     */
+    private void parseIfNecessary() {
+        if (endOffset < 0) {
+            parse();
+        }
+    }
+
+    /**
+     * Does the actual parsing.
+     */
+    private void parse() {
+        determineOffsets();
+
+        if (observer != null) {
+            observer.parsed(bytes, 8, 2,
+                            "constant_pool_count: " + Hex.u2(offsets.length));
+            observer.parsed(bytes, 10, 0, "\nconstant_pool:");
+            observer.changeIndent(1);
+        }
+
+        for (int i = 1; i < offsets.length; i++) {
+            int offset = offsets[i];
+            if ((offset != 0) && (pool.getOrNull(i) == null)) {
+                parse0(i);
+            }
+        }
+
+        if (observer != null) {
+            for (int i = 1; i < offsets.length; i++) {
+                Constant cst = pool.getOrNull(i);
+                if (cst == null) {
+                    continue;
+                }
+                int offset = offsets[i];
+                int nextOffset = endOffset;
+                for (int j = i + 1; j < offsets.length; j++) {
+                    int off = offsets[j];
+                    if (off != 0) {
+                        nextOffset = off;
+                        break;
+                    }
+                }
+                observer.parsed(bytes, offset, nextOffset - offset,
+                                Hex.u2(i) + ": " + cst.toString());
+            }
+
+            observer.changeIndent(-1);
+            observer.parsed(bytes, endOffset, 0, "end constant_pool");
+        }
+    }
+
+    /**
+     * Populates {@link #offsets} and also completely parse utf8 constants.
+     */
+    private void determineOffsets() {
+        int at = 10; // offset from the start of the file to the first cst
+        int lastCategory;
+
+        for (int i = 1; i < offsets.length; i += lastCategory) {
+            offsets[i] = at;
+            int tag = bytes.getUnsignedByte(at);
+            switch (tag) {
+                case CONSTANT_Integer:
+                case CONSTANT_Float:
+                case CONSTANT_Fieldref:
+                case CONSTANT_Methodref:
+                case CONSTANT_InterfaceMethodref:
+                case CONSTANT_NameAndType: {
+                    lastCategory = 1;
+                    at += 5;
+                    break;
+                }
+                case CONSTANT_Long:
+                case CONSTANT_Double: {
+                    lastCategory = 2;
+                    at += 9;
+                    break;
+                }
+                case CONSTANT_Class:
+                case CONSTANT_String: {
+                    lastCategory = 1;
+                    at += 3;
+                    break;
+                }
+                case CONSTANT_Utf8: {
+                    lastCategory = 1;
+                    at += bytes.getUnsignedShort(at + 1) + 3;
+                    break;
+                }
+                default: {
+                    ParseException ex =
+                        new ParseException("unknown tag byte: " + Hex.u1(tag));
+                    ex.addContext("...while preparsing cst " + Hex.u2(i) +
+                                  " at offset " + Hex.u4(at));
+                    throw ex;
+                }
+            }
+        }
+
+        endOffset = at;
+    }
+
+    /**
+     * Parses the constant for the given index if it hasn't already been
+     * parsed, also storing it in the constant pool. This will also
+     * have the side effect of parsing any entries the indicated one
+     * depends on.
+     *
+     * @param idx which constant
+     * @return {@code non-null;} the parsed constant
+     */
+    private Constant parse0(int idx) {
+        Constant cst = pool.getOrNull(idx);
+        if (cst != null) {
+            return cst;
+        }
+
+        int at = offsets[idx];
+
+        try {
+            int tag = bytes.getUnsignedByte(at);
+            switch (tag) {
+                case CONSTANT_Utf8: {
+                    cst = parseUtf8(at);
+                    break;
+                }
+                case CONSTANT_Integer: {
+                    int value = bytes.getInt(at + 1);
+                    cst = CstInteger.make(value);
+                    break;
+                }
+                case CONSTANT_Float: {
+                    int bits = bytes.getInt(at + 1);
+                    cst = CstFloat.make(bits);
+                    break;
+                }
+                case CONSTANT_Long: {
+                    long value = bytes.getLong(at + 1);
+                    cst = CstLong.make(value);
+                    break;
+                }
+                case CONSTANT_Double: {
+                    long bits = bytes.getLong(at + 1);
+                    cst = CstDouble.make(bits);
+                    break;
+                }
+                case CONSTANT_Class: {
+                    int nameIndex = bytes.getUnsignedShort(at + 1);
+                    CstUtf8 name = (CstUtf8) parse0(nameIndex);
+                    cst = new CstType(Type.internClassName(name.getString()));
+                    break;
+                }
+                case CONSTANT_String: {
+                    int stringIndex = bytes.getUnsignedShort(at + 1);
+                    CstUtf8 string = (CstUtf8) parse0(stringIndex);
+                    cst = new CstString(string);
+                    break;
+                }
+                case CONSTANT_Fieldref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex);
+                    cst = new CstFieldRef(type, nat);
+                    break;
+                }
+                case CONSTANT_Methodref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex);
+                    cst = new CstMethodRef(type, nat);
+                    break;
+                }
+                case CONSTANT_InterfaceMethodref: {
+                    int classIndex = bytes.getUnsignedShort(at + 1);
+                    CstType type = (CstType) parse0(classIndex);
+                    int natIndex = bytes.getUnsignedShort(at + 3);
+                    CstNat nat = (CstNat) parse0(natIndex);
+                    cst = new CstInterfaceMethodRef(type, nat);
+                    break;
+                }
+                case CONSTANT_NameAndType: {
+                    int nameIndex = bytes.getUnsignedShort(at + 1);
+                    CstUtf8 name = (CstUtf8) parse0(nameIndex);
+                    int descriptorIndex = bytes.getUnsignedShort(at + 3);
+                    CstUtf8 descriptor = (CstUtf8) parse0(descriptorIndex);
+                    cst = new CstNat(name, descriptor);
+                    break;
+                }
+            }
+        } catch (ParseException ex) {
+            ex.addContext("...while parsing cst " + Hex.u2(idx) +
+                          " at offset " + Hex.u4(at));
+            throw ex;
+        } catch (RuntimeException ex) {
+            ParseException pe = new ParseException(ex);
+            pe.addContext("...while parsing cst " + Hex.u2(idx) +
+                          " at offset " + Hex.u4(at));
+            throw pe;
+        }
+
+        pool.set(idx, cst);
+        return cst;
+    }
+
+    /**
+     * Parses a utf8 constant.
+     *
+     * @param at offset to the start of the constant (where the tag byte is)
+     * @return {@code non-null;} the parsed value
+     */
+    private CstUtf8 parseUtf8(int at) {
+        int length = bytes.getUnsignedShort(at + 1);
+
+        at += 3; // Skip to the data.
+
+        ByteArray ubytes = bytes.slice(at, at + length);
+
+        try {
+            return new CstUtf8(ubytes);
+        } catch (IllegalArgumentException ex) {
+            // Translate the exception
+            throw new ParseException(ex);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/cst/ConstantTags.java b/dx/src/com/android/dx/cf/cst/ConstantTags.java
new file mode 100644
index 0000000..9febbdf
--- /dev/null
+++ b/dx/src/com/android/dx/cf/cst/ConstantTags.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.cst;
+
+/**
+ * Tags for constant pool constants.
+ */
+public interface ConstantTags {
+    /** tag for a {@code CONSTANT_Utf8_info} */
+    int CONSTANT_Utf8 = 1;
+
+    /** tag for a {@code CONSTANT_Integer_info} */
+    int CONSTANT_Integer = 3;
+
+    /** tag for a {@code CONSTANT_Float_info} */
+    int CONSTANT_Float = 4;
+
+    /** tag for a {@code CONSTANT_Long_info} */
+    int CONSTANT_Long = 5;
+
+    /** tag for a {@code CONSTANT_Double_info} */
+    int CONSTANT_Double = 6;
+
+    /** tag for a {@code CONSTANT_Class_info} */
+    int CONSTANT_Class = 7;
+
+    /** tag for a {@code CONSTANT_String_info} */
+    int CONSTANT_String = 8;
+
+    /** tag for a {@code CONSTANT_Fieldref_info} */
+    int CONSTANT_Fieldref = 9;
+
+    /** tag for a {@code CONSTANT_Methodref_info} */
+    int CONSTANT_Methodref = 10;
+
+    /** tag for a {@code CONSTANT_InterfaceMethodref_info} */
+    int CONSTANT_InterfaceMethodref = 11;
+
+    /** tag for a {@code CONSTANT_NameAndType_info} */
+    int CONSTANT_NameAndType = 12;
+}
diff --git a/dx/src/com/android/dx/cf/direct/AnnotationParser.java b/dx/src/com/android/dx/cf/direct/AnnotationParser.java
new file mode 100644
index 0000000..ca38fc5
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AnnotationParser.java
@@ -0,0 +1,474 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import java.io.IOException;
+
+/**
+ * Parser for annotations.
+ */
+public final class AnnotationParser {
+    /** {@code non-null;} class file being parsed */
+    private final DirectClassFile cf;
+
+    /** {@code non-null;} constant pool to use */
+    private final ConstantPool pool;
+
+    /** {@code non-null;} bytes of the attribute data */
+    private final ByteArray bytes;
+
+    /** {@code null-ok;} parse observer, if any */
+    private final ParseObserver observer;
+
+    /** {@code non-null;} input stream to parse from */
+    private final ByteArray.MyDataInputStream input;
+
+    /**
+     * {@code non-null;} cursor for use when informing the observer of what
+     * was parsed
+     */
+    private int parseCursor;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cf {@code non-null;} class file to parse from
+     * @param offset {@code >= 0;} offset into the class file data to parse at
+     * @param length {@code >= 0;} number of bytes left in the attribute data
+     * @param observer {@code null-ok;} parse observer to notify, if any
+     */
+    public AnnotationParser(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (cf == null) {
+            throw new NullPointerException("cf == null");
+        }
+
+        this.cf = cf;
+        this.pool = cf.getConstantPool();
+        this.observer = observer;
+        this.bytes = cf.getBytes().slice(offset, offset + length);
+        this.input = bytes.makeDataInputStream();
+        this.parseCursor = 0;
+    }
+
+    /**
+     * Parses an annotation value ({@code element_value}) attribute.
+     *
+     * @return {@code non-null;} the parsed constant value
+     */
+    public Constant parseValueAttribute() {
+        Constant result;
+
+        try {
+            result = parseValue();
+
+            if (input.available() != 0) {
+                throw new ParseException("extra data in attribute");
+            }
+        } catch (IOException ex) {
+            // ByteArray.MyDataInputStream should never throw.
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a parameter annotation attribute.
+     *
+     * @param visibility {@code non-null;} visibility of the parsed annotations
+     * @return {@code non-null;} the parsed list of lists of annotations
+     */
+    public AnnotationsList parseParameterAttribute(
+            AnnotationVisibility visibility) {
+        AnnotationsList result;
+
+        try {
+            result = parseAnnotationsList(visibility);
+
+            if (input.available() != 0) {
+                throw new ParseException("extra data in attribute");
+            }
+        } catch (IOException ex) {
+            // ByteArray.MyDataInputStream should never throw.
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses an annotation attribute, per se.
+     *
+     * @param visibility {@code non-null;} visibility of the parsed annotations
+     * @return {@code non-null;} the list of annotations read from the attribute
+     * data
+     */
+    public Annotations parseAnnotationAttribute(
+            AnnotationVisibility visibility) {
+        Annotations result;
+
+        try {
+            result = parseAnnotations(visibility);
+
+            if (input.available() != 0) {
+                throw new ParseException("extra data in attribute");
+            }
+        } catch (IOException ex) {
+            // ByteArray.MyDataInputStream should never throw.
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a list of annotation lists.
+     *
+     * @param visibility {@code non-null;} visibility of the parsed annotations
+     * @return {@code non-null;} the list of annotation lists read from the attribute
+     * data
+     */
+    private AnnotationsList parseAnnotationsList(
+            AnnotationVisibility visibility) throws IOException {
+        int count = input.readUnsignedByte();
+
+        if (observer != null) {
+            parsed(1, "num_parameters: " + Hex.u1(count));
+        }
+
+        AnnotationsList outerList = new AnnotationsList(count);
+
+        for (int i = 0; i < count; i++) {
+            if (observer != null) {
+                parsed(0, "parameter_annotations[" + i + "]:");
+                changeIndent(1);
+            }
+
+            Annotations annotations = parseAnnotations(visibility);
+            outerList.set(i, annotations);
+
+            if (observer != null) {
+                observer.changeIndent(-1);
+            }
+        }
+
+        outerList.setImmutable();
+        return outerList;
+    }
+
+    /**
+     * Parses an annotation list.
+     *
+     * @param visibility {@code non-null;} visibility of the parsed annotations
+     * @return {@code non-null;} the list of annotations read from the attribute
+     * data
+     */
+    private Annotations parseAnnotations(AnnotationVisibility visibility)
+            throws IOException {
+        int count = input.readUnsignedShort();
+
+        if (observer != null) {
+            parsed(2, "num_annotations: " + Hex.u2(count));
+        }
+
+        Annotations annotations = new Annotations();
+
+        for (int i = 0; i < count; i++) {
+            if (observer != null) {
+                parsed(0, "annotations[" + i + "]:");
+                changeIndent(1);
+            }
+
+            Annotation annotation = parseAnnotation(visibility);
+            annotations.add(annotation);
+
+            if (observer != null) {
+                observer.changeIndent(-1);
+            }
+        }
+
+        annotations.setImmutable();
+        return annotations;
+    }
+
+    /**
+     * Parses a single annotation.
+     *
+     * @param visibility {@code non-null;} visibility of the parsed annotation
+     * @return {@code non-null;} the parsed annotation
+     */
+    private Annotation parseAnnotation(AnnotationVisibility visibility)
+            throws IOException {
+        requireLength(4);
+
+        int typeIndex = input.readUnsignedShort();
+        int numElements = input.readUnsignedShort();
+        CstUtf8 typeUtf8 = (CstUtf8) pool.get(typeIndex);
+        CstType type = new CstType(Type.intern(typeUtf8.getString()));
+
+        if (observer != null) {
+            parsed(2, "type: " + type.toHuman());
+            parsed(2, "num_elements: " + numElements);
+        }
+
+        Annotation annotation = new Annotation(type, visibility);
+
+        for (int i = 0; i < numElements; i++) {
+            if (observer != null) {
+                parsed(0, "elements[" + i + "]:");
+                changeIndent(1);
+            }
+
+            NameValuePair element = parseElement();
+            annotation.add(element);
+
+            if (observer != null) {
+                changeIndent(-1);
+            }
+        }
+
+        annotation.setImmutable();
+        return annotation;
+    }
+
+    /**
+     * Parses a {@link NameValuePair}.
+     *
+     * @return {@code non-null;} the parsed element
+     */
+    private NameValuePair parseElement() throws IOException {
+        requireLength(5);
+
+        int elementNameIndex = input.readUnsignedShort();
+        CstUtf8 elementName = (CstUtf8) pool.get(elementNameIndex);
+
+        if (observer != null) {
+            parsed(2, "element_name: " + elementName.toHuman());
+            parsed(0, "value: ");
+            changeIndent(1);
+        }
+
+        Constant value = parseValue();
+
+        if (observer != null) {
+            changeIndent(-1);
+        }
+
+        return new NameValuePair(elementName, value);
+    }
+
+    /**
+     * Parses an annotation value.
+     *
+     * @return {@code non-null;} the parsed value
+     */
+    private Constant parseValue() throws IOException {
+        int tag = input.readUnsignedByte();
+
+        if (observer != null) {
+            CstUtf8 humanTag = new CstUtf8(Character.toString((char) tag));
+            parsed(1, "tag: " + humanTag.toQuoted());
+        }
+
+        switch (tag) {
+            case 'B': {
+                CstInteger value = (CstInteger) parseConstant();
+                return CstByte.make(value.getValue());
+            }
+            case 'C': {
+                CstInteger value = (CstInteger) parseConstant();
+                int intValue = value.getValue();
+                return CstChar.make(value.getValue());
+            }
+            case 'D': {
+                CstDouble value = (CstDouble) parseConstant();
+                return value;
+            }
+            case 'F': {
+                CstFloat value = (CstFloat) parseConstant();
+                return value;
+            }
+            case 'I': {
+                CstInteger value = (CstInteger) parseConstant();
+                return value;
+            }
+            case 'J': {
+                CstLong value = (CstLong) parseConstant();
+                return value;
+            }
+            case 'S': {
+                CstInteger value = (CstInteger) parseConstant();
+                return CstShort.make(value.getValue());
+            }
+            case 'Z': {
+                CstInteger value = (CstInteger) parseConstant();
+                return CstBoolean.make(value.getValue());
+            }
+            case 'c': {
+                int classInfoIndex = input.readUnsignedShort();
+                CstUtf8 value = (CstUtf8) pool.get(classInfoIndex);
+                Type type = Type.internReturnType(value.getString());
+
+                if (observer != null) {
+                    parsed(2, "class_info: " + type.toHuman());
+                }
+
+                return new CstType(type);
+            }
+            case 's': {
+                CstString value = new CstString((CstUtf8) parseConstant());
+                return value;
+            }
+            case 'e': {
+                requireLength(4);
+
+                int typeNameIndex = input.readUnsignedShort();
+                int constNameIndex = input.readUnsignedShort();
+                CstUtf8 typeName = (CstUtf8) pool.get(typeNameIndex);
+                CstUtf8 constName = (CstUtf8) pool.get(constNameIndex);
+
+                if (observer != null) {
+                    parsed(2, "type_name: " + typeName.toHuman());
+                    parsed(2, "const_name: " + constName.toHuman());
+                }
+
+                return new CstEnumRef(new CstNat(constName, typeName));
+            }
+            case '@': {
+                Annotation annotation =
+                    parseAnnotation(AnnotationVisibility.EMBEDDED);
+                return new CstAnnotation(annotation);
+            }
+            case '[': {
+                requireLength(2);
+
+                int numValues = input.readUnsignedShort();
+                CstArray.List list = new CstArray.List(numValues);
+
+                if (observer != null) {
+                    parsed(2, "num_values: " + numValues);
+                    changeIndent(1);
+                }
+
+                for (int i = 0; i < numValues; i++) {
+                    if (observer != null) {
+                        changeIndent(-1);
+                        parsed(0, "element_value[" + i + "]:");
+                        changeIndent(1);
+                    }
+                    list.set(i, parseValue());
+                }
+
+                if (observer != null) {
+                    changeIndent(-1);
+                }
+
+                list.setImmutable();
+                return new CstArray(list);
+            }
+            default: {
+                throw new ParseException("unknown annotation tag: " +
+                        Hex.u1(tag));
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #parseValue}, which parses a constant reference
+     * and returns the referred-to constant value.
+     *
+     * @return {@code non-null;} the parsed value
+     */
+    private Constant parseConstant() throws IOException {
+        int constValueIndex = input.readUnsignedShort();
+        Constant value = (Constant) pool.get(constValueIndex);
+
+        if (observer != null) {
+            String human = (value instanceof CstUtf8)
+                ? ((CstUtf8) value).toQuoted()
+                : value.toHuman();
+            parsed(2, "constant_value: " + human);
+        }
+
+        return value;
+    }
+
+    /**
+     * Helper which will throw an exception if the given number of bytes
+     * is not available to be read.
+     *
+     * @param requiredLength the number of required bytes
+     */
+    private void requireLength(int requiredLength) throws IOException {
+        if (input.available() < requiredLength) {
+            throw new ParseException("truncated annotation attribute");
+        }
+    }
+
+    /**
+     * Helper which indicates that some bytes were just parsed. This should
+     * only be used (for efficiency sake) if the parse is known to be
+     * observed.
+     *
+     * @param length {@code >= 0;} number of bytes parsed
+     * @param message {@code non-null;} associated message
+     */
+    private void parsed(int length, String message) {
+        observer.parsed(bytes, parseCursor, length, message);
+        parseCursor += length;
+    }
+
+    /**
+     * Convenience wrapper that simply calls through to
+     * {@code observer.changeIndent()}.
+     *
+     * @param indent the amount to change the indent by
+     */
+    private void changeIndent(int indent) {
+        observer.changeIndent(indent);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/AttributeFactory.java b/dx/src/com/android/dx/cf/direct/AttributeFactory.java
new file mode 100644
index 0000000..f98372c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AttributeFactory.java
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.RawAttribute;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Factory capable of instantiating various {@link Attribute} subclasses
+ * depending on the context and name.
+ */
+public class AttributeFactory {
+    /** context for attributes on class files */
+    public static final int CTX_CLASS = 0;
+
+    /** context for attributes on fields */
+    public static final int CTX_FIELD = 1;
+
+    /** context for attributes on methods */
+    public static final int CTX_METHOD = 2;
+
+    /** context for attributes on code attributes */
+    public static final int CTX_CODE = 3;
+
+    /** number of contexts */
+    public static final int CTX_COUNT = 4;
+
+    /**
+     * Constructs an instance.
+     */
+    public AttributeFactory() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Parses and makes an attribute based on the bytes at the
+     * indicated position in the given array. This method figures out
+     * the name, and then does all the setup to call on to {@link #parse0},
+     * which does the actual construction.
+     *
+     * @param cf {@code non-null;} class file to parse from
+     * @param context context to parse in; one of the {@code CTX_*}
+     * constants
+     * @param offset offset into {@code dcf}'s {@code bytes}
+     * to start parsing at
+     * @param observer {@code null-ok;} parse observer to report to, if any
+     * @return {@code non-null;} an appropriately-constructed {@link Attribute}
+     */
+    public final Attribute parse(DirectClassFile cf, int context, int offset,
+                                 ParseObserver observer) {
+        if (cf == null) {
+            throw new NullPointerException("cf == null");
+        }
+
+        if ((context < 0) || (context >= CTX_COUNT)) {
+            throw new IllegalArgumentException("bad context");
+        }
+
+        CstUtf8 name = null;
+
+        try {
+            ByteArray bytes = cf.getBytes();
+            ConstantPool pool = cf.getConstantPool();
+            int nameIdx = bytes.getUnsignedShort(offset);
+            int length = bytes.getInt(offset + 2);
+
+            name = (CstUtf8) pool.get(nameIdx);
+
+            if (observer != null) {
+                observer.parsed(bytes, offset, 2,
+                                "name: " + name.toHuman());
+                observer.parsed(bytes, offset + 2, 4,
+                                "length: " + Hex.u4(length));
+            }
+
+            return parse0(cf, context, name.getString(), offset + 6, length,
+                          observer);
+        } catch (ParseException ex) {
+            ex.addContext("...while parsing " +
+                    ((name != null) ? (name.toHuman() + " ") : "") +
+                    "attribute at offset " + Hex.u4(offset));
+            throw ex;
+        }
+    }
+
+    /**
+     * Parses attribute content. The base class implements this by constructing
+     * an instance of {@link RawAttribute}. Subclasses are expected to
+     * override this to do something better in most cases.
+     *
+     * @param cf {@code non-null;} class file to parse from
+     * @param context context to parse in; one of the {@code CTX_*}
+     * constants
+     * @param name {@code non-null;} the attribute name
+     * @param offset offset into {@code bytes} to start parsing at; this
+     * is the offset to the start of attribute data, not to the header
+     * @param length the length of the attribute data
+     * @param observer {@code null-ok;} parse observer to report to, if any
+     * @return {@code non-null;} an appropriately-constructed {@link Attribute}
+     */
+    protected Attribute parse0(DirectClassFile cf, int context, String name,
+                               int offset, int length,
+                               ParseObserver observer) {
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        Attribute result = new RawAttribute(name, bytes, offset, length, pool);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, length, "attribute data");
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/AttributeListParser.java b/dx/src/com/android/dx/cf/direct/AttributeListParser.java
new file mode 100644
index 0000000..2715e6a
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AttributeListParser.java
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Parser for lists of attributes.
+ */
+final /*package*/ class AttributeListParser {
+    /** {@code non-null;} the class file to parse from */
+    private final DirectClassFile cf;
+
+    /** attribute parsing context */
+    private final int context;
+
+    /** offset in the byte array of the classfile to the start of the list */
+    private final int offset;
+
+    /** {@code non-null;} attribute factory to use */
+    private final AttributeFactory attributeFactory;
+
+    /** {@code non-null;} list of parsed attributes */
+    private final StdAttributeList list;
+
+    /** {@code >= -1;} the end offset of this list in the byte array of the
+     * classfile, or {@code -1} if not yet parsed */
+    private int endOffset;
+
+    /** {@code null-ok;} parse observer, if any */
+    private ParseObserver observer;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cf {@code non-null;} class file to parse from
+     * @param context attribute parsing context (see {@link AttributeFactory})
+     * @param offset offset in {@code bytes} to the start of the list
+     * @param attributeFactory {@code non-null;} attribute factory to use
+     */
+    public AttributeListParser(DirectClassFile cf, int context, int offset,
+                               AttributeFactory attributeFactory) {
+        if (cf == null) {
+            throw new NullPointerException("cf == null");
+        }
+
+        if (attributeFactory == null) {
+            throw new NullPointerException("attributeFactory == null");
+        }
+
+        int size = cf.getBytes().getUnsignedShort(offset);
+
+        this.cf = cf;
+        this.context = context;
+        this.offset = offset;
+        this.attributeFactory = attributeFactory;
+        this.list = new StdAttributeList(size);
+        this.endOffset = -1;
+    }
+
+    /**
+     * Sets the parse observer for this instance.
+     *
+     * @param observer {@code null-ok;} the observer
+     */
+    public void setObserver(ParseObserver observer) {
+        this.observer = observer;
+    }
+
+    /**
+     * Gets the end offset of this constant pool in the {@code byte[]}
+     * which it came from.
+     *
+     * @return {@code >= 0;} the end offset
+     */
+    public int getEndOffset() {
+        parseIfNecessary();
+        return endOffset;
+    }
+
+    /**
+     * Gets the parsed list.
+     *
+     * @return {@code non-null;} the list
+     */
+    public StdAttributeList getList() {
+        parseIfNecessary();
+        return list;
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run successfully.
+     */
+    private void parseIfNecessary() {
+        if (endOffset < 0) {
+            parse();
+        }
+    }
+
+    /**
+     * Does the actual parsing.
+     */
+    private void parse() {
+        int sz = list.size();
+        int at = offset + 2; // Skip the count.
+
+        ByteArray bytes = cf.getBytes();
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "attributes_count: " + Hex.u2(sz));
+        }
+
+        for (int i = 0; i < sz; i++) {
+            try {
+                if (observer != null) {
+                    observer.parsed(bytes, at, 0,
+                                    "\nattributes[" + i + "]:\n");
+                    observer.changeIndent(1);
+                }
+
+                Attribute attrib =
+                    attributeFactory.parse(cf, context, at, observer);
+
+                at += attrib.byteLength();
+                list.set(i, attrib);
+
+                if (observer != null) {
+                    observer.changeIndent(-1);
+                    observer.parsed(bytes, at, 0,
+                                    "end attributes[" + i + "]\n");
+                }
+            } catch (ParseException ex) {
+                ex.addContext("...while parsing attributes[" + i + "]");
+                throw ex;
+            } catch (RuntimeException ex) {
+                ParseException pe = new ParseException(ex);
+                pe.addContext("...while parsing attributes[" + i + "]");
+                throw pe;
+            }
+        }
+
+        endOffset = at;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/ClassPathOpener.java b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
new file mode 100644
index 0000000..4e8c435
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.util.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Opens all the class files found in a class path element. Path elements
+ * can point to class files, {jar,zip,apk} files, or directories containing
+ * class files.
+ */
+public class ClassPathOpener {
+
+    /** {@code non-null;} pathname to start with */
+    private final String pathname;
+    /** {@code non-null;} callback interface */
+    private final Consumer consumer;
+    /**
+     * If true, sort such that classes appear before their inner
+     * classes and "package-info" occurs before all other classes in that
+     * package.
+     */
+    private final boolean sort;
+
+    /**
+     * Callback interface for {@code ClassOpener}.
+     */
+    public interface Consumer {
+
+        /**
+         * Provides the file name and byte array for a class path element.
+         *
+         * @param name {@code non-null;} filename of element. May not be a valid
+         * filesystem path.
+         *
+         * @param bytes {@code non-null;} file data
+         * @return true on success. Result is or'd with all other results
+         * from {@code processFileBytes} and returned to the caller
+         * of {@code process()}.
+         */
+        boolean processFileBytes(String name, byte[] bytes);
+
+        /**
+         * Informs consumer that an exception occurred while processing
+         * this path element. Processing will continue if possible.
+         *
+         * @param ex {@code non-null;} exception
+         */
+        void onException(Exception ex);
+
+        /**
+         * Informs consumer that processing of an archive file has begun.
+         *
+         * @param file {@code non-null;} archive file being processed
+         */
+        void onProcessArchiveStart(File file);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param pathname {@code non-null;} path element to process
+     * @param sort if true, sort such that classes appear before their inner
+     * classes and "package-info" occurs before all other classes in that
+     * package.
+     * @param consumer {@code non-null;} callback interface
+     */
+    public ClassPathOpener(String pathname, boolean sort, Consumer consumer) {
+        this.pathname = pathname;
+        this.sort = sort;
+        this.consumer = consumer;
+    }
+
+    /**
+     * Processes a path element.
+     *
+     * @return the OR of all return values
+     * from {@code Consumer.processFileBytes()}.
+     */
+    public boolean process() {
+        File file = new File(pathname);
+
+        return processOne(file, true);
+    }
+
+    /**
+     * Processes one file.
+     *
+     * @param file {@code non-null;} the file to process
+     * @param topLevel whether this is a top-level file (that is,
+     * specified directly on the commandline)
+     * @return whether any processing actually happened
+     */
+    private boolean processOne(File file, boolean topLevel) {
+        try {
+            if (file.isDirectory()) {
+                return processDirectory(file, topLevel);
+            }
+
+            String path = file.getPath();
+
+            if (path.endsWith(".zip") ||
+                    path.endsWith(".jar") ||
+                    path.endsWith(".apk")) {
+                return processArchive(file);
+            }
+
+            byte[] bytes = FileUtils.readFile(file);
+            return consumer.processFileBytes(path, bytes);
+        } catch (Exception ex) {
+            consumer.onException(ex);
+            return false;
+        }
+    }
+
+    /**
+     * Sorts java class names such that outer classes preceed their inner
+     * classes and "package-info" preceeds all other classes in its package.
+     *
+     * @param a {@code non-null;} first class name
+     * @param b {@code non-null;} second class name
+     * @return {@code compareTo()}-style result
+     */
+    private static int compareClassNames(String a, String b) {
+        // Ensure inner classes sort second
+        a = a.replace('$','0');
+        b = b.replace('$','0');
+
+        /*
+         * Assuming "package-info" only occurs at the end, ensures package-info
+         * sorts first.
+         */
+        a = a.replace("package-info", "");
+        b = b.replace("package-info", "");
+
+        return a.compareTo(b);
+    }
+
+    /**
+     * Processes a directory recursively.
+     *
+     * @param dir {@code non-null;} file representing the directory
+     * @param topLevel whether this is a top-level directory (that is,
+     * specified directly on the commandline)
+     * @return whether any processing actually happened
+     */
+    private boolean processDirectory(File dir, boolean topLevel) {
+        if (topLevel) {
+            dir = new File(dir, ".");
+        }
+
+        File[] files = dir.listFiles();
+        int len = files.length;
+        boolean any = false;
+
+        if (sort) {
+            Arrays.sort(files, new Comparator<File>() {
+                public int compare(File a, File b) {
+                    return compareClassNames(a.getName(), b.getName());
+                }
+            });
+        }
+
+        for (int i = 0; i < len; i++) {
+            any |= processOne(files[i], false);
+        }
+
+        return any;
+    }
+
+    /**
+     * Processes the contents of an archive ({@code .zip},
+     * {@code .jar}, or {@code .apk}).
+     *
+     * @param file {@code non-null;} archive file to process
+     * @return whether any processing actually happened
+     * @throws IOException on i/o problem
+     */
+    private boolean processArchive(File file) throws IOException {
+        ZipFile zip = new ZipFile(file);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(40000);
+        byte[] buf = new byte[20000];
+        boolean any = false;
+
+        ArrayList<? extends java.util.zip.ZipEntry> entriesList
+                = Collections.list(zip.entries());
+
+        if (sort) {
+            Collections.sort(entriesList, new Comparator<ZipEntry>() {
+               public int compare (ZipEntry a, ZipEntry b) {
+                   return compareClassNames(a.getName(), b.getName());
+               }
+            });
+        }
+
+        consumer.onProcessArchiveStart(file);
+
+        for (ZipEntry one : entriesList) {
+            if (one.isDirectory()) {
+                continue;
+            }
+
+            String path = one.getName();
+            InputStream in = zip.getInputStream(one);
+
+            baos.reset();
+            for (;;) {
+                int amt = in.read(buf);
+                if (amt < 0) {
+                    break;
+                }
+
+                baos.write(buf, 0, amt);
+            }
+
+            in.close();
+
+            byte[] bytes = baos.toByteArray();
+            any |= consumer.processFileBytes(path, bytes);
+        }
+
+        zip.close();
+        return any;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/CodeObserver.java b/dx/src/com/android/dx/cf/direct/CodeObserver.java
new file mode 100644
index 0000000..efcc80b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/CodeObserver.java
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.code.ByteOps;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.SwitchList;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Bytecode visitor to use when "observing" bytecode getting parsed.
+ */
+public class CodeObserver implements BytecodeArray.Visitor {
+    /** {@code non-null;} actual array of bytecode */
+    private final ByteArray bytes;
+
+    /** {@code non-null;} observer to inform of parsing */
+    private final ParseObserver observer;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes {@code non-null;} actual array of bytecode
+     * @param observer {@code non-null;} observer to inform of parsing
+     */
+    public CodeObserver(ByteArray bytes, ParseObserver observer) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        if (observer == null) {
+            throw new NullPointerException("observer == null");
+        }
+
+        this.bytes = bytes;
+        this.observer = observer;
+    }
+
+    /** {@inheritDoc} */
+    public void visitInvalid(int opcode, int offset, int length) {
+        observer.parsed(bytes, offset, length, header(offset));
+    }
+
+    /** {@inheritDoc} */
+    public void visitNoArgs(int opcode, int offset, int length, Type type) {
+        observer.parsed(bytes, offset, length, header(offset));
+    }
+
+    /** {@inheritDoc} */
+    public void visitLocal(int opcode, int offset, int length,
+            int idx, Type type, int value) {
+        String idxStr = (length <= 3) ? Hex.u1(idx) : Hex.u2(idx);
+        boolean argComment = (length == 1);
+        String valueStr = "";
+
+        if (opcode == ByteOps.IINC) {
+            valueStr = ", #" +
+                ((length <= 3) ? Hex.s1(value) : Hex.s2(value));
+        }
+
+        String catStr = "";
+        if (type.isCategory2()) {
+            catStr = (argComment ? "," : " //") + " category-2";
+        }
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + (argComment ? " // " : " ") +
+                        idxStr + valueStr + catStr);
+    }
+
+    /** {@inheritDoc} */
+    public void visitConstant(int opcode, int offset, int length,
+            Constant cst, int value) {
+        if (cst instanceof CstKnownNull) {
+            // This is aconst_null.
+            visitNoArgs(opcode, offset, length, null);
+            return;
+        }
+
+        if (cst instanceof CstInteger) {
+            visitLiteralInt(opcode, offset, length, value);
+            return;
+        }
+
+        if (cst instanceof CstLong) {
+            visitLiteralLong(opcode, offset, length,
+                             ((CstLong) cst).getValue());
+            return;
+        }
+
+        if (cst instanceof CstFloat) {
+            visitLiteralFloat(opcode, offset, length,
+                              ((CstFloat) cst).getIntBits());
+            return;
+        }
+
+        if (cst instanceof CstDouble) {
+            visitLiteralDouble(opcode, offset, length,
+                             ((CstDouble) cst).getLongBits());
+            return;
+        }
+
+        String valueStr = "";
+        if (value != 0) {
+            valueStr = ", ";
+            if (opcode == ByteOps.MULTIANEWARRAY) {
+                valueStr += Hex.u1(value);
+            } else {
+                valueStr += Hex.u2(value);
+            }
+        }
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + " " + cst + valueStr);
+    }
+
+    /** {@inheritDoc} */
+    public void visitBranch(int opcode, int offset, int length,
+                            int target) {
+        String targetStr = (length <= 3) ? Hex.u2(target) : Hex.u4(target);
+        observer.parsed(bytes, offset, length,
+                        header(offset) + " " + targetStr);
+    }
+
+    /** {@inheritDoc} */
+    public void visitSwitch(int opcode, int offset, int length,
+            SwitchList cases, int padding) {
+        int sz = cases.size();
+        StringBuffer sb = new StringBuffer(sz * 20 + 100);
+
+        sb.append(header(offset));
+        if (padding != 0) {
+            sb.append(" // padding: " + Hex.u4(padding));
+        }
+        sb.append('\n');
+
+        for (int i = 0; i < sz; i++) {
+            sb.append("  ");
+            sb.append(Hex.s4(cases.getValue(i)));
+            sb.append(": ");
+            sb.append(Hex.u2(cases.getTarget(i)));
+            sb.append('\n');
+        }
+
+        sb.append("  default: ");
+        sb.append(Hex.u2(cases.getDefaultTarget()));
+
+        observer.parsed(bytes, offset, length, sb.toString());
+    }
+
+    /** {@inheritDoc} */
+    public void visitNewarray(int offset, int length, CstType cst,
+            ArrayList<Constant> intVals) {
+        String commentOrSpace = (length == 1) ? " // " : " ";
+        String typeName = cst.getClassType().getComponentType().toHuman();
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + commentOrSpace + typeName);
+    }
+
+    /** {@inheritDoc} */
+    public void setPreviousOffset(int offset) {
+        // Do nothing
+    }
+
+    /** {@inheritDoc} */
+    public int getPreviousOffset() {
+        return -1;
+    }
+
+    /**
+     * Helper to produce the first bit of output for each instruction.
+     *
+     * @param offset the offset to the start of the instruction
+     */
+    private String header(int offset) {
+        /*
+         * Note: This uses the original bytecode, not the
+         * possibly-transformed one.
+         */
+        int opcode = bytes.getUnsignedByte(offset);
+        String name = ByteOps.opName(opcode);
+
+        if (opcode == ByteOps.WIDE) {
+            opcode = bytes.getUnsignedByte(offset + 1);
+            name += " " + ByteOps.opName(opcode);
+        }
+
+        return Hex.u2(offset) + ": " + name;
+    }
+
+    /**
+     * Helper for {@link #visitConstant} where the constant is an
+     * {@code int}.
+     *
+     * @param opcode the opcode
+     * @param offset offset to the instruction
+     * @param length instruction length
+     * @param value constant value
+     */
+    private void visitLiteralInt(int opcode, int offset, int length,
+            int value) {
+        String commentOrSpace = (length == 1) ? " // " : " ";
+        String valueStr;
+
+        opcode = bytes.getUnsignedByte(offset); // Compare with orig op below.
+        if ((length == 1) || (opcode == ByteOps.BIPUSH)) {
+            valueStr = "#" + Hex.s1(value);
+        } else if (opcode == ByteOps.SIPUSH) {
+            valueStr = "#" + Hex.s2(value);
+        } else {
+            valueStr = "#" + Hex.s4(value);
+        }
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + commentOrSpace + valueStr);
+    }
+
+    /**
+     * Helper for {@link #visitConstant} where the constant is a
+     * {@code long}.
+     *
+     * @param opcode the opcode
+     * @param offset offset to the instruction
+     * @param length instruction length
+     * @param value constant value
+     */
+    private void visitLiteralLong(int opcode, int offset, int length,
+            long value) {
+        String commentOrLit = (length == 1) ? " // " : " #";
+        String valueStr;
+
+        if (length == 1) {
+            valueStr = Hex.s1((int) value);
+        } else {
+            valueStr = Hex.s8(value);
+        }
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + commentOrLit + valueStr);
+    }
+
+    /**
+     * Helper for {@link #visitConstant} where the constant is a
+     * {@code float}.
+     *
+     * @param opcode the opcode
+     * @param offset offset to the instruction
+     * @param length instruction length
+     * @param bits constant value, as float-bits
+     */
+    private void visitLiteralFloat(int opcode, int offset, int length,
+            int bits) {
+        String optArg = (length != 1) ? " #" + Hex.u4(bits) : "";
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + optArg + " // " +
+                        Float.intBitsToFloat(bits));
+    }
+
+    /**
+     * Helper for {@link #visitConstant} where the constant is a
+     * {@code double}.
+     *
+     * @param opcode the opcode
+     * @param offset offset to the instruction
+     * @param length instruction length
+     * @param bits constant value, as double-bits
+     */
+    private void visitLiteralDouble(int opcode, int offset, int length,
+            long bits) {
+        String optArg = (length != 1) ? " #" + Hex.u8(bits) : "";
+
+        observer.parsed(bytes, offset, length,
+                        header(offset) + optArg + " // " +
+                        Double.longBitsToDouble(bits));
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/DirectClassFile.java b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
new file mode 100644
index 0000000..b194572
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
@@ -0,0 +1,633 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.cst.ConstantPoolParser;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.ClassFile;
+import com.android.dx.cf.iface.FieldList;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.StdConstantPool;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Class file with info taken from a {@code byte[]} or slice thereof.
+ */
+public class DirectClassFile implements ClassFile {
+    /** the expected value of the ClassFile.magic field */
+    private static final int CLASS_FILE_MAGIC = 0xcafebabe;
+
+    /**
+     * minimum {@code .class} file major version
+     *
+     * The class file definition (vmspec/2nd-edition) says:
+     *
+     *     "Implementations of version 1.2 of the
+     *     Java 2 platform can support class file
+     *     formats of versions in the range 45.0
+     *     through 46.0 inclusive."
+     *
+     * The class files generated by the build are currently
+     * (as of 11/2006) reporting version 49.0 (0x31.0x00),
+     * however, so we use that as our upper bound.
+     *
+     * Valid ranges are typically of the form
+     * "A.0 through B.C inclusive" where A <= B and C >= 0,
+     * which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION.
+     */
+    private static final int CLASS_FILE_MIN_MAJOR_VERSION = 45;
+
+    /** maximum {@code .class} file major version */
+    private static final int CLASS_FILE_MAX_MAJOR_VERSION = 50;
+
+    /** maximum {@code .class} file minor version */
+    private static final int CLASS_FILE_MAX_MINOR_VERSION = 0;
+
+    /**
+     * {@code non-null;} the file path for the class, excluding any base directory
+     * specification
+     */
+    private final String filePath;
+
+    /** {@code non-null;} the bytes of the file */
+    private final ByteArray bytes;
+
+    /**
+     * whether to be strict about parsing; if
+     * {@code false}, this avoids doing checks that only exist
+     * for purposes of verification (such as magic number matching and
+     * path-package consistency checking)
+     */
+    private final boolean strictParse;
+
+    /**
+     * {@code null-ok;} the constant pool; only ever {@code null}
+     * before the constant pool is successfully parsed
+     */
+    private StdConstantPool pool;
+
+    /**
+     * the class file field {@code access_flags}; will be {@code -1}
+     * before the file is successfully parsed
+     */
+    private int accessFlags;
+
+    /**
+     * {@code null-ok;} the class file field {@code this_class},
+     * interpreted as a type constant; only ever {@code null}
+     * before the file is successfully parsed
+     */
+    private CstType thisClass;
+
+    /**
+     * {@code null-ok;} the class file field {@code super_class}, interpreted
+     * as a type constant if non-zero
+     */
+    private CstType superClass;
+
+    /**
+     * {@code null-ok;} the class file field {@code interfaces}; only
+     * ever {@code null} before the file is successfully
+     * parsed
+     */
+    private TypeList interfaces;
+
+    /**
+     * {@code null-ok;} the class file field {@code fields}; only ever
+     * {@code null} before the file is successfully parsed
+     */
+    private FieldList fields;
+
+    /**
+     * {@code null-ok;} the class file field {@code methods}; only ever
+     * {@code null} before the file is successfully parsed
+     */
+    private MethodList methods;
+
+    /**
+     * {@code null-ok;} the class file field {@code attributes}; only
+     * ever {@code null} before the file is successfully
+     * parsed
+     */
+    private StdAttributeList attributes;
+
+    /** {@code null-ok;} attribute factory, if any */
+    private AttributeFactory attributeFactory;
+
+    /** {@code null-ok;} parse observer, if any */
+    private ParseObserver observer;
+
+    /**
+     * Returns the string form of an object or {@code "(none)"}
+     * (rather than {@code "null"}) for {@code null}.
+     *
+     * @param obj {@code null-ok;} the object to stringify
+     * @return {@code non-null;} the appropriate string form
+     */
+    public static String stringOrNone(Object obj) {
+        if (obj == null) {
+            return "(none)";
+        }
+
+        return obj.toString();
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes {@code non-null;} the bytes of the file
+     * @param filePath {@code non-null;} the file path for the class,
+     * excluding any base directory specification
+     * @param strictParse whether to be strict about parsing; if
+     * {@code false}, this avoids doing checks that only exist
+     * for purposes of verification (such as magic number matching and
+     * path-package consistency checking)
+     */
+    public DirectClassFile(ByteArray bytes, String filePath,
+                           boolean strictParse) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        if (filePath == null) {
+            throw new NullPointerException("filePath == null");
+        }
+
+        this.filePath = filePath;
+        this.bytes = bytes;
+        this.strictParse = strictParse;
+        this.accessFlags = -1;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes {@code non-null;} the bytes of the file
+     * @param filePath {@code non-null;} the file path for the class,
+     * excluding any base directory specification
+     * @param strictParse whether to be strict about parsing; if
+     * {@code false}, this avoids doing checks that only exist
+     * for purposes of verification (such as magic number matching and
+     * path-package consistency checking)
+     */
+    public DirectClassFile(byte[] bytes, String filePath,
+                           boolean strictParse) {
+        this(new ByteArray(bytes), filePath, strictParse);
+    }
+
+    /**
+     * Sets the parse observer for this instance.
+     *
+     * @param observer {@code null-ok;} the observer
+     */
+    public void setObserver(ParseObserver observer) {
+        this.observer = observer;
+    }
+
+    /**
+     * Sets the attribute factory to use.
+     *
+     * @param attributeFactory {@code non-null;} the attribute factory
+     */
+    public void setAttributeFactory(AttributeFactory attributeFactory) {
+        if (attributeFactory == null) {
+            throw new NullPointerException("attributeFactory == null");
+        }
+
+        this.attributeFactory = attributeFactory;
+    }
+
+    /**
+     * Gets the {@link ByteArray} that this instance's data comes from.
+     *
+     * @return {@code non-null;} the bytes
+     */
+    public ByteArray getBytes() {
+        return bytes;
+    }
+
+    /** {@inheritDoc} */
+    public int getMagic() {
+        parseToInterfacesIfNecessary();
+        return getMagic0();
+    }
+
+    /** {@inheritDoc} */
+    public int getMinorVersion() {
+        parseToInterfacesIfNecessary();
+        return getMinorVersion0();
+    }
+
+    /** {@inheritDoc} */
+    public int getMajorVersion() {
+        parseToInterfacesIfNecessary();
+        return getMajorVersion0();
+    }
+
+    /** {@inheritDoc} */
+    public int getAccessFlags() {
+        parseToInterfacesIfNecessary();
+        return accessFlags;
+    }
+
+    /** {@inheritDoc} */
+    public CstType getThisClass() {
+        parseToInterfacesIfNecessary();
+        return thisClass;
+    }
+
+    /** {@inheritDoc} */
+    public CstType getSuperclass() {
+        parseToInterfacesIfNecessary();
+        return superClass;
+    }
+
+    /** {@inheritDoc} */
+    public ConstantPool getConstantPool() {
+        parseToInterfacesIfNecessary();
+        return pool;
+    }
+
+    /** {@inheritDoc} */
+    public TypeList getInterfaces() {
+        parseToInterfacesIfNecessary();
+        return interfaces;
+    }
+
+    /** {@inheritDoc} */
+    public FieldList getFields() {
+        parseToEndIfNecessary();
+        return fields;
+    }
+
+    /** {@inheritDoc} */
+    public MethodList getMethods() {
+        parseToEndIfNecessary();
+        return methods;
+    }
+
+    /** {@inheritDoc} */
+    public AttributeList getAttributes() {
+        parseToEndIfNecessary();
+        return attributes;
+    }
+
+    /** {@inheritDoc} */
+    public CstUtf8 getSourceFile() {
+        AttributeList attribs = getAttributes();
+        Attribute attSf = attribs.findFirst(AttSourceFile.ATTRIBUTE_NAME);
+
+        if (attSf instanceof AttSourceFile) {
+            return ((AttSourceFile) attSf).getSourceFile();
+        }
+
+        return null;
+    }
+
+    /**
+     * Constructs and returns an instance of {@link TypeList} whose
+     * data comes from the bytes of this instance, interpreted as a
+     * list of constant pool indices for classes, which are in turn
+     * translated to type constants. Instance construction will fail
+     * if any of the (alleged) indices turn out not to refer to
+     * constant pool entries of type {@code Class}.
+     *
+     * @param offset offset into {@link #bytes} for the start of the
+     * data
+     * @param size number of elements in the list (not number of bytes)
+     * @return {@code non-null;} an appropriately-constructed class list
+     */
+    public TypeList makeTypeList(int offset, int size) {
+        if (size == 0) {
+            return StdTypeList.EMPTY;
+        }
+
+        if (pool == null) {
+            throw new IllegalStateException("pool not yet initialized");
+        }
+
+        return new DcfTypeList(bytes, offset, size, pool, observer);
+    }
+
+    /**
+     * Gets the class file field {@code magic}, but without doing any
+     * checks or parsing first.
+     *
+     * @return the magic value
+     */
+    public int getMagic0() {
+        return bytes.getInt(0);
+    }
+
+    /**
+     * Gets the class file field {@code minor_version}, but
+     * without doing any checks or parsing first.
+     *
+     * @return the minor version
+     */
+    public int getMinorVersion0() {
+        return bytes.getUnsignedShort(4);
+    }
+
+    /**
+     * Gets the class file field {@code major_version}, but
+     * without doing any checks or parsing first.
+     *
+     * @return the major version
+     */
+    public int getMajorVersion0() {
+        return bytes.getUnsignedShort(6);
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run to cover up to
+     * the interfaces list.
+     */
+    private void parseToInterfacesIfNecessary() {
+        if (accessFlags == -1) {
+            parse();
+        }
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run successfully.
+     */
+    private void parseToEndIfNecessary() {
+        if (attributes == null) {
+            parse();
+        }
+    }
+
+    /**
+     * Does the parsing, handing exceptions.
+     */
+    private void parse() {
+        try {
+            parse0();
+        } catch (ParseException ex) {
+            ex.addContext("...while parsing " + filePath);
+            throw ex;
+        } catch (RuntimeException ex) {
+            ParseException pe = new ParseException(ex);
+            pe.addContext("...while parsing " + filePath);
+            throw pe;
+        }
+    }
+
+    /**
+     * Sees if the .class file header magic/version are within
+     * range.
+     *
+     * @param magic the value of a classfile "magic" field
+     * @param minorVersion the value of a classfile "minor_version" field
+     * @param majorVersion the value of a classfile "major_version" field
+     * @return true iff the parameters are valid and within range
+     */
+    private boolean isGoodVersion(int magic, int minorVersion,
+            int majorVersion) {
+        /* Valid version ranges are typically of the form
+         * "A.0 through B.C inclusive" where A <= B and C >= 0,
+         * which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION.
+         */
+        if (magic == CLASS_FILE_MAGIC && minorVersion >= 0) {
+            /* Check against max first to handle the case where
+             * MIN_MAJOR == MAX_MAJOR.
+             */
+            if (majorVersion == CLASS_FILE_MAX_MAJOR_VERSION) {
+                if (minorVersion <= CLASS_FILE_MAX_MINOR_VERSION) {
+                    return true;
+                }
+            } else if (majorVersion < CLASS_FILE_MAX_MAJOR_VERSION &&
+                       majorVersion >= CLASS_FILE_MIN_MAJOR_VERSION) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Does the actual parsing.
+     */
+    private void parse0() {
+        if (bytes.size() < 10) {
+            throw new ParseException("severely truncated class file");
+        }
+
+        if (observer != null) {
+            observer.parsed(bytes, 0, 0, "begin classfile");
+            observer.parsed(bytes, 0, 4, "magic: " + Hex.u4(getMagic0()));
+            observer.parsed(bytes, 4, 2,
+                            "minor_version: " + Hex.u2(getMinorVersion0()));
+            observer.parsed(bytes, 6, 2,
+                            "major_version: " + Hex.u2(getMajorVersion0()));
+        }
+
+        if (strictParse) {
+            /* Make sure that this looks like a valid class file with a
+             * version that we can handle.
+             */
+            if (!isGoodVersion(getMagic0(), getMinorVersion0(),
+                               getMajorVersion0())) {
+                throw new ParseException("bad class file magic (" +
+                                         Hex.u4(getMagic0()) +
+                                         ") or version (" +
+                                         Hex.u2(getMajorVersion0()) + "." +
+                                         Hex.u2(getMinorVersion0()) + ")");
+            }
+        }
+
+        ConstantPoolParser cpParser = new ConstantPoolParser(bytes);
+        cpParser.setObserver(observer);
+        pool = cpParser.getPool();
+        pool.setImmutable();
+
+        int at = cpParser.getEndOffset();
+        int accessFlags = bytes.getUnsignedShort(at); // u2 access_flags;
+        int cpi = bytes.getUnsignedShort(at + 2); // u2 this_class;
+        thisClass = (CstType) pool.get(cpi);
+        cpi = bytes.getUnsignedShort(at + 4); // u2 super_class;
+        superClass = (CstType) pool.get0Ok(cpi);
+        int count = bytes.getUnsignedShort(at + 6); // u2 interfaces_count
+
+        if (observer != null) {
+            observer.parsed(bytes, at, 2,
+                            "access_flags: " +
+                            AccessFlags.classString(accessFlags));
+            observer.parsed(bytes, at + 2, 2, "this_class: " + thisClass);
+            observer.parsed(bytes, at + 4, 2, "super_class: " +
+                            stringOrNone(superClass));
+            observer.parsed(bytes, at + 6, 2,
+                            "interfaces_count: " + Hex.u2(count));
+            if (count != 0) {
+                observer.parsed(bytes, at + 8, 0, "interfaces:");
+            }
+        }
+
+        at += 8;
+        interfaces = makeTypeList(at, count);
+        at += count * 2;
+
+        if (strictParse) {
+            /*
+             * Make sure that the file/jar path matches the declared
+             * package/class name.
+             */
+            String thisClassName = thisClass.getClassType().getClassName();
+            if (!(filePath.endsWith(".class") &&
+                  filePath.startsWith(thisClassName) &&
+                  (filePath.length() == (thisClassName.length() + 6)))) {
+                throw new ParseException("class name (" + thisClassName +
+                                         ") does not match path (" +
+                                         filePath + ")");
+            }
+        }
+
+        /*
+         * Only set the instance variable accessFlags here, since
+         * that's what signals a successful parse of the first part of
+         * the file (through the interfaces list).
+         */
+        this.accessFlags = accessFlags;
+
+        FieldListParser flParser =
+            new FieldListParser(this, thisClass, at, attributeFactory);
+        flParser.setObserver(observer);
+        fields = flParser.getList();
+        at = flParser.getEndOffset();
+
+        MethodListParser mlParser =
+            new MethodListParser(this, thisClass, at, attributeFactory);
+        mlParser.setObserver(observer);
+        methods = mlParser.getList();
+        at = mlParser.getEndOffset();
+
+        AttributeListParser alParser =
+            new AttributeListParser(this, AttributeFactory.CTX_CLASS, at,
+                                    attributeFactory);
+        alParser.setObserver(observer);
+        attributes = alParser.getList();
+        attributes.setImmutable();
+        at = alParser.getEndOffset();
+
+        if (at != bytes.size()) {
+            throw new ParseException("extra bytes at end of class file, " +
+                                     "at offset " + Hex.u4(at));
+        }
+
+        if (observer != null) {
+            observer.parsed(bytes, at, 0, "end classfile");
+        }
+    }
+
+    /**
+     * Implementation of {@link TypeList} whose data comes directly
+     * from the bytes of an instance of this (outer) class,
+     * interpreted as a list of constant pool indices for classes
+     * which are in turn returned as type constants. Instance
+     * construction will fail if any of the (alleged) indices turn out
+     * not to refer to constant pool entries of type
+     * {@code Class}.
+     */
+    private static class DcfTypeList implements TypeList {
+        /** {@code non-null;} array containing the data */
+        private final ByteArray bytes;
+
+        /** number of elements in the list (not number of bytes) */
+        private final int size;
+
+        /** {@code non-null;} the constant pool */
+        private final StdConstantPool pool;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param bytes {@code non-null;} original classfile's bytes
+         * @param offset offset into {@link #bytes} for the start of the
+         * data
+         * @param size number of elements in the list (not number of bytes)
+         * @param pool {@code non-null;} the constant pool to use
+         * @param observer {@code null-ok;} parse observer to use, if any
+         */
+        public DcfTypeList(ByteArray bytes, int offset, int size,
+                StdConstantPool pool, ParseObserver observer) {
+            if (size < 0) {
+                throw new IllegalArgumentException("size < 0");
+            }
+
+            bytes = bytes.slice(offset, offset + size * 2);
+            this.bytes = bytes;
+            this.size = size;
+            this.pool = pool;
+
+            for (int i = 0; i < size; i++) {
+                offset = i * 2;
+                int idx = bytes.getUnsignedShort(offset);
+                CstType type;
+                try {
+                    type = (CstType) pool.get(idx);
+                } catch (ClassCastException ex) {
+                    // Translate the exception.
+                    throw new RuntimeException("bogus class cpi", ex);
+                }
+                if (observer != null) {
+                    observer.parsed(bytes, offset, 2, "  " + type);
+                }
+            }
+        }
+
+        /** {@inheritDoc} */
+        public boolean isMutable() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
+        public int size() {
+            return size;
+        }
+
+        /** {@inheritDoc} */
+        public int getWordCount() {
+            // It is the same as size because all elements are classes.
+            return size;
+        }
+
+        /** {@inheritDoc} */
+        public Type getType(int n) {
+            int idx = bytes.getUnsignedShort(n * 2);
+            return ((CstType) pool.get(idx)).getClassType();
+        }
+
+        /** {@inheritDoc} */
+        public TypeList withAddedType(Type type) {
+            throw new UnsupportedOperationException("unsupported");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/FieldListParser.java b/dx/src/com/android/dx/cf/direct/FieldListParser.java
new file mode 100644
index 0000000..2d8280d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/FieldListParser.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.StdField;
+import com.android.dx.cf.iface.StdFieldList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Parser for lists of fields in a class file.
+ */
+final /*package*/ class FieldListParser extends MemberListParser {
+    /** {@code non-null;} list in progress */
+    private final StdFieldList fields;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cf {@code non-null;} the class file to parse from
+     * @param definer {@code non-null;} class being defined
+     * @param offset offset in {@code bytes} to the start of the list
+     * @param attributeFactory {@code non-null;} attribute factory to use
+     */
+    public FieldListParser(DirectClassFile cf, CstType definer, int offset,
+            AttributeFactory attributeFactory) {
+        super(cf, definer, offset, attributeFactory);
+        fields = new StdFieldList(getCount());
+    }
+
+    /**
+     * Gets the parsed list.
+     *
+     * @return {@code non-null;} the parsed list
+     */
+    public StdFieldList getList() {
+        parseIfNecessary();
+        return fields;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String humanName() {
+        return "field";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String humanAccessFlags(int accessFlags) {
+        return AccessFlags.fieldString(accessFlags);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int getAttributeContext() {
+        return AttributeFactory.CTX_FIELD;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Member set(int n, int accessFlags, CstNat nat,
+                         AttributeList attributes) {
+        StdField field =
+            new StdField(getDefiner(), accessFlags, nat, attributes);
+
+        fields.set(n, field);
+        return field;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/MemberListParser.java b/dx/src/com/android/dx/cf/direct/MemberListParser.java
new file mode 100644
index 0000000..91a5e1d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/MemberListParser.java
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Parser for lists of class file members (that is, fields and methods).
+ */
+abstract /*package*/ class MemberListParser {
+    /** {@code non-null;} the class file to parse from */
+    private final DirectClassFile cf;
+
+    /** {@code non-null;} class being defined */
+    private final CstType definer;
+
+    /** offset in the byte array of the classfile to the start of the list */
+    private final int offset;
+
+    /** {@code non-null;} attribute factory to use */
+    private final AttributeFactory attributeFactory;
+
+    /** {@code >= -1;} the end offset of this list in the byte array of the
+     * classfile, or {@code -1} if not yet parsed */
+    private int endOffset;
+
+    /** {@code null-ok;} parse observer, if any */
+    private ParseObserver observer;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cf {@code non-null;} the class file to parse from
+     * @param definer {@code non-null;} class being defined
+     * @param offset offset in {@code bytes} to the start of the list
+     * @param attributeFactory {@code non-null;} attribute factory to use
+     */
+    public MemberListParser(DirectClassFile cf, CstType definer,
+            int offset, AttributeFactory attributeFactory) {
+        if (cf == null) {
+            throw new NullPointerException("cf == null");
+        }
+
+        if (offset < 0) {
+            throw new IllegalArgumentException("offset < 0");
+        }
+
+        if (attributeFactory == null) {
+            throw new NullPointerException("attributeFactory == null");
+        }
+
+        this.cf = cf;
+        this.definer = definer;
+        this.offset = offset;
+        this.attributeFactory = attributeFactory;
+        this.endOffset = -1;
+    }
+
+    /**
+     * Gets the end offset of this constant pool in the {@code byte[]}
+     * which it came from.
+     *
+     * @return {@code >= 0;} the end offset
+     */
+    public int getEndOffset() {
+        parseIfNecessary();
+        return endOffset;
+    }
+
+    /**
+     * Sets the parse observer for this instance.
+     *
+     * @param observer {@code null-ok;} the observer
+     */
+    public final void setObserver(ParseObserver observer) {
+        this.observer = observer;
+    }
+
+    /**
+     * Runs {@link #parse} if it has not yet been run successfully.
+     */
+    protected final void parseIfNecessary() {
+        if (endOffset < 0) {
+            parse();
+        }
+    }
+
+    /**
+     * Gets the count of elements in the list.
+     *
+     * @return the count
+     */
+    protected final int getCount() {
+        ByteArray bytes = cf.getBytes();
+        return bytes.getUnsignedShort(offset);
+    }
+
+    /**
+     * Gets the class file being defined.
+     *
+     * @return {@code non-null;} the class
+     */
+    protected final CstType getDefiner() {
+        return definer;
+    }
+
+    /**
+     * Gets the human-oriented name for what this instance is parsing.
+     * Subclasses must override this method.
+     *
+     * @return {@code non-null;} the human oriented name
+     */
+    protected abstract String humanName();
+
+    /**
+     * Gets the human-oriented string for the given access flags.
+     * Subclasses must override this method.
+     *
+     * @param accessFlags the flags
+     * @return {@code non-null;} the string form
+     */
+    protected abstract String humanAccessFlags(int accessFlags);
+
+    /**
+     * Gets the {@code CTX_*} constant to use when parsing attributes.
+     * Subclasses must override this method.
+     *
+     * @return {@code non-null;} the human oriented name
+     */
+    protected abstract int getAttributeContext();
+
+    /**
+     * Sets an element in the list. Subclasses must override this method.
+     *
+     * @param n which element
+     * @param accessFlags the {@code access_flags}
+     * @param nat the interpreted name and type (based on the two
+     * {@code *_index} fields)
+     * @param attributes list of parsed attributes
+     * @return {@code non-null;} the constructed member
+     */
+    protected abstract Member set(int n, int accessFlags, CstNat nat,
+            AttributeList attributes);
+
+    /**
+     * Does the actual parsing.
+     */
+    private void parse() {
+        int attributeContext = getAttributeContext();
+        int count = getCount();
+        int at = offset + 2; // Skip the count.
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            humanName() + "s_count: " + Hex.u2(count));
+        }
+
+        for (int i = 0; i < count; i++) {
+            try {
+                int accessFlags = bytes.getUnsignedShort(at);
+                int nameIdx = bytes.getUnsignedShort(at + 2);
+                int descIdx = bytes.getUnsignedShort(at + 4);
+                CstUtf8 name = (CstUtf8) pool.get(nameIdx);
+                CstUtf8 desc = (CstUtf8) pool.get(descIdx);
+
+                if (observer != null) {
+                    observer.startParsingMember(bytes, at, name.getString(),
+                                                desc.getString());
+                    observer.parsed(bytes, at, 0, "\n" + humanName() +
+                                    "s[" + i + "]:\n");
+                    observer.changeIndent(1);
+                    observer.parsed(bytes, at, 2,
+                                    "access_flags: " +
+                                    humanAccessFlags(accessFlags));
+                    observer.parsed(bytes, at + 2, 2,
+                                    "name: " + name.toHuman());
+                    observer.parsed(bytes, at + 4, 2,
+                                    "descriptor: " + desc.toHuman());
+                }
+
+                at += 6;
+                AttributeListParser parser =
+                    new AttributeListParser(cf, attributeContext, at,
+                                            attributeFactory);
+                parser.setObserver(observer);
+                at = parser.getEndOffset();
+                StdAttributeList attributes = parser.getList();
+                attributes.setImmutable();
+                CstNat nat = new CstNat(name, desc);
+                Member member = set(i, accessFlags, nat, attributes);
+
+                if (observer != null) {
+                    observer.changeIndent(-1);
+                    observer.parsed(bytes, at, 0, "end " + humanName() +
+                                    "s[" + i + "]\n");
+                    observer.endParsingMember(bytes, at, name.getString(),
+                                              desc.getString(), member);
+                }
+            } catch (ParseException ex) {
+                ex.addContext("...while parsing " + humanName() + "s[" + i +
+                              "]");
+                throw ex;
+            } catch (RuntimeException ex) {
+                ParseException pe = new ParseException(ex);
+                pe.addContext("...while parsing " + humanName() + "s[" + i +
+                              "]");
+                throw pe;
+            }
+        }
+
+        endOffset = at;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/MethodListParser.java b/dx/src/com/android/dx/cf/direct/MethodListParser.java
new file mode 100644
index 0000000..9e3494e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/MethodListParser.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.StdMethod;
+import com.android.dx.cf.iface.StdMethodList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Parser for lists of methods in a class file.
+ */
+final /*package*/ class MethodListParser extends MemberListParser {
+    /** {@code non-null;} list in progress */
+    final private StdMethodList methods;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cf {@code non-null;} the class file to parse from
+     * @param definer {@code non-null;} class being defined
+     * @param offset offset in {@code bytes} to the start of the list
+     * @param attributeFactory {@code non-null;} attribute factory to use
+     */
+    public MethodListParser(DirectClassFile cf, CstType definer,
+            int offset, AttributeFactory attributeFactory) {
+        super(cf, definer, offset, attributeFactory);
+        methods = new StdMethodList(getCount());
+    }
+
+    /**
+     * Gets the parsed list.
+     *
+     * @return {@code non-null;} the parsed list
+     */
+    public StdMethodList getList() {
+        parseIfNecessary();
+        return methods;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String humanName() {
+        return "method";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String humanAccessFlags(int accessFlags) {
+        return AccessFlags.methodString(accessFlags);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int getAttributeContext() {
+        return AttributeFactory.CTX_METHOD;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Member set(int n, int accessFlags, CstNat nat,
+                         AttributeList attributes) {
+        StdMethod meth =
+            new StdMethod(getDefiner(), accessFlags, nat, attributes);
+
+        methods.set(n, meth);
+        return meth;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java
new file mode 100644
index 0000000..a6a97af
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java
@@ -0,0 +1,764 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.AttAnnotationDefault;
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.attrib.AttConstantValue;
+import com.android.dx.cf.attrib.AttDeprecated;
+import com.android.dx.cf.attrib.AttEnclosingMethod;
+import com.android.dx.cf.attrib.AttExceptions;
+import com.android.dx.cf.attrib.AttInnerClasses;
+import com.android.dx.cf.attrib.AttLineNumberTable;
+import com.android.dx.cf.attrib.AttLocalVariableTable;
+import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttSignature;
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.attrib.AttSynthetic;
+import com.android.dx.cf.attrib.InnerClassList;
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.LineNumberList;
+import com.android.dx.cf.code.LocalVariableList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import java.io.IOException;
+
+/**
+ * Standard subclass of {@link AttributeFactory}, which knows how to parse
+ * all the standard attribute types.
+ */
+public class StdAttributeFactory
+    extends AttributeFactory {
+    /** {@code non-null;} shared instance of this class */
+    public static final StdAttributeFactory THE_ONE =
+        new StdAttributeFactory();
+
+    /**
+     * Constructs an instance.
+     */
+    public StdAttributeFactory() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Attribute parse0(DirectClassFile cf, int context, String name,
+            int offset, int length, ParseObserver observer) {
+        switch (context) {
+            case CTX_CLASS: {
+                if (name == AttDeprecated.ATTRIBUTE_NAME) {
+                    return deprecated(cf, offset, length, observer);
+                }
+                if (name == AttEnclosingMethod.ATTRIBUTE_NAME) {
+                    return enclosingMethod(cf, offset, length, observer);
+                }
+                if (name == AttInnerClasses.ATTRIBUTE_NAME) {
+                    return innerClasses(cf, offset, length, observer);
+                }
+                if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeInvisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeVisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttSynthetic.ATTRIBUTE_NAME) {
+                    return synthetic(cf, offset, length, observer);
+                }
+                if (name == AttSignature.ATTRIBUTE_NAME) {
+                    return signature(cf, offset, length, observer);
+                }
+                if (name == AttSourceFile.ATTRIBUTE_NAME) {
+                    return sourceFile(cf, offset, length, observer);
+                }
+                break;
+            }
+            case CTX_FIELD: {
+                if (name == AttConstantValue.ATTRIBUTE_NAME) {
+                    return constantValue(cf, offset, length, observer);
+                }
+                if (name == AttDeprecated.ATTRIBUTE_NAME) {
+                    return deprecated(cf, offset, length, observer);
+                }
+                if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeInvisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeVisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttSignature.ATTRIBUTE_NAME) {
+                    return signature(cf, offset, length, observer);
+                }
+                if (name == AttSynthetic.ATTRIBUTE_NAME) {
+                    return synthetic(cf, offset, length, observer);
+                }
+                break;
+            }
+            case CTX_METHOD: {
+                if (name == AttAnnotationDefault.ATTRIBUTE_NAME) {
+                    return annotationDefault(cf, offset, length, observer);
+                }
+                if (name == AttCode.ATTRIBUTE_NAME) {
+                    return code(cf, offset, length, observer);
+                }
+                if (name == AttDeprecated.ATTRIBUTE_NAME) {
+                    return deprecated(cf, offset, length, observer);
+                }
+                if (name == AttExceptions.ATTRIBUTE_NAME) {
+                    return exceptions(cf, offset, length, observer);
+                }
+                if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeInvisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+                    return runtimeVisibleAnnotations(cf, offset, length,
+                            observer);
+                }
+                if (name == AttRuntimeInvisibleParameterAnnotations.
+                        ATTRIBUTE_NAME) {
+                    return runtimeInvisibleParameterAnnotations(
+                            cf, offset, length, observer);
+                }
+                if (name == AttRuntimeVisibleParameterAnnotations.
+                        ATTRIBUTE_NAME) {
+                    return runtimeVisibleParameterAnnotations(
+                            cf, offset, length, observer);
+                }
+                if (name == AttSignature.ATTRIBUTE_NAME) {
+                    return signature(cf, offset, length, observer);
+                }
+                if (name == AttSynthetic.ATTRIBUTE_NAME) {
+                    return synthetic(cf, offset, length, observer);
+                }
+                break;
+            }
+            case CTX_CODE: {
+                if (name == AttLineNumberTable.ATTRIBUTE_NAME) {
+                    return lineNumberTable(cf, offset, length, observer);
+                }
+                if (name == AttLocalVariableTable.ATTRIBUTE_NAME) {
+                    return localVariableTable(cf, offset, length, observer);
+                }
+                if (name == AttLocalVariableTypeTable.ATTRIBUTE_NAME) {
+                    return localVariableTypeTable(cf, offset, length,
+                            observer);
+                }
+                break;
+            }
+        }
+
+        return super.parse0(cf, context, name, offset, length, observer);
+    }
+
+    /**
+     * Parses an {@code AnnotationDefault} attribute.
+     */
+    private Attribute annotationDefault(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        Constant cst = ap.parseValueAttribute();
+
+        return new AttAnnotationDefault(cst, length);
+    }
+
+    /**
+     * Parses a {@code Code} attribute.
+     */
+    private Attribute code(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length < 12) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int maxStack = bytes.getUnsignedShort(offset); // u2 max_stack
+        int maxLocals = bytes.getUnsignedShort(offset + 2); // u2 max_locals
+        int codeLength = bytes.getInt(offset + 4); // u4 code_length
+        int origOffset = offset;
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "max_stack: " + Hex.u2(maxStack));
+            observer.parsed(bytes, offset + 2, 2,
+                            "max_locals: " + Hex.u2(maxLocals));
+            observer.parsed(bytes, offset + 4, 4,
+                            "code_length: " + Hex.u4(codeLength));
+        }
+
+        offset += 8;
+        length -= 8;
+
+        if (length < (codeLength + 4)) {
+            return throwTruncated();
+        }
+
+        int codeOffset = offset;
+        offset += codeLength;
+        length -= codeLength;
+        BytecodeArray code =
+            new BytecodeArray(bytes.slice(codeOffset, codeOffset + codeLength),
+                              pool);
+        if (observer != null) {
+            code.forEach(new CodeObserver(code.getBytes(), observer));
+        }
+
+        // u2 exception_table_length
+        int exceptionTableLength = bytes.getUnsignedShort(offset);
+        ByteCatchList catches = (exceptionTableLength == 0) ?
+            ByteCatchList.EMPTY :
+            new ByteCatchList(exceptionTableLength);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "exception_table_length: " +
+                            Hex.u2(exceptionTableLength));
+        }
+
+        offset += 2;
+        length -= 2;
+
+        if (length < (exceptionTableLength * 8 + 2)) {
+            return throwTruncated();
+        }
+
+        for (int i = 0; i < exceptionTableLength; i++) {
+            if (observer != null) {
+                observer.changeIndent(1);
+            }
+
+            int startPc = bytes.getUnsignedShort(offset);
+            int endPc = bytes.getUnsignedShort(offset + 2);
+            int handlerPc = bytes.getUnsignedShort(offset + 4);
+            int catchTypeIdx = bytes.getUnsignedShort(offset + 6);
+            CstType catchType = (CstType) pool.get0Ok(catchTypeIdx);
+            catches.set(i, startPc, endPc, handlerPc, catchType);
+            if (observer != null) {
+                observer.parsed(bytes, offset, 8,
+                                Hex.u2(startPc) + ".." + Hex.u2(endPc) +
+                                " -> " + Hex.u2(handlerPc) + " " +
+                                ((catchType == null) ? "<any>" :
+                                 catchType.toHuman()));
+            }
+            offset += 8;
+            length -= 8;
+
+            if (observer != null) {
+                observer.changeIndent(-1);
+            }
+        }
+
+        catches.setImmutable();
+
+        AttributeListParser parser =
+            new AttributeListParser(cf, CTX_CODE, offset, this);
+        parser.setObserver(observer);
+
+        StdAttributeList attributes = parser.getList();
+        attributes.setImmutable();
+
+        int attributeByteCount = parser.getEndOffset() - offset;
+        if (attributeByteCount != length) {
+            return throwBadLength(attributeByteCount + (offset - origOffset));
+        }
+
+        return new AttCode(maxStack, maxLocals, code, catches, attributes);
+    }
+
+    /**
+     * Parses a {@code ConstantValue} attribute.
+     */
+    private Attribute constantValue(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 2) {
+            return throwBadLength(2);
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int idx = bytes.getUnsignedShort(offset);
+        TypedConstant cst = (TypedConstant) pool.get(idx);
+        Attribute result = new AttConstantValue(cst);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2, "value: " + cst);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a {@code Deprecated} attribute.
+     */
+    private Attribute deprecated(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 0) {
+            return throwBadLength(0);
+        }
+
+        return new AttDeprecated();
+    }
+
+    /**
+     * Parses an {@code EnclosingMethod} attribute.
+     */
+    private Attribute enclosingMethod(DirectClassFile cf, int offset,
+            int length, ParseObserver observer) {
+        if (length != 4) {
+            throwBadLength(4);
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+
+        int idx = bytes.getUnsignedShort(offset);
+        CstType type = (CstType) pool.get(idx);
+
+        idx = bytes.getUnsignedShort(offset + 2);
+        CstNat method = (CstNat) pool.get0Ok(idx);
+
+        Attribute result = new AttEnclosingMethod(type, method);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2, "class: " + type);
+            observer.parsed(bytes, offset + 2, 2, "method: " +
+                            DirectClassFile.stringOrNone(method));
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses an {@code Exceptions} attribute.
+     */
+    private Attribute exceptions(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        int count = bytes.getUnsignedShort(offset); // number_of_exceptions
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "number_of_exceptions: " + Hex.u2(count));
+        }
+
+        offset += 2;
+        length -= 2;
+
+        if (length != (count * 2)) {
+            throwBadLength((count * 2) + 2);
+        }
+
+        TypeList list = cf.makeTypeList(offset, count);
+        return new AttExceptions(list);
+    }
+
+    /**
+     * Parses an {@code InnerClasses} attribute.
+     */
+    private Attribute innerClasses(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int count = bytes.getUnsignedShort(offset); // number_of_classes
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "number_of_classes: " + Hex.u2(count));
+        }
+
+        offset += 2;
+        length -= 2;
+
+        if (length != (count * 8)) {
+            throwBadLength((count * 8) + 2);
+        }
+
+        InnerClassList list = new InnerClassList(count);
+
+        for (int i = 0; i < count; i++) {
+            int innerClassIdx = bytes.getUnsignedShort(offset);
+            int outerClassIdx = bytes.getUnsignedShort(offset + 2);
+            int nameIdx = bytes.getUnsignedShort(offset + 4);
+            int accessFlags = bytes.getUnsignedShort(offset + 6);
+            CstType innerClass = (CstType) pool.get(innerClassIdx);
+            CstType outerClass = (CstType) pool.get0Ok(outerClassIdx);
+            CstUtf8 name = (CstUtf8) pool.get0Ok(nameIdx);
+            list.set(i, innerClass, outerClass, name, accessFlags);
+            if (observer != null) {
+                observer.parsed(bytes, offset, 2,
+                                "inner_class: " +
+                                DirectClassFile.stringOrNone(innerClass));
+                observer.parsed(bytes, offset + 2, 2,
+                                "  outer_class: " +
+                                DirectClassFile.stringOrNone(outerClass));
+                observer.parsed(bytes, offset + 4, 2,
+                                "  name: " +
+                                DirectClassFile.stringOrNone(name));
+                observer.parsed(bytes, offset + 6, 2,
+                                "  access_flags: " +
+                                AccessFlags.innerClassString(accessFlags));
+            }
+            offset += 8;
+        }
+
+        list.setImmutable();
+        return new AttInnerClasses(list);
+    }
+
+    /**
+     * Parses a {@code LineNumberTable} attribute.
+     */
+    private Attribute lineNumberTable(DirectClassFile cf, int offset,
+            int length, ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        int count = bytes.getUnsignedShort(offset); // line_number_table_length
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                            "line_number_table_length: " + Hex.u2(count));
+        }
+
+        offset += 2;
+        length -= 2;
+
+        if (length != (count * 4)) {
+            throwBadLength((count * 4) + 2);
+        }
+
+        LineNumberList list = new LineNumberList(count);
+
+        for (int i = 0; i < count; i++) {
+            int startPc = bytes.getUnsignedShort(offset);
+            int lineNumber = bytes.getUnsignedShort(offset + 2);
+            list.set(i, startPc, lineNumber);
+            if (observer != null) {
+                observer.parsed(bytes, offset, 4,
+                                Hex.u2(startPc) + " " + lineNumber);
+            }
+            offset += 4;
+        }
+
+        list.setImmutable();
+        return new AttLineNumberTable(list);
+    }
+
+    /**
+     * Parses a {@code LocalVariableTable} attribute.
+     */
+    private Attribute localVariableTable(DirectClassFile cf, int offset,
+            int length, ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        int count = bytes.getUnsignedShort(offset);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                    "local_variable_table_length: " + Hex.u2(count));
+        }
+
+        LocalVariableList list = parseLocalVariables(
+                bytes.slice(offset + 2, offset + length), cf.getConstantPool(),
+                observer, count, false);
+        return new AttLocalVariableTable(list);
+    }
+
+    /**
+     * Parses a {@code LocalVariableTypeTable} attribute.
+     */
+    private Attribute localVariableTypeTable(DirectClassFile cf, int offset,
+            int length, ParseObserver observer) {
+        if (length < 2) {
+            return throwSeverelyTruncated();
+        }
+
+        ByteArray bytes = cf.getBytes();
+        int count = bytes.getUnsignedShort(offset);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2,
+                    "local_variable_type_table_length: " + Hex.u2(count));
+        }
+
+        LocalVariableList list = parseLocalVariables(
+                bytes.slice(offset + 2, offset + length), cf.getConstantPool(),
+                observer, count, true);
+        return new AttLocalVariableTypeTable(list);
+    }
+
+    /**
+     * Parse the table part of either a {@code LocalVariableTable}
+     * or a {@code LocalVariableTypeTable}.
+     *
+     * @param bytes {@code non-null;} bytes to parse, which should <i>only</i>
+     * contain the table data (no header)
+     * @param pool {@code non-null;} constant pool to use
+     * @param count {@code >= 0;} the number of entries
+     * @param typeTable {@code true} iff this is for a type table
+     * @return {@code non-null;} the constructed list
+     */
+    private LocalVariableList parseLocalVariables(ByteArray bytes,
+            ConstantPool pool, ParseObserver observer, int count,
+            boolean typeTable) {
+        if (bytes.size() != (count * 10)) {
+            // "+ 2" is for the count.
+            throwBadLength((count * 10) + 2);
+        }
+
+        ByteArray.MyDataInputStream in = bytes.makeDataInputStream();
+        LocalVariableList list = new LocalVariableList(count);
+
+        try {
+            for (int i = 0; i < count; i++) {
+                int startPc = in.readUnsignedShort();
+                int length = in.readUnsignedShort();
+                int nameIdx = in.readUnsignedShort();
+                int typeIdx = in.readUnsignedShort();
+                int index = in.readUnsignedShort();
+                CstUtf8 name = (CstUtf8) pool.get(nameIdx);
+                CstUtf8 type = (CstUtf8) pool.get(typeIdx);
+                CstUtf8 descriptor = null;
+                CstUtf8 signature = null;
+
+                if (typeTable) {
+                    signature = type;
+                } else {
+                    descriptor = type;
+                }
+
+                list.set(i, startPc, length, name,
+                        descriptor, signature, index);
+
+                if (observer != null) {
+                    observer.parsed(bytes, i * 10, 10, Hex.u2(startPc) +
+                            ".." + Hex.u2(startPc + length) + " " +
+                            Hex.u2(index) + " " + name.toHuman() + " " +
+                            type.toHuman());
+                }
+            }
+        } catch (IOException ex) {
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        list.setImmutable();
+        return list;
+    }
+
+    /**
+     * Parses a {@code RuntimeInvisibleAnnotations} attribute.
+     */
+    private Attribute runtimeInvisibleAnnotations(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        Annotations annotations =
+            ap.parseAnnotationAttribute(AnnotationVisibility.BUILD);
+
+        return new AttRuntimeInvisibleAnnotations(annotations, length);
+    }
+
+    /**
+     * Parses a {@code RuntimeVisibleAnnotations} attribute.
+     */
+    private Attribute runtimeVisibleAnnotations(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        Annotations annotations =
+            ap.parseAnnotationAttribute(AnnotationVisibility.RUNTIME);
+
+        return new AttRuntimeVisibleAnnotations(annotations, length);
+    }
+
+    /**
+     * Parses a {@code RuntimeInvisibleParameterAnnotations} attribute.
+     */
+    private Attribute runtimeInvisibleParameterAnnotations(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        AnnotationsList list =
+            ap.parseParameterAttribute(AnnotationVisibility.BUILD);
+
+        return new AttRuntimeInvisibleParameterAnnotations(list, length);
+    }
+
+    /**
+     * Parses a {@code RuntimeVisibleParameterAnnotations} attribute.
+     */
+    private Attribute runtimeVisibleParameterAnnotations(DirectClassFile cf,
+            int offset, int length, ParseObserver observer) {
+        if (length < 2) {
+            throwSeverelyTruncated();
+        }
+
+        AnnotationParser ap =
+            new AnnotationParser(cf, offset, length, observer);
+        AnnotationsList list =
+            ap.parseParameterAttribute(AnnotationVisibility.RUNTIME);
+
+        return new AttRuntimeVisibleParameterAnnotations(list, length);
+    }
+
+    /**
+     * Parses a {@code Signature} attribute.
+     */
+    private Attribute signature(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 2) {
+            throwBadLength(2);
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int idx = bytes.getUnsignedShort(offset);
+        CstUtf8 cst = (CstUtf8) pool.get(idx);
+        Attribute result = new AttSignature(cst);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2, "signature: " + cst);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a {@code SourceFile} attribute.
+     */
+    private Attribute sourceFile(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 2) {
+            throwBadLength(2);
+        }
+
+        ByteArray bytes = cf.getBytes();
+        ConstantPool pool = cf.getConstantPool();
+        int idx = bytes.getUnsignedShort(offset);
+        CstUtf8 cst = (CstUtf8) pool.get(idx);
+        Attribute result = new AttSourceFile(cst);
+
+        if (observer != null) {
+            observer.parsed(bytes, offset, 2, "source: " + cst);
+        }
+
+        return result;
+    }
+
+    /**
+     * Parses a {@code Synthetic} attribute.
+     */
+    private Attribute synthetic(DirectClassFile cf, int offset, int length,
+            ParseObserver observer) {
+        if (length != 0) {
+            return throwBadLength(0);
+        }
+
+        return new AttSynthetic();
+    }
+
+    /**
+     * Throws the right exception when a known attribute has a way too short
+     * length.
+     *
+     * @return never
+     * @throws ParseException always thrown
+     */
+    private static Attribute throwSeverelyTruncated() {
+        throw new ParseException("severely truncated attribute");
+    }
+
+    /**
+     * Throws the right exception when a known attribute has a too short
+     * length.
+     *
+     * @return never
+     * @throws ParseException always thrown
+     */
+    private static Attribute throwTruncated() {
+        throw new ParseException("truncated attribute");
+    }
+
+    /**
+     * Throws the right exception when an attribute has an unexpected length
+     * (given its contents).
+     *
+     * @param expected expected length
+     * @return never
+     * @throws ParseException always thrown
+     */
+    private static Attribute throwBadLength(int expected) {
+        throw new ParseException("bad attribute length; expected length " +
+                                 Hex.u4(expected));
+    }
+}
diff --git a/dx/src/com/android/dx/cf/direct/package.html b/dx/src/com/android/dx/cf/direct/package.html
new file mode 100644
index 0000000..2a46198
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/package.html
@@ -0,0 +1,12 @@
+<body>
+<p>Implementation of <code>cf.iface.*</code> based on a direct representation
+of class files as <code>byte[]</code>s.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.attrib</code></li>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/iface/Attribute.java b/dx/src/com/android/dx/cf/iface/Attribute.java
new file mode 100644
index 0000000..b075251
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Attribute.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface representing attributes of class files (directly or indirectly).
+ */
+public interface Attribute {
+    /**
+     * Get the name of the attribute.
+     *
+     * @return {@code non-null;} the name
+     */
+    public String getName();
+
+    /**
+     * Get the total length of the attribute in bytes, including the
+     * header. Since the header is always six bytes, the result of
+     * this method is always at least {@code 6}.
+     *
+     * @return {@code >= 6;} the total length, in bytes
+     */
+    public int byteLength();
+}
diff --git a/dx/src/com/android/dx/cf/iface/AttributeList.java b/dx/src/com/android/dx/cf/iface/AttributeList.java
new file mode 100644
index 0000000..f7a1d27
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/AttributeList.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of attributes.
+ */
+public interface AttributeList {
+    /**
+     * Get whether this instance is mutable. Note that the
+     * {@code AttributeList} interface itself doesn't provide any means
+     * of mutation, but that doesn't mean that there isn't a non-interface
+     * way of mutating an instance.
+     *
+     * @return {@code true} iff this instance is somehow mutable
+     */
+    public boolean isMutable();
+
+    /**
+     * Get the number of attributes in the list.
+     *
+     * @return the size
+     */
+    public int size();
+
+    /**
+     * Get the {@code n}th attribute.
+     *
+     * @param n {@code n >= 0, n < size();} which attribute
+     * @return {@code non-null;} the attribute in question
+     */
+    public Attribute get(int n);
+
+    /**
+     * Get the total length of this list in bytes, when part of a
+     * class file. The returned value includes the two bytes for the
+     * {@code attributes_count} length indicator.
+     *
+     * @return {@code >= 2;} the total length, in bytes
+     */
+    public int byteLength();
+
+    /**
+     * Get the first attribute in the list with the given name, if any.
+     *
+     * @param name {@code non-null;} attribute name
+     * @return {@code null-ok;} first attribute in the list with the given name,
+     * or {@code null} if there is none
+     */
+    public Attribute findFirst(String name);
+
+    /**
+     * Get the next attribute in the list after the given one, with the same
+     * name, if any.
+     *
+     * @param attrib {@code non-null;} attribute to start looking after
+     * @return {@code null-ok;} next attribute after {@code attrib} with the
+     * same name as {@code attrib}
+     */
+    public Attribute findNext(Attribute attrib);
+}
diff --git a/dx/src/com/android/dx/cf/iface/ClassFile.java b/dx/src/com/android/dx/cf/iface/ClassFile.java
new file mode 100644
index 0000000..0f584ba
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ClassFile.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Interface for things which purport to be class files or reasonable
+ * facsimiles thereof.
+ *
+ * <p><b>Note:</b> The fields referred to in this documentation are of the
+ * {@code ClassFile} structure defined in vmspec-2 sec4.1.
+ */
+public interface ClassFile {
+    /**
+     * Gets the field {@code magic}.
+     *
+     * @return the value in question
+     */
+    public int getMagic();
+
+    /**
+     * Gets the field {@code minor_version}.
+     *
+     * @return the value in question
+     */
+    public int getMinorVersion();
+
+    /**
+     * Gets the field {@code major_version}.
+     *
+     * @return the value in question
+     */
+    public int getMajorVersion();
+
+    /**
+     * Gets the field {@code access_flags}.
+     *
+     * @return the value in question
+     */
+    public int getAccessFlags();
+
+    /**
+     * Gets the field {@code this_class}, interpreted as a type constant.
+     *
+     * @return {@code non-null;} the value in question
+     */
+    public CstType getThisClass();
+
+    /**
+     * Gets the field {@code super_class}, interpreted as a type constant
+     * if non-zero.
+     *
+     * @return {@code null-ok;} the value in question
+     */
+    public CstType getSuperclass();
+
+    /**
+     * Gets the field {@code constant_pool} (along with
+     * {@code constant_pool_count}).
+     *
+     * @return {@code non-null;} the constant pool
+     */
+    public ConstantPool getConstantPool();
+
+    /**
+     * Gets the field {@code interfaces} (along with
+     * {@code interfaces_count}).
+     *
+     * @return {@code non-null;} the list of interfaces
+     */
+    public TypeList getInterfaces();
+
+    /**
+     * Gets the field {@code fields} (along with
+     * {@code fields_count}).
+     *
+     * @return {@code non-null;} the list of fields
+     */
+    public FieldList getFields();
+
+    /**
+     * Gets the field {@code methods} (along with
+     * {@code methods_count}).
+     *
+     * @return {@code non-null;} the list of fields
+     */
+    public MethodList getMethods();
+
+    /**
+     * Gets the field {@code attributes} (along with
+     * {@code attributes_count}).
+     *
+     * @return {@code non-null;} the list of attributes
+     */
+    public AttributeList getAttributes();
+
+    /**
+     * Gets the name out of the {@code SourceFile} attribute of this
+     * file, if any. This is a convenient shorthand for scrounging around
+     * the class's attributes.
+     *
+     * @return {@code non-null;} the constant pool
+     */
+    public CstUtf8 getSourceFile();
+}
diff --git a/dx/src/com/android/dx/cf/iface/Field.java b/dx/src/com/android/dx/cf/iface/Field.java
new file mode 100644
index 0000000..e3002bc
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Field.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Interface representing fields of class files.
+ */
+public interface Field
+        extends Member {
+    /**
+     * Get the constant value for this field, if any. This only returns
+     * non-{@code null} for a {@code static final} field which
+     * includes a {@code ConstantValue} attribute.
+     *
+     * @return {@code null-ok;} the constant value, or {@code null} if this
+     * field isn't a constant
+     */
+    public TypedConstant getConstantValue();
+}
diff --git a/dx/src/com/android/dx/cf/iface/FieldList.java b/dx/src/com/android/dx/cf/iface/FieldList.java
new file mode 100644
index 0000000..9cd27a3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/FieldList.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of fields.
+ */
+public interface FieldList
+{
+    /**
+     * Get whether this instance is mutable. Note that the
+     * {@code FieldList} interface itself doesn't provide any means
+     * of mutation, but that doesn't mean that there isn't a non-interface
+     * way of mutating an instance.
+     *
+     * @return {@code true} iff this instance is somehow mutable
+     */
+    public boolean isMutable();
+
+    /**
+     * Get the number of fields in the list.
+     *
+     * @return the size
+     */
+    public int size();
+
+    /**
+     * Get the {@code n}th field.
+     *
+     * @param n {@code n >= 0, n < size();} which field
+     * @return {@code non-null;} the field in question
+     */
+    public Field get(int n);
+}
diff --git a/dx/src/com/android/dx/cf/iface/Member.java b/dx/src/com/android/dx/cf/iface/Member.java
new file mode 100644
index 0000000..0453a6f
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Member.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Interface representing members of class files (that is, fields and methods).
+ */
+public interface Member {
+    /**
+     * Get the defining class.
+     *
+     * @return {@code non-null;} the defining class
+     */
+    public CstType getDefiningClass();
+
+    /**
+     * Get the field {@code access_flags}.
+     *
+     * @return the access flags
+     */
+    public int getAccessFlags();
+
+    /**
+     * Get the field {@code name_index} of the member. This is
+     * just a convenient shorthand for {@code getNat().getName()}.
+     *
+     * @return {@code non-null;} the name
+     */
+    public CstUtf8 getName();
+
+    /**
+     * Get the field {@code descriptor_index} of the member. This is
+     * just a convenient shorthand for {@code getNat().getDescriptor()}.
+     *
+     * @return {@code non-null;} the descriptor
+     */
+    public CstUtf8 getDescriptor();
+
+    /**
+     * Get the name and type associated with this member. This is a
+     * combination of the fields {@code name_index} and
+     * {@code descriptor_index} in the original classfile, interpreted
+     * via the constant pool.
+     *
+     * @return {@code non-null;} the name and type
+     */
+    public CstNat getNat();
+
+    /**
+     * Get the field {@code attributes} (along with
+     * {@code attributes_count}).
+     *
+     * @return {@code non-null;} the constant pool
+     */
+    public AttributeList getAttributes();
+}
diff --git a/dx/src/com/android/dx/cf/iface/Method.java b/dx/src/com/android/dx/cf/iface/Method.java
new file mode 100644
index 0000000..18b9af6
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Method.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Interface representing methods of class files.
+ */
+public interface Method
+    extends Member
+{
+    /**
+     * Get the <i>effective</i> method descriptor, which includes, if
+     * necessary, a first {@code this} parameter.
+     *
+     * @return {@code non-null;} the effective method descriptor
+     */
+    public Prototype getEffectiveDescriptor();
+}
diff --git a/dx/src/com/android/dx/cf/iface/MethodList.java b/dx/src/com/android/dx/cf/iface/MethodList.java
new file mode 100644
index 0000000..dfa6528
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/MethodList.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of methods.
+ */
+public interface MethodList {
+    /**
+     * Get whether this instance is mutable. Note that the
+     * {@code MethodList} interface itself doesn't provide any means
+     * of mutation, but that doesn't mean that there isn't a non-interface
+     * way of mutating an instance.
+     *
+     * @return {@code true} iff this instance is somehow mutable
+     */
+    public boolean isMutable();
+
+    /**
+     * Get the number of methods in the list.
+     *
+     * @return the size
+     */
+    public int size();
+
+    /**
+     * Get the {@code n}th method.
+     *
+     * @param n {@code n >= 0, n < size();} which method
+     * @return {@code non-null;} the method in question
+     */
+    public Method get(int n);
+}
diff --git a/dx/src/com/android/dx/cf/iface/ParseException.java b/dx/src/com/android/dx/cf/iface/ParseException.java
new file mode 100644
index 0000000..18a9d0e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ParseException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Exception from parsing.
+ */
+public class ParseException
+        extends ExceptionWithContext {
+    public ParseException(String message) {
+        super(message);
+    }
+
+    public ParseException(Throwable cause) {
+        super(cause);
+    }
+
+    public ParseException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/ParseObserver.java b/dx/src/com/android/dx/cf/iface/ParseObserver.java
new file mode 100644
index 0000000..98d5a75
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ParseObserver.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.ByteArray;
+
+/**
+ * Observer of parsing in action. This is used to supply feedback from
+ * the various things that parse particularly to the dumping utilities.
+ */
+public interface ParseObserver {
+    /**
+     * Indicate that the level of indentation for a dump should increase
+     * or decrease (positive or negative argument, respectively).
+     *
+     * @param indentDelta the amount to change indentation
+     */
+    public void changeIndent(int indentDelta);
+
+    /**
+     * Indicate that a particular member is now being parsed.
+     *
+     * @param bytes {@code non-null;} the source that is being parsed
+     * @param offset offset into {@code bytes} for the start of the
+     * member
+     * @param name {@code non-null;} name of the member
+     * @param descriptor {@code non-null;} descriptor of the member
+     */
+    public void startParsingMember(ByteArray bytes, int offset, String name,
+                                   String descriptor);
+
+    /**
+     * Indicate that a particular member is no longer being parsed.
+     *
+     * @param bytes {@code non-null;} the source that was parsed
+     * @param offset offset into {@code bytes} for the end of the
+     * member
+     * @param name {@code non-null;} name of the member
+     * @param descriptor {@code non-null;} descriptor of the member
+     * @param member {@code non-null;} the actual member that was parsed
+     */
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member);
+
+    /**
+     * Indicate that some parsing happened.
+     *
+     * @param bytes {@code non-null;} the source that was parsed
+     * @param offset offset into {@code bytes} for what was parsed
+     * @param len number of bytes parsed
+     * @param human {@code non-null;} human form for what was parsed
+     */
+    public void parsed(ByteArray bytes, int offset, int len, String human);
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdAttributeList.java b/dx/src/com/android/dx/cf/iface/StdAttributeList.java
new file mode 100644
index 0000000..287b8c7
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdAttributeList.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link AttributeList}, which directly stores
+ * an array of {@link Attribute} objects and can be made immutable.
+ */
+public final class StdAttributeList extends FixedSizeList
+        implements AttributeList {
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public StdAttributeList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Attribute get(int n) {
+        return (Attribute) get0(n);
+    }
+
+    /** {@inheritDoc} */
+    public int byteLength() {
+        int sz = size();
+        int result = 2; // u2 attributes_count
+
+        for (int i = 0; i < sz; i++) {
+            result += get(i).byteLength();
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public Attribute findFirst(String name) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            Attribute att = get(i);
+            if (att.getName().equals(name)) {
+                return att;
+            }
+        }
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public Attribute findNext(Attribute attrib) {
+        int sz = size();
+        int at;
+
+        outer: {
+            for (at = 0; at < sz; at++) {
+                Attribute att = get(at);
+                if (att == attrib) {
+                    break outer;
+                }
+            }
+
+            return null;
+        }
+
+        String name = attrib.getName();
+
+        for (at++; at < sz; at++) {
+            Attribute att = get(at);
+            if (att.getName().equals(name)) {
+                return att;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the attribute at the given index.
+     *
+     * @param n {@code >= 0, < size();} which attribute
+     * @param attribute {@code null-ok;} the attribute object
+     */
+    public void set(int n, Attribute attribute) {
+        set0(n, attribute);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdField.java b/dx/src/com/android/dx/cf/iface/StdField.java
new file mode 100644
index 0000000..ef9873d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdField.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.cf.attrib.AttConstantValue;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Standard implementation of {@link Field}, which directly stores
+ * all the associated data.
+ */
+public final class StdField extends StdMember implements Field {
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass {@code non-null;} the defining class
+     * @param accessFlags access flags
+     * @param nat {@code non-null;} member name and type (descriptor)
+     * @param attributes {@code non-null;} list of associated attributes
+     */
+    public StdField(CstType definingClass, int accessFlags, CstNat nat,
+                    AttributeList attributes) {
+        super(definingClass, accessFlags, nat, attributes);
+    }
+
+    /** {@inheritDoc} */
+    public TypedConstant getConstantValue() {
+        AttributeList attribs = getAttributes();
+        AttConstantValue cval = (AttConstantValue)
+            attribs.findFirst(AttConstantValue.ATTRIBUTE_NAME);
+
+        if (cval == null) {
+            return null;
+        }
+
+        return cval.getConstantValue();
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdFieldList.java b/dx/src/com/android/dx/cf/iface/StdFieldList.java
new file mode 100644
index 0000000..f27bd22
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdFieldList.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link FieldList}, which directly stores
+ * an array of {@link Field} objects and can be made immutable.
+ */
+public final class StdFieldList extends FixedSizeList implements FieldList {
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public StdFieldList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Field get(int n) {
+        return (Field) get0(n);
+    }
+
+    /**
+     * Sets the field at the given index.
+     *
+     * @param n {@code >= 0, < size();} which field
+     * @param field {@code null-ok;} the field object
+     */
+    public void set(int n, Field field) {
+        set0(n, field);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMember.java b/dx/src/com/android/dx/cf/iface/StdMember.java
new file mode 100644
index 0000000..9f15585
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMember.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Standard implementation of {@link Member}, which directly stores
+ * all the associated data.
+ */
+public abstract class StdMember implements Member {
+    /** {@code non-null;} the defining class */
+    private final CstType definingClass;
+
+    /** access flags */
+    private final int accessFlags;
+
+    /** {@code non-null;} member name and type */
+    private final CstNat nat;
+
+    /** {@code non-null;} list of associated attributes */
+    private final AttributeList attributes;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass {@code non-null;} the defining class
+     * @param accessFlags access flags
+     * @param nat {@code non-null;} member name and type (descriptor)
+     * @param attributes {@code non-null;} list of associated attributes
+     */
+    public StdMember(CstType definingClass, int accessFlags, CstNat nat,
+                     AttributeList attributes) {
+        if (definingClass == null) {
+            throw new NullPointerException("definingClass == null");
+        }
+
+        if (nat == null) {
+            throw new NullPointerException("nat == null");
+        }
+
+        if (attributes == null) {
+            throw new NullPointerException("attributes == null");
+        }
+
+        this.definingClass = definingClass;
+        this.accessFlags = accessFlags;
+        this.nat = nat;
+        this.attributes = attributes;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append('{');
+        sb.append(nat.toHuman());
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    public final CstType getDefiningClass() {
+        return definingClass;
+    }
+
+    /** {@inheritDoc} */
+    public final int getAccessFlags() {
+        return accessFlags;
+    }
+
+    /** {@inheritDoc} */
+    public final CstNat getNat() {
+        return nat;
+    }
+
+    /** {@inheritDoc} */
+    public final CstUtf8 getName() {
+        return nat.getName();
+    }
+
+    /** {@inheritDoc} */
+    public final CstUtf8 getDescriptor() {
+        return nat.getDescriptor();
+    }
+
+    /** {@inheritDoc} */
+    public final AttributeList getAttributes() {
+        return attributes;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMethod.java b/dx/src/com/android/dx/cf/iface/StdMethod.java
new file mode 100644
index 0000000..c511d7d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMethod.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Standard implementation of {@link Method}, which directly stores
+ * all the associated data.
+ */
+public final class StdMethod extends StdMember implements Method {
+    /** {@code non-null;} the effective method descriptor */
+    private final Prototype effectiveDescriptor;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass {@code non-null;} the defining class
+     * @param accessFlags access flags
+     * @param nat {@code non-null;} member name and type (descriptor)
+     * @param attributes {@code non-null;} list of associated attributes
+     */
+    public StdMethod(CstType definingClass, int accessFlags, CstNat nat,
+            AttributeList attributes) {
+        super(definingClass, accessFlags, nat, attributes);
+
+        String descStr = getDescriptor().getString();
+        effectiveDescriptor =
+            Prototype.intern(descStr, definingClass.getClassType(),
+                                    AccessFlags.isStatic(accessFlags),
+                                    nat.isInstanceInit());
+    }
+
+    /** {@inheritDoc} */
+    public Prototype getEffectiveDescriptor() {
+        return effectiveDescriptor;
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMethodList.java b/dx/src/com/android/dx/cf/iface/StdMethodList.java
new file mode 100644
index 0000000..417cdee
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMethodList.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link MethodList}, which directly stores
+ * an array of {@link Method} objects and can be made immutable.
+ */
+public final class StdMethodList extends FixedSizeList implements MethodList {
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public StdMethodList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Method get(int n) {
+        return (Method) get0(n);
+    }
+
+    /**
+     * Sets the method at the given index.
+     *
+     * @param n {@code >= 0, < size();} which method
+     * @param method {@code null-ok;} the method object
+     */
+    public void set(int n, Method method) {
+        set0(n, method);
+    }
+}
diff --git a/dx/src/com/android/dx/cf/iface/package.html b/dx/src/com/android/dx/cf/iface/package.html
new file mode 100644
index 0000000..c734552
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/package.html
@@ -0,0 +1,10 @@
+<body>
+<p>Interfaces and base classes for dealing with class files. This package
+doesn't have any parsing but does have basic container implementations.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/command/DxConsole.java b/dx/src/com/android/dx/command/DxConsole.java
new file mode 100644
index 0000000..9ce9836
--- /dev/null
+++ b/dx/src/com/android/dx/command/DxConsole.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command;
+
+import java.io.PrintStream;
+
+/**
+ * Provides standard and error PrintStream object to output information.<br>
+ * By default the PrintStream objects link to {@code System.out} and
+ * {@code System.err} but they can be changed to link to other
+ * PrintStream.
+ */
+public class DxConsole {
+    /**
+     * Standard output stream. Links to {@code System.out} by default.
+     */
+    public static PrintStream out = System.out;
+
+    /**
+     * Error output stream. Links to {@code System.err} by default.
+     */
+    public static PrintStream err = System.err;
+}
diff --git a/dx/src/com/android/dx/command/Main.java b/dx/src/com/android/dx/command/Main.java
new file mode 100644
index 0000000..626cf7a
--- /dev/null
+++ b/dx/src/com/android/dx/command/Main.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command;
+
+import com.android.dx.Version;
+
+import junit.textui.TestRunner;
+
+/**
+ * Main class for dx. It recognizes enough options to be able to dispatch
+ * to the right "actual" main.
+ */
+public class Main {
+    private static String USAGE_MESSAGE =
+        "usage:\n" +
+        "  dx --dex [--debug] [--verbose] [--positions=<style>] " +
+        "[--no-locals]\n" +
+        "  [--no-optimize] [--statistics] [--[no-]optimize-list=<file>] " +
+        "[--no-strict]\n" +
+        "  [--keep-classes] [--output=<file>] [--dump-to=<file>] " +
+        "[--dump-width=<n>]\n" +
+        "  [--dump-method=<name>[*]] [--verbose-dump] [--no-files] " +
+        "[--core-library]\n" +
+        "  [<file>.class | <file>.{zip,jar,apk} | <directory>] ...\n" +
+        "    Convert a set of classfiles into a dex file, optionally " +
+        "embedded in a\n" +
+        "    jar/zip. Output name must end with one of: .dex .jar " +
+        ".zip .apk. Positions\n" +
+        "    options: none, important, lines.\n" +
+        "  dx --annotool --annotation=<class> [--element=<element types>]\n" +
+        "  [--print=<print types>]\n" +
+        "  dx --dump [--debug] [--strict] [--bytes] [--optimize]\n" +
+        "  [--basic-blocks | --rop-blocks | --ssa-blocks | --dot] " +
+        "[--ssa-step=<step>]\n" +
+        "  [--width=<n>] [<file>.class | <file>.txt] ...\n" +
+        "    Dump classfiles, or transformations thereof, in a " +
+        "human-oriented format.\n" +
+        "  dx --junit [-wait] <TestClass>\n" +
+        "    Run the indicated unit test.\n" +
+        "  dx -J<option> ... <arguments, in one of the above " +
+        "forms>\n" +
+        "    Pass VM-specific options to the virtual machine that " +
+        "runs dx.\n" +
+        "  dx --version\n" +
+        "    Print the version of this tool (" + Version.VERSION +
+        ").\n" +
+        "  dx --help\n" +
+        "    Print this message.";
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Main() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Run!
+     */
+    public static void main(String[] args) {
+        boolean gotCmd = false;
+        boolean showUsage = false;
+
+        try {
+            for (int i = 0; i < args.length; i++) {
+                String arg = args[i];
+                if (arg.equals("--") || !arg.startsWith("--")) {
+                    gotCmd = false;
+                    showUsage = true;
+                    break;
+                }
+
+                gotCmd = true;
+                if (arg.equals("--dex")) {
+                    com.android.dx.command.dexer.Main.main(without(args, i));
+                    break;
+                } else if (arg.equals("--dump")) {
+                    com.android.dx.command.dump.Main.main(without(args, i));
+                    break;
+                } else if (arg.equals("--annotool")) {
+                    com.android.dx.command.annotool.Main.main(
+                            without(args, i));
+                    break;
+                } else if (arg.equals("--junit")) {
+                    TestRunner.main(without(args, i));
+                    break;
+                } else if (arg.equals("--version")) {
+                    version();
+                    break;
+                } else if (arg.equals("--help")) {
+                    showUsage = true;
+                    break;
+                } else {
+                    gotCmd = false;
+                }
+            }
+        } catch (UsageException ex) {
+            showUsage = true;
+        } catch (RuntimeException ex) {
+            System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
+            ex.printStackTrace();
+            System.exit(2);
+        } catch (Throwable ex) {
+            System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:");
+            ex.printStackTrace();
+            if ((ex instanceof NoClassDefFoundError)
+                    || (ex instanceof NoSuchMethodError)) {
+                System.err.println(
+                        "Note: You may be using an incompatible " +
+                        "virtual machine or class library.\n" +
+                        "(This program is known to be incompatible " +
+                        "with recent releases of GCJ.)");
+            }
+            System.exit(3);
+        }
+
+        if (!gotCmd) {
+            System.err.println("error: no command specified");
+            showUsage = true;
+        }
+
+        if (showUsage) {
+            usage();
+            System.exit(1);
+        }
+    }
+
+    /**
+     * Prints the version message.
+     */
+    private static void version() {
+        System.err.println("dx version " + Version.VERSION);
+        System.exit(0);
+    }
+
+    /**
+     * Prints the usage message.
+     */
+    private static void usage() {
+        System.err.println(USAGE_MESSAGE);
+    }
+
+    /**
+     * Returns a copy of the given args array, but without the indicated
+     * element.
+     *
+     * @param orig {@code non-null;} original array
+     * @param n which element to omit
+     * @return {@code non-null;} new array
+     */
+    private static String[] without(String[] orig, int n) {
+        int len = orig.length - 1;
+        String[] newa = new String[len];
+        System.arraycopy(orig, 0, newa, 0, n);
+        System.arraycopy(orig, n + 1, newa, n, len - n);
+        return newa;
+    }
+}
diff --git a/dx/src/com/android/dx/command/UsageException.java b/dx/src/com/android/dx/command/UsageException.java
new file mode 100644
index 0000000..6809bf4
--- /dev/null
+++ b/dx/src/com/android/dx/command/UsageException.java
@@ -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.
+ */
+
+package com.android.dx.command;
+
+/**
+ * Simple exception class used to communicate that the command-line tool
+ * should print the usage message.
+ */
+public class UsageException extends RuntimeException {
+    // This space intentionally left blank.
+}
diff --git a/dx/src/com/android/dx/command/annotool/AnnotationLister.java b/dx/src/com/android/dx/command/annotool/AnnotationLister.java
new file mode 100644
index 0000000..a29e5ba
--- /dev/null
+++ b/dx/src/com/android/dx/command/annotool/AnnotationLister.java
@@ -0,0 +1,283 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.annotool;
+
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.BaseAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.util.ByteArray;
+import com.android.dx.rop.annotation.Annotation;
+
+import java.io.File;
+import java.lang.annotation.ElementType;
+import java.util.HashSet;
+
+/**
+ * Greps annotations on a set of class files and prints matching elements
+ * to stdout. What counts as a match and what should be printed is controlled
+ * by the {@code Main.Arguments} instance.
+ */
+class AnnotationLister {
+    /**
+     * The string name of the pseudo-class that
+     * contains package-wide annotations
+     */
+    private static final String PACKAGE_INFO = "package-info";
+
+    /** current match configuration */
+    private final Main.Arguments args;
+
+    /** Set of classes whose inner classes should be considered matched */
+    HashSet<String> matchInnerClassesOf = new HashSet<String>();
+
+    /** set of packages whose classes should be considered matched */
+    HashSet<String> matchPackages = new HashSet<String>();
+
+    AnnotationLister (Main.Arguments args) {
+        this.args = args;
+    }
+
+    /** Processes based on configuration specified in constructor. */
+    void process() {
+        for (String path : args.files) {
+            ClassPathOpener opener;
+
+            opener = new ClassPathOpener(path, true,
+                    new ClassPathOpener.Consumer() {
+                public boolean processFileBytes(String name, byte[] bytes) {
+                    if (!name.endsWith(".class")) {
+                        return true;
+                    }
+
+                    ByteArray ba = new ByteArray(bytes);
+                    DirectClassFile cf
+                        = new DirectClassFile(ba, name, true);
+
+                    cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+                    AttributeList attributes = cf.getAttributes();
+                    Attribute att;
+
+                    String cfClassName
+                            = cf.getThisClass().getClassType().getClassName();
+
+                    if (cfClassName.endsWith(PACKAGE_INFO)) {
+                        att = attributes.findFirst(
+                                AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+                        for (;att != null; att = attributes.findNext(att)) {
+                            BaseAnnotations ann = (BaseAnnotations)att;
+                            visitPackageAnnotation(cf, ann);
+                        }
+
+                        att = attributes.findFirst(
+                                AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+
+                        for (;att != null; att = attributes.findNext(att)) {
+                            BaseAnnotations ann = (BaseAnnotations)att;
+                            visitPackageAnnotation(cf, ann);
+                        }
+                    } else if (isMatchingInnerClass(cfClassName)
+                            || isMatchingPackage(cfClassName)) {
+                        printMatch(cf);
+                    } else {
+                        att = attributes.findFirst(
+                                AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+                        for (;att != null; att = attributes.findNext(att)) {
+                            BaseAnnotations ann = (BaseAnnotations)att;
+                            visitClassAnnotation(cf, ann);
+                        }
+
+                        att = attributes.findFirst(
+                                AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+
+                        for (;att != null; att = attributes.findNext(att)) {
+                            BaseAnnotations ann = (BaseAnnotations)att;
+                            visitClassAnnotation(cf, ann);
+                        }
+                    }
+
+                    return true;
+                }
+
+                public void onException(Exception ex) {
+                    throw new RuntimeException(ex);
+                }
+
+                public void onProcessArchiveStart(File file) {
+
+                }
+
+            });
+
+            opener.process();
+        }
+    }
+
+    /**
+     * Inspects a class annotation.
+     *
+     * @param cf {@code non-null;} class file
+     * @param ann {@code non-null;} annotation
+     */
+    private void visitClassAnnotation(DirectClassFile cf,
+            BaseAnnotations ann) {
+
+        if (!args.eTypes.contains(ElementType.TYPE)) {
+            return;
+        }
+
+        for (Annotation anAnn : ann.getAnnotations().getAnnotations()) {
+            String annClassName
+                    = anAnn.getType().getClassType().getClassName();
+            if (args.aclass.equals(annClassName)) {
+                printMatch(cf);
+            }
+        }
+    }
+
+    /**
+     * Inspects a package annotation
+     *
+     * @param cf {@code non-null;} class file of "package-info" pseudo-class
+     * @param ann {@code non-null;} annotation
+     */
+    private void visitPackageAnnotation(
+            DirectClassFile cf, BaseAnnotations ann) {
+
+        if (!args.eTypes.contains(ElementType.PACKAGE)) {
+            return;
+        }
+
+        String packageName = cf.getThisClass().getClassType().getClassName();
+
+        int slashIndex = packageName.lastIndexOf('/');
+
+        if (slashIndex == -1) {
+            packageName = "";
+        } else {
+            packageName
+                    = packageName.substring(0, slashIndex);
+        }
+
+
+        for (Annotation anAnn : ann.getAnnotations().getAnnotations()) {
+            String annClassName
+                    = anAnn.getType().getClassType().getClassName();
+            if (args.aclass.equals(annClassName)) {
+                printMatchPackage(packageName);
+            }
+        }
+    }
+
+
+    /**
+     * Prints, or schedules for printing, elements related to a
+     * matching package.
+     *
+     * @param packageName {@code non-null;} name of package
+     */
+    private void printMatchPackage(String packageName) {
+        for (Main.PrintType pt : args.printTypes) {
+            switch (pt) {
+                case CLASS:
+                case INNERCLASS:
+                case METHOD:
+                    matchPackages.add(packageName);
+                    break;
+                case PACKAGE:
+                    System.out.println(packageName.replace('/','.'));
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Prints, or schedules for printing, elements related to a matching
+     * class.
+     *
+     * @param cf {@code non-null;} matching class
+     */
+    private void printMatch(DirectClassFile cf) {
+        for (Main.PrintType pt : args.printTypes) {
+            switch (pt) {
+                case CLASS:
+                    String classname;
+                    classname =
+                        cf.getThisClass().getClassType().getClassName();
+                    classname = classname.replace('/','.');
+                    System.out.println(classname);
+                    break;
+                case INNERCLASS:
+                    matchInnerClassesOf.add(
+                            cf.getThisClass().getClassType().getClassName());
+                    break;
+                case METHOD:
+                    //TODO
+                    break;
+                case PACKAGE:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Checks to see if a specified class name should be considered a match
+     * due to previous matches.
+     *
+     * @param s {@code non-null;} class name
+     * @return true if this class should be considered a match
+     */
+    private boolean isMatchingInnerClass(String s) {
+        int i;
+
+        while (0 < (i = s.lastIndexOf('$'))) {
+            s = s.substring(0, i);
+            if (matchInnerClassesOf.contains(s)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks to see if a specified package should be considered a match due
+     * to previous matches.
+     *
+     * @param s {@code non-null;} package name
+     * @return true if this package should be considered a match
+     */
+    private boolean isMatchingPackage(String s) {
+        int slashIndex = s.lastIndexOf('/');
+
+        String packageName;
+        if (slashIndex == -1) {
+            packageName = "";
+        } else {
+            packageName
+                    = s.substring(0, slashIndex);
+        }
+
+        return matchPackages.contains(packageName);
+    }
+}
diff --git a/dx/src/com/android/dx/command/annotool/Main.java b/dx/src/com/android/dx/command/annotool/Main.java
new file mode 100644
index 0000000..7661c3d
--- /dev/null
+++ b/dx/src/com/android/dx/command/annotool/Main.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.annotool;
+
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.BaseAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.util.ByteArray;
+import com.android.dx.rop.annotation.Annotation;
+
+import java.io.File;
+import java.lang.annotation.ElementType;
+import java.util.EnumSet;
+import java.util.Arrays;
+
+
+public class Main {
+
+    private static class InvalidArgumentException extends Exception {
+        InvalidArgumentException() {
+            super();
+        }
+
+        InvalidArgumentException(String s) {
+            super(s);
+        }
+    }
+
+    enum PrintType {
+        CLASS,
+        INNERCLASS,
+        METHOD,
+        PACKAGE
+    }
+
+
+    static class Arguments {
+        /**
+         * from --annotation, dot-seperated classname
+         * of annotation to look for
+         */
+        String aclass;
+
+        /** from --eTypes */
+        EnumSet<ElementType> eTypes = EnumSet.noneOf(ElementType.class);
+
+        /** from --print */
+        EnumSet<PrintType> printTypes = EnumSet.noneOf(PrintType.class);
+
+        /** remaining positional arguments */
+        String[] files;
+
+        Arguments() {
+        }
+
+        void parse (String[] argArray) throws InvalidArgumentException {
+            for (int i = 0; i < argArray.length; i++) {
+                String arg = argArray[i];
+
+                if (arg.startsWith("--annotation=")) {
+                    String argParam = arg.substring(arg.indexOf('=') + 1);
+                    if (aclass != null) {
+                        throw new InvalidArgumentException(
+                                "--annotation can only be specified once.");
+                    }
+                    aclass = argParam.replace('.','/');
+                } else if (arg.startsWith("--element=")) {
+                    String argParam = arg.substring(arg.indexOf('=') + 1);
+
+                    try {
+                        for (String p : argParam.split(",")) {
+                            eTypes.add(ElementType.valueOf(p.toUpperCase()));
+                        }
+                    } catch (IllegalArgumentException ex) {
+                        throw new InvalidArgumentException(
+                                "invalid --element");
+                    }
+                } else if (arg.startsWith("--print=")) {
+                    String argParam = arg.substring(arg.indexOf('=') + 1);
+
+                    try {
+                        for (String p : argParam.split(",")) {
+                            printTypes.add(PrintType.valueOf(p.toUpperCase()));
+                        }
+                    } catch (IllegalArgumentException ex) {
+                        throw new InvalidArgumentException("invalid --print");
+                    }
+                } else {
+                    files = new String[argArray.length - i];
+                    System.arraycopy(argArray, i, files, 0, files.length);
+                    break;
+                }
+            }
+
+            if (aclass == null) {
+                throw new InvalidArgumentException(
+                        "--annotation must be specified");
+            }
+
+            if (printTypes.isEmpty()) {
+                printTypes.add(PrintType.CLASS);
+            }
+
+            if (eTypes.isEmpty()) {
+                eTypes.add(ElementType.TYPE);
+            }
+
+            EnumSet<ElementType> set = eTypes.clone();
+
+            set.remove(ElementType.TYPE);
+            set.remove(ElementType.PACKAGE);
+            if (!set.isEmpty()) {
+                throw new InvalidArgumentException(
+                        "only --element parameters 'type' and 'package' "
+                                + "supported");
+            }
+        }
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Main() {
+        // This space intentionally left blank.
+    }
+
+    public static void main(String[] argArray) {
+
+        final Arguments args = new Arguments();
+
+        try {
+            args.parse(argArray);
+        } catch (InvalidArgumentException ex) {
+            System.err.println(ex.getMessage());
+
+            throw new RuntimeException("usage");
+        }
+
+        new AnnotationLister(args).process();
+    }
+}
diff --git a/dx/src/com/android/dx/command/dexer/Main.java b/dx/src/com/android/dx/command/dexer/Main.java
new file mode 100644
index 0000000..363741a
--- /dev/null
+++ b/dx/src/com/android/dx/command/dexer/Main.java
@@ -0,0 +1,930 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.dexer;
+
+import com.android.dx.Version;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.command.DxConsole;
+import com.android.dx.command.UsageException;
+import com.android.dx.dex.cf.CfOptions;
+import com.android.dx.dex.cf.CfTranslator;
+import com.android.dx.dex.cf.CodeStatistics;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.dex.file.ClassDefItem;
+import com.android.dx.dex.file.DexFile;
+import com.android.dx.dex.file.EncodedMethod;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstUtf8;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+/**
+ * Main class for the class file translator.
+ */
+public class Main {
+    /**
+     * {@code non-null;} the lengthy message that tries to discourage
+     * people from defining core classes in applications
+     */
+    private static final String IN_RE_CORE_CLASSES =
+        "Ill-advised or mistaken usage of a core class (java.* or javax.*)\n" +
+        "when not building a core library.\n\n" +
+        "This is often due to inadvertently including a core library file\n" +
+        "in your application's project, when using an IDE (such as\n" +
+        "Eclipse). If you are sure you're not intentionally defining a\n" +
+        "core class, then this is the most likely explanation of what's\n" +
+        "going on.\n\n" +
+        "However, you might actually be trying to define a class in a core\n" +
+        "namespace, the source of which you may have taken, for example,\n" +
+        "from a non-Android virtual machine project. This will most\n" +
+        "assuredly not work. At a minimum, it jeopardizes the\n" +
+        "compatibility of your app with future versions of the platform.\n" +
+        "It is also often of questionable legality.\n\n" +
+        "If you really intend to build a core library -- which is only\n" +
+        "appropriate as part of creating a full virtual machine\n" +
+        "distribution, as opposed to compiling an application -- then use\n" +
+        "the \"--core-library\" option to suppress this error message.\n\n" +
+        "If you go ahead and use \"--core-library\" but are in fact\n" +
+        "building an application, then be forewarned that your application\n" +
+        "will still fail to build or run, at some point. Please be\n" +
+        "prepared for angry customers who find, for example, that your\n" +
+        "application ceases to function once they upgrade their operating\n" +
+        "system. You will be to blame for this problem.\n\n" +
+        "If you are legitimately using some code that happens to be in a\n" +
+        "core package, then the easiest safe alternative you have is to\n" +
+        "repackage that code. That is, move the classes in question into\n" +
+        "your own package namespace. This means that they will never be in\n" +
+        "conflict with core system classes. JarJar is a tool that may help\n" +
+        "you in this endeavor. If you find that you cannot do this, then\n" +
+        "that is an indication that the path you are on will ultimately\n" +
+        "lead to pain, suffering, grief, and lamentation.\n";
+
+    /**
+     * {@code non-null;} name for the {@code .dex} file that goes into
+     * {@code .jar} files
+     */
+    private static final String DEX_IN_JAR_NAME = "classes.dex";
+
+    /**
+     * {@code non-null;} name of the standard manifest file in {@code .jar}
+     * files
+     */
+    private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+
+    /**
+     * {@code non-null;} attribute name for the (quasi-standard?)
+     * {@code Created-By} attribute
+     */
+    private static final Attributes.Name CREATED_BY =
+        new Attributes.Name("Created-By");
+
+    /**
+     * {@code non-null;} list of {@code javax} subpackages that are considered
+     * to be "core". <b>Note:</b>: This list must be sorted, since it
+     * is binary-searched.
+     */
+    private static final String[] JAVAX_CORE = {
+        "accessibility", "crypto", "imageio", "management", "naming", "net",
+        "print", "rmi", "security", "sound", "sql", "swing", "transaction",
+        "xml"
+    };
+
+    /** number of warnings during processing */
+    private static int warnings = 0;
+
+    /** number of errors during processing */
+    private static int errors = 0;
+
+    /** {@code non-null;} parsed command-line arguments */
+    private static Arguments args;
+
+    /** {@code non-null;} output file in-progress */
+    private static DexFile outputDex;
+
+    /**
+     * {@code null-ok;} map of resources to include in the output, or
+     * {@code null} if resources are being ignored
+     */
+    private static TreeMap<String, byte[]> outputResources;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Main() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Run and exit if something unexpected happened.
+     * @param argArray the command line arguments
+     */
+    public static void main(String[] argArray) {
+        Arguments arguments = new Arguments();
+        arguments.parse(argArray);
+
+        int result = run(arguments);
+        if (result != 0) {
+            System.exit(result);
+        }
+    }
+
+    /**
+     * Run and return a result code.
+     * @param arguments the data + parameters for the conversion
+     * @return 0 if success > 0 otherwise.
+     */
+    public static int run(Arguments arguments) {
+        // Reset the error/warning count to start fresh.
+        warnings = 0;
+        errors = 0;
+
+        args = arguments;
+        args.makeCfOptions();
+
+        if (!processAllFiles()) {
+            return 1;
+        }
+
+        byte[] outArray = writeDex();
+
+        if (outArray == null) {
+            return 2;
+        }
+
+        if (args.jarOutput) {
+            // Effectively free up the (often massive) DexFile memory.
+            outputDex = null;
+
+            if (!createJar(args.outName, outArray)) {
+                return 3;
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * Constructs the output {@link DexFile}, fill it in with all the
+     * specified classes, and populate the resources map if required.
+     *
+     * @return whether processing was successful
+     */
+    private static boolean processAllFiles() {
+        outputDex = new DexFile();
+
+        if (args.jarOutput) {
+            outputResources = new TreeMap<String, byte[]>();
+        }
+
+        if (args.dumpWidth != 0) {
+            outputDex.setDumpWidth(args.dumpWidth);
+        }
+
+        boolean any = false;
+        String[] fileNames = args.fileNames;
+
+        try {
+            for (int i = 0; i < fileNames.length; i++) {
+                any |= processOne(fileNames[i]);
+            }
+        } catch (StopProcessing ex) {
+            /*
+             * Ignore it and just let the warning/error reporting do
+             * their things.
+             */
+        }
+
+        if (warnings != 0) {
+            DxConsole.err.println(warnings + " warning" +
+                               ((warnings == 1) ? "" : "s"));
+        }
+
+        if (errors != 0) {
+            DxConsole.err.println(errors + " error" +
+                    ((errors == 1) ? "" : "s") + "; aborting");
+            return false;
+        }
+
+        if (!(any || args.emptyOk)) {
+            DxConsole.err.println("no classfiles specified");
+            return false;
+        }
+
+        if (args.optimize && args.statistics) {
+            CodeStatistics.dumpStatistics(DxConsole.out);
+        }
+
+        return true;
+    }
+
+    /**
+     * Processes one pathname element.
+     *
+     * @param pathname {@code non-null;} the pathname to process. May
+     * be the path of a class file, a jar file, or a directory
+     * containing class files.
+     * @return whether any processing actually happened
+     */
+    private static boolean processOne(String pathname) {
+        ClassPathOpener opener;
+
+        opener = new ClassPathOpener(pathname, false,
+                new ClassPathOpener.Consumer() {
+            public boolean processFileBytes(String name, byte[] bytes) {
+                return Main.processFileBytes(name, bytes);
+            }
+            public void onException(Exception ex) {
+                if (ex instanceof StopProcessing) {
+                    throw (StopProcessing) ex;
+                }
+                DxConsole.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
+                ex.printStackTrace(DxConsole.err);
+                errors++;
+            }
+            public void onProcessArchiveStart(File file) {
+                if (args.verbose) {
+                    DxConsole.out.println("processing archive " + file +
+                            "...");
+                }
+            }
+        });
+
+        return opener.process();
+    }
+
+    /**
+     * Processes one file, which may be either a class or a resource.
+     *
+     * @param name {@code non-null;} name of the file
+     * @param bytes {@code non-null;} contents of the file
+     * @return whether processing was successful
+     */
+    private static boolean processFileBytes(String name, byte[] bytes) {
+        boolean isClass = name.endsWith(".class");
+        boolean keepResources = (outputResources != null);
+
+        if (!isClass && !keepResources) {
+            if (args.verbose) {
+                DxConsole.out.println("ignored resource " + name);
+            }
+            return false;
+        }
+
+        if (args.verbose) {
+            DxConsole.out.println("processing " + name + "...");
+        }
+
+        String fixedName = fixPath(name);
+
+        if (isClass) {
+            if (keepResources && args.keepClassesInJar) {
+                outputResources.put(fixedName, bytes);
+            }
+            return processClass(fixedName, bytes);
+        } else {
+            outputResources.put(fixedName, bytes);
+            return true;
+        }
+    }
+
+    /**
+     * Processes one classfile.
+     *
+     * @param name {@code non-null;} name of the file, clipped such that it
+     * <i>should</i> correspond to the name of the class it contains
+     * @param bytes {@code non-null;} contents of the file
+     * @return whether processing was successful
+     */
+    private static boolean processClass(String name, byte[] bytes) {
+        if (! args.coreLibrary) {
+            checkClassName(name);
+        }
+
+        try {
+            ClassDefItem clazz =
+                CfTranslator.translate(name, bytes, args.cfOptions);
+            outputDex.add(clazz);
+            return true;
+        } catch (ParseException ex) {
+            DxConsole.err.println("\ntrouble processing:");
+            if (args.debug) {
+                ex.printStackTrace(DxConsole.err);
+            } else {
+                ex.printContext(DxConsole.err);
+            }
+        }
+
+        warnings++;
+        return false;
+    }
+
+    /**
+     * Check the class name to make sure it's not a "core library"
+     * class. If there is a problem, this updates the error count and
+     * throws an exception to stop processing.
+     *
+     * @param name {@code non-null;} the fully-qualified internal-form
+     * class name
+     */
+    private static void checkClassName(String name) {
+        boolean bogus = false;
+
+        if (name.startsWith("java/")) {
+            bogus = true;
+        } else if (name.startsWith("javax/")) {
+            int slashAt = name.indexOf('/', 6);
+            if (slashAt == -1) {
+                // Top-level javax classes are verboten.
+                bogus = true;
+            } else {
+                String pkg = name.substring(6, slashAt);
+                bogus = (Arrays.binarySearch(JAVAX_CORE, pkg) >= 0);
+            }
+        }
+
+        if (! bogus) {
+            return;
+        }
+
+        /*
+         * The user is probably trying to include an entire desktop
+         * core library in a misguided attempt to get their application
+         * working. Try to help them understand what's happening.
+         */
+
+        DxConsole.err.println("\ntrouble processing \"" + name + "\":\n\n" +
+                IN_RE_CORE_CLASSES);
+        errors++;
+        throw new StopProcessing();
+    }
+
+    /**
+     * Converts {@link #outputDex} into a {@code byte[]}, write
+     * it out to the proper file (if any), and also do whatever human-oriented
+     * dumping is required.
+     *
+     * @return {@code null-ok;} the converted {@code byte[]} or {@code null}
+     * if there was a problem
+     */
+    private static byte[] writeDex() {
+        byte[] outArray = null;
+
+        try {
+            OutputStream out = null;
+            OutputStream humanOutRaw = null;
+            OutputStreamWriter humanOut = null;
+            try {
+                if (args.humanOutName != null) {
+                    humanOutRaw = openOutput(args.humanOutName);
+                    humanOut = new OutputStreamWriter(humanOutRaw);
+                }
+
+                if (args.methodToDump != null) {
+                    /*
+                     * Simply dump the requested method. Note: The call
+                     * to toDex() is required just to get the underlying
+                     * structures ready.
+                     */
+                    outputDex.toDex(null, false);
+                    dumpMethod(outputDex, args.methodToDump, humanOut);
+                } else {
+                    /*
+                     * This is the usual case: Create an output .dex file,
+                     * and write it, dump it, etc.
+                     */
+                    outArray = outputDex.toDex(humanOut, args.verboseDump);
+
+                    if ((args.outName != null) && !args.jarOutput) {
+                        out = openOutput(args.outName);
+                        out.write(outArray);
+                    }
+                }
+
+                if (args.statistics) {
+                    DxConsole.out.println(outputDex.getStatistics().toHuman());
+                }
+            } finally {
+                if (humanOut != null) {
+                    humanOut.flush();
+                }
+                closeOutput(out);
+                closeOutput(humanOutRaw);
+            }
+        } catch (Exception ex) {
+            if (args.debug) {
+                DxConsole.err.println("\ntrouble writing output:");
+                ex.printStackTrace(DxConsole.err);
+            } else {
+                DxConsole.err.println("\ntrouble writing output: " +
+                                   ex.getMessage());
+            }
+            return null;
+        }
+
+        return outArray;
+    }
+
+    /**
+     * Creates a jar file from the resources and given dex file array.
+     *
+     * @param fileName {@code non-null;} name of the file
+     * @param dexArray {@code non-null;} array containing the dex file
+     * to include
+     * @return whether the creation was successful
+     */
+    private static boolean createJar(String fileName, byte[] dexArray) {
+        /*
+         * Make or modify the manifest (as appropriate), put the dex
+         * array into the resources map, and then process the entire
+         * resources map in a uniform manner.
+         */
+
+        try {
+            Manifest manifest = makeManifest();
+            OutputStream out = openOutput(fileName);
+            JarOutputStream jarOut = new JarOutputStream(out, manifest);
+
+            outputResources.put(DEX_IN_JAR_NAME, dexArray);
+
+            try {
+                for (Map.Entry<String, byte[]> e :
+                         outputResources.entrySet()) {
+                    String name = e.getKey();
+                    byte[] contents = e.getValue();
+                    JarEntry entry = new JarEntry(name);
+
+                    if (args.verbose) {
+                        DxConsole.out.println("writing " + name + "; size " +
+                                           contents.length + "...");
+                    }
+
+                    entry.setSize(contents.length);
+                    jarOut.putNextEntry(entry);
+                    jarOut.write(contents);
+                    jarOut.closeEntry();
+                }
+            } finally {
+                jarOut.finish();
+                jarOut.flush();
+                closeOutput(out);
+            }
+        } catch (Exception ex) {
+            if (args.debug) {
+                DxConsole.err.println("\ntrouble writing output:");
+                ex.printStackTrace(DxConsole.err);
+            } else {
+                DxConsole.err.println("\ntrouble writing output: " +
+                                   ex.getMessage());
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Creates and returns the manifest to use for the output. This may
+     * modify {@link #outputResources} (removing the pre-existing manifest).
+     *
+     * @return {@code non-null;} the manifest
+     */
+    private static Manifest makeManifest() throws IOException {
+        byte[] manifestBytes = outputResources.get(MANIFEST_NAME);
+        Manifest manifest;
+        Attributes attribs;
+
+        if (manifestBytes == null) {
+            // We need to construct an entirely new manifest.
+            manifest = new Manifest();
+            attribs = manifest.getMainAttributes();
+            attribs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        } else {
+            manifest = new Manifest(new ByteArrayInputStream(manifestBytes));
+            attribs = manifest.getMainAttributes();
+            outputResources.remove(MANIFEST_NAME);
+        }
+
+        String createdBy = attribs.getValue(CREATED_BY);
+        if (createdBy == null) {
+            createdBy = "";
+        } else {
+            createdBy += " + ";
+        }
+        createdBy += "dx " + Version.VERSION;
+
+        attribs.put(CREATED_BY, createdBy);
+        attribs.putValue("Dex-Location", DEX_IN_JAR_NAME);
+
+        return manifest;
+    }
+
+    /**
+     * Opens and returns the named file for writing, treating "-" specially.
+     *
+     * @param name {@code non-null;} the file name
+     * @return {@code non-null;} the opened file
+     */
+    private static OutputStream openOutput(String name) throws IOException {
+        if (name.equals("-") ||
+                name.startsWith("-.")) {
+            return System.out;
+        }
+
+        return new FileOutputStream(name);
+    }
+
+    /**
+     * Flushes and closes the given output stream, except if it happens to be
+     * {@link System#out} in which case this method does the flush but not
+     * the close. This method will also silently do nothing if given a
+     * {@code null} argument.
+     *
+     * @param stream {@code null-ok;} what to close
+     */
+    private static void closeOutput(OutputStream stream) throws IOException {
+        if (stream == null) {
+            return;
+        }
+
+        stream.flush();
+
+        if (stream != System.out) {
+            stream.close();
+        }
+    }
+
+    /**
+     * Returns the "fixed" version of a given file path, suitable for
+     * use as a path within a {@code .jar} file and for checking
+     * against a classfile-internal "this class" name. This looks for
+     * the last instance of the substring {@code "/./"} within
+     * the path, and if it finds it, it takes the portion after to be
+     * the fixed path. If that isn't found but the path starts with
+     * {@code "./"}, then that prefix is removed and the rest is
+     * return. If neither of these is the case, this method returns
+     * its argument.
+     *
+     * @param path {@code non-null;} the path to "fix"
+     * @return {@code non-null;} the fixed version (which might be the same as
+     * the given {@code path})
+     */
+    private static String fixPath(String path) {
+        /*
+         * If the path separator is \ (like on windows), we convert the
+         * path to a standard '/' separated path.
+         */
+        if (File.separatorChar == '\\') {
+            path = path.replace('\\', '/');
+        }
+
+        int index = path.lastIndexOf("/./");
+
+        if (index != -1) {
+            return path.substring(index + 3);
+        }
+
+        if (path.startsWith("./")) {
+            return path.substring(2);
+        }
+
+        return path;
+    }
+
+    /**
+     * Dumps any method with the given name in the given file.
+     *
+     * @param dex {@code non-null;} the dex file
+     * @param fqName {@code non-null;} the fully-qualified name of the
+     * method(s)
+     * @param out {@code non-null;} where to dump to
+     */
+    private static void dumpMethod(DexFile dex, String fqName,
+            OutputStreamWriter out) {
+        boolean wildcard = fqName.endsWith("*");
+        int lastDot = fqName.lastIndexOf('.');
+
+        if ((lastDot <= 0) || (lastDot == (fqName.length() - 1))) {
+            DxConsole.err.println("bogus fully-qualified method name: " +
+                               fqName);
+            return;
+        }
+
+        String className = fqName.substring(0, lastDot).replace('.', '/');
+        String methodName = fqName.substring(lastDot + 1);
+        ClassDefItem clazz = dex.getClassOrNull(className);
+
+        if (clazz == null) {
+            DxConsole.err.println("no such class: " + className);
+            return;
+        }
+
+        if (wildcard) {
+            methodName = methodName.substring(0, methodName.length() - 1);
+        }
+
+        ArrayList<EncodedMethod> allMeths = clazz.getMethods();
+        TreeMap<CstNat, EncodedMethod> meths =
+            new TreeMap<CstNat, EncodedMethod>();
+
+        /*
+         * Figure out which methods to include in the output, and get them
+         * all sorted, so that the printout code is robust with respect to
+         * changes in the underlying order.
+         */
+        for (EncodedMethod meth : allMeths) {
+            String methName = meth.getName().getString();
+            if ((wildcard && methName.startsWith(methodName)) ||
+                (!wildcard && methName.equals(methodName))) {
+                meths.put(meth.getRef().getNat(), meth);
+            }
+        }
+
+        if (meths.size() == 0) {
+            DxConsole.err.println("no such method: " + fqName);
+            return;
+        }
+
+        PrintWriter pw = new PrintWriter(out);
+
+        for (EncodedMethod meth : meths.values()) {
+            // TODO: Better stuff goes here, perhaps.
+            meth.debugPrint(pw, args.verboseDump);
+
+            /*
+             * The (default) source file is an attribute of the class, but
+             * it's useful to see it in method dumps.
+             */
+            CstUtf8 sourceFile = clazz.getSourceFile();
+            if (sourceFile != null) {
+                pw.println("  source file: " + sourceFile.toQuoted());
+            }
+
+            Annotations methodAnnotations =
+                clazz.getMethodAnnotations(meth.getRef());
+            AnnotationsList parameterAnnotations =
+                clazz.getParameterAnnotations(meth.getRef());
+
+            if (methodAnnotations != null) {
+                pw.println("  method annotations:");
+                for (Annotation a : methodAnnotations.getAnnotations()) {
+                    pw.println("    " + a);
+                }
+            }
+
+            if (parameterAnnotations != null) {
+                pw.println("  parameter annotations:");
+                int sz = parameterAnnotations.size();
+                for (int i = 0; i < sz; i++) {
+                    pw.println("    parameter " + i);
+                    Annotations annotations = parameterAnnotations.get(i);
+                    for (Annotation a : annotations.getAnnotations()) {
+                        pw.println("      " + a);
+                    }
+                }
+            }
+        }
+
+        pw.flush();
+    }
+
+    /**
+     * Exception class used to halt processing prematurely.
+     */
+    private static class StopProcessing extends RuntimeException {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Command-line argument parser and access.
+     */
+    public static class Arguments {
+        /** whether to run in debug mode */
+        public boolean debug = false;
+
+        /** whether to emit high-level verbose human-oriented output */
+        public boolean verbose = false;
+
+        /** whether to emit verbose human-oriented output in the dump file */
+        public boolean verboseDump = false;
+
+        /** whether we are constructing a core library */
+        public boolean coreLibrary = false;
+
+        /** {@code null-ok;} particular method to dump */
+        public String methodToDump = null;
+
+        /** max width for columnar output */
+        public int dumpWidth = 0;
+
+        /** {@code null-ok;} output file name for binary file */
+        public String outName = null;
+
+        /** {@code null-ok;} output file name for human-oriented dump */
+        public String humanOutName = null;
+
+        /** whether strict file-name-vs-class-name checking should be done */
+        public boolean strictNameCheck = true;
+
+        /**
+         * whether it is okay for there to be no {@code .class} files
+         * to process
+         */
+        public boolean emptyOk = false;
+
+        /**
+         * whether the binary output is to be a {@code .jar} file
+         * instead of a plain {@code .dex}
+         */
+        public boolean jarOutput = false;
+
+        /**
+         * when writing a {@code .jar} file, whether to still
+         * keep the {@code .class} files
+         */
+        public boolean keepClassesInJar = false;
+
+        /** how much source position info to preserve */
+        public int positionInfo = PositionList.LINES;
+
+        /** whether to keep local variable information */
+        public boolean localInfo = true;
+
+        /** {@code non-null after {@link #parse};} file name arguments */
+        public String[] fileNames;
+
+        /** whether to do SSA/register optimization */
+        public boolean optimize = true;
+
+        /** Filename containg list of methods to optimize */
+        public String optimizeListFile = null;
+
+        /** Filename containing list of methods to NOT optimize */
+        public String dontOptimizeListFile = null;
+
+        /** Whether to print statistics to stdout at end of compile cycle */
+        public boolean statistics;
+
+        /** Options for dex.cf.* */
+        public CfOptions cfOptions;
+
+        /**
+         * Parses the given command-line arguments.
+         *
+         * @param args {@code non-null;} the arguments
+         */
+        public void parse(String[] args) {
+            int at = 0;
+
+            for (/*at*/; at < args.length; at++) {
+                String arg = args[at];
+                if (arg.equals("--") || !arg.startsWith("--")) {
+                    break;
+                } else if (arg.equals("--debug")) {
+                    debug = true;
+                } else if (arg.equals("--verbose")) {
+                    verbose = true;
+                } else if (arg.equals("--verbose-dump")) {
+                    verboseDump = true;
+                } else if (arg.equals("--no-files")) {
+                    emptyOk = true;
+                } else if (arg.equals("--no-optimize")) {
+                    optimize = false;
+                } else if (arg.equals("--no-strict")) {
+                    strictNameCheck = false;
+                } else if (arg.equals("--core-library")) {
+                    coreLibrary = true;
+                } else if (arg.equals("--statistics")) {
+                    statistics = true;
+                } else if (arg.startsWith("--optimize-list=")) {
+                    if (dontOptimizeListFile != null) {
+                        System.err.println("--optimize-list and "
+                                + "--no-optimize-list are incompatible.");
+                        throw new UsageException();
+                    }
+                    optimize = true;
+                    optimizeListFile = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.startsWith("--no-optimize-list=")) {
+                    if (dontOptimizeListFile != null) {
+                        System.err.println("--optimize-list and "
+                                + "--no-optimize-list are incompatible.");
+                        throw new UsageException();
+                    }
+                    optimize = true;
+                    dontOptimizeListFile = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.equals("--keep-classes")) {
+                    keepClassesInJar = true;
+                } else if (arg.startsWith("--output=")) {
+                    outName = arg.substring(arg.indexOf('=') + 1);
+                    if (outName.endsWith(".zip") ||
+                            outName.endsWith(".jar") ||
+                            outName.endsWith(".apk")) {
+                        jarOutput = true;
+                    } else if (outName.endsWith(".dex") ||
+                               outName.equals("-")) {
+                        jarOutput = false;
+                    } else {
+                        System.err.println("unknown output extension: " +
+                                           outName);
+                        throw new UsageException();
+                    }
+                } else if (arg.startsWith("--dump-to=")) {
+                    humanOutName = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.startsWith("--dump-width=")) {
+                    arg = arg.substring(arg.indexOf('=') + 1);
+                    dumpWidth = Integer.parseInt(arg);
+                } else if (arg.startsWith("--dump-method=")) {
+                    methodToDump = arg.substring(arg.indexOf('=') + 1);
+                    jarOutput = false;
+                } else if (arg.startsWith("--positions=")) {
+                    String pstr = arg.substring(arg.indexOf('=') + 1).intern();
+                    if (pstr == "none") {
+                        positionInfo = PositionList.NONE;
+                    } else if (pstr == "important") {
+                        positionInfo = PositionList.IMPORTANT;
+                    } else if (pstr == "lines") {
+                        positionInfo = PositionList.LINES;
+                    } else {
+                        System.err.println("unknown positions option: " +
+                                           pstr);
+                        throw new UsageException();
+                    }
+                } else if (arg.equals("--no-locals")) {
+                    localInfo = false;
+                } else {
+                    System.err.println("unknown option: " + arg);
+                    throw new UsageException();
+                }
+            }
+
+            int fileCount = args.length - at;
+
+            if (fileCount == 0) {
+                if (!emptyOk) {
+                    System.err.println("no input files specified");
+                    throw new UsageException();
+                }
+            } else if (emptyOk) {
+                System.out.println("ignoring input files");
+                at = 0;
+                fileCount = 0;
+            }
+
+            fileNames = new String[fileCount];
+            System.arraycopy(args, at, fileNames, 0, fileCount);
+
+            if ((humanOutName == null) && (methodToDump != null)) {
+                humanOutName = "-";
+            }
+
+            makeCfOptions();
+        }
+
+        /**
+         * Copies relevent arguments over into a CfOptions instance.
+         */
+        private void makeCfOptions() {
+            cfOptions = new CfOptions();
+
+            cfOptions.positionInfo = positionInfo;
+            cfOptions.localInfo = localInfo;
+            cfOptions.strictNameCheck = strictNameCheck;
+            cfOptions.optimize = optimize;
+            cfOptions.optimizeListFile = optimizeListFile;
+            cfOptions.dontOptimizeListFile = dontOptimizeListFile;
+            cfOptions.statistics = statistics;
+            cfOptions.warn = DxConsole.err;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/Args.java b/dx/src/com/android/dx/command/dump/Args.java
new file mode 100644
index 0000000..042fae2
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/Args.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.dump;
+
+/**
+ * contains command line parsedArgs values
+ */
+class Args {
+    /** whether to run in debug mode */
+    boolean debug = false;
+
+    /** whether to dump raw bytes where salient */
+    boolean rawBytes = false;
+
+    /** whether to dump information about basic blocks */
+    boolean basicBlocks = false;
+
+    /** whether to dump regiserized blocks */
+    boolean ropBlocks = false;
+
+    /** whether to dump SSA-form blocks */
+    boolean ssaBlocks = false;
+
+    /** Step in SSA processing to stop at, or null for all */
+    String ssaStep = null;
+
+    /** whether to run SSA optimizations */
+    boolean optimize = false;
+
+    /** whether to be strict about parsing classfiles*/
+    boolean strictParse = false;
+
+    /** max width for columnar output */
+    int width = 0;
+
+    /** whether to dump flow-graph in "dot" format */
+    boolean dotDump = false;
+
+    /** if non-null, an explicit method to dump */
+    String method;
+
+}
diff --git a/dx/src/com/android/dx/command/dump/BaseDumper.java b/dx/src/com/android/dx/command/dump/BaseDumper.java
new file mode 100644
index 0000000..ad6540b
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/BaseDumper.java
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IndentingWriter;
+import com.android.dx.util.TwoColumnOutput;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringWriter;
+
+/**
+ * Base class for the various human-friendly dumpers.
+ */
+public abstract class BaseDumper
+        implements ParseObserver {
+    /** {@code non-null;} array of data being dumped */
+    private final byte[] bytes;
+
+    /** whether or not to include the raw bytes (in a column on the left) */
+    private final boolean rawBytes;
+
+    /** {@code non-null;} where to dump to */
+    private final PrintStream out;
+
+    /** width of the output in columns */
+    private final int width;
+
+    /**
+     * {@code non-null;} the file path for the class, excluding any base
+     * directory specification
+     */
+    private final String filePath;
+
+    /** whether to be strict about parsing */
+    private final boolean strictParse;
+
+     /** number of bytes per line in hex dumps */
+    private final int hexCols;
+
+    /** the current level of indentation */
+    private int indent;
+
+    /** {@code non-null;} the current column separator string */
+    private String separator;
+
+    /** the offset of the next byte to dump */
+    private int at;
+
+    /** commandline parsedArgs */
+    protected Args args;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes {@code non-null;} bytes of the (alleged) class file
+     * on the left)
+     * @param out {@code non-null;} where to dump to
+     * @param filePath the file path for the class, excluding any base
+     * directory specification
+     */
+    public BaseDumper(byte[] bytes, PrintStream out,
+                      String filePath, Args args) {
+        this.bytes = bytes;
+        this.rawBytes = args.rawBytes;
+        this.out = out;
+        this.width = (args.width <= 0) ? 79 : args.width;
+        this.filePath = filePath;
+        this.strictParse = args.strictParse;
+        this.indent = 0;
+        this.separator = rawBytes ? "|" : "";
+        this.at = 0;
+        this.args = args;
+
+        int hexCols = (((width - 5) / 15) + 1) & ~1;
+        if (hexCols < 6) {
+            hexCols = 6;
+        } else if (hexCols > 10) {
+            hexCols = 10;
+        }
+        this.hexCols = hexCols;
+    }
+
+    /**
+     * Computes the total width, in register-units, of the parameters for
+     * this method.
+     * @param meth method to process
+     * @return width in register-units
+     */
+    static int computeParamWidth(ConcreteMethod meth, boolean isStatic) {
+        return meth.getEffectiveDescriptor().getParameterTypes().
+            getWordCount();
+    }
+
+    /** {@inheritDoc} */
+    public void changeIndent(int indentDelta) {
+        indent += indentDelta;
+
+        separator = rawBytes ? "|" : "";
+        for (int i = 0; i < indent; i++) {
+            separator += "  ";
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void parsed(ByteArray bytes, int offset, int len, String human) {
+        offset = bytes.underlyingOffset(offset, getBytes());
+
+        boolean rawBytes = getRawBytes();
+
+        if (offset < at) {
+            println("<dump skipped backwards to " + Hex.u4(offset) + ">");
+            at = offset;
+        } else if (offset > at) {
+            String hex = rawBytes ? hexDump(at, offset - at) : "";
+            print(twoColumns(hex, "<skipped to " + Hex.u4(offset) + ">"));
+            at = offset;
+        }
+
+        String hex = rawBytes ? hexDump(offset, len) : "";
+        print(twoColumns(hex, human));
+        at += len;
+    }
+
+    /** {@inheritDoc} */
+    public void startParsingMember(ByteArray bytes, int offset, String name,
+                                   String descriptor) {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member) {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the current dump cursor (that is, the offset of the expected
+     * next byte to dump).
+     *
+     * @return {@code >= 0;} the dump cursor
+     */
+    protected final int getAt() {
+        return at;
+    }
+
+    /**
+     * Sets the dump cursor to the indicated offset in the given array.
+     *
+     * @param arr {@code non-null;} array in question
+     * @param offset {@code >= 0;} offset into the array
+     */
+    protected final void setAt(ByteArray arr, int offset) {
+        at = arr.underlyingOffset(offset, bytes);
+    }
+
+    /**
+     * Gets the array of {@code byte}s to process.
+     *
+     * @return {@code non-null;} the bytes
+     */
+    protected final byte[] getBytes() {
+        return bytes;
+    }
+
+    /**
+     * Gets the filesystem/jar path of the file being dumped.
+     *
+     * @return {@code non-null;} the path
+     */
+    protected final String getFilePath() {
+        return filePath;
+    }
+
+    /**
+     * Gets whether to be strict about parsing.
+     *
+     * @return whether to be strict about parsing
+     */
+    protected final boolean getStrictParse() {
+        return strictParse;
+    }
+
+    /**
+     * Prints the given string to this instance's output stream.
+     *
+     * @param s {@code null-ok;} string to print
+     */
+    protected final void print(String s) {
+        out.print(s);
+    }
+
+    /**
+     * Prints the given string to this instance's output stream, followed
+     * by a newline.
+     *
+     * @param s {@code null-ok;} string to print
+     */
+    protected final void println(String s) {
+        out.println(s);
+    }
+
+    /**
+     * Gets whether this dump is to include raw bytes.
+     *
+     * @return the raw bytes flag
+     */
+    protected final boolean getRawBytes() {
+        return rawBytes;
+    }
+
+    /**
+     * Gets the width of the first column of output. This is {@code 0}
+     * unless raw bytes are being included in the output.
+     *
+     * @return {@code >= 0;} the width of the first column
+     */
+    protected final int getWidth1() {
+        if (rawBytes) {
+            return 5 + (hexCols * 2) + (hexCols / 2);
+        }
+
+        return 0;
+    }
+
+    /**
+     * Gets the width of the second column of output.
+     *
+     * @return {@code >= 0;} the width of the second column
+     */
+    protected final int getWidth2() {
+        int w1 = rawBytes ? (getWidth1() + 1) : 0;
+        return width - w1 - (indent * 2);
+    }
+
+    /**
+     * Constructs a hex data dump of the given portion of {@link #bytes}.
+     *
+     * @param offset offset to start dumping at
+     * @param len length to dump
+     * @return {@code non-null;} the dump
+     */
+    protected final String hexDump(int offset, int len) {
+        return Hex.dump(bytes, offset, len, offset, hexCols, 4);
+    }
+
+    /**
+     * Combines a pair of strings as two columns, or if this is one-column
+     * output, format the otherwise-second column.
+     *
+     * @param s1 {@code non-null;} the first column's string
+     * @param s2 {@code non-null;} the second column's string
+     * @return {@code non-null;} the combined output
+     */
+    protected final String twoColumns(String s1, String s2) {
+        int w1 = getWidth1();
+        int w2 = getWidth2();
+
+        try {
+            if (w1 == 0) {
+                int len2 = s2.length();
+                StringWriter sw = new StringWriter(len2 * 2);
+                IndentingWriter iw = new IndentingWriter(sw, w2, separator);
+
+                iw.write(s2);
+                if ((len2 == 0) || (s2.charAt(len2 - 1) != '\n')) {
+                    iw.write('\n');
+                }
+                iw.flush();
+
+                return sw.toString();
+            } else {
+                return TwoColumnOutput.toString(s1, w1, separator, s2, w2);
+            }
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/BlockDumper.java b/dx/src/com/android/dx/command/dump/BlockDumper.java
new file mode 100644
index 0000000..7a11888
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/BlockDumper.java
@@ -0,0 +1,355 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.code.BasicBlocker;
+import com.android.dx.cf.code.ByteBlock;
+import com.android.dx.cf.code.ByteBlockList;
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.CodeObserver;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.io.PrintStream;
+
+/**
+ * Utility to dump basic block info from methods in a human-friendly form.
+ */
+public class BlockDumper
+        extends BaseDumper {
+    /** whether or not to registerize (make rop blocks) */
+    private boolean rop;
+
+    /**
+     * {@code null-ok;} the class file object being constructed;
+     * becomes non-null during {@link #dump}
+     */
+    protected DirectClassFile classFile;
+
+    /** whether or not to suppress dumping */
+    protected boolean suppressDump;
+
+    /** whether this is the first method being dumped */
+    private boolean first;
+
+    /** whether or not to run the ssa optimziations */
+    private boolean optimize;
+
+    /**
+     * Dumps the given array, interpreting it as a class file and dumping
+     * methods with indications of block-level stuff.
+     *
+     * @param bytes {@code non-null;} bytes of the (alleged) class file
+     * @param out {@code non-null;} where to dump to
+     * @param filePath the file path for the class, excluding any base
+     * directory specification
+     * @param rop whether or not to registerize (make rop blocks)
+     * @param args commandline parsedArgs
+     */
+    public static void dump(byte[] bytes, PrintStream out,
+                            String filePath, boolean rop, Args args) {
+        BlockDumper bd =
+            new BlockDumper(bytes, out, filePath,
+                            rop, args);
+        bd.dump();
+    }
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable.
+     * Use {@link #dump}.
+     */
+    BlockDumper(byte[] bytes, PrintStream out,
+                        String filePath,
+                        boolean rop, Args args) {
+        super(bytes, out, filePath, args);
+
+        this.rop = rop;
+        this.classFile = null;
+        this.suppressDump = true;
+        this.first = true;
+        this.optimize = args.optimize;
+    }
+
+    /**
+     * Does the dumping.
+     */
+    public void dump() {
+        byte[] bytes = getBytes();
+        ByteArray ba = new ByteArray(bytes);
+
+        /*
+         * First, parse the file completely, so we can safely refer to
+         * attributes, etc.
+         */
+        classFile = new DirectClassFile(ba, getFilePath(), getStrictParse());
+        classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        classFile.getMagic(); // Force parsing to happen.
+
+        // Next, reparse it and observe the process.
+        DirectClassFile liveCf =
+            new DirectClassFile(ba, getFilePath(), getStrictParse());
+        liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        liveCf.setObserver(this);
+        liveCf.getMagic(); // Force parsing to happen.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void changeIndent(int indentDelta) {
+        if (!suppressDump) {
+            super.changeIndent(indentDelta);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void parsed(ByteArray bytes, int offset, int len, String human) {
+        if (!suppressDump) {
+            super.parsed(bytes, offset, len, human);
+        }
+    }
+
+    /**
+     * @param name method name
+     * @return true if this method should be dumped
+     */
+    protected boolean shouldDumpMethod(String name) {
+        return args.method == null || args.method.equals(name);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void startParsingMember(ByteArray bytes, int offset, String name,
+                                   String descriptor) {
+        if (descriptor.indexOf('(') < 0) {
+            // It's a field, not a method
+            return;
+        }
+
+        if (!shouldDumpMethod(name)) {
+            return;
+        }
+
+        // Reset the dump cursor to the start of the method.
+        setAt(bytes, offset);
+
+        suppressDump = false;
+
+        if (first) {
+            first = false;
+        } else {
+            parsed(bytes, offset, 0, "\n");
+        }
+
+        parsed(bytes, offset, 0, "method " + name + " " + descriptor);
+        suppressDump = true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member) {
+        if (!(member instanceof Method)) {
+            return;
+        }
+
+        if (!shouldDumpMethod(name)) {
+            return;
+        }
+
+        ConcreteMethod meth = new ConcreteMethod((Method) member, classFile,
+                                                 true, true);
+
+        if (rop) {
+            ropDump(meth);
+        } else {
+            regularDump(meth);
+        }
+    }
+
+    /**
+     * Does a regular basic block dump.
+     *
+     * @param meth {@code non-null;} method data to dump
+     */
+    private void regularDump(ConcreteMethod meth) {
+        BytecodeArray code = meth.getCode();
+        ByteArray bytes = code.getBytes();
+        ByteBlockList list = BasicBlocker.identifyBlocks(meth);
+        int sz = list.size();
+        CodeObserver codeObserver = new CodeObserver(bytes, BlockDumper.this);
+
+        // Reset the dump cursor to the start of the bytecode
+        setAt(bytes, 0);
+
+        suppressDump = false;
+
+        int byteAt = 0;
+        for (int i = 0; i < sz; i++) {
+            ByteBlock bb = list.get(i);
+            int start = bb.getStart();
+            int end = bb.getEnd();
+
+            if (byteAt < start) {
+                parsed(bytes, byteAt, start - byteAt,
+                       "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(start));
+            }
+
+            parsed(bytes, start, 0,
+                   "block " + Hex.u2(bb.getLabel()) + ": " +
+                   Hex.u2(start) + ".." + Hex.u2(end));
+            changeIndent(1);
+
+            int len;
+            for (int j = start; j < end; j += len) {
+                len = code.parseInstruction(j, codeObserver);
+                codeObserver.setPreviousOffset(j);
+            }
+
+            IntList successors = bb.getSuccessors();
+            int ssz = successors.size();
+            if (ssz == 0) {
+                parsed(bytes, end, 0, "returns");
+            } else {
+                for (int j = 0; j < ssz; j++) {
+                    int succ = successors.get(j);
+                    parsed(bytes, end, 0, "next " + Hex.u2(succ));
+                }
+            }
+
+            ByteCatchList catches = bb.getCatches();
+            int csz = catches.size();
+            for (int j = 0; j < csz; j++) {
+                ByteCatchList.Item one = catches.get(j);
+                CstType exceptionClass = one.getExceptionClass();
+                parsed(bytes, end, 0,
+                       "catch " +
+                       ((exceptionClass == CstType.OBJECT) ? "<any>" :
+                        exceptionClass.toHuman()) + " -> " +
+                       Hex.u2(one.getHandlerPc()));
+            }
+
+            changeIndent(-1);
+            byteAt = end;
+        }
+
+        int end = bytes.size();
+        if (byteAt < end) {
+            parsed(bytes, byteAt, end - byteAt,
+                   "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(end));
+        }
+
+        suppressDump = true;
+    }
+
+    /**
+     * Does a registerizing dump.
+     *
+     * @param meth {@code non-null;} method data to dump
+     */
+    private void ropDump(ConcreteMethod meth) {
+        BytecodeArray code = meth.getCode();
+        ByteArray bytes = code.getBytes();
+
+        TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+
+        RopMethod rmeth =
+            Ropper.convert(meth, advice);
+        StringBuffer sb = new StringBuffer(2000);
+
+        if (optimize) {
+            boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+
+            int paramWidth = computeParamWidth(meth, isStatic);
+            rmeth = Optimizer.optimize(rmeth, paramWidth, isStatic, true,
+                    advice);
+        }
+
+        BasicBlockList blocks = rmeth.getBlocks();
+
+        sb.append("first " + Hex.u2(rmeth.getFirstLabel()) + "\n");
+
+        int sz = blocks.size();
+        for (int i = 0; i < sz; i++) {
+            BasicBlock bb = blocks.get(i);
+            int label = bb.getLabel();
+            sb.append("block ");
+            sb.append(Hex.u2(label));
+            sb.append("\n");
+
+            IntList preds = rmeth.labelToPredecessors(label);
+            int psz = preds.size();
+            for (int j = 0; j < psz; j++) {
+                sb.append("  pred ");
+                sb.append(Hex.u2(preds.get(j)));
+                sb.append("\n");
+            }
+
+            InsnList il = bb.getInsns();
+            int ilsz = il.size();
+            for (int j = 0; j < ilsz; j++) {
+                Insn one = il.get(j);
+                sb.append("  ");
+                sb.append(il.get(j).toHuman());
+                sb.append("\n");
+            }
+
+            IntList successors = bb.getSuccessors();
+            int ssz = successors.size();
+            if (ssz == 0) {
+                sb.append("  returns\n");
+            } else {
+                int primary = bb.getPrimarySuccessor();
+                for (int j = 0; j < ssz; j++) {
+                    int succ = successors.get(j);
+                    sb.append("  next ");
+                    sb.append(Hex.u2(succ));
+
+                    if ((ssz != 1) && (succ == primary)) {
+                        sb.append(" *");
+                    }
+
+                    sb.append("\n");
+                }
+            }
+        }
+
+        suppressDump = false;
+        setAt(bytes, 0);
+        parsed(bytes, 0, bytes.size(), sb.toString());
+        suppressDump = true;
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/ClassDumper.java b/dx/src/com/android/dx/command/dump/ClassDumper.java
new file mode 100644
index 0000000..e98b6d6
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/ClassDumper.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.util.ByteArray;
+
+import java.io.PrintStream;
+
+/**
+ * Utility to dump the contents of class files in a human-friendly form.
+ */
+public final class ClassDumper
+        extends BaseDumper {
+    /**
+     * Dumps the given array, interpreting it as a class file.
+     *
+     * @param bytes {@code non-null;} bytes of the (alleged) class file
+     * @param out {@code non-null;} where to dump to
+     * passed in as &lt;= 0
+     * @param filePath the file path for the class, excluding any base
+     * directory specification
+     * @param args bag of commandline arguments
+     */
+    public static void dump(byte[] bytes, PrintStream out,
+                            String filePath, Args args) {
+        ClassDumper cd =
+            new ClassDumper(bytes, out, filePath, args);
+        cd.dump();
+    }
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable.
+     * Use {@link #dump}.
+     */
+    private ClassDumper(byte[] bytes, PrintStream out,
+                        String filePath, Args args) {
+        super(bytes, out, filePath, args);
+    }
+
+    /**
+     * Does the dumping.
+     */
+    public void dump() {
+        byte[] bytes = getBytes();
+        ByteArray ba = new ByteArray(bytes);
+        DirectClassFile cf =
+            new DirectClassFile(ba, getFilePath(), getStrictParse());
+
+        cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        cf.setObserver(this);
+        cf.getMagic(); // Force parsing to happen.
+
+        int at = getAt();
+        if (at != bytes.length) {
+            parsed(ba, at, bytes.length - at, "<extra data at end of file>");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/DotDumper.java b/dx/src/com/android/dx/command/dump/DotDumper.java
new file mode 100644
index 0000000..9de48fc
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/DotDumper.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * Dumps the pred/succ graph of methods into a format compatible
+ * with the popular graph utility "dot".
+ */
+public class DotDumper implements ParseObserver {
+    private DirectClassFile classFile;
+
+    private final byte[] bytes;
+    private final String filePath;
+    private final boolean strictParse;
+    private final boolean optimize;
+    private final Args args;
+
+    static void dump(byte[] bytes, String filePath, Args args) {
+        new DotDumper(bytes, filePath, args).run();
+    }
+
+    DotDumper(byte[] bytes, String filePath, Args args) {
+        this.bytes = bytes;
+        this.filePath = filePath;
+        this.strictParse = args.strictParse;
+        this.optimize = args.optimize;
+        this.args = args;
+    }
+
+    private void run() {
+        ByteArray ba = new ByteArray(bytes);
+
+        /*
+         * First, parse the file completely, so we can safely refer to
+         * attributes, etc.
+         */
+        classFile = new DirectClassFile(ba, filePath, strictParse);
+        classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        classFile.getMagic(); // Force parsing to happen.
+
+        // Next, reparse it and observe the process.
+        DirectClassFile liveCf =
+            new DirectClassFile(ba, filePath, strictParse);
+        liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        liveCf.setObserver(this);
+        liveCf.getMagic(); // Force parsing to happen.
+    }
+
+    /**
+     * @param name method name
+     * @return true if this method should be dumped
+     */
+    protected boolean shouldDumpMethod(String name) {
+        return args.method == null || args.method.equals(name);
+    }
+
+    public void changeIndent(int indentDelta) {
+        // This space intentionally left blank.
+    }
+
+    public void parsed(ByteArray bytes, int offset, int len, String human) {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    public void startParsingMember(ByteArray bytes, int offset, String name,
+                                   String descriptor) {
+        // This space intentionally left blank.
+    }
+
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+                                 String descriptor, Member member) {
+        if (!(member instanceof Method)) {
+            return;
+        }
+
+        if (!shouldDumpMethod(name)) {
+            return;
+        }
+
+        ConcreteMethod meth = new ConcreteMethod((Method) member, classFile,
+                                                 true, true);
+
+        TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+        RopMethod rmeth =
+            Ropper.convert(meth, advice);
+
+        if (optimize) {
+            boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+            rmeth = Optimizer.optimize(rmeth,
+                    BaseDumper.computeParamWidth(meth, isStatic), isStatic,
+                    true, advice);
+        }
+
+        System.out.println("digraph "  + name + "{");
+
+        System.out.println("\tfirst -> n"
+                + Hex.u2(rmeth.getFirstLabel()) + ";");
+
+        BasicBlockList blocks = rmeth.getBlocks();
+
+        int sz = blocks.size();
+        for (int i = 0; i < sz; i++) {
+            BasicBlock bb = blocks.get(i);
+            int label = bb.getLabel();
+            IntList successors = bb.getSuccessors();
+
+            if (successors.size() == 0) {
+                System.out.println("\tn" + Hex.u2(label) + " -> returns;");
+            } else if (successors.size() == 1) {
+                System.out.println("\tn" + Hex.u2(label) + " -> n"
+                        + Hex.u2(successors.get(0)) + ";");
+            } else {
+                System.out.print("\tn" + Hex.u2(label) + " -> {");
+                for (int j = 0; j < successors.size(); j++ ) {
+                    int successor = successors.get(j);
+
+                    if (successor != bb.getPrimarySuccessor()) {
+                        System.out.print(" n" + Hex.u2(successor) + " ");
+                    }
+
+                }
+                System.out.println("};");
+
+                System.out.println("\tn" + Hex.u2(label) + " -> n"
+                        + Hex.u2(bb.getPrimarySuccessor())
+                        + " [label=\"primary\"];");
+
+
+            }
+        }
+
+        System.out.println("}");
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/Main.java b/dx/src/com/android/dx/command/dump/Main.java
new file mode 100644
index 0000000..d6ba374
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/Main.java
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.util.FileUtils;
+import com.android.dx.util.HexParser;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Main class for the class file dumper.
+ */
+public class Main {
+
+    static Args parsedArgs = new Args();
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Main() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Run!
+     */
+    public static void main(String[] args) {
+        int at = 0;
+
+        for (/*at*/; at < args.length; at++) {
+            String arg = args[at];
+            if (arg.equals("--") || !arg.startsWith("--")) {
+                break;
+            } else if (arg.equals("--bytes")) {
+                parsedArgs.rawBytes = true;
+            } else if (arg.equals("--basic-blocks")) {
+                parsedArgs.basicBlocks = true;
+            } else if (arg.equals("--rop-blocks")) {
+                parsedArgs.ropBlocks = true;
+            } else if (arg.equals("--optimize")) {
+                parsedArgs.optimize = true;
+            } else if (arg.equals("--ssa-blocks")) {
+                parsedArgs.ssaBlocks = true;
+            } else if (arg.startsWith("--ssa-step=")) {
+                parsedArgs.ssaStep = arg.substring(arg.indexOf('=') + 1);
+            } else if (arg.equals("--debug")) {
+                parsedArgs.debug = true;
+            } else if (arg.equals("--dot")) {
+                parsedArgs.dotDump = true;
+            } else if (arg.equals("--strict")) {
+                parsedArgs.strictParse = true;
+            } else if (arg.startsWith("--width=")) {
+                arg = arg.substring(arg.indexOf('=') + 1);
+                parsedArgs.width = Integer.parseInt(arg);
+            } else if (arg.startsWith("--method=")) {
+                arg = arg.substring(arg.indexOf('=') + 1);
+                parsedArgs.method = arg;
+            } else {
+                System.err.println("unknown option: " + arg);
+                throw new RuntimeException("usage");
+            }
+        }
+
+        if (at == args.length) {
+            System.err.println("no input files specified");
+            throw new RuntimeException("usage");
+        }
+
+        for (/*at*/; at < args.length; at++) {
+            try {
+                String name = args[at];
+                System.out.println("reading " + name + "...");
+                byte[] bytes = FileUtils.readFile(name);
+                if (!name.endsWith(".class")) {
+                    String src;
+                    try {
+                        src = new String(bytes, "utf-8");
+                    } catch (UnsupportedEncodingException ex) {
+                        throw new RuntimeException("shouldn't happen", ex);
+                    }
+                    bytes = HexParser.parse(src);
+                }
+                processOne(name, bytes);
+            } catch (ParseException ex) {
+                System.err.println("\ntrouble parsing:");
+                if (parsedArgs.debug) {
+                    ex.printStackTrace();
+                } else {
+                    ex.printContext(System.err);
+                }
+            }
+        }
+    }
+
+    /**
+     * Processes one file.
+     *
+     * @param name {@code non-null;} name of the file
+     * @param bytes {@code non-null;} contents of the file
+     */
+    private static void processOne(String name, byte[] bytes) {
+        if (parsedArgs.dotDump) {
+            DotDumper.dump(bytes, name, parsedArgs);
+        } else if (parsedArgs.basicBlocks) {
+            BlockDumper.dump(bytes, System.out, name, false, parsedArgs);
+        } else if (parsedArgs.ropBlocks) {
+            BlockDumper.dump(bytes, System.out, name, true, parsedArgs);
+        } else if (parsedArgs.ssaBlocks) {
+            // --optimize ignored with --ssa-blocks
+            parsedArgs.optimize = false;
+            SsaDumper.dump(bytes, System.out, name, parsedArgs);
+        } else {
+            ClassDumper.dump(bytes, System.out, name, parsedArgs);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/command/dump/SsaDumper.java b/dx/src/com/android/dx/command/dump/SsaDumper.java
new file mode 100644
index 0000000..de44c83
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/SsaDumper.java
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.ssa.DeadCodeRemover;
+import com.android.dx.ssa.PhiTypeResolver;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaConverter;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.ssa.ConstCollector;
+import com.android.dx.ssa.SCCP;
+import com.android.dx.ssa.LiteralOpUpgrader;
+import com.android.dx.ssa.back.SsaToRop;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.EnumSet;
+
+/**
+ * Dumper for the SSA-translated blocks of a method.
+ */
+public class SsaDumper extends BlockDumper {
+    /**
+     * Does the dump.
+     *
+     * @param bytes {@code non-null;} bytes of the original class file
+     * @param out {@code non-null;} where to dump to
+     * @param filePath the file path for the class, excluding any base
+     * directory specification
+     * @param args commandline parsedArgs
+     */
+    public static void dump(byte[] bytes, PrintStream out,
+            String filePath, Args args) {
+        SsaDumper sd = new SsaDumper(bytes, out, filePath, args);
+        sd.dump();
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes {@code non-null;} bytes of the original class file
+     * @param out {@code non-null;} where to dump to
+     * @param filePath the file path for the class, excluding any base
+     * directory specification
+     * @param args commandline parsedArgs
+     */
+    private SsaDumper(byte[] bytes, PrintStream out, String filePath,
+            Args args) {
+        super(bytes, out, filePath, true, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void endParsingMember(ByteArray bytes, int offset, String name,
+            String descriptor, Member member) {
+        if (!(member instanceof Method)) {
+            return;
+        }
+
+        if (!shouldDumpMethod(name)) {
+            return;
+        }
+
+        ConcreteMethod meth =
+            new ConcreteMethod((Method) member, classFile, true, true);
+        TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+        RopMethod rmeth = Ropper.convert(meth, advice);
+        SsaMethod ssaMeth = null;
+        boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+        int paramWidth = computeParamWidth(meth, isStatic);
+
+        if (args.ssaStep == null) {
+            ssaMeth = Optimizer.debugNoRegisterAllocation(rmeth,
+                    paramWidth, isStatic, true, advice,
+                    EnumSet.allOf(Optimizer.OptionalStep.class));
+        } else if ("edge-split".equals(args.ssaStep)) {
+            ssaMeth = Optimizer.debugEdgeSplit(rmeth, paramWidth,
+                    isStatic, true, advice);
+        } else if ("phi-placement".equals(args.ssaStep)) {
+            ssaMeth = Optimizer.debugPhiPlacement(
+                    rmeth, paramWidth, isStatic, true, advice);
+        } else if ("renaming".equals(args.ssaStep)) {
+            ssaMeth = Optimizer.debugRenaming(
+                    rmeth, paramWidth, isStatic, true, advice);
+        } else if ("dead-code".equals(args.ssaStep)) {
+            ssaMeth = Optimizer.debugDeadCodeRemover(
+                    rmeth, paramWidth, isStatic,true, advice);
+        }
+
+        StringBuffer sb = new StringBuffer(2000);
+
+        sb.append("first ");
+        sb.append(Hex.u2(
+                ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex())));
+        sb.append('\n');
+
+        ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+        ArrayList<SsaBasicBlock> sortedBlocks =
+            (ArrayList<SsaBasicBlock>) blocks.clone();
+        Collections.sort(sortedBlocks, SsaBasicBlock.LABEL_COMPARATOR);
+
+        for (SsaBasicBlock block : sortedBlocks) {
+            sb.append("block ")
+                    .append(Hex.u2(block.getRopLabel())).append('\n');
+
+            BitSet preds = block.getPredecessors();
+
+            for (int i = preds.nextSetBit(0); i >= 0;
+                 i = preds.nextSetBit(i+1)) {
+                sb.append("  pred ");
+                sb.append(Hex.u2(ssaMeth.blockIndexToRopLabel(i)));
+                sb.append('\n');
+            }
+
+            sb.append("  live in:" + block.getLiveInRegs());
+            sb.append("\n");
+
+            for (SsaInsn insn : block.getInsns()) {
+                sb.append("  ");
+                sb.append(insn.toHuman());
+                sb.append('\n');
+            }
+
+            if (block.getSuccessors().cardinality() == 0) {
+                sb.append("  returns\n");
+            } else {
+                int primary = block.getPrimarySuccessorRopLabel();
+
+                IntList succLabelList = block.getRopLabelSuccessorList();
+
+                int szSuccLabels = succLabelList.size();
+
+                for (int i = 0; i < szSuccLabels; i++) {
+                    sb.append("  next ");
+                    sb.append(Hex.u2(succLabelList.get(i)));
+
+                    if (szSuccLabels != 1 && primary == succLabelList.get(i)) {
+                        sb.append(" *");
+                    }
+                    sb.append('\n');
+                }
+            }
+
+            sb.append("  live out:" + block.getLiveOutRegs());
+            sb.append("\n");
+        }
+
+        suppressDump = false;
+        setAt(bytes, 0);
+        parsed(bytes, 0, bytes.size(), sb.toString());
+        suppressDump = true;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/AttributeTranslator.java b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java
new file mode 100644
index 0000000..2508a59
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.cf.attrib.AttAnnotationDefault;
+import com.android.dx.cf.attrib.AttEnclosingMethod;
+import com.android.dx.cf.attrib.AttExceptions;
+import com.android.dx.cf.attrib.AttInnerClasses;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttSignature;
+import com.android.dx.cf.attrib.InnerClassList;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.dex.file.AnnotationUtils;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Warning;
+
+import java.util.ArrayList;
+
+/**
+ * Utility methods that translate various classfile attributes
+ * into forms suitable for use in creating {@code dex} files.
+ */
+/*package*/ class AttributeTranslator {
+    /**
+     * This class is uninstantiable.
+     */
+    private AttributeTranslator() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the list of thrown exceptions for a given method.
+     *
+     * @param method {@code non-null;} the method in question
+     * @return {@code non-null;} the list of thrown exceptions
+     */
+    public static TypeList getExceptions(Method method) {
+        AttributeList attribs = method.getAttributes();
+        AttExceptions exceptions = (AttExceptions)
+            attribs.findFirst(AttExceptions.ATTRIBUTE_NAME);
+
+        if (exceptions == null) {
+            return StdTypeList.EMPTY;
+        }
+
+        return exceptions.getExceptions();
+    }
+
+    /**
+     * Gets the annotations out of a given {@link AttributeList}. This
+     * combines both visible and invisible annotations into a single
+     * result set and also adds in a system annotation for the
+     * {@code Signature} attribute if present.
+     *
+     * @param attribs {@code non-null;} the attributes list to search in
+     * @return {@code non-null;} the set of annotations, which may be empty
+     */
+    public static Annotations getAnnotations(AttributeList attribs) {
+        Annotations result = getAnnotations0(attribs);
+        Annotation signature = getSignature(attribs);
+
+        if (signature != null) {
+            result = Annotations.combine(result, signature);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the annotations out of a given class, similar to {@link
+     * #getAnnotations}, also including annotations for translations
+     * of class-level attributes {@code EnclosingMethod} and
+     * {@code InnerClasses}, if present. Additionally, if the
+     * class is an annotation class, then this also includes a
+     * representation of all the {@code AnnotationDefault}
+     * values.
+     *
+     * @param cf {@code non-null;} the class in question
+     * @param args {@code non-null;} the high-level options
+     * @return {@code non-null;} the set of annotations, which may be empty
+     */
+    public static Annotations getClassAnnotations(DirectClassFile cf,
+            CfOptions args) {
+        CstType thisClass = cf.getThisClass();
+        AttributeList attribs = cf.getAttributes();
+        Annotations result = getAnnotations(attribs);
+        Annotation enclosingMethod = translateEnclosingMethod(attribs);
+
+        try {
+            Annotations innerClassAnnotations =
+                translateInnerClasses(thisClass, attribs,
+                        enclosingMethod == null);
+            if (innerClassAnnotations != null) {
+                result = Annotations.combine(result, innerClassAnnotations);
+            }
+        } catch (Warning warn) {
+            args.warn.println("warning: " + warn.getMessage());
+        }
+
+        if (enclosingMethod != null) {
+            result = Annotations.combine(result, enclosingMethod);
+        }
+
+        if (AccessFlags.isAnnotation(cf.getAccessFlags())) {
+            Annotation annotationDefault =
+                translateAnnotationDefaults(cf);
+            if (annotationDefault != null) {
+                result = Annotations.combine(result, annotationDefault);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the annotations out of a given method, similar to {@link
+     * #getAnnotations}, also including an annotation for the translation
+     * of the method-specific attribute {@code Exceptions}.
+     *
+     * @param method {@code non-null;} the method in question
+     * @return {@code non-null;} the set of annotations, which may be empty
+     */
+    public static Annotations getMethodAnnotations(Method method) {
+        Annotations result = getAnnotations(method.getAttributes());
+        TypeList exceptions = getExceptions(method);
+
+        if (exceptions.size() != 0) {
+            Annotation throwsAnnotation =
+                AnnotationUtils.makeThrows(exceptions);
+            result = Annotations.combine(result, throwsAnnotation);
+        }
+
+        return result;
+    }
+
+    /**
+     * Helper method for {@link #getAnnotations} which just gets the
+     * existing annotations, per se.
+     *
+     * @param attribs {@code non-null;} the attributes list to search in
+     * @return {@code non-null;} the set of annotations, which may be empty
+     */
+    private static Annotations getAnnotations0(AttributeList attribs) {
+        AttRuntimeVisibleAnnotations visible =
+            (AttRuntimeVisibleAnnotations)
+            attribs.findFirst(AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+        AttRuntimeInvisibleAnnotations invisible =
+            (AttRuntimeInvisibleAnnotations)
+            attribs.findFirst(AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+        if (visible == null) {
+            if (invisible == null) {
+                return Annotations.EMPTY;
+            }
+            return invisible.getAnnotations();
+        }
+
+        if (invisible == null) {
+            return visible.getAnnotations();
+        }
+
+        // Both are non-null, so combine them.
+
+        return Annotations.combine(visible.getAnnotations(),
+                invisible.getAnnotations());
+    }
+
+    /**
+     * Gets the {@code Signature} attribute out of a given
+     * {@link AttributeList}, if any, translating it to an annotation.
+     *
+     * @param attribs {@code non-null;} the attributes list to search in
+     * @return {@code null-ok;} the converted {@code Signature} annotation,
+     * if there was an attribute to translate
+     */
+    private static Annotation getSignature(AttributeList attribs) {
+        AttSignature signature = (AttSignature)
+            attribs.findFirst(AttSignature.ATTRIBUTE_NAME);
+
+        if (signature == null) {
+            return null;
+        }
+
+        return AnnotationUtils.makeSignature(signature.getSignature());
+    }
+
+    /**
+     * Gets the {@code EnclosingMethod} attribute out of a given
+     * {@link AttributeList}, if any, translating it to an annotation.
+     * If the class really has an enclosing method, this returns an
+     * {@code EnclosingMethod} annotation; if not, this returns
+     * an {@code EnclosingClass} annotation.
+     *
+     * @param attribs {@code non-null;} the attributes list to search in
+     * @return {@code null-ok;} the converted {@code EnclosingMethod} or
+     * {@code EnclosingClass} annotation, if there was an
+     * attribute to translate
+     */
+    private static Annotation translateEnclosingMethod(AttributeList attribs) {
+        AttEnclosingMethod enclosingMethod = (AttEnclosingMethod)
+            attribs.findFirst(AttEnclosingMethod.ATTRIBUTE_NAME);
+
+        if (enclosingMethod == null) {
+            return null;
+        }
+
+        CstType enclosingClass = enclosingMethod.getEnclosingClass();
+        CstNat nat = enclosingMethod.getMethod();
+
+        if (nat == null) {
+            /*
+             * Dalvik doesn't use EnclosingMethod annotations unless
+             * there really is an enclosing method. Anonymous classes
+             * are unambiguously identified by having an InnerClass
+             * annotation with an empty name along with an appropriate
+             * EnclosingClass.
+             */
+            return AnnotationUtils.makeEnclosingClass(enclosingClass);
+        }
+
+        return AnnotationUtils.makeEnclosingMethod(
+                new CstMethodRef(enclosingClass, nat));
+    }
+
+    /**
+     * Gets the {@code InnerClasses} attribute out of a given
+     * {@link AttributeList}, if any, translating it to one or more of an
+     * {@code InnerClass}, {@code EnclosingClass}, or
+     * {@code MemberClasses} annotation.
+     *
+     * @param thisClass {@code non-null;} type representing the class being
+     * processed
+     * @param attribs {@code non-null;} the attributes list to search in
+     * @param needEnclosingClass whether to include an
+     * {@code EnclosingClass} annotation
+     * @return {@code null-ok;} the converted list of annotations, if there
+     * was an attribute to translate
+     */
+    private static Annotations translateInnerClasses(CstType thisClass,
+            AttributeList attribs, boolean needEnclosingClass) {
+        AttInnerClasses innerClasses = (AttInnerClasses)
+            attribs.findFirst(AttInnerClasses.ATTRIBUTE_NAME);
+
+        if (innerClasses == null) {
+            return null;
+        }
+
+        /*
+         * Search the list for the element representing the current class
+         * as well as for any named member classes.
+         */
+
+        InnerClassList list = innerClasses.getInnerClasses();
+        int size = list.size();
+        InnerClassList.Item foundThisClass = null;
+        ArrayList<Type> membersList = new ArrayList<Type>();
+
+        for (int i = 0; i < size; i++) {
+            InnerClassList.Item item = list.get(i);
+            CstType innerClass = item.getInnerClass();
+            if (innerClass.equals(thisClass)) {
+                foundThisClass = item;
+            } else if (thisClass.equals(item.getOuterClass())) {
+                membersList.add(innerClass.getClassType());
+            }
+        }
+
+        int membersSize = membersList.size();
+
+        if ((foundThisClass == null) && (membersSize == 0)) {
+            return null;
+        }
+
+        Annotations result = new Annotations();
+
+        if (foundThisClass != null) {
+            result.add(AnnotationUtils.makeInnerClass(
+                               foundThisClass.getInnerName(),
+                               foundThisClass.getAccessFlags()));
+            if (needEnclosingClass) {
+                CstType outer = foundThisClass.getOuterClass();
+                if (outer == null) {
+                    throw new Warning(
+                            "Ignoring InnerClasses attribute for an " +
+                            "anonymous inner class\n" +
+                            "(" + thisClass.toHuman() +
+                            ") that doesn't come with an\n" +
+                            "associated EnclosingMethod attribute. " +
+                            "This class was probably produced by a\n" +
+                            "compiler that did not target the modern " +
+                            ".class file format. The recommended\n" +
+                            "solution is to recompile the class from " +
+                            "source, using an up-to-date compiler\n" +
+                            "and without specifying any \"-target\" type " +
+                            "options. The consequence of ignoring\n" +
+                            "this warning is that reflective operations " +
+                            "on this class will incorrectly\n" +
+                            "indicate that it is *not* an inner class.");
+                }
+                result.add(AnnotationUtils.makeEnclosingClass(
+                                   foundThisClass.getOuterClass()));
+            }
+        }
+
+        if (membersSize != 0) {
+            StdTypeList typeList = new StdTypeList(membersSize);
+            for (int i = 0; i < membersSize; i++) {
+                typeList.set(i, membersList.get(i));
+            }
+            typeList.setImmutable();
+            result.add(AnnotationUtils.makeMemberClasses(typeList));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Gets the parameter annotations out of a given method. This
+     * combines both visible and invisible annotations into a single
+     * result set.
+     *
+     * @param method {@code non-null;} the method in question
+     * @return {@code non-null;} the list of annotation sets, which may be
+     * empty
+     */
+    public static AnnotationsList getParameterAnnotations(Method method) {
+        AttributeList attribs = method.getAttributes();
+        AttRuntimeVisibleParameterAnnotations visible =
+            (AttRuntimeVisibleParameterAnnotations)
+            attribs.findFirst(
+                    AttRuntimeVisibleParameterAnnotations.ATTRIBUTE_NAME);
+        AttRuntimeInvisibleParameterAnnotations invisible =
+            (AttRuntimeInvisibleParameterAnnotations)
+            attribs.findFirst(
+                    AttRuntimeInvisibleParameterAnnotations.ATTRIBUTE_NAME);
+
+        if (visible == null) {
+            if (invisible == null) {
+                return AnnotationsList.EMPTY;
+            }
+            return invisible.getParameterAnnotations();
+        }
+
+        if (invisible == null) {
+            return visible.getParameterAnnotations();
+        }
+
+        // Both are non-null, so combine them.
+
+        return AnnotationsList.combine(visible.getParameterAnnotations(),
+                invisible.getParameterAnnotations());
+    }
+
+    /**
+     * Gets the {@code AnnotationDefault} attributes out of a
+     * given class, if any, reforming them as an
+     * {@code AnnotationDefault} annotation.
+     *
+     * @param cf {@code non-null;} the class in question
+     * @return {@code null-ok;} an appropriately-constructed
+     * {@code AnnotationDefault} annotation, if there were any
+     * annotation defaults in the class, or {@code null} if not
+     */
+    private static Annotation translateAnnotationDefaults(DirectClassFile cf) {
+        CstType thisClass = cf.getThisClass();
+        MethodList methods = cf.getMethods();
+        int sz = methods.size();
+        Annotation result =
+            new Annotation(thisClass, AnnotationVisibility.EMBEDDED);
+        boolean any = false;
+
+        for (int i = 0; i < sz; i++) {
+            Method one = methods.get(i);
+            AttributeList attribs = one.getAttributes();
+            AttAnnotationDefault oneDefault = (AttAnnotationDefault)
+                attribs.findFirst(AttAnnotationDefault.ATTRIBUTE_NAME);
+
+            if (oneDefault != null) {
+                NameValuePair pair = new NameValuePair(
+                        one.getNat().getName(),
+                        oneDefault.getValue());
+                result.add(pair);
+                any = true;
+            }
+        }
+
+        if (! any) {
+            return null;
+        }
+
+        result.setImmutable();
+        return AnnotationUtils.makeAnnotationDefault(result);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/CfOptions.java b/dx/src/com/android/dx/dex/cf/CfOptions.java
new file mode 100644
index 0000000..468f0be
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CfOptions.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.dex.code.PositionList;
+
+import java.io.PrintStream;
+
+/**
+ * A class to contain options passed into dex.cf
+ */
+public class CfOptions {
+    /** how much source position info to preserve */
+    public int positionInfo = PositionList.LINES;
+
+    /** whether to keep local variable information */
+    public boolean localInfo = false;
+
+    /** whether strict file-name-vs-class-name checking should be done */
+    public boolean strictNameCheck = true;
+
+    /** whether to do SSA/register optimization */
+    public boolean optimize = false;
+
+    /** filename containing list of methods to optimize */
+    public String optimizeListFile = null;
+
+    /** filename containing list of methods <i>not</i> to optimize */
+    public String dontOptimizeListFile = null;
+
+    /** whether to print statistics to stdout at end of compile cycle */
+    public boolean statistics;
+
+    /** where to issue warnings to */
+    public PrintStream warn = System.err;
+}
diff --git a/dx/src/com/android/dx/dex/cf/CfTranslator.java b/dx/src/com/android/dx/dex/cf/CfTranslator.java
new file mode 100644
index 0000000..1a9aa47
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CfTranslator.java
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Field;
+import com.android.dx.cf.iface.FieldList;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.dex.code.RopTranslator;
+import com.android.dx.dex.file.ClassDefItem;
+import com.android.dx.dex.file.EncodedField;
+import com.android.dx.dex.file.EncodedMethod;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.code.LocalVariableExtractor;
+import com.android.dx.rop.code.LocalVariableInfo;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Static method that turns {@code byte[]}s containing Java
+ * classfiles into {@link ClassDefItem} instances.
+ */
+public class CfTranslator {
+    /** set to {@code true} to enable development-time debugging code */
+    private static final boolean DEBUG = false;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private CfTranslator() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Takes a {@code byte[]}, interprets it as a Java classfile, and
+     * translates it into a {@link ClassDefItem}.
+     *
+     * @param filePath {@code non-null;} the file path for the class,
+     * excluding any base directory specification
+     * @param bytes {@code non-null;} contents of the file
+     * @param args command-line arguments
+     * @return {@code non-null;} the translated class
+     */
+    public static ClassDefItem translate(String filePath, byte[] bytes,
+            CfOptions args) {
+        try {
+            return translate0(filePath, bytes, args);
+        } catch (RuntimeException ex) {
+            String msg = "...while processing " + filePath;
+            throw ExceptionWithContext.withContext(ex, msg);
+        }
+    }
+
+    /**
+     * Performs the main act of translation. This method is separated
+     * from {@link #translate} just to keep things a bit simpler in
+     * terms of exception handling.
+     *
+     * @param filePath {@code non-null;} the file path for the class,
+     * excluding any base directory specification
+     * @param bytes {@code non-null;} contents of the file
+     * @param args command-line arguments
+     * @return {@code non-null;} the translated class
+     */
+    private static ClassDefItem translate0(String filePath, byte[] bytes,
+            CfOptions args) {
+        DirectClassFile cf =
+            new DirectClassFile(bytes, filePath, args.strictNameCheck);
+
+        cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+        cf.getMagic();
+
+        OptimizerOptions.loadOptimizeLists(args.optimizeListFile,
+                args.dontOptimizeListFile);
+
+        // Build up a class to output.
+
+        CstType thisClass = cf.getThisClass();
+        int classAccessFlags = cf.getAccessFlags() & ~AccessFlags.ACC_SUPER;
+        CstUtf8 sourceFile = (args.positionInfo == PositionList.NONE) ? null :
+            cf.getSourceFile();
+        ClassDefItem out =
+            new ClassDefItem(thisClass, classAccessFlags,
+                    cf.getSuperclass(), cf.getInterfaces(), sourceFile);
+
+        Annotations classAnnotations =
+            AttributeTranslator.getClassAnnotations(cf, args);
+        if (classAnnotations.size() != 0) {
+            out.setClassAnnotations(classAnnotations);
+        }
+
+        processFields(cf, out);
+        processMethods(cf, args, out);
+
+        return out;
+    }
+
+    /**
+     * Processes the fields of the given class.
+     *
+     * @param cf {@code non-null;} class being translated
+     * @param out {@code non-null;} output class
+     */
+    private static void processFields(DirectClassFile cf, ClassDefItem out) {
+        CstType thisClass = cf.getThisClass();
+        FieldList fields = cf.getFields();
+        int sz = fields.size();
+
+        for (int i = 0; i < sz; i++) {
+            Field one = fields.get(i);
+            try {
+                CstFieldRef field = new CstFieldRef(thisClass, one.getNat());
+                int accessFlags = one.getAccessFlags();
+
+                if (AccessFlags.isStatic(accessFlags)) {
+                    TypedConstant constVal = one.getConstantValue();
+                    EncodedField fi = new EncodedField(field, accessFlags);
+                    if (constVal != null) {
+                        constVal = coerceConstant(constVal, field.getType());
+                    }
+                    out.addStaticField(fi, constVal);
+                } else {
+                    EncodedField fi = new EncodedField(field, accessFlags);
+                    out.addInstanceField(fi);
+                }
+
+                Annotations annotations =
+                    AttributeTranslator.getAnnotations(one.getAttributes());
+                if (annotations.size() != 0) {
+                    out.addFieldAnnotations(field, annotations);
+                }
+            } catch (RuntimeException ex) {
+                String msg = "...while processing " + one.getName().toHuman() +
+                    " " + one.getDescriptor().toHuman();
+                throw ExceptionWithContext.withContext(ex, msg);
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #processFields}, which translates constants into
+     * more specific types if necessary.
+     *
+     * @param constant {@code non-null;} the constant in question
+     * @param type {@code non-null;} the desired type
+     */
+    private static TypedConstant coerceConstant(TypedConstant constant,
+            Type type) {
+        Type constantType = constant.getType();
+
+        if (constantType.equals(type)) {
+            return constant;
+        }
+
+        switch (type.getBasicType()) {
+            case Type.BT_BOOLEAN: {
+                return CstBoolean.make(((CstInteger) constant).getValue());
+            }
+            case Type.BT_BYTE: {
+                return CstByte.make(((CstInteger) constant).getValue());
+            }
+            case Type.BT_CHAR: {
+                return CstChar.make(((CstInteger) constant).getValue());
+            }
+            case Type.BT_SHORT: {
+                return CstShort.make(((CstInteger) constant).getValue());
+            }
+            default: {
+                throw new UnsupportedOperationException("can't coerce " +
+                        constant + " to " + type);
+            }
+        }
+    }
+
+    /**
+     * Processes the methods of the given class.
+     *
+     * @param cf {@code non-null;} class being translated
+     * @param args {@code non-null;} command-line args
+     * @param out {@code non-null;} output class
+     */
+    private static void processMethods(DirectClassFile cf,
+            CfOptions args, ClassDefItem out) {
+        CstType thisClass = cf.getThisClass();
+        MethodList methods = cf.getMethods();
+        int sz = methods.size();
+
+        for (int i = 0; i < sz; i++) {
+            Method one = methods.get(i);
+            try {
+                CstMethodRef meth = new CstMethodRef(thisClass, one.getNat());
+                int accessFlags = one.getAccessFlags();
+                boolean isStatic = AccessFlags.isStatic(accessFlags);
+                boolean isPrivate = AccessFlags.isPrivate(accessFlags);
+                boolean isNative = AccessFlags.isNative(accessFlags);
+                boolean isAbstract = AccessFlags.isAbstract(accessFlags);
+                boolean isConstructor = meth.isInstanceInit() ||
+                    meth.isClassInit();
+                DalvCode code;
+
+                if (isNative || isAbstract) {
+                    // There's no code for native or abstract methods.
+                    code = null;
+                } else {
+                    ConcreteMethod concrete =
+                        new ConcreteMethod(one, cf,
+                                (args.positionInfo != PositionList.NONE),
+                                args.localInfo);
+
+                    TranslationAdvice advice;
+
+                    advice = DexTranslationAdvice.THE_ONE;
+
+                    RopMethod rmeth = Ropper.convert(concrete, advice);
+                    RopMethod nonOptRmeth = null;
+                    int paramSize;
+
+                    paramSize = meth.getParameterWordCount(isStatic);
+
+                    String canonicalName
+                            = thisClass.getClassType().getDescriptor()
+                                + "." + one.getName().getString();
+
+                    if (args.optimize &&
+                            OptimizerOptions.shouldOptimize(canonicalName)) {
+                        if (DEBUG) {
+                            System.err.println("Optimizing " + canonicalName);
+                        }
+
+                        nonOptRmeth = rmeth;
+                        rmeth = Optimizer.optimize(rmeth,
+                                paramSize, isStatic, args.localInfo, advice);
+
+                        if (DEBUG) {
+                            OptimizerOptions.compareOptimizerStep(nonOptRmeth,
+                                    paramSize, isStatic, args, advice, rmeth);
+                        }
+
+                        if (args.statistics) {
+                            CodeStatistics.updateRopStatistics(
+                                    nonOptRmeth, rmeth);
+                        }
+                    }
+
+                    LocalVariableInfo locals = null;
+
+                    if (args.localInfo) {
+                        locals = LocalVariableExtractor.extract(rmeth);
+                    }
+
+                    code = RopTranslator.translate(rmeth, args.positionInfo,
+                            locals, paramSize);
+
+                    if (args.statistics && nonOptRmeth != null) {
+                        updateDexStatistics(args, rmeth, nonOptRmeth, locals,
+                                paramSize, concrete.getCode().size());
+                    }
+                }
+
+                // Preserve the synchronized flag as its "declared" variant...
+                if (AccessFlags.isSynchronized(accessFlags)) {
+                    accessFlags |= AccessFlags.ACC_DECLARED_SYNCHRONIZED;
+
+                    /*
+                     * ...but only native methods are actually allowed to be
+                     * synchronized.
+                     */
+                    if (!isNative) {
+                        accessFlags &= ~AccessFlags.ACC_SYNCHRONIZED;
+                    }
+                }
+
+                if (isConstructor) {
+                    accessFlags |= AccessFlags.ACC_CONSTRUCTOR;
+                }
+
+                TypeList exceptions = AttributeTranslator.getExceptions(one);
+                EncodedMethod mi =
+                    new EncodedMethod(meth, accessFlags, code, exceptions);
+
+                if (meth.isInstanceInit() || meth.isClassInit() ||
+                    isStatic || isPrivate) {
+                    out.addDirectMethod(mi);
+                } else {
+                    out.addVirtualMethod(mi);
+                }
+
+                Annotations annotations =
+                    AttributeTranslator.getMethodAnnotations(one);
+                if (annotations.size() != 0) {
+                    out.addMethodAnnotations(meth, annotations);
+                }
+
+                AnnotationsList list =
+                    AttributeTranslator.getParameterAnnotations(one);
+                if (list.size() != 0) {
+                    out.addParameterAnnotations(meth, list);
+                }
+            } catch (RuntimeException ex) {
+                String msg = "...while processing " + one.getName().toHuman() +
+                    " " + one.getDescriptor().toHuman();
+                throw ExceptionWithContext.withContext(ex, msg);
+            }
+        }
+    }
+
+    /**
+     * Helper that updates the dex statistics.
+     */
+    private static void updateDexStatistics(CfOptions args,
+            RopMethod optRmeth, RopMethod nonOptRmeth,
+            LocalVariableInfo locals, int paramSize, int originalByteCount) {
+        /*
+         * Run rop->dex again on optimized vs. non-optimized method to
+         * collect statistics. We have to totally convert both ways,
+         * since converting the "real" method getting added to the
+         * file would corrupt it (by messing with its constant pool
+         * indices).
+         */
+
+        DalvCode optCode = RopTranslator.translate(optRmeth,
+                args.positionInfo, locals, paramSize);
+        DalvCode nonOptCode = RopTranslator.translate(nonOptRmeth,
+                args.positionInfo, locals, paramSize);
+
+        /*
+         * Fake out the indices, so code.getInsns() can work well enough
+         * for the current purpose.
+         */
+
+        DalvCode.AssignIndicesCallback callback =
+            new DalvCode.AssignIndicesCallback() {
+                public int getIndex(Constant cst) {
+                    // Everything is at index 0!
+                    return 0;
+                }
+            };
+
+        optCode.assignIndices(callback);
+        nonOptCode.assignIndices(callback);
+
+        CodeStatistics.updateDexStatistics(nonOptCode, optCode);
+        CodeStatistics.updateOriginalByteCount(originalByteCount);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/CodeStatistics.java b/dx/src/com/android/dx/dex/cf/CodeStatistics.java
new file mode 100644
index 0000000..33d03fd
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CodeStatistics.java
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.code.RopMethod;
+
+import java.io.PrintStream;
+
+/**
+ * Static methods and variables for collecting statistics on generated
+ * code.
+ */
+public final class CodeStatistics {
+    /** set to {@code true} to enable development-time debugging code */
+    private static final boolean DEBUG = false;
+
+    /**
+     * running sum of the number of registers added/removed in
+     * SSA form by the optimizer
+     */
+    public static int runningDeltaRegisters = 0;
+
+    /**
+     * running sum of the number of insns added/removed in
+     * SSA form by the optimizer
+     */
+    public static int runningDeltaInsns = 0;
+
+    /** running sum of the total number of Rop insns processed */
+    public static int runningTotalInsns = 0;
+
+    /**
+     * running sum of the number of dex-form registers added/removed in
+     * SSA form by the optimizer. Only valid if args.statistics is true.
+     */
+    public static int dexRunningDeltaRegisters = 0;
+
+    /**
+     * running sum of the number of dex-form insns (actually code
+     * units) added/removed in SSA form by the optimizer. Only valid
+     * if args.statistics is true.
+     */
+    public static int dexRunningDeltaInsns = 0;
+
+    /**
+     * running sum of the total number of dex insns (actually code
+     * units) processed
+     */
+    public static int dexRunningTotalInsns = 0;
+
+    /** running sum of original class bytecode bytes */
+    public static int runningOriginalBytes = 0;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private CodeStatistics() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Updates the number of original bytecode bytes processed.
+     *
+     * @param count {@code >= 0;} the number of bytes to add
+     */
+    public static void updateOriginalByteCount(int count) {
+        runningOriginalBytes += count;
+    }
+
+    /**
+     * Updates the dex statistics.
+     *
+     * @param nonOptCode non-optimized code block
+     * @param code optimized code block
+     */
+    public static void updateDexStatistics(DalvCode nonOptCode,
+            DalvCode code) {
+        if (DEBUG) {
+            System.err.println("dex insns (old/new) "
+                    + nonOptCode.getInsns().codeSize()
+                    + "/" + code.getInsns().codeSize()
+                    + " regs (o/n) "
+                    + nonOptCode.getInsns().getRegistersSize()
+                    + "/" + code.getInsns().getRegistersSize()
+            );
+        }
+
+        dexRunningDeltaInsns
+            += (code.getInsns().codeSize()
+                - nonOptCode.getInsns().codeSize());
+
+        dexRunningDeltaRegisters
+            += (code.getInsns().getRegistersSize()
+                - nonOptCode.getInsns().getRegistersSize());
+
+        dexRunningTotalInsns += code.getInsns().codeSize();
+    }
+
+    /**
+     * Updates the ROP statistics.
+     *
+     * @param nonOptRmeth non-optimized method
+     * @param rmeth optimized method
+     */
+    public static void updateRopStatistics(RopMethod nonOptRmeth,
+            RopMethod rmeth) {
+        int oldCountInsns
+                = nonOptRmeth.getBlocks().getEffectiveInstructionCount();
+        int oldCountRegs = nonOptRmeth.getBlocks().getRegCount();
+
+        if (DEBUG) {
+            System.err.println("insns (old/new): "
+                    + oldCountInsns + "/"
+                    + rmeth.getBlocks().getEffectiveInstructionCount()
+                    + " regs (o/n):" + oldCountRegs
+                    + "/"  +  rmeth.getBlocks().getRegCount());
+        }
+
+        int newCountInsns
+                = rmeth.getBlocks().getEffectiveInstructionCount();
+
+        runningDeltaInsns
+            += (newCountInsns - oldCountInsns);
+
+        runningDeltaRegisters
+            += (rmeth.getBlocks().getRegCount() - oldCountRegs);
+
+        runningTotalInsns += newCountInsns;
+    }
+
+    /**
+     * Prints out the collected statistics.
+     *
+     * @param out {@code non-null;} where to output to
+     */
+    public static void dumpStatistics(PrintStream out) {
+        out.printf("Optimizer Delta Rop Insns: %d total: %d "
+                + "(%.2f%%) Delta Registers: %d\n",
+                runningDeltaInsns,
+                runningTotalInsns,
+                (100.0 * (((float) runningDeltaInsns)
+                        / (runningTotalInsns + Math.abs(runningDeltaInsns)))),
+                runningDeltaRegisters);
+
+        out.printf("Optimizer Delta Dex Insns: Insns: %d total: %d "
+                + "(%.2f%%) Delta Registers: %d\n",
+                dexRunningDeltaInsns,
+                dexRunningTotalInsns,
+                (100.0 * (((float) dexRunningDeltaInsns)
+                        / (dexRunningTotalInsns
+                                + Math.abs(dexRunningDeltaInsns)))),
+                dexRunningDeltaRegisters);
+
+        out.printf("Original bytecode byte count: %d\n",
+                runningOriginalBytes);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/OptimizerOptions.java b/dx/src/com/android/dx/dex/cf/OptimizerOptions.java
new file mode 100644
index 0000000..a66421e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/OptimizerOptions.java
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.ssa.Optimizer;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.HashSet;
+
+/**
+ * Settings for optimization of code.
+ */
+public class OptimizerOptions {
+    /**
+     * {@code null-ok;} hash set of class name + method names that
+     * should be optimized. {@code null} if this constraint was not
+     * specified on the command line
+     */
+    private static HashSet<String> optimizeList;
+
+    /**
+     * {@code null-ok;} hash set of class name + method names that should NOT
+     * be optimized.  null if this constraint was not specified on the
+     * command line
+     */
+    private static HashSet<String> dontOptimizeList;
+
+    /** true if the above lists have been loaded */
+    private static boolean optimizeListsLoaded;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private OptimizerOptions() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Loads the optimize/don't optimize lists from files.
+     *
+     * @param optimizeListFile Pathname
+     * @param dontOptimizeListFile Pathname
+     */
+    public static void loadOptimizeLists(String optimizeListFile,
+            String dontOptimizeListFile) {
+        if (optimizeListsLoaded) {
+            return;
+        }
+
+        if (optimizeListFile != null && dontOptimizeListFile != null) {
+            /*
+             * We shouldn't get this far. The condition should have
+             * been caught in the arg processor.
+             */
+            throw new RuntimeException("optimize and don't optimize lists "
+                    + " are mutually exclusive.");
+        }
+
+        if (optimizeListFile != null) {
+            optimizeList = loadStringsFromFile(optimizeListFile);
+        }
+
+        if (dontOptimizeListFile != null) {
+            dontOptimizeList = loadStringsFromFile(dontOptimizeListFile);
+        }
+
+        optimizeListsLoaded = true;
+    }
+
+    /**
+     * Loads a list of newline-separated strings into a new HashSet and returns
+     * the HashSet.
+     *
+     * @param filename filename to process
+     * @return set of all unique lines in the file
+     */
+    private static HashSet<String> loadStringsFromFile(String filename) {
+        HashSet<String> result = new HashSet<String>();
+
+        try {
+            FileReader fr = new FileReader(filename);
+            BufferedReader bfr = new BufferedReader(fr);
+
+            String line;
+
+            while (null != (line = bfr.readLine())) {
+                result.add(line);
+            }
+
+            fr.close();
+        } catch (IOException ex) {
+            // Let the exception percolate up as a RuntimeException.
+            throw new RuntimeException("Error with optimize list: " +
+                    filename, ex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Compares the output of the optimizer run normally with a run skipping
+     * some optional steps. Results are printed to stderr.
+     *
+     * @param nonOptRmeth {@code non-null;} origional rop method
+     * @param paramSize {@code >= 0;} parameter size of method
+     * @param isStatic true if this method has no 'this' pointer argument.
+     * @param args {@code non-null;} translator arguments
+     * @param advice {@code non-null;} translation advice
+     * @param rmeth {@code non-null;} method with all optimization steps run.
+     */
+    public static void compareOptimizerStep(RopMethod nonOptRmeth,
+            int paramSize, boolean isStatic, CfOptions args,
+            TranslationAdvice advice, RopMethod rmeth) {
+        EnumSet<Optimizer.OptionalStep> steps;
+
+        steps = EnumSet.allOf(Optimizer.OptionalStep.class);
+
+        // This is the step to skip.
+        steps.remove(Optimizer.OptionalStep.CONST_COLLECTOR);
+
+        RopMethod skipRopMethod
+                = Optimizer.optimize(nonOptRmeth,
+                        paramSize, isStatic, args.localInfo, advice, steps);
+
+        int normalInsns
+                = rmeth.getBlocks().getEffectiveInstructionCount();
+        int skipInsns
+                = skipRopMethod.getBlocks().getEffectiveInstructionCount();
+
+        System.err.printf(
+                "optimize step regs:(%d/%d/%.2f%%)"
+                + " insns:(%d/%d/%.2f%%)\n",
+                rmeth.getBlocks().getRegCount(),
+                skipRopMethod.getBlocks().getRegCount(),
+                100.0 * ((skipRopMethod.getBlocks().getRegCount()
+                        - rmeth.getBlocks().getRegCount())
+                        / (float) skipRopMethod.getBlocks().getRegCount()),
+                normalInsns, skipInsns,
+                100.0 * ((skipInsns - normalInsns) / (float) skipInsns));
+    }
+
+    /**
+     * Checks whether the specified method should be optimized
+     *
+     * @param canonicalMethodName name of method being considered
+     * @return true if it should be optimized
+     */
+    public static boolean shouldOptimize(String canonicalMethodName) {
+        // Optimize only what's in the optimize list.
+        if (optimizeList != null) {
+            return optimizeList.contains(canonicalMethodName);
+        }
+
+        /*
+         * Or don't optimize what's listed here. (The two lists are
+         * mutually exclusive.
+         */
+
+        if (dontOptimizeList != null) {
+            return !dontOptimizeList.contains(canonicalMethodName);
+        }
+
+        // If neither list has been specified, then optimize everything.
+        return true;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/cf/package.html b/dx/src/com/android/dx/dex/cf/package.html
new file mode 100644
index 0000000..d56e8a7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/package.html
@@ -0,0 +1,15 @@
+<body>
+<p>Classes for translating Java classfiles into Dalvik classes.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.code</code></li>
+<li><code>com.android.dx.cf.direct</code></li>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.dex.code</code></li>
+<li><code>com.android.dx.dex.file</code></li>
+<li><code>com.android.dx.rop.code</code></li>
+<li><code>com.android.dx.rop.cst</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/dex/code/ArrayData.java b/dx/src/com/android/dx/dex/code/ArrayData.java
new file mode 100644
index 0000000..145f2c2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/ArrayData.java
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.*;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.rop.type.Type;
+import java.util.ArrayList;
+
+/**
+ * Pseudo-instruction which holds fill array data.
+ */
+public final class ArrayData extends VariableSizeInsn {
+    /**
+     * {@code non-null;} address representing the instruction that uses this
+     * instance
+     */
+    private final CodeAddress user;
+
+    /** {@code non-null;} initial values to be filled into an array */
+    private final ArrayList<Constant> values;
+
+    /** non-null: type of constant that initializes the array */
+    private final Constant arrayType;
+
+    /** Width of the init value element */
+    private final int elemWidth;
+
+    /** Length of the init list */
+    private final int initLength;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     * @param user {@code non-null;} address representing the instruction that
+     * uses this instance
+     * @param values {@code non-null;} initial values to be filled into an array
+     */
+    public ArrayData(SourcePosition position, CodeAddress user,
+                     ArrayList<Constant> values,
+                     Constant arrayType) {
+        super(position, RegisterSpecList.EMPTY);
+
+        if (user == null) {
+            throw new NullPointerException("user == null");
+        }
+
+        if (values == null) {
+            throw new NullPointerException("values == null");
+        }
+
+        int sz = values.size();
+
+        if (sz <= 0) {
+            throw new IllegalArgumentException("Illegal number of init values");
+        }
+
+        this.arrayType = arrayType;
+
+        if (arrayType == CstType.BYTE_ARRAY ||
+                arrayType == CstType.BOOLEAN_ARRAY) {
+            elemWidth = 1;
+        } else if (arrayType == CstType.SHORT_ARRAY ||
+                arrayType == CstType.CHAR_ARRAY) {
+            elemWidth = 2;
+        } else if (arrayType == CstType.INT_ARRAY ||
+                arrayType == CstType.FLOAT_ARRAY) {
+            elemWidth = 4;
+        } else if (arrayType == CstType.LONG_ARRAY ||
+                arrayType == CstType.DOUBLE_ARRAY) {
+            elemWidth = 8;
+        } else {
+            throw new IllegalArgumentException("Unexpected constant type");
+        }
+        this.user = user;
+        this.values = values;
+        initLength = values.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        int sz = initLength;
+        // Note: the unit here is 16-bit
+        return 4 + ((sz * elemWidth) + 1) / 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out) {
+        int sz = values.size();
+
+        out.writeShort(0x300 | DalvOps.NOP);
+        out.writeShort(elemWidth);
+        out.writeInt(initLength);
+
+
+        // For speed reasons, replicate the for loop in each case
+        switch (elemWidth) {
+            case 1: {
+                for (int i = 0; i < sz; i++) {
+                    Constant cst = values.get(i);
+                    out.writeByte((byte) ((CstLiteral32) cst).getIntBits());
+                }
+                break;
+            }
+            case 2: {
+                for (int i = 0; i < sz; i++) {
+                    Constant cst = values.get(i);
+                    out.writeShort((short) ((CstLiteral32) cst).getIntBits());
+                }
+                break;
+            }
+            case 4: {
+                for (int i = 0; i < sz; i++) {
+                    Constant cst = values.get(i);
+                    out.writeInt(((CstLiteral32) cst).getIntBits());
+                }
+                break;
+            }
+            case 8: {
+                for (int i = 0; i < sz; i++) {
+                    Constant cst = values.get(i);
+                    out.writeLong(((CstLiteral64) cst).getLongBits());
+                }
+                break;
+            }
+            default:
+                break;
+        }
+
+        // Pad one byte to make the size of data table multiples of 16-bits
+        if (elemWidth == 1 && (sz % 2 != 0)) {
+            out.writeByte(0x00);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new ArrayData(getPosition(), user, values, arrayType);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        int sz = values.size();
+        for (int i = 0; i < sz; i++) {
+            sb.append("\n    ");
+            sb.append(i);
+            sb.append(": ");
+            sb.append(values.get(i).toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        int baseAddress = user.getAddress();
+        StringBuffer sb = new StringBuffer(100);
+        int sz = values.size();
+
+        sb.append("array-data // for fill-array-data @ ");
+        sb.append(Hex.u2(baseAddress));
+
+        for (int i = 0; i < sz; i++) {
+            sb.append("\n  ");
+            sb.append(i);
+            sb.append(": ");
+            sb.append(values.get(i).toHuman());
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/BlockAddresses.java b/dx/src/com/android/dx/dex/code/BlockAddresses.java
new file mode 100644
index 0000000..1a1d184
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/BlockAddresses.java
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Container for the set of {@link CodeAddress} instances associated with
+ * the blocks of a particular method. Each block has a corresponding
+ * start address, end address, and last instruction address.
+ */
+public final class BlockAddresses {
+    /** {@code non-null;} array containing addresses for the start of each basic
+     * block (indexed by basic block label) */
+    private final CodeAddress[] starts;
+
+    /** {@code non-null;} array containing addresses for the final instruction
+     * of each basic block (indexed by basic block label) */
+    private final CodeAddress[] lasts;
+
+    /** {@code non-null;} array containing addresses for the end (just past the
+     * final instruction) of each basic block (indexed by basic block
+     * label) */
+    private final CodeAddress[] ends;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method {@code non-null;} the method to have block addresses for
+     */
+    public BlockAddresses(RopMethod method) {
+        BasicBlockList blocks = method.getBlocks();
+        int maxLabel = blocks.getMaxLabel();
+
+        this.starts = new CodeAddress[maxLabel];
+        this.lasts = new CodeAddress[maxLabel];
+        this.ends = new CodeAddress[maxLabel];
+
+        setupArrays(method);
+    }
+
+    /**
+     * Gets the instance for the start of the given block.
+     *
+     * @param block {@code non-null;} the block in question
+     * @return {@code non-null;} the appropriate instance
+     */
+    public CodeAddress getStart(BasicBlock block) {
+        return starts[block.getLabel()];
+    }
+
+    /**
+     * Gets the instance for the start of the block with the given label.
+     *
+     * @param label {@code non-null;} the label of the block in question
+     * @return {@code non-null;} the appropriate instance
+     */
+    public CodeAddress getStart(int label) {
+        return starts[label];
+    }
+
+    /**
+     * Gets the instance for the final instruction of the given block.
+     *
+     * @param block {@code non-null;} the block in question
+     * @return {@code non-null;} the appropriate instance
+     */
+    public CodeAddress getLast(BasicBlock block) {
+        return lasts[block.getLabel()];
+    }
+
+    /**
+     * Gets the instance for the final instruction of the block with
+     * the given label.
+     *
+     * @param label {@code non-null;} the label of the block in question
+     * @return {@code non-null;} the appropriate instance
+     */
+    public CodeAddress getLast(int label) {
+        return lasts[label];
+    }
+
+    /**
+     * Gets the instance for the end (address after the final instruction)
+     * of the given block.
+     *
+     * @param block {@code non-null;} the block in question
+     * @return {@code non-null;} the appropriate instance
+     */
+    public CodeAddress getEnd(BasicBlock block) {
+        return ends[block.getLabel()];
+    }
+
+    /**
+     * Gets the instance for the end (address after the final instruction)
+     * of the block with the given label.
+     *
+     * @param label {@code non-null;} the label of the block in question
+     * @return {@code non-null;} the appropriate instance
+     */
+    public CodeAddress getEnd(int label) {
+        return ends[label];
+    }
+
+    /**
+     * Sets up the address arrays.
+     */
+    private void setupArrays(RopMethod method) {
+        BasicBlockList blocks = method.getBlocks();
+        int sz = blocks.size();
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = blocks.get(i);
+            int label = one.getLabel();
+            Insn insn = one.getInsns().get(0);
+
+            starts[label] = new CodeAddress(insn.getPosition());
+
+            SourcePosition pos = one.getLastInsn().getPosition();
+
+            lasts[label] = new CodeAddress(pos);
+            ends[label] = new CodeAddress(pos);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchBuilder.java b/dx/src/com/android/dx/dex/code/CatchBuilder.java
new file mode 100644
index 0000000..90d2e8d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchBuilder.java
@@ -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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.type.Type;
+
+import java.util.HashSet;
+
+/**
+ * Interface for the construction of {@link CatchTable} instances.
+ */
+public interface CatchBuilder {
+    /**
+     * Builds and returns the catch table for this instance.
+     *
+     * @return {@code non-null;} the constructed table
+     */
+    public CatchTable build();
+
+    /**
+     * Gets whether this instance has any catches at all (either typed
+     * or catch-all).
+     *
+     * @return whether this instance has any catches at all
+     */
+    public boolean hasAnyCatches();
+
+    /**
+     * Gets the set of catch types associated with this instance.
+     *
+     * @return {@code non-null;} the set of catch types
+     */
+    public HashSet<Type> getCatchTypes();
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchHandlerList.java b/dx/src/com/android/dx/dex/code/CatchHandlerList.java
new file mode 100644
index 0000000..8472584
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchHandlerList.java
@@ -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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.Hex;
+
+/**
+ * Ordered list of (exception type, handler address) entries.
+ */
+public final class CatchHandlerList extends FixedSizeList
+        implements Comparable<CatchHandlerList> {
+    /** {@code non-null;} empty instance */
+    public static final CatchHandlerList EMPTY = new CatchHandlerList(0);
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size {@code >= 0;} the size of the list
+     */
+    public CatchHandlerList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @return {@code non-null;} element at that index
+     */
+    public Entry get(int n) {
+        return (Entry) get0(n);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toHuman("", "");
+    }
+
+    /**
+     * Get the human form of this instance, prefixed on each line
+     * with the string.
+     *
+     * @param prefix {@code non-null;} the prefix for every line
+     * @param header {@code non-null;} the header for the first line (after the
+     * first prefix)
+     * @return {@code non-null;} the human form
+     */
+    public String toHuman(String prefix, String header) {
+        StringBuilder sb = new StringBuilder(100);
+        int size = size();
+
+        sb.append(prefix);
+        sb.append(header);
+        sb.append("catch ");
+
+        for (int i = 0; i < size; i++) {
+            Entry entry = get(i);
+
+            if (i != 0) {
+                sb.append(",\n");
+                sb.append(prefix);
+                sb.append("  ");
+            }
+
+            if ((i == (size - 1)) && catchesAll()) {
+                sb.append("<any>");
+            } else {
+                sb.append(entry.getExceptionType().toHuman());
+            }
+
+            sb.append(" -> ");
+            sb.append(Hex.u2or4(entry.getHandler()));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns whether or not this instance ends with a "catch-all"
+     * handler.
+     *
+     * @return {@code true} if this instance ends with a "catch-all"
+     * handler or {@code false} if not
+     */
+    public boolean catchesAll() {
+        int size = size();
+
+        if (size == 0) {
+            return false;
+        }
+
+        Entry last = get(size - 1);
+        return last.getExceptionType().equals(CstType.OBJECT);
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param exceptionType {@code non-null;} type of exception handled
+     * @param handler {@code >= 0;} exception handler address
+     */
+    public void set(int n, CstType exceptionType, int handler) {
+        set0(n, new Entry(exceptionType, handler));
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param entry {@code non-null;} the entry to set at {@code n}
+     */
+    public void set(int n, Entry entry) {
+        set0(n, entry);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(CatchHandlerList other) {
+        if (this == other) {
+            // Easy out.
+            return 0;
+        }
+
+        int thisSize = size();
+        int otherSize = other.size();
+        int checkSize = Math.min(thisSize, otherSize);
+
+        for (int i = 0; i < checkSize; i++) {
+            Entry thisEntry = get(i);
+            Entry otherEntry = other.get(i);
+            int compare = thisEntry.compareTo(otherEntry);
+            if (compare != 0) {
+                return compare;
+            }
+        }
+
+        if (thisSize < otherSize) {
+            return -1;
+        } else if (thisSize > otherSize) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Entry in the list.
+     */
+    public static class Entry implements Comparable<Entry> {
+        /** {@code non-null;} type of exception handled */
+        private final CstType exceptionType;
+
+        /** {@code >= 0;} exception handler address */
+        private final int handler;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param exceptionType {@code non-null;} type of exception handled
+         * @param handler {@code >= 0;} exception handler address
+         */
+        public Entry(CstType exceptionType, int handler) {
+            if (handler < 0) {
+                throw new IllegalArgumentException("handler < 0");
+            }
+
+            if (exceptionType == null) {
+                throw new NullPointerException("exceptionType == null");
+            }
+
+            this.handler = handler;
+            this.exceptionType = exceptionType;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int hashCode() {
+            return (handler * 31) + exceptionType.hashCode();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof Entry) {
+                return (compareTo((Entry) other) == 0);
+            }
+
+            return false;
+        }
+
+        /** {@inheritDoc} */
+        public int compareTo(Entry other) {
+            if (handler < other.handler) {
+                return -1;
+            } else if (handler > other.handler) {
+                return 1;
+            }
+
+            return exceptionType.compareTo(other.exceptionType);
+        }
+
+        /**
+         * Gets the exception type handled.
+         *
+         * @return {@code non-null;} the exception type
+         */
+        public CstType getExceptionType() {
+            return exceptionType;
+        }
+
+        /**
+         * Gets the handler address.
+         *
+         * @return {@code >= 0;} the handler address
+         */
+        public int getHandler() {
+            return handler;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchTable.java b/dx/src/com/android/dx/dex/code/CatchTable.java
new file mode 100644
index 0000000..0ee890f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchTable.java
@@ -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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Table of catch entries. Each entry includes a range of code
+ * addresses for which it is valid and an associated {@link
+ * CatchHandlerList}.
+ */
+public final class CatchTable extends FixedSizeList
+        implements Comparable<CatchTable> {
+    /** {@code non-null;} empty instance */
+    public static final CatchTable EMPTY = new CatchTable(0);
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size {@code >= 0;} the size of the table
+     */
+    public CatchTable(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @return {@code non-null;} element at that index
+     */
+    public Entry get(int n) {
+        return (Entry) get0(n);
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param entry {@code non-null;} the entry to set at {@code n}
+     */
+    public void set(int n, Entry entry) {
+        set0(n, entry);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(CatchTable other) {
+        if (this == other) {
+            // Easy out.
+            return 0;
+        }
+
+        int thisSize = size();
+        int otherSize = other.size();
+        int checkSize = Math.min(thisSize, otherSize);
+
+        for (int i = 0; i < checkSize; i++) {
+            Entry thisEntry = get(i);
+            Entry otherEntry = other.get(i);
+            int compare = thisEntry.compareTo(otherEntry);
+            if (compare != 0) {
+                return compare;
+            }
+        }
+
+        if (thisSize < otherSize) {
+            return -1;
+        } else if (thisSize > otherSize) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    /**
+     * Entry in a catch list.
+     */
+    public static class Entry implements Comparable<Entry> {
+        /** {@code >= 0;} start address */
+        private final int start;
+
+        /** {@code > start;} end address (exclusive) */
+        private final int end;
+
+        /** {@code non-null;} list of catch handlers */
+        private final CatchHandlerList handlers;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param start {@code >= 0;} start address
+         * @param end {@code > start;} end address (exclusive)
+         * @param handlers {@code non-null;} list of catch handlers
+         */
+        public Entry(int start, int end, CatchHandlerList handlers) {
+            if (start < 0) {
+                throw new IllegalArgumentException("start < 0");
+            }
+
+            if (end <= start) {
+                throw new IllegalArgumentException("end <= start");
+            }
+
+            if (handlers.isMutable()) {
+                throw new IllegalArgumentException("handlers.isMutable()");
+            }
+
+            this.start = start;
+            this.end = end;
+            this.handlers = handlers;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int hashCode() {
+            int hash = (start * 31) + end;
+            hash = (hash * 31) + handlers.hashCode();
+            return hash;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof Entry) {
+                return (compareTo((Entry) other) == 0);
+            }
+
+            return false;
+        }
+
+        /** {@inheritDoc} */
+        public int compareTo(Entry other) {
+            if (start < other.start) {
+                return -1;
+            } else if (start > other.start) {
+                return 1;
+            }
+
+            if (end < other.end) {
+                return -1;
+            } else if (end > other.end) {
+                return 1;
+            }
+
+            return handlers.compareTo(other.handlers);
+        }
+
+        /**
+         * Gets the start address.
+         *
+         * @return {@code >= 0;} the start address
+         */
+        public int getStart() {
+            return start;
+        }
+
+        /**
+         * Gets the end address (exclusive).
+         *
+         * @return {@code > start;} the end address (exclusive)
+         */
+        public int getEnd() {
+            return end;
+        }
+
+        /**
+         * Gets the handlers.
+         *
+         * @return {@code non-null;} the handlers
+         */
+        public CatchHandlerList getHandlers() {
+            return handlers;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CodeAddress.java b/dx/src/com/android/dx/dex/code/CodeAddress.java
new file mode 100644
index 0000000..5d26bd1
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CodeAddress.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to track an address within a code
+ * array. Instances are used for such things as branch targets and
+ * exception handler ranges. Its code size is zero, and so instances
+ * do not in general directly wind up in any output (either
+ * human-oriented or binary file).
+ */
+public final class CodeAddress extends ZeroSizeInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     */
+    public CodeAddress(SourcePosition position) {
+        super(position);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withRegisters(RegisterSpecList registers) {
+        return new CodeAddress(getPosition());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        return "code-address";
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/CstInsn.java b/dx/src/com/android/dx/dex/code/CstInsn.java
new file mode 100644
index 0000000..3f848c0
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CstInsn.java
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Instruction which has a single constant argument in addition
+ * to all the normal instruction information.
+ */
+public final class CstInsn extends FixedSizeInsn {
+    /** {@code non-null;} the constant argument for this instruction */
+    private final Constant constant;
+
+    /**
+     * {@code >= -1;} the constant pool index for {@link #constant}, or
+     * {@code -1} if not yet set
+     */
+    private int index;
+
+    /**
+     * {@code >= -1;} the constant pool index for the class reference in
+     * {@link #constant} if any, or {@code -1} if not yet set
+     */
+    private int classIndex;
+
+    /**
+     * Constructs an instance. The output address of this instance is
+     * initially unknown ({@code -1}) as is the constant pool index.
+     *
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position {@code non-null;} source position
+     * @param registers {@code non-null;} register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins or outs)
+     * @param constant {@code non-null;} constant argument
+     */
+    public CstInsn(Dop opcode, SourcePosition position,
+                   RegisterSpecList registers, Constant constant) {
+        super(opcode, position, registers);
+
+        if (constant == null) {
+            throw new NullPointerException("constant == null");
+        }
+
+        this.constant = constant;
+        this.index = -1;
+        this.classIndex = -1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withOpcode(Dop opcode) {
+        CstInsn result =
+            new CstInsn(opcode, getPosition(), getRegisters(), constant);
+
+        if (index >= 0) {
+            result.setIndex(index);
+        }
+
+        if (classIndex >= 0) {
+            result.setClassIndex(classIndex);
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        CstInsn result =
+            new CstInsn(getOpcode(), getPosition(), registers, constant);
+
+        if (index >= 0) {
+            result.setIndex(index);
+        }
+
+        if (classIndex >= 0) {
+            result.setClassIndex(classIndex);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the constant argument.
+     *
+     * @return {@code non-null;} the constant argument
+     */
+    public Constant getConstant() {
+        return constant;
+    }
+
+    /**
+     * Gets the constant's index. It is only valid to call this after
+     * {@link #setIndex} has been called.
+     *
+     * @return {@code >= 0;} the constant pool index
+     */
+    public int getIndex() {
+        if (index < 0) {
+            throw new RuntimeException("index not yet set for " + constant);
+        }
+
+        return index;
+    }
+
+    /**
+     * Returns whether the constant's index has been set for this instance.
+     *
+     * @see #setIndex
+     *
+     * @return {@code true} iff the index has been set
+     */
+    public boolean hasIndex() {
+        return (index >= 0);
+    }
+
+    /**
+     * Sets the constant's index. It is only valid to call this method once
+     * per instance.
+     *
+     * @param index {@code >= 0;} the constant pool index
+     */
+    public void setIndex(int index) {
+        if (index < 0) {
+            throw new IllegalArgumentException("index < 0");
+        }
+
+        if (this.index >= 0) {
+            throw new RuntimeException("index already set");
+        }
+
+        this.index = index;
+    }
+
+    /**
+     * Gets the constant's class index. It is only valid to call this after
+     * {@link #setClassIndex} has been called.
+     *
+     * @return {@code >= 0;} the constant's class's constant pool index
+     */
+    public int getClassIndex() {
+        if (classIndex < 0) {
+            throw new RuntimeException("class index not yet set");
+        }
+
+        return classIndex;
+    }
+
+    /**
+     * Returns whether the constant's class index has been set for this
+     * instance.
+     *
+     * @see #setClassIndex
+     *
+     * @return {@code true} iff the index has been set
+     */
+    public boolean hasClassIndex() {
+        return (classIndex >= 0);
+    }
+
+    /**
+     * Sets the constant's class index. This is the constant pool index
+     * for the class referred to by this instance's constant. Only
+     * reference constants have a class, so it is only on instances
+     * with reference constants that this method should ever be
+     * called. It is only valid to call this method once per instance.
+     *
+     * @param index {@code >= 0;} the constant's class's constant pool index
+     */
+    public void setClassIndex(int index) {
+        if (index < 0) {
+            throw new IllegalArgumentException("index < 0");
+        }
+
+        if (this.classIndex >= 0) {
+            throw new RuntimeException("class index already set");
+        }
+
+        this.classIndex = index;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return constant.toHuman();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvCode.java b/dx/src/com/android/dx/dex/code/DalvCode.java
new file mode 100644
index 0000000..58f191b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvCode.java
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+
+import java.util.HashSet;
+
+/**
+ * Container for all the pieces of a concrete method. Each instance
+ * corresponds to a {@code code} structure in a {@code .dex} file.
+ */
+public final class DalvCode {
+    /**
+     * how much position info to preserve; one of the static
+     * constants in {@link PositionList}
+     */
+    private final int positionInfo;
+
+    /**
+     * {@code null-ok;} the instruction list, ready for final processing;
+     * nulled out in {@link #finishProcessingIfNecessary}
+     */
+    private OutputFinisher unprocessedInsns;
+
+    /**
+     * {@code non-null;} unprocessed catch table;
+     * nulled out in {@link #finishProcessingIfNecessary}
+     */
+    private CatchBuilder unprocessedCatches;
+
+    /**
+     * {@code null-ok;} catch table; set in
+     * {@link #finishProcessingIfNecessary}
+     */
+    private CatchTable catches;
+
+    /**
+     * {@code null-ok;} source positions list; set in
+     * {@link #finishProcessingIfNecessary}
+     */
+    private PositionList positions;
+
+    /**
+     * {@code null-ok;} local variable list; set in
+     * {@link #finishProcessingIfNecessary}
+     */
+    private LocalList locals;
+
+    /**
+     * {@code null-ok;} the processed instruction list; set in
+     * {@link #finishProcessingIfNecessary}
+     */
+    private DalvInsnList insns;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param positionInfo how much position info to preserve; one of the
+     * static constants in {@link PositionList}
+     * @param unprocessedInsns {@code non-null;} the instruction list, ready
+     * for final processing
+     * @param unprocessedCatches {@code non-null;} unprocessed catch
+     * (exception handler) table
+     */
+    public DalvCode(int positionInfo, OutputFinisher unprocessedInsns,
+            CatchBuilder unprocessedCatches) {
+        if (unprocessedInsns == null) {
+            throw new NullPointerException("unprocessedInsns == null");
+        }
+
+        if (unprocessedCatches == null) {
+            throw new NullPointerException("unprocessedCatches == null");
+        }
+
+        this.positionInfo = positionInfo;
+        this.unprocessedInsns = unprocessedInsns;
+        this.unprocessedCatches = unprocessedCatches;
+        this.catches = null;
+        this.positions = null;
+        this.locals = null;
+        this.insns = null;
+    }
+
+    /**
+     * Finish up processing of the method.
+     */
+    private void finishProcessingIfNecessary() {
+        if (insns != null) {
+            return;
+        }
+
+        insns = unprocessedInsns.finishProcessingAndGetList();
+        positions = PositionList.make(insns, positionInfo);
+        locals = LocalList.make(insns);
+        catches = unprocessedCatches.build();
+
+        // Let them be gc'ed.
+        unprocessedInsns = null;
+        unprocessedCatches = null;
+    }
+
+    /**
+     * Assign indices in all instructions that need them, using the
+     * given callback to perform lookups. This must be called before
+     * {@link #getInsns}.
+     *
+     * @param callback {@code non-null;} callback object
+     */
+    public void assignIndices(AssignIndicesCallback callback) {
+        unprocessedInsns.assignIndices(callback);
+    }
+
+    /**
+     * Gets whether this instance has any position data to represent.
+     *
+     * @return {@code true} iff this instance has any position
+     * data to represent
+     */
+    public boolean hasPositions() {
+        return (positionInfo != PositionList.NONE)
+            && unprocessedInsns.hasAnyPositionInfo();
+    }
+
+    /**
+     * Gets whether this instance has any local variable data to represent.
+     *
+     * @return {@code true} iff this instance has any local variable
+     * data to represent
+     */
+    public boolean hasLocals() {
+        return unprocessedInsns.hasAnyLocalInfo();
+    }
+
+    /**
+     * Gets whether this instance has any catches at all (either typed
+     * or catch-all).
+     *
+     * @return whether this instance has any catches at all
+     */
+    public boolean hasAnyCatches() {
+        return unprocessedCatches.hasAnyCatches();
+    }
+
+    /**
+     * Gets the set of catch types handled anywhere in the code.
+     *
+     * @return {@code non-null;} the set of catch types
+     */
+    public HashSet<Type> getCatchTypes() {
+        return unprocessedCatches.getCatchTypes();
+    }
+
+    /**
+     * Gets the set of all constants referred to by instructions in
+     * the code.
+     *
+     * @return {@code non-null;} the set of constants
+     */
+    public HashSet<Constant> getInsnConstants() {
+        return unprocessedInsns.getAllConstants();
+    }
+
+    /**
+     * Gets the list of instructions.
+     *
+     * @return {@code non-null;} the instruction list
+     */
+    public DalvInsnList getInsns() {
+        finishProcessingIfNecessary();
+        return insns;
+    }
+
+    /**
+     * Gets the catch (exception handler) table.
+     *
+     * @return {@code non-null;} the catch table
+     */
+    public CatchTable getCatches() {
+        finishProcessingIfNecessary();
+        return catches;
+    }
+
+    /**
+     * Gets the source positions list.
+     *
+     * @return {@code non-null;} the source positions list
+     */
+    public PositionList getPositions() {
+        finishProcessingIfNecessary();
+        return positions;
+    }
+
+    /**
+     * Gets the source positions list.
+     *
+     * @return {@code non-null;} the source positions list
+     */
+    public LocalList getLocals() {
+        finishProcessingIfNecessary();
+        return locals;
+    }
+
+    /**
+     * Class used as a callback for {@link #assignIndices}.
+     */
+    public static interface AssignIndicesCallback {
+        /**
+         * Gets the index for the given constant.
+         *
+         * @param cst {@code non-null;} the constant
+         * @return {@code >= -1;} the index or {@code -1} if the constant
+         * shouldn't actually be reified with an index
+         */
+        public int getIndex(Constant cst);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvInsn.java b/dx/src/com/android/dx/dex/code/DalvInsn.java
new file mode 100644
index 0000000..f203817
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvInsn.java
@@ -0,0 +1,422 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.TwoColumnOutput;
+
+/**
+ * Base class for Dalvik instructions.
+ */
+public abstract class DalvInsn {
+    /**
+     * the actual output address of this instance, if known, or
+     * {@code -1} if not
+     */
+    private int address;
+
+    /** the opcode; one of the constants from {@link Dops} */
+    private final Dop opcode;
+
+    /** {@code non-null;} source position */
+    private final SourcePosition position;
+
+    /** {@code non-null;} list of register arguments */
+    private final RegisterSpecList registers;
+
+    /**
+     * Makes a move instruction, appropriate and ideal for the given arguments.
+     *
+     * @param position {@code non-null;} source position information
+     * @param dest {@code non-null;} destination register
+     * @param src {@code non-null;} source register
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static SimpleInsn makeMove(SourcePosition position,
+            RegisterSpec dest, RegisterSpec src) {
+        boolean category1 = dest.getCategory() == 1;
+        boolean reference = dest.getType().isReference();
+        int destReg = dest.getReg();
+        int srcReg = src.getReg();
+        Dop opcode;
+
+        if ((srcReg | destReg) < 16) {
+            opcode = reference ? Dops.MOVE_OBJECT :
+                (category1 ? Dops.MOVE : Dops.MOVE_WIDE);
+        } else if (destReg < 256) {
+            opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
+                (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
+        } else {
+            opcode = reference ? Dops.MOVE_OBJECT_16 :
+                (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
+        }
+
+        return new SimpleInsn(opcode, position,
+                              RegisterSpecList.make(dest, src));
+    }
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * <p><b>Note:</b> In the unlikely event that an instruction takes
+     * absolutely no registers (e.g., a {@code nop} or a
+     * no-argument no-result static method call), then the given
+     * register list may be passed as {@link
+     * RegisterSpecList#EMPTY}.</p>
+     *
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position {@code non-null;} source position
+     * @param registers {@code non-null;} register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins and outs)
+     */
+    public DalvInsn(Dop opcode, SourcePosition position,
+                    RegisterSpecList registers) {
+        if (opcode == null) {
+            throw new NullPointerException("opcode == null");
+        }
+
+        if (position == null) {
+            throw new NullPointerException("position == null");
+        }
+
+        if (registers == null) {
+            throw new NullPointerException("registers == null");
+        }
+
+        this.address = -1;
+        this.opcode = opcode;
+        this.position = position;
+        this.registers = registers;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(identifierString());
+        sb.append(' ');
+        sb.append(position);
+
+        sb.append(": ");
+        sb.append(opcode.getName());
+
+        boolean needComma = false;
+        if (registers.size() != 0) {
+            sb.append(registers.toHuman(" ", ", ", null));
+            needComma = true;
+        }
+
+        String extra = argString();
+        if (extra != null) {
+            if (needComma) {
+                sb.append(',');
+            }
+            sb.append(' ');
+            sb.append(extra);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets whether the address of this instruction is known.
+     *
+     * @see #getAddress
+     * @see #setAddress
+     */
+    public final boolean hasAddress() {
+        return (address >= 0);
+    }
+
+    /**
+     * Gets the output address of this instruction, if it is known. This throws
+     * a {@code RuntimeException} if it has not yet been set.
+     *
+     * @see #setAddress
+     *
+     * @return {@code >= 0;} the output address
+     */
+    public final int getAddress() {
+        if (address < 0) {
+            throw new RuntimeException("address not yet known");
+        }
+
+        return address;
+    }
+
+    /**
+     * Gets the opcode.
+     *
+     * @return {@code non-null;} the opcode
+     */
+    public final Dop getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the source position.
+     *
+     * @return {@code non-null;} the source position
+     */
+    public final SourcePosition getPosition() {
+        return position;
+    }
+
+    /**
+     * Gets the register list for this instruction.
+     *
+     * @return {@code non-null;} the registers
+     */
+    public final RegisterSpecList getRegisters() {
+        return registers;
+    }
+
+    /**
+     * Returns whether this instance's opcode uses a result register.
+     * This method is a convenient shorthand for
+     * {@code getOpcode().hasResult()}.
+     *
+     * @return {@code true} iff this opcode uses a result register
+     */
+    public final boolean hasResult() {
+        return opcode.hasResult();
+    }
+
+    /**
+     * Gets the minimum distinct registers required for this instruction.
+     * This assumes that the result (if any) can share registers with the
+     * sources (if any), that each source register is unique, and that
+     * (to be explicit here) category-2 values take up two consecutive
+     * registers.
+     *
+     * @return {@code >= 0;} the minimum distinct register requirement
+     */
+    public final int getMinimumRegisterRequirement() {
+        boolean hasResult = hasResult();
+        int regSz = registers.size();
+        int resultRequirement = hasResult ? registers.get(0).getCategory() : 0;
+        int sourceRequirement = 0;
+
+        for (int i = hasResult ? 1 : 0; i < regSz; i++) {
+            sourceRequirement += registers.get(i).getCategory();
+        }
+
+        return Math.max(sourceRequirement, resultRequirement);
+    }
+
+    /**
+     * Gets the instruction prefix required, if any, to use in a high
+     * register transformed version of this instance.
+     *
+     * @see #hrVersion
+     *
+     * @return {@code null-ok;} the prefix, if any
+     */
+    public DalvInsn hrPrefix() {
+        RegisterSpecList regs = registers;
+        int sz = regs.size();
+
+        if (hasResult()) {
+            if (sz == 1) {
+                return null;
+            }
+            regs = regs.withoutFirst();
+        } else if (sz == 0) {
+            return null;
+        }
+
+        return new HighRegisterPrefix(position, regs);
+    }
+
+    /**
+     * Gets the instruction suffix required, if any, to use in a high
+     * register transformed version of this instance.
+     *
+     * @see #hrVersion
+     *
+     * @return {@code null-ok;} the suffix, if any
+     */
+    public DalvInsn hrSuffix() {
+        if (hasResult()) {
+            RegisterSpec r = registers.get(0);
+            return makeMove(position, r, r.withReg(0));
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the instruction that is equivalent to this one, except that
+     * uses sequential registers starting at {@code 0} (storing
+     * the result, if any, in register {@code 0} as well). The
+     * sequence of instructions from {@link #hrPrefix} and {@link
+     * #hrSuffix} (if non-null) surrounding the result of a call to
+     * this method are the high register transformation of this
+     * instance, and it is guaranteed that the number of low registers
+     * used will be the number returned by {@link
+     * #getMinimumRegisterRequirement}.
+     *
+     * @return {@code non-null;} the replacement
+     */
+    public DalvInsn hrVersion() {
+        RegisterSpecList regs =
+            registers.withSequentialRegisters(0, hasResult());
+        return withRegisters(regs);
+    }
+
+    /**
+     * Gets the short identifier for this instruction. This is its
+     * address, if assigned, or its identity hashcode if not.
+     *
+     * @return {@code non-null;} the identifier
+     */
+    public final String identifierString() {
+        if (address != -1) {
+            return String.format("%04x", address);
+        }
+
+        return Hex.u4(System.identityHashCode(this));
+    }
+
+    /**
+     * Returns the string form of this instance suitable for inclusion in
+     * a human-oriented listing dump. This method will return {@code null}
+     * if this instance should not appear in a listing.
+     *
+     * @param prefix {@code non-null;} prefix before the address; each follow-on
+     * line will be indented to match as well
+     * @param width {@code >= 0;} the width of the output or {@code 0} for
+     * unlimited width
+     * @param noteIndices whether to include an explicit notation of
+     * constant pool indices
+     * @return {@code null-ok;} the string form or {@code null} if this
+     * instance should not appear in a listing
+     */
+    public final String listingString(String prefix, int width,
+            boolean noteIndices) {
+        String insnPerSe = listingString0(noteIndices);
+
+        if (insnPerSe == null) {
+            return null;
+        }
+
+        String addr = prefix + identifierString() + ": ";
+        int w1 = addr.length();
+        int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
+
+        return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
+    }
+
+    /**
+     * Sets the output address.
+     *
+     * @param address {@code >= 0;} the output address
+     */
+    public final void setAddress(int address) {
+        if (address < 0) {
+            throw new IllegalArgumentException("address < 0");
+        }
+
+        this.address = address;
+    }
+
+    /**
+     * Gets the address immediately after this instance. This is only
+     * calculable if this instance's address is known, and it is equal
+     * to the address plus the length of the instruction format of this
+     * instance's opcode.
+     *
+     * @return {@code >= 0;} the next address
+     */
+    public final int getNextAddress() {
+        return getAddress() + codeSize();
+    }
+
+    /**
+     * Gets the size of this instruction, in 16-bit code units.
+     *
+     * @return {@code >= 0;} the code size of this instruction
+     */
+    public abstract int codeSize();
+
+    /**
+     * Writes this instance to the given output. This method should
+     * never annotate the output.
+     *
+     * @param out {@code non-null;} where to write to
+     */
+    public abstract void writeTo(AnnotatedOutput out);
+
+    /**
+     * Returns an instance that is just like this one, except that its
+     * opcode is replaced by the one given, and its address is reset.
+     *
+     * @param opcode {@code non-null;} the new opcode
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public abstract DalvInsn withOpcode(Dop opcode);
+
+    /**
+     * Returns an instance that is just like this one, except that all
+     * register references have been offset by the given delta, and its
+     * address is reset.
+     *
+     * @param delta the amount to offset register references by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public abstract DalvInsn withRegisterOffset(int delta);
+
+    /**
+     * Returns an instance that is just like this one, except that the
+     * register list is replaced by the given one, and its address is
+     * reset.
+     *
+     * @param registers {@code non-null;} new register list
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public abstract DalvInsn withRegisters(RegisterSpecList registers);
+
+    /**
+     * Gets the string form for any arguments to this instance. Subclasses
+     * must override this.
+     *
+     * @return {@code null-ok;} the string version of any arguments or
+     * {@code null} if there are none
+     */
+    protected abstract String argString();
+
+    /**
+     * Helper for {@link #listingString}, which returns the string
+     * form of this instance suitable for inclusion in a
+     * human-oriented listing dump, not including the instruction
+     * address and without respect for any output formatting. This
+     * method should return {@code null} if this instance should
+     * not appear in a listing.
+     *
+     * @param noteIndices whether to include an explicit notation of
+     * constant pool indices
+     * @return {@code null-ok;} the listing string
+     */
+    protected abstract String listingString0(boolean noteIndices);
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvInsnList.java b/dx/src/com/android/dx/dex/code/DalvInsnList.java
new file mode 100644
index 0000000..0f8c23d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvInsnList.java
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.IndentingWriter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * List of {@link DalvInsn} instances.
+ */
+public final class DalvInsnList extends FixedSizeList {
+
+    /**
+     * The amount of register space, in register units, required for this
+     * code block. This may be greater than the largest observed register+
+     * category because the method this code block exists in may
+     * specify arguments that are unused by the method.
+     */
+    private final int regCount;
+
+    /**
+     * Constructs and returns an immutable instance whose elements are
+     * identical to the ones in the given list, in the same order.
+     *
+     * @param list {@code non-null;} the list to use for elements
+     * @param regCount count, in register-units, of the number of registers
+     * this code block requires.
+     * @return {@code non-null;} an appropriately-constructed instance of this
+     * class
+     */
+    public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list,
+            int regCount) {
+        int size = list.size();
+        DalvInsnList result = new DalvInsnList(size, regCount);
+
+        for (int i = 0; i < size; i++) {
+            result.set(i, list.get(i));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public DalvInsnList(int size, int regCount) {
+        super(size);
+        this.regCount = regCount;
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @return {@code non-null;} element at that index
+     */
+    public DalvInsn get(int n) {
+        return (DalvInsn) get0(n);
+    }
+
+    /**
+     * Sets the instruction at the given index.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param insn {@code non-null;} the instruction to set at {@code n}
+     */
+    public void set(int n, DalvInsn insn) {
+        set0(n, insn);
+    }
+
+    /**
+     * Gets the size of this instance, in 16-bit code units. This will only
+     * return a meaningful result if the instructions in this instance all
+     * have valid addresses.
+     *
+     * @return {@code >= 0;} the size
+     */
+    public int codeSize() {
+        int sz = size();
+
+        if (sz == 0) {
+            return 0;
+        }
+
+        DalvInsn last = get(sz - 1);
+        return last.getNextAddress();
+    }
+
+    /**
+     * Writes all the instructions in this instance to the given output
+     * destination.
+     *
+     * @param out {@code non-null;} where to write to
+     */
+    public void writeTo(AnnotatedOutput out) {
+        int startCursor = out.getCursor();
+        int sz = size();
+
+        if (out.annotates()) {
+            boolean verbose = out.isVerbose();
+
+            for (int i = 0; i < sz; i++) {
+                DalvInsn insn = (DalvInsn) get0(i);
+                int codeBytes = insn.codeSize() * 2;
+                String s;
+
+                if ((codeBytes != 0) || verbose) {
+                    s = insn.listingString("  ", out.getAnnotationWidth(),
+                            true);
+                } else {
+                    s = null;
+                }
+
+                if (s != null) {
+                    out.annotate(codeBytes, s);
+                } else if (codeBytes != 0) {
+                    out.annotate(codeBytes, "");
+                }
+            }
+        }
+
+        for (int i = 0; i < sz; i++) {
+            DalvInsn insn = (DalvInsn) get0(i);
+            try {
+                insn.writeTo(out);
+            } catch (RuntimeException ex) {
+                throw ExceptionWithContext.withContext(ex,
+                        "...while writing " + insn);
+            }
+        }
+
+        // Sanity check of the amount written.
+        int written = (out.getCursor() - startCursor) / 2;
+        if (written != codeSize()) {
+            throw new RuntimeException("write length mismatch; expected " +
+                    codeSize() + " but actually wrote " + written);
+        }
+    }
+
+    /**
+     * Gets the minimum required register count implied by this
+     * instance.  This includes any unused parameters that could
+     * potentially be at the top of the register space.
+     * @return {@code >= 0;} the required registers size
+     */
+    public int getRegistersSize() {
+        return regCount;
+    }
+
+    /**
+     * Gets the size of the outgoing arguments area required by this
+     * method. This is equal to the largest argument word count of any
+     * method referred to by this instance.
+     *
+     * @return {@code >= 0;} the required outgoing arguments size
+     */
+    public int getOutsSize() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            DalvInsn insn = (DalvInsn) get0(i);
+
+            if (!(insn instanceof CstInsn)) {
+                continue;
+            }
+
+            Constant cst = ((CstInsn) insn).getConstant();
+
+            if (!(cst instanceof CstBaseMethodRef)) {
+                continue;
+            }
+
+            boolean isStatic =
+                (insn.getOpcode().getFamily() == DalvOps.INVOKE_STATIC);
+            int count =
+                ((CstBaseMethodRef) cst).getParameterWordCount(isStatic);
+
+            if (count > result) {
+                result = count;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out {@code non-null;} where to dump
+     * @param prefix {@code non-null;} prefix to attach to each line of output
+     * @param verbose whether to be verbose; verbose output includes
+     * lines for zero-size instructions and explicit constant pool indices
+     */
+    public void debugPrint(Writer out, String prefix, boolean verbose) {
+        IndentingWriter iw = new IndentingWriter(out, 0, prefix);
+        int sz = size();
+
+        try {
+            for (int i = 0; i < sz; i++) {
+                DalvInsn insn = (DalvInsn) get0(i);
+                String s;
+
+                if ((insn.codeSize() != 0) || verbose) {
+                    s = insn.listingString("", 0, verbose);
+                } else {
+                    s = null;
+                }
+
+                if (s != null) {
+                    iw.write(s);
+                }
+            }
+
+            iw.flush();
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out {@code non-null;} where to dump
+     * @param prefix {@code non-null;} prefix to attach to each line of output
+     * @param verbose whether to be verbose; verbose output includes
+     * lines for zero-size instructions
+     */
+    public void debugPrint(OutputStream out, String prefix, boolean verbose) {
+        Writer w = new OutputStreamWriter(out);
+        debugPrint(w, prefix, verbose);
+
+        try {
+            w.flush();
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvOps.java b/dx/src/com/android/dx/dex/code/DalvOps.java
new file mode 100644
index 0000000..7dc648e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvOps.java
@@ -0,0 +1,298 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+/**
+ * All the Dalvik opcode value constants. See the related spec
+ * document for the meaning and instruction format of each opcode.
+ */
+public final class DalvOps {
+    /** pseudo-opcode used for nonstandard format "instructions" */
+    public static final int SPECIAL_FORMAT = -1;
+
+    /** minimum valid opcode value */
+    public static final int MIN_VALUE = -1;
+
+    /** maximum valid opcode value */
+    public static final int MAX_VALUE = 0xff;
+
+    // BEGIN(opcodes); GENERATED AUTOMATICALLY BY opcode-gen
+    public static final int NOP = 0x00;
+    public static final int MOVE = 0x01;
+    public static final int MOVE_FROM16 = 0x02;
+    public static final int MOVE_16 = 0x03;
+    public static final int MOVE_WIDE = 0x04;
+    public static final int MOVE_WIDE_FROM16 = 0x05;
+    public static final int MOVE_WIDE_16 = 0x06;
+    public static final int MOVE_OBJECT = 0x07;
+    public static final int MOVE_OBJECT_FROM16 = 0x08;
+    public static final int MOVE_OBJECT_16 = 0x09;
+    public static final int MOVE_RESULT = 0x0a;
+    public static final int MOVE_RESULT_WIDE = 0x0b;
+    public static final int MOVE_RESULT_OBJECT = 0x0c;
+    public static final int MOVE_EXCEPTION = 0x0d;
+    public static final int RETURN_VOID = 0x0e;
+    public static final int RETURN = 0x0f;
+    public static final int RETURN_WIDE = 0x10;
+    public static final int RETURN_OBJECT = 0x11;
+    public static final int CONST_4 = 0x12;
+    public static final int CONST_16 = 0x13;
+    public static final int CONST = 0x14;
+    public static final int CONST_HIGH16 = 0x15;
+    public static final int CONST_WIDE_16 = 0x16;
+    public static final int CONST_WIDE_32 = 0x17;
+    public static final int CONST_WIDE = 0x18;
+    public static final int CONST_WIDE_HIGH16 = 0x19;
+    public static final int CONST_STRING = 0x1a;
+    public static final int CONST_STRING_JUMBO = 0x1b;
+    public static final int CONST_CLASS = 0x1c;
+    public static final int MONITOR_ENTER = 0x1d;
+    public static final int MONITOR_EXIT = 0x1e;
+    public static final int CHECK_CAST = 0x1f;
+    public static final int INSTANCE_OF = 0x20;
+    public static final int ARRAY_LENGTH = 0x21;
+    public static final int NEW_INSTANCE = 0x22;
+    public static final int NEW_ARRAY = 0x23;
+    public static final int FILLED_NEW_ARRAY = 0x24;
+    public static final int FILLED_NEW_ARRAY_RANGE = 0x25;
+    public static final int FILL_ARRAY_DATA = 0x26;
+    public static final int THROW = 0x27;
+    public static final int GOTO = 0x28;
+    public static final int GOTO_16 = 0x29;
+    public static final int GOTO_32 = 0x2a;
+    public static final int PACKED_SWITCH = 0x2b;
+    public static final int SPARSE_SWITCH = 0x2c;
+    public static final int CMPL_FLOAT = 0x2d;
+    public static final int CMPG_FLOAT = 0x2e;
+    public static final int CMPL_DOUBLE = 0x2f;
+    public static final int CMPG_DOUBLE = 0x30;
+    public static final int CMP_LONG = 0x31;
+    public static final int IF_EQ = 0x32;
+    public static final int IF_NE = 0x33;
+    public static final int IF_LT = 0x34;
+    public static final int IF_GE = 0x35;
+    public static final int IF_GT = 0x36;
+    public static final int IF_LE = 0x37;
+    public static final int IF_EQZ = 0x38;
+    public static final int IF_NEZ = 0x39;
+    public static final int IF_LTZ = 0x3a;
+    public static final int IF_GEZ = 0x3b;
+    public static final int IF_GTZ = 0x3c;
+    public static final int IF_LEZ = 0x3d;
+    public static final int UNUSED_3E = 0x3e;
+    public static final int UNUSED_3F = 0x3f;
+    public static final int UNUSED_40 = 0x40;
+    public static final int UNUSED_41 = 0x41;
+    public static final int UNUSED_42 = 0x42;
+    public static final int UNUSED_43 = 0x43;
+    public static final int AGET = 0x44;
+    public static final int AGET_WIDE = 0x45;
+    public static final int AGET_OBJECT = 0x46;
+    public static final int AGET_BOOLEAN = 0x47;
+    public static final int AGET_BYTE = 0x48;
+    public static final int AGET_CHAR = 0x49;
+    public static final int AGET_SHORT = 0x4a;
+    public static final int APUT = 0x4b;
+    public static final int APUT_WIDE = 0x4c;
+    public static final int APUT_OBJECT = 0x4d;
+    public static final int APUT_BOOLEAN = 0x4e;
+    public static final int APUT_BYTE = 0x4f;
+    public static final int APUT_CHAR = 0x50;
+    public static final int APUT_SHORT = 0x51;
+    public static final int IGET = 0x52;
+    public static final int IGET_WIDE = 0x53;
+    public static final int IGET_OBJECT = 0x54;
+    public static final int IGET_BOOLEAN = 0x55;
+    public static final int IGET_BYTE = 0x56;
+    public static final int IGET_CHAR = 0x57;
+    public static final int IGET_SHORT = 0x58;
+    public static final int IPUT = 0x59;
+    public static final int IPUT_WIDE = 0x5a;
+    public static final int IPUT_OBJECT = 0x5b;
+    public static final int IPUT_BOOLEAN = 0x5c;
+    public static final int IPUT_BYTE = 0x5d;
+    public static final int IPUT_CHAR = 0x5e;
+    public static final int IPUT_SHORT = 0x5f;
+    public static final int SGET = 0x60;
+    public static final int SGET_WIDE = 0x61;
+    public static final int SGET_OBJECT = 0x62;
+    public static final int SGET_BOOLEAN = 0x63;
+    public static final int SGET_BYTE = 0x64;
+    public static final int SGET_CHAR = 0x65;
+    public static final int SGET_SHORT = 0x66;
+    public static final int SPUT = 0x67;
+    public static final int SPUT_WIDE = 0x68;
+    public static final int SPUT_OBJECT = 0x69;
+    public static final int SPUT_BOOLEAN = 0x6a;
+    public static final int SPUT_BYTE = 0x6b;
+    public static final int SPUT_CHAR = 0x6c;
+    public static final int SPUT_SHORT = 0x6d;
+    public static final int INVOKE_VIRTUAL = 0x6e;
+    public static final int INVOKE_SUPER = 0x6f;
+    public static final int INVOKE_DIRECT = 0x70;
+    public static final int INVOKE_STATIC = 0x71;
+    public static final int INVOKE_INTERFACE = 0x72;
+    public static final int UNUSED_73 = 0x73;
+    public static final int INVOKE_VIRTUAL_RANGE = 0x74;
+    public static final int INVOKE_SUPER_RANGE = 0x75;
+    public static final int INVOKE_DIRECT_RANGE = 0x76;
+    public static final int INVOKE_STATIC_RANGE = 0x77;
+    public static final int INVOKE_INTERFACE_RANGE = 0x78;
+    public static final int UNUSED_79 = 0x79;
+    public static final int UNUSED_7A = 0x7a;
+    public static final int NEG_INT = 0x7b;
+    public static final int NOT_INT = 0x7c;
+    public static final int NEG_LONG = 0x7d;
+    public static final int NOT_LONG = 0x7e;
+    public static final int NEG_FLOAT = 0x7f;
+    public static final int NEG_DOUBLE = 0x80;
+    public static final int INT_TO_LONG = 0x81;
+    public static final int INT_TO_FLOAT = 0x82;
+    public static final int INT_TO_DOUBLE = 0x83;
+    public static final int LONG_TO_INT = 0x84;
+    public static final int LONG_TO_FLOAT = 0x85;
+    public static final int LONG_TO_DOUBLE = 0x86;
+    public static final int FLOAT_TO_INT = 0x87;
+    public static final int FLOAT_TO_LONG = 0x88;
+    public static final int FLOAT_TO_DOUBLE = 0x89;
+    public static final int DOUBLE_TO_INT = 0x8a;
+    public static final int DOUBLE_TO_LONG = 0x8b;
+    public static final int DOUBLE_TO_FLOAT = 0x8c;
+    public static final int INT_TO_BYTE = 0x8d;
+    public static final int INT_TO_CHAR = 0x8e;
+    public static final int INT_TO_SHORT = 0x8f;
+    public static final int ADD_INT = 0x90;
+    public static final int SUB_INT = 0x91;
+    public static final int MUL_INT = 0x92;
+    public static final int DIV_INT = 0x93;
+    public static final int REM_INT = 0x94;
+    public static final int AND_INT = 0x95;
+    public static final int OR_INT = 0x96;
+    public static final int XOR_INT = 0x97;
+    public static final int SHL_INT = 0x98;
+    public static final int SHR_INT = 0x99;
+    public static final int USHR_INT = 0x9a;
+    public static final int ADD_LONG = 0x9b;
+    public static final int SUB_LONG = 0x9c;
+    public static final int MUL_LONG = 0x9d;
+    public static final int DIV_LONG = 0x9e;
+    public static final int REM_LONG = 0x9f;
+    public static final int AND_LONG = 0xa0;
+    public static final int OR_LONG = 0xa1;
+    public static final int XOR_LONG = 0xa2;
+    public static final int SHL_LONG = 0xa3;
+    public static final int SHR_LONG = 0xa4;
+    public static final int USHR_LONG = 0xa5;
+    public static final int ADD_FLOAT = 0xa6;
+    public static final int SUB_FLOAT = 0xa7;
+    public static final int MUL_FLOAT = 0xa8;
+    public static final int DIV_FLOAT = 0xa9;
+    public static final int REM_FLOAT = 0xaa;
+    public static final int ADD_DOUBLE = 0xab;
+    public static final int SUB_DOUBLE = 0xac;
+    public static final int MUL_DOUBLE = 0xad;
+    public static final int DIV_DOUBLE = 0xae;
+    public static final int REM_DOUBLE = 0xaf;
+    public static final int ADD_INT_2ADDR = 0xb0;
+    public static final int SUB_INT_2ADDR = 0xb1;
+    public static final int MUL_INT_2ADDR = 0xb2;
+    public static final int DIV_INT_2ADDR = 0xb3;
+    public static final int REM_INT_2ADDR = 0xb4;
+    public static final int AND_INT_2ADDR = 0xb5;
+    public static final int OR_INT_2ADDR = 0xb6;
+    public static final int XOR_INT_2ADDR = 0xb7;
+    public static final int SHL_INT_2ADDR = 0xb8;
+    public static final int SHR_INT_2ADDR = 0xb9;
+    public static final int USHR_INT_2ADDR = 0xba;
+    public static final int ADD_LONG_2ADDR = 0xbb;
+    public static final int SUB_LONG_2ADDR = 0xbc;
+    public static final int MUL_LONG_2ADDR = 0xbd;
+    public static final int DIV_LONG_2ADDR = 0xbe;
+    public static final int REM_LONG_2ADDR = 0xbf;
+    public static final int AND_LONG_2ADDR = 0xc0;
+    public static final int OR_LONG_2ADDR = 0xc1;
+    public static final int XOR_LONG_2ADDR = 0xc2;
+    public static final int SHL_LONG_2ADDR = 0xc3;
+    public static final int SHR_LONG_2ADDR = 0xc4;
+    public static final int USHR_LONG_2ADDR = 0xc5;
+    public static final int ADD_FLOAT_2ADDR = 0xc6;
+    public static final int SUB_FLOAT_2ADDR = 0xc7;
+    public static final int MUL_FLOAT_2ADDR = 0xc8;
+    public static final int DIV_FLOAT_2ADDR = 0xc9;
+    public static final int REM_FLOAT_2ADDR = 0xca;
+    public static final int ADD_DOUBLE_2ADDR = 0xcb;
+    public static final int SUB_DOUBLE_2ADDR = 0xcc;
+    public static final int MUL_DOUBLE_2ADDR = 0xcd;
+    public static final int DIV_DOUBLE_2ADDR = 0xce;
+    public static final int REM_DOUBLE_2ADDR = 0xcf;
+    public static final int ADD_INT_LIT16 = 0xd0;
+    public static final int RSUB_INT = 0xd1;
+    public static final int MUL_INT_LIT16 = 0xd2;
+    public static final int DIV_INT_LIT16 = 0xd3;
+    public static final int REM_INT_LIT16 = 0xd4;
+    public static final int AND_INT_LIT16 = 0xd5;
+    public static final int OR_INT_LIT16 = 0xd6;
+    public static final int XOR_INT_LIT16 = 0xd7;
+    public static final int ADD_INT_LIT8 = 0xd8;
+    public static final int RSUB_INT_LIT8 = 0xd9;
+    public static final int MUL_INT_LIT8 = 0xda;
+    public static final int DIV_INT_LIT8 = 0xdb;
+    public static final int REM_INT_LIT8 = 0xdc;
+    public static final int AND_INT_LIT8 = 0xdd;
+    public static final int OR_INT_LIT8 = 0xde;
+    public static final int XOR_INT_LIT8 = 0xdf;
+    public static final int SHL_INT_LIT8 = 0xe0;
+    public static final int SHR_INT_LIT8 = 0xe1;
+    public static final int USHR_INT_LIT8 = 0xe2;
+    public static final int UNUSED_E3 = 0xe3;
+    public static final int UNUSED_E4 = 0xe4;
+    public static final int UNUSED_E5 = 0xe5;
+    public static final int UNUSED_E6 = 0xe6;
+    public static final int UNUSED_E7 = 0xe7;
+    public static final int UNUSED_E8 = 0xe8;
+    public static final int UNUSED_E9 = 0xe9;
+    public static final int UNUSED_EA = 0xea;
+    public static final int UNUSED_EB = 0xeb;
+    public static final int UNUSED_EC = 0xec;
+    public static final int UNUSED_ED = 0xed;
+    public static final int UNUSED_EE = 0xee;
+    public static final int UNUSED_EF = 0xef;
+    public static final int UNUSED_F0 = 0xf0;
+    public static final int UNUSED_F1 = 0xf1;
+    public static final int UNUSED_F2 = 0xf2;
+    public static final int UNUSED_F3 = 0xf3;
+    public static final int UNUSED_F4 = 0xf4;
+    public static final int UNUSED_F5 = 0xf5;
+    public static final int UNUSED_F6 = 0xf6;
+    public static final int UNUSED_F7 = 0xf7;
+    public static final int UNUSED_F8 = 0xf8;
+    public static final int UNUSED_F9 = 0xf9;
+    public static final int UNUSED_FA = 0xfa;
+    public static final int UNUSED_FB = 0xfb;
+    public static final int UNUSED_FC = 0xfc;
+    public static final int UNUSED_FD = 0xfd;
+    public static final int UNUSED_FE = 0xfe;
+    public static final int UNUSED_FF = 0xff;
+    // END(opcodes)
+
+    /**
+     * This class is uninstantiable.
+     */
+    private DalvOps() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/Dop.java b/dx/src/com/android/dx/dex/code/Dop.java
new file mode 100644
index 0000000..2b2a08f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/Dop.java
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+/**
+ * Representation of an opcode.
+ */
+public final class Dop {
+    /** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode value itself */
+    private final int opcode;
+
+    /** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family */
+    private final int family;
+
+    /** {@code non-null;} the instruction format */
+    private final InsnFormat format;
+
+    /** whether this opcode uses a result register */
+    private final boolean hasResult;
+
+    /** {@code non-null;} the name */
+    private final String name;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode
+     * value itself
+     * @param family {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+     * @param format {@code non-null;} the instruction format
+     * @param hasResult whether the opcode has a result register; if so it
+     * is always the first register
+     * @param name {@code non-null;} the name
+     */
+    public Dop(int opcode, int family, InsnFormat format,
+               boolean hasResult, String name) {
+        if ((opcode < DalvOps.MIN_VALUE) || (opcode > DalvOps.MAX_VALUE)) {
+            throw new IllegalArgumentException("bogus opcode");
+        }
+
+        if ((family < DalvOps.MIN_VALUE) || (family > DalvOps.MAX_VALUE)) {
+            throw new IllegalArgumentException("bogus family");
+        }
+
+        if (format == null) {
+            throw new NullPointerException("format == null");
+        }
+
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        this.opcode = opcode;
+        this.family = family;
+        this.format = format;
+        this.hasResult = hasResult;
+        this.name = name;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * Gets the opcode value.
+     *
+     * @return {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode value
+     */
+    public int getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the opcode family. The opcode family is the unmarked (no
+     * "/...") opcode that has equivalent semantics to this one.
+     *
+     * @return {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+     */
+    public int getFamily() {
+        return family;
+    }
+
+    /**
+     * Gets the instruction format.
+     *
+     * @return {@code non-null;} the instruction format
+     */
+    public InsnFormat getFormat() {
+        return format;
+    }
+
+    /**
+     * Returns whether this opcode uses a result register.
+     *
+     * @return {@code true} iff this opcode uses a result register
+     */
+    public boolean hasResult() {
+        return hasResult;
+    }
+
+    /**
+     * Gets the opcode name.
+     *
+     * @return {@code non-null;} the opcode name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the opcode for the opposite test of this instance. This is only
+     * valid for opcodes which are in fact tests.
+     *
+     * @return {@code non-null;} the opposite test
+     */
+    public Dop getOppositeTest() {
+        switch (opcode) {
+            case DalvOps.IF_EQ:  return Dops.IF_NE;
+            case DalvOps.IF_NE:  return Dops.IF_EQ;
+            case DalvOps.IF_LT:  return Dops.IF_GE;
+            case DalvOps.IF_GE:  return Dops.IF_LT;
+            case DalvOps.IF_GT:  return Dops.IF_LE;
+            case DalvOps.IF_LE:  return Dops.IF_GT;
+            case DalvOps.IF_EQZ: return Dops.IF_NEZ;
+            case DalvOps.IF_NEZ: return Dops.IF_EQZ;
+            case DalvOps.IF_LTZ: return Dops.IF_GEZ;
+            case DalvOps.IF_GEZ: return Dops.IF_LTZ;
+            case DalvOps.IF_GTZ: return Dops.IF_LEZ;
+            case DalvOps.IF_LEZ: return Dops.IF_GTZ;
+        }
+
+        throw new IllegalArgumentException("bogus opcode: " + this);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/Dops.java b/dx/src/com/android/dx/dex/code/Dops.java
new file mode 100644
index 0000000..0211a40
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/Dops.java
@@ -0,0 +1,1231 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.dex.code.form.Form10t;
+import com.android.dx.dex.code.form.Form10x;
+import com.android.dx.dex.code.form.Form11n;
+import com.android.dx.dex.code.form.Form11x;
+import com.android.dx.dex.code.form.Form12x;
+import com.android.dx.dex.code.form.Form20t;
+import com.android.dx.dex.code.form.Form21c;
+import com.android.dx.dex.code.form.Form21h;
+import com.android.dx.dex.code.form.Form21s;
+import com.android.dx.dex.code.form.Form21t;
+import com.android.dx.dex.code.form.Form22b;
+import com.android.dx.dex.code.form.Form22c;
+import com.android.dx.dex.code.form.Form22s;
+import com.android.dx.dex.code.form.Form22t;
+import com.android.dx.dex.code.form.Form22x;
+import com.android.dx.dex.code.form.Form23x;
+import com.android.dx.dex.code.form.Form30t;
+import com.android.dx.dex.code.form.Form31c;
+import com.android.dx.dex.code.form.Form31i;
+import com.android.dx.dex.code.form.Form31t;
+import com.android.dx.dex.code.form.Form32x;
+import com.android.dx.dex.code.form.Form35c;
+import com.android.dx.dex.code.form.Form3rc;
+import com.android.dx.dex.code.form.Form51l;
+import com.android.dx.dex.code.form.SpecialFormat;
+
+/**
+ * Standard instances of {@link Dop} and utility methods for getting
+ * them.
+ */
+public final class Dops {
+    /** {@code non-null;} array containing all the standard instances */
+    private static final Dop[] DOPS;
+
+    /**
+     * pseudo-opcode used for nonstandard formatted "instructions"
+     * (which are mostly not actually instructions, though they do
+     * appear in instruction lists)
+     */
+    public static final Dop SPECIAL_FORMAT =
+        new Dop(DalvOps.SPECIAL_FORMAT, DalvOps.SPECIAL_FORMAT,
+                SpecialFormat.THE_ONE, false, "<special>");
+
+    // BEGIN(dops); GENERATED AUTOMATICALLY BY opcode-gen
+    public static final Dop NOP =
+        new Dop(DalvOps.NOP, DalvOps.NOP,
+            Form10x.THE_ONE, false, "nop");
+
+    public static final Dop MOVE =
+        new Dop(DalvOps.MOVE, DalvOps.MOVE,
+            Form12x.THE_ONE, true, "move");
+
+    public static final Dop MOVE_FROM16 =
+        new Dop(DalvOps.MOVE_FROM16, DalvOps.MOVE,
+            Form22x.THE_ONE, true, "move/from16");
+
+    public static final Dop MOVE_16 =
+        new Dop(DalvOps.MOVE_16, DalvOps.MOVE,
+            Form32x.THE_ONE, true, "move/16");
+
+    public static final Dop MOVE_WIDE =
+        new Dop(DalvOps.MOVE_WIDE, DalvOps.MOVE_WIDE,
+            Form12x.THE_ONE, true, "move-wide");
+
+    public static final Dop MOVE_WIDE_FROM16 =
+        new Dop(DalvOps.MOVE_WIDE_FROM16, DalvOps.MOVE_WIDE,
+            Form22x.THE_ONE, true, "move-wide/from16");
+
+    public static final Dop MOVE_WIDE_16 =
+        new Dop(DalvOps.MOVE_WIDE_16, DalvOps.MOVE_WIDE,
+            Form32x.THE_ONE, true, "move-wide/16");
+
+    public static final Dop MOVE_OBJECT =
+        new Dop(DalvOps.MOVE_OBJECT, DalvOps.MOVE_OBJECT,
+            Form12x.THE_ONE, true, "move-object");
+
+    public static final Dop MOVE_OBJECT_FROM16 =
+        new Dop(DalvOps.MOVE_OBJECT_FROM16, DalvOps.MOVE_OBJECT,
+            Form22x.THE_ONE, true, "move-object/from16");
+
+    public static final Dop MOVE_OBJECT_16 =
+        new Dop(DalvOps.MOVE_OBJECT_16, DalvOps.MOVE_OBJECT,
+            Form32x.THE_ONE, true, "move-object/16");
+
+    public static final Dop MOVE_RESULT =
+        new Dop(DalvOps.MOVE_RESULT, DalvOps.MOVE_RESULT,
+            Form11x.THE_ONE, true, "move-result");
+
+    public static final Dop MOVE_RESULT_WIDE =
+        new Dop(DalvOps.MOVE_RESULT_WIDE, DalvOps.MOVE_RESULT_WIDE,
+            Form11x.THE_ONE, true, "move-result-wide");
+
+    public static final Dop MOVE_RESULT_OBJECT =
+        new Dop(DalvOps.MOVE_RESULT_OBJECT, DalvOps.MOVE_RESULT_OBJECT,
+            Form11x.THE_ONE, true, "move-result-object");
+
+    public static final Dop MOVE_EXCEPTION =
+        new Dop(DalvOps.MOVE_EXCEPTION, DalvOps.MOVE_EXCEPTION,
+            Form11x.THE_ONE, true, "move-exception");
+
+    public static final Dop RETURN_VOID =
+        new Dop(DalvOps.RETURN_VOID, DalvOps.RETURN_VOID,
+            Form10x.THE_ONE, false, "return-void");
+
+    public static final Dop RETURN =
+        new Dop(DalvOps.RETURN, DalvOps.RETURN,
+            Form11x.THE_ONE, false, "return");
+
+    public static final Dop RETURN_WIDE =
+        new Dop(DalvOps.RETURN_WIDE, DalvOps.RETURN_WIDE,
+            Form11x.THE_ONE, false, "return-wide");
+
+    public static final Dop RETURN_OBJECT =
+        new Dop(DalvOps.RETURN_OBJECT, DalvOps.RETURN_OBJECT,
+            Form11x.THE_ONE, false, "return-object");
+
+    public static final Dop CONST_4 =
+        new Dop(DalvOps.CONST_4, DalvOps.CONST,
+            Form11n.THE_ONE, true, "const/4");
+
+    public static final Dop CONST_16 =
+        new Dop(DalvOps.CONST_16, DalvOps.CONST,
+            Form21s.THE_ONE, true, "const/16");
+
+    public static final Dop CONST =
+        new Dop(DalvOps.CONST, DalvOps.CONST,
+            Form31i.THE_ONE, true, "const");
+
+    public static final Dop CONST_HIGH16 =
+        new Dop(DalvOps.CONST_HIGH16, DalvOps.CONST,
+            Form21h.THE_ONE, true, "const/high16");
+
+    public static final Dop CONST_WIDE_16 =
+        new Dop(DalvOps.CONST_WIDE_16, DalvOps.CONST_WIDE,
+            Form21s.THE_ONE, true, "const-wide/16");
+
+    public static final Dop CONST_WIDE_32 =
+        new Dop(DalvOps.CONST_WIDE_32, DalvOps.CONST_WIDE,
+            Form31i.THE_ONE, true, "const-wide/32");
+
+    public static final Dop CONST_WIDE =
+        new Dop(DalvOps.CONST_WIDE, DalvOps.CONST_WIDE,
+            Form51l.THE_ONE, true, "const-wide");
+
+    public static final Dop CONST_WIDE_HIGH16 =
+        new Dop(DalvOps.CONST_WIDE_HIGH16, DalvOps.CONST_WIDE,
+            Form21h.THE_ONE, true, "const-wide/high16");
+
+    public static final Dop CONST_STRING =
+        new Dop(DalvOps.CONST_STRING, DalvOps.CONST_STRING,
+            Form21c.THE_ONE, true, "const-string");
+
+    public static final Dop CONST_STRING_JUMBO =
+        new Dop(DalvOps.CONST_STRING_JUMBO, DalvOps.CONST_STRING,
+            Form31c.THE_ONE, true, "const-string/jumbo");
+
+    public static final Dop CONST_CLASS =
+        new Dop(DalvOps.CONST_CLASS, DalvOps.CONST_CLASS,
+            Form21c.THE_ONE, true, "const-class");
+
+    public static final Dop MONITOR_ENTER =
+        new Dop(DalvOps.MONITOR_ENTER, DalvOps.MONITOR_ENTER,
+            Form11x.THE_ONE, false, "monitor-enter");
+
+    public static final Dop MONITOR_EXIT =
+        new Dop(DalvOps.MONITOR_EXIT, DalvOps.MONITOR_EXIT,
+            Form11x.THE_ONE, false, "monitor-exit");
+
+    public static final Dop CHECK_CAST =
+        new Dop(DalvOps.CHECK_CAST, DalvOps.CHECK_CAST,
+            Form21c.THE_ONE, true, "check-cast");
+
+    public static final Dop INSTANCE_OF =
+        new Dop(DalvOps.INSTANCE_OF, DalvOps.INSTANCE_OF,
+            Form22c.THE_ONE, true, "instance-of");
+
+    public static final Dop ARRAY_LENGTH =
+        new Dop(DalvOps.ARRAY_LENGTH, DalvOps.ARRAY_LENGTH,
+            Form12x.THE_ONE, true, "array-length");
+
+    public static final Dop NEW_INSTANCE =
+        new Dop(DalvOps.NEW_INSTANCE, DalvOps.NEW_INSTANCE,
+            Form21c.THE_ONE, true, "new-instance");
+
+    public static final Dop NEW_ARRAY =
+        new Dop(DalvOps.NEW_ARRAY, DalvOps.NEW_ARRAY,
+            Form22c.THE_ONE, true, "new-array");
+
+    public static final Dop FILLED_NEW_ARRAY =
+        new Dop(DalvOps.FILLED_NEW_ARRAY, DalvOps.FILLED_NEW_ARRAY,
+            Form35c.THE_ONE, false, "filled-new-array");
+
+    public static final Dop FILLED_NEW_ARRAY_RANGE =
+        new Dop(DalvOps.FILLED_NEW_ARRAY_RANGE, DalvOps.FILLED_NEW_ARRAY,
+            Form3rc.THE_ONE, false, "filled-new-array/range");
+
+    public static final Dop FILL_ARRAY_DATA =
+        new Dop(DalvOps.FILL_ARRAY_DATA, DalvOps.FILL_ARRAY_DATA,
+            Form31t.THE_ONE, false, "fill-array-data");
+
+    public static final Dop THROW =
+        new Dop(DalvOps.THROW, DalvOps.THROW,
+            Form11x.THE_ONE, false, "throw");
+
+    public static final Dop GOTO =
+        new Dop(DalvOps.GOTO, DalvOps.GOTO,
+            Form10t.THE_ONE, false, "goto");
+
+    public static final Dop GOTO_16 =
+        new Dop(DalvOps.GOTO_16, DalvOps.GOTO,
+            Form20t.THE_ONE, false, "goto/16");
+
+    public static final Dop GOTO_32 =
+        new Dop(DalvOps.GOTO_32, DalvOps.GOTO,
+            Form30t.THE_ONE, false, "goto/32");
+
+    public static final Dop PACKED_SWITCH =
+        new Dop(DalvOps.PACKED_SWITCH, DalvOps.PACKED_SWITCH,
+            Form31t.THE_ONE, false, "packed-switch");
+
+    public static final Dop SPARSE_SWITCH =
+        new Dop(DalvOps.SPARSE_SWITCH, DalvOps.SPARSE_SWITCH,
+            Form31t.THE_ONE, false, "sparse-switch");
+
+    public static final Dop CMPL_FLOAT =
+        new Dop(DalvOps.CMPL_FLOAT, DalvOps.CMPL_FLOAT,
+            Form23x.THE_ONE, true, "cmpl-float");
+
+    public static final Dop CMPG_FLOAT =
+        new Dop(DalvOps.CMPG_FLOAT, DalvOps.CMPG_FLOAT,
+            Form23x.THE_ONE, true, "cmpg-float");
+
+    public static final Dop CMPL_DOUBLE =
+        new Dop(DalvOps.CMPL_DOUBLE, DalvOps.CMPL_DOUBLE,
+            Form23x.THE_ONE, true, "cmpl-double");
+
+    public static final Dop CMPG_DOUBLE =
+        new Dop(DalvOps.CMPG_DOUBLE, DalvOps.CMPG_DOUBLE,
+            Form23x.THE_ONE, true, "cmpg-double");
+
+    public static final Dop CMP_LONG =
+        new Dop(DalvOps.CMP_LONG, DalvOps.CMP_LONG,
+            Form23x.THE_ONE, true, "cmp-long");
+
+    public static final Dop IF_EQ =
+        new Dop(DalvOps.IF_EQ, DalvOps.IF_EQ,
+            Form22t.THE_ONE, false, "if-eq");
+
+    public static final Dop IF_NE =
+        new Dop(DalvOps.IF_NE, DalvOps.IF_NE,
+            Form22t.THE_ONE, false, "if-ne");
+
+    public static final Dop IF_LT =
+        new Dop(DalvOps.IF_LT, DalvOps.IF_LT,
+            Form22t.THE_ONE, false, "if-lt");
+
+    public static final Dop IF_GE =
+        new Dop(DalvOps.IF_GE, DalvOps.IF_GE,
+            Form22t.THE_ONE, false, "if-ge");
+
+    public static final Dop IF_GT =
+        new Dop(DalvOps.IF_GT, DalvOps.IF_GT,
+            Form22t.THE_ONE, false, "if-gt");
+
+    public static final Dop IF_LE =
+        new Dop(DalvOps.IF_LE, DalvOps.IF_LE,
+            Form22t.THE_ONE, false, "if-le");
+
+    public static final Dop IF_EQZ =
+        new Dop(DalvOps.IF_EQZ, DalvOps.IF_EQZ,
+            Form21t.THE_ONE, false, "if-eqz");
+
+    public static final Dop IF_NEZ =
+        new Dop(DalvOps.IF_NEZ, DalvOps.IF_NEZ,
+            Form21t.THE_ONE, false, "if-nez");
+
+    public static final Dop IF_LTZ =
+        new Dop(DalvOps.IF_LTZ, DalvOps.IF_LTZ,
+            Form21t.THE_ONE, false, "if-ltz");
+
+    public static final Dop IF_GEZ =
+        new Dop(DalvOps.IF_GEZ, DalvOps.IF_GEZ,
+            Form21t.THE_ONE, false, "if-gez");
+
+    public static final Dop IF_GTZ =
+        new Dop(DalvOps.IF_GTZ, DalvOps.IF_GTZ,
+            Form21t.THE_ONE, false, "if-gtz");
+
+    public static final Dop IF_LEZ =
+        new Dop(DalvOps.IF_LEZ, DalvOps.IF_LEZ,
+            Form21t.THE_ONE, false, "if-lez");
+
+    public static final Dop AGET =
+        new Dop(DalvOps.AGET, DalvOps.AGET,
+            Form23x.THE_ONE, true, "aget");
+
+    public static final Dop AGET_WIDE =
+        new Dop(DalvOps.AGET_WIDE, DalvOps.AGET_WIDE,
+            Form23x.THE_ONE, true, "aget-wide");
+
+    public static final Dop AGET_OBJECT =
+        new Dop(DalvOps.AGET_OBJECT, DalvOps.AGET_OBJECT,
+            Form23x.THE_ONE, true, "aget-object");
+
+    public static final Dop AGET_BOOLEAN =
+        new Dop(DalvOps.AGET_BOOLEAN, DalvOps.AGET_BOOLEAN,
+            Form23x.THE_ONE, true, "aget-boolean");
+
+    public static final Dop AGET_BYTE =
+        new Dop(DalvOps.AGET_BYTE, DalvOps.AGET_BYTE,
+            Form23x.THE_ONE, true, "aget-byte");
+
+    public static final Dop AGET_CHAR =
+        new Dop(DalvOps.AGET_CHAR, DalvOps.AGET_CHAR,
+            Form23x.THE_ONE, true, "aget-char");
+
+    public static final Dop AGET_SHORT =
+        new Dop(DalvOps.AGET_SHORT, DalvOps.AGET_SHORT,
+            Form23x.THE_ONE, true, "aget-short");
+
+    public static final Dop APUT =
+        new Dop(DalvOps.APUT, DalvOps.APUT,
+            Form23x.THE_ONE, false, "aput");
+
+    public static final Dop APUT_WIDE =
+        new Dop(DalvOps.APUT_WIDE, DalvOps.APUT_WIDE,
+            Form23x.THE_ONE, false, "aput-wide");
+
+    public static final Dop APUT_OBJECT =
+        new Dop(DalvOps.APUT_OBJECT, DalvOps.APUT_OBJECT,
+            Form23x.THE_ONE, false, "aput-object");
+
+    public static final Dop APUT_BOOLEAN =
+        new Dop(DalvOps.APUT_BOOLEAN, DalvOps.APUT_BOOLEAN,
+            Form23x.THE_ONE, false, "aput-boolean");
+
+    public static final Dop APUT_BYTE =
+        new Dop(DalvOps.APUT_BYTE, DalvOps.APUT_BYTE,
+            Form23x.THE_ONE, false, "aput-byte");
+
+    public static final Dop APUT_CHAR =
+        new Dop(DalvOps.APUT_CHAR, DalvOps.APUT_CHAR,
+            Form23x.THE_ONE, false, "aput-char");
+
+    public static final Dop APUT_SHORT =
+        new Dop(DalvOps.APUT_SHORT, DalvOps.APUT_SHORT,
+            Form23x.THE_ONE, false, "aput-short");
+
+    public static final Dop IGET =
+        new Dop(DalvOps.IGET, DalvOps.IGET,
+            Form22c.THE_ONE, true, "iget");
+
+    public static final Dop IGET_WIDE =
+        new Dop(DalvOps.IGET_WIDE, DalvOps.IGET_WIDE,
+            Form22c.THE_ONE, true, "iget-wide");
+
+    public static final Dop IGET_OBJECT =
+        new Dop(DalvOps.IGET_OBJECT, DalvOps.IGET_OBJECT,
+            Form22c.THE_ONE, true, "iget-object");
+
+    public static final Dop IGET_BOOLEAN =
+        new Dop(DalvOps.IGET_BOOLEAN, DalvOps.IGET_BOOLEAN,
+            Form22c.THE_ONE, true, "iget-boolean");
+
+    public static final Dop IGET_BYTE =
+        new Dop(DalvOps.IGET_BYTE, DalvOps.IGET_BYTE,
+            Form22c.THE_ONE, true, "iget-byte");
+
+    public static final Dop IGET_CHAR =
+        new Dop(DalvOps.IGET_CHAR, DalvOps.IGET_CHAR,
+            Form22c.THE_ONE, true, "iget-char");
+
+    public static final Dop IGET_SHORT =
+        new Dop(DalvOps.IGET_SHORT, DalvOps.IGET_SHORT,
+            Form22c.THE_ONE, true, "iget-short");
+
+    public static final Dop IPUT =
+        new Dop(DalvOps.IPUT, DalvOps.IPUT,
+            Form22c.THE_ONE, false, "iput");
+
+    public static final Dop IPUT_WIDE =
+        new Dop(DalvOps.IPUT_WIDE, DalvOps.IPUT_WIDE,
+            Form22c.THE_ONE, false, "iput-wide");
+
+    public static final Dop IPUT_OBJECT =
+        new Dop(DalvOps.IPUT_OBJECT, DalvOps.IPUT_OBJECT,
+            Form22c.THE_ONE, false, "iput-object");
+
+    public static final Dop IPUT_BOOLEAN =
+        new Dop(DalvOps.IPUT_BOOLEAN, DalvOps.IPUT_BOOLEAN,
+            Form22c.THE_ONE, false, "iput-boolean");
+
+    public static final Dop IPUT_BYTE =
+        new Dop(DalvOps.IPUT_BYTE, DalvOps.IPUT_BYTE,
+            Form22c.THE_ONE, false, "iput-byte");
+
+    public static final Dop IPUT_CHAR =
+        new Dop(DalvOps.IPUT_CHAR, DalvOps.IPUT_CHAR,
+            Form22c.THE_ONE, false, "iput-char");
+
+    public static final Dop IPUT_SHORT =
+        new Dop(DalvOps.IPUT_SHORT, DalvOps.IPUT_SHORT,
+            Form22c.THE_ONE, false, "iput-short");
+
+    public static final Dop SGET =
+        new Dop(DalvOps.SGET, DalvOps.SGET,
+            Form21c.THE_ONE, true, "sget");
+
+    public static final Dop SGET_WIDE =
+        new Dop(DalvOps.SGET_WIDE, DalvOps.SGET_WIDE,
+            Form21c.THE_ONE, true, "sget-wide");
+
+    public static final Dop SGET_OBJECT =
+        new Dop(DalvOps.SGET_OBJECT, DalvOps.SGET_OBJECT,
+            Form21c.THE_ONE, true, "sget-object");
+
+    public static final Dop SGET_BOOLEAN =
+        new Dop(DalvOps.SGET_BOOLEAN, DalvOps.SGET_BOOLEAN,
+            Form21c.THE_ONE, true, "sget-boolean");
+
+    public static final Dop SGET_BYTE =
+        new Dop(DalvOps.SGET_BYTE, DalvOps.SGET_BYTE,
+            Form21c.THE_ONE, true, "sget-byte");
+
+    public static final Dop SGET_CHAR =
+        new Dop(DalvOps.SGET_CHAR, DalvOps.SGET_CHAR,
+            Form21c.THE_ONE, true, "sget-char");
+
+    public static final Dop SGET_SHORT =
+        new Dop(DalvOps.SGET_SHORT, DalvOps.SGET_SHORT,
+            Form21c.THE_ONE, true, "sget-short");
+
+    public static final Dop SPUT =
+        new Dop(DalvOps.SPUT, DalvOps.SPUT,
+            Form21c.THE_ONE, false, "sput");
+
+    public static final Dop SPUT_WIDE =
+        new Dop(DalvOps.SPUT_WIDE, DalvOps.SPUT_WIDE,
+            Form21c.THE_ONE, false, "sput-wide");
+
+    public static final Dop SPUT_OBJECT =
+        new Dop(DalvOps.SPUT_OBJECT, DalvOps.SPUT_OBJECT,
+            Form21c.THE_ONE, false, "sput-object");
+
+    public static final Dop SPUT_BOOLEAN =
+        new Dop(DalvOps.SPUT_BOOLEAN, DalvOps.SPUT_BOOLEAN,
+            Form21c.THE_ONE, false, "sput-boolean");
+
+    public static final Dop SPUT_BYTE =
+        new Dop(DalvOps.SPUT_BYTE, DalvOps.SPUT_BYTE,
+            Form21c.THE_ONE, false, "sput-byte");
+
+    public static final Dop SPUT_CHAR =
+        new Dop(DalvOps.SPUT_CHAR, DalvOps.SPUT_CHAR,
+            Form21c.THE_ONE, false, "sput-char");
+
+    public static final Dop SPUT_SHORT =
+        new Dop(DalvOps.SPUT_SHORT, DalvOps.SPUT_SHORT,
+            Form21c.THE_ONE, false, "sput-short");
+
+    public static final Dop INVOKE_VIRTUAL =
+        new Dop(DalvOps.INVOKE_VIRTUAL, DalvOps.INVOKE_VIRTUAL,
+            Form35c.THE_ONE, false, "invoke-virtual");
+
+    public static final Dop INVOKE_SUPER =
+        new Dop(DalvOps.INVOKE_SUPER, DalvOps.INVOKE_SUPER,
+            Form35c.THE_ONE, false, "invoke-super");
+
+    public static final Dop INVOKE_DIRECT =
+        new Dop(DalvOps.INVOKE_DIRECT, DalvOps.INVOKE_DIRECT,
+            Form35c.THE_ONE, false, "invoke-direct");
+
+    public static final Dop INVOKE_STATIC =
+        new Dop(DalvOps.INVOKE_STATIC, DalvOps.INVOKE_STATIC,
+            Form35c.THE_ONE, false, "invoke-static");
+
+    public static final Dop INVOKE_INTERFACE =
+        new Dop(DalvOps.INVOKE_INTERFACE, DalvOps.INVOKE_INTERFACE,
+            Form35c.THE_ONE, false, "invoke-interface");
+
+    public static final Dop INVOKE_VIRTUAL_RANGE =
+        new Dop(DalvOps.INVOKE_VIRTUAL_RANGE, DalvOps.INVOKE_VIRTUAL,
+            Form3rc.THE_ONE, false, "invoke-virtual/range");
+
+    public static final Dop INVOKE_SUPER_RANGE =
+        new Dop(DalvOps.INVOKE_SUPER_RANGE, DalvOps.INVOKE_SUPER,
+            Form3rc.THE_ONE, false, "invoke-super/range");
+
+    public static final Dop INVOKE_DIRECT_RANGE =
+        new Dop(DalvOps.INVOKE_DIRECT_RANGE, DalvOps.INVOKE_DIRECT,
+            Form3rc.THE_ONE, false, "invoke-direct/range");
+
+    public static final Dop INVOKE_STATIC_RANGE =
+        new Dop(DalvOps.INVOKE_STATIC_RANGE, DalvOps.INVOKE_STATIC,
+            Form3rc.THE_ONE, false, "invoke-static/range");
+
+    public static final Dop INVOKE_INTERFACE_RANGE =
+        new Dop(DalvOps.INVOKE_INTERFACE_RANGE, DalvOps.INVOKE_INTERFACE,
+            Form3rc.THE_ONE, false, "invoke-interface/range");
+
+    public static final Dop NEG_INT =
+        new Dop(DalvOps.NEG_INT, DalvOps.NEG_INT,
+            Form12x.THE_ONE, true, "neg-int");
+
+    public static final Dop NOT_INT =
+        new Dop(DalvOps.NOT_INT, DalvOps.NOT_INT,
+            Form12x.THE_ONE, true, "not-int");
+
+    public static final Dop NEG_LONG =
+        new Dop(DalvOps.NEG_LONG, DalvOps.NEG_LONG,
+            Form12x.THE_ONE, true, "neg-long");
+
+    public static final Dop NOT_LONG =
+        new Dop(DalvOps.NOT_LONG, DalvOps.NOT_LONG,
+            Form12x.THE_ONE, true, "not-long");
+
+    public static final Dop NEG_FLOAT =
+        new Dop(DalvOps.NEG_FLOAT, DalvOps.NEG_FLOAT,
+            Form12x.THE_ONE, true, "neg-float");
+
+    public static final Dop NEG_DOUBLE =
+        new Dop(DalvOps.NEG_DOUBLE, DalvOps.NEG_DOUBLE,
+            Form12x.THE_ONE, true, "neg-double");
+
+    public static final Dop INT_TO_LONG =
+        new Dop(DalvOps.INT_TO_LONG, DalvOps.INT_TO_LONG,
+            Form12x.THE_ONE, true, "int-to-long");
+
+    public static final Dop INT_TO_FLOAT =
+        new Dop(DalvOps.INT_TO_FLOAT, DalvOps.INT_TO_FLOAT,
+            Form12x.THE_ONE, true, "int-to-float");
+
+    public static final Dop INT_TO_DOUBLE =
+        new Dop(DalvOps.INT_TO_DOUBLE, DalvOps.INT_TO_DOUBLE,
+            Form12x.THE_ONE, true, "int-to-double");
+
+    public static final Dop LONG_TO_INT =
+        new Dop(DalvOps.LONG_TO_INT, DalvOps.LONG_TO_INT,
+            Form12x.THE_ONE, true, "long-to-int");
+
+    public static final Dop LONG_TO_FLOAT =
+        new Dop(DalvOps.LONG_TO_FLOAT, DalvOps.LONG_TO_FLOAT,
+            Form12x.THE_ONE, true, "long-to-float");
+
+    public static final Dop LONG_TO_DOUBLE =
+        new Dop(DalvOps.LONG_TO_DOUBLE, DalvOps.LONG_TO_DOUBLE,
+            Form12x.THE_ONE, true, "long-to-double");
+
+    public static final Dop FLOAT_TO_INT =
+        new Dop(DalvOps.FLOAT_TO_INT, DalvOps.FLOAT_TO_INT,
+            Form12x.THE_ONE, true, "float-to-int");
+
+    public static final Dop FLOAT_TO_LONG =
+        new Dop(DalvOps.FLOAT_TO_LONG, DalvOps.FLOAT_TO_LONG,
+            Form12x.THE_ONE, true, "float-to-long");
+
+    public static final Dop FLOAT_TO_DOUBLE =
+        new Dop(DalvOps.FLOAT_TO_DOUBLE, DalvOps.FLOAT_TO_DOUBLE,
+            Form12x.THE_ONE, true, "float-to-double");
+
+    public static final Dop DOUBLE_TO_INT =
+        new Dop(DalvOps.DOUBLE_TO_INT, DalvOps.DOUBLE_TO_INT,
+            Form12x.THE_ONE, true, "double-to-int");
+
+    public static final Dop DOUBLE_TO_LONG =
+        new Dop(DalvOps.DOUBLE_TO_LONG, DalvOps.DOUBLE_TO_LONG,
+            Form12x.THE_ONE, true, "double-to-long");
+
+    public static final Dop DOUBLE_TO_FLOAT =
+        new Dop(DalvOps.DOUBLE_TO_FLOAT, DalvOps.DOUBLE_TO_FLOAT,
+            Form12x.THE_ONE, true, "double-to-float");
+
+    public static final Dop INT_TO_BYTE =
+        new Dop(DalvOps.INT_TO_BYTE, DalvOps.INT_TO_BYTE,
+            Form12x.THE_ONE, true, "int-to-byte");
+
+    public static final Dop INT_TO_CHAR =
+        new Dop(DalvOps.INT_TO_CHAR, DalvOps.INT_TO_CHAR,
+            Form12x.THE_ONE, true, "int-to-char");
+
+    public static final Dop INT_TO_SHORT =
+        new Dop(DalvOps.INT_TO_SHORT, DalvOps.INT_TO_SHORT,
+            Form12x.THE_ONE, true, "int-to-short");
+
+    public static final Dop ADD_INT =
+        new Dop(DalvOps.ADD_INT, DalvOps.ADD_INT,
+            Form23x.THE_ONE, true, "add-int");
+
+    public static final Dop SUB_INT =
+        new Dop(DalvOps.SUB_INT, DalvOps.SUB_INT,
+            Form23x.THE_ONE, true, "sub-int");
+
+    public static final Dop MUL_INT =
+        new Dop(DalvOps.MUL_INT, DalvOps.MUL_INT,
+            Form23x.THE_ONE, true, "mul-int");
+
+    public static final Dop DIV_INT =
+        new Dop(DalvOps.DIV_INT, DalvOps.DIV_INT,
+            Form23x.THE_ONE, true, "div-int");
+
+    public static final Dop REM_INT =
+        new Dop(DalvOps.REM_INT, DalvOps.REM_INT,
+            Form23x.THE_ONE, true, "rem-int");
+
+    public static final Dop AND_INT =
+        new Dop(DalvOps.AND_INT, DalvOps.AND_INT,
+            Form23x.THE_ONE, true, "and-int");
+
+    public static final Dop OR_INT =
+        new Dop(DalvOps.OR_INT, DalvOps.OR_INT,
+            Form23x.THE_ONE, true, "or-int");
+
+    public static final Dop XOR_INT =
+        new Dop(DalvOps.XOR_INT, DalvOps.XOR_INT,
+            Form23x.THE_ONE, true, "xor-int");
+
+    public static final Dop SHL_INT =
+        new Dop(DalvOps.SHL_INT, DalvOps.SHL_INT,
+            Form23x.THE_ONE, true, "shl-int");
+
+    public static final Dop SHR_INT =
+        new Dop(DalvOps.SHR_INT, DalvOps.SHR_INT,
+            Form23x.THE_ONE, true, "shr-int");
+
+    public static final Dop USHR_INT =
+        new Dop(DalvOps.USHR_INT, DalvOps.USHR_INT,
+            Form23x.THE_ONE, true, "ushr-int");
+
+    public static final Dop ADD_LONG =
+        new Dop(DalvOps.ADD_LONG, DalvOps.ADD_LONG,
+            Form23x.THE_ONE, true, "add-long");
+
+    public static final Dop SUB_LONG =
+        new Dop(DalvOps.SUB_LONG, DalvOps.SUB_LONG,
+            Form23x.THE_ONE, true, "sub-long");
+
+    public static final Dop MUL_LONG =
+        new Dop(DalvOps.MUL_LONG, DalvOps.MUL_LONG,
+            Form23x.THE_ONE, true, "mul-long");
+
+    public static final Dop DIV_LONG =
+        new Dop(DalvOps.DIV_LONG, DalvOps.DIV_LONG,
+            Form23x.THE_ONE, true, "div-long");
+
+    public static final Dop REM_LONG =
+        new Dop(DalvOps.REM_LONG, DalvOps.REM_LONG,
+            Form23x.THE_ONE, true, "rem-long");
+
+    public static final Dop AND_LONG =
+        new Dop(DalvOps.AND_LONG, DalvOps.AND_LONG,
+            Form23x.THE_ONE, true, "and-long");
+
+    public static final Dop OR_LONG =
+        new Dop(DalvOps.OR_LONG, DalvOps.OR_LONG,
+            Form23x.THE_ONE, true, "or-long");
+
+    public static final Dop XOR_LONG =
+        new Dop(DalvOps.XOR_LONG, DalvOps.XOR_LONG,
+            Form23x.THE_ONE, true, "xor-long");
+
+    public static final Dop SHL_LONG =
+        new Dop(DalvOps.SHL_LONG, DalvOps.SHL_LONG,
+            Form23x.THE_ONE, true, "shl-long");
+
+    public static final Dop SHR_LONG =
+        new Dop(DalvOps.SHR_LONG, DalvOps.SHR_LONG,
+            Form23x.THE_ONE, true, "shr-long");
+
+    public static final Dop USHR_LONG =
+        new Dop(DalvOps.USHR_LONG, DalvOps.USHR_LONG,
+            Form23x.THE_ONE, true, "ushr-long");
+
+    public static final Dop ADD_FLOAT =
+        new Dop(DalvOps.ADD_FLOAT, DalvOps.ADD_FLOAT,
+            Form23x.THE_ONE, true, "add-float");
+
+    public static final Dop SUB_FLOAT =
+        new Dop(DalvOps.SUB_FLOAT, DalvOps.SUB_FLOAT,
+            Form23x.THE_ONE, true, "sub-float");
+
+    public static final Dop MUL_FLOAT =
+        new Dop(DalvOps.MUL_FLOAT, DalvOps.MUL_FLOAT,
+            Form23x.THE_ONE, true, "mul-float");
+
+    public static final Dop DIV_FLOAT =
+        new Dop(DalvOps.DIV_FLOAT, DalvOps.DIV_FLOAT,
+            Form23x.THE_ONE, true, "div-float");
+
+    public static final Dop REM_FLOAT =
+        new Dop(DalvOps.REM_FLOAT, DalvOps.REM_FLOAT,
+            Form23x.THE_ONE, true, "rem-float");
+
+    public static final Dop ADD_DOUBLE =
+        new Dop(DalvOps.ADD_DOUBLE, DalvOps.ADD_DOUBLE,
+            Form23x.THE_ONE, true, "add-double");
+
+    public static final Dop SUB_DOUBLE =
+        new Dop(DalvOps.SUB_DOUBLE, DalvOps.SUB_DOUBLE,
+            Form23x.THE_ONE, true, "sub-double");
+
+    public static final Dop MUL_DOUBLE =
+        new Dop(DalvOps.MUL_DOUBLE, DalvOps.MUL_DOUBLE,
+            Form23x.THE_ONE, true, "mul-double");
+
+    public static final Dop DIV_DOUBLE =
+        new Dop(DalvOps.DIV_DOUBLE, DalvOps.DIV_DOUBLE,
+            Form23x.THE_ONE, true, "div-double");
+
+    public static final Dop REM_DOUBLE =
+        new Dop(DalvOps.REM_DOUBLE, DalvOps.REM_DOUBLE,
+            Form23x.THE_ONE, true, "rem-double");
+
+    public static final Dop ADD_INT_2ADDR =
+        new Dop(DalvOps.ADD_INT_2ADDR, DalvOps.ADD_INT,
+            Form12x.THE_ONE, true, "add-int/2addr");
+
+    public static final Dop SUB_INT_2ADDR =
+        new Dop(DalvOps.SUB_INT_2ADDR, DalvOps.SUB_INT,
+            Form12x.THE_ONE, true, "sub-int/2addr");
+
+    public static final Dop MUL_INT_2ADDR =
+        new Dop(DalvOps.MUL_INT_2ADDR, DalvOps.MUL_INT,
+            Form12x.THE_ONE, true, "mul-int/2addr");
+
+    public static final Dop DIV_INT_2ADDR =
+        new Dop(DalvOps.DIV_INT_2ADDR, DalvOps.DIV_INT,
+            Form12x.THE_ONE, true, "div-int/2addr");
+
+    public static final Dop REM_INT_2ADDR =
+        new Dop(DalvOps.REM_INT_2ADDR, DalvOps.REM_INT,
+            Form12x.THE_ONE, true, "rem-int/2addr");
+
+    public static final Dop AND_INT_2ADDR =
+        new Dop(DalvOps.AND_INT_2ADDR, DalvOps.AND_INT,
+            Form12x.THE_ONE, true, "and-int/2addr");
+
+    public static final Dop OR_INT_2ADDR =
+        new Dop(DalvOps.OR_INT_2ADDR, DalvOps.OR_INT,
+            Form12x.THE_ONE, true, "or-int/2addr");
+
+    public static final Dop XOR_INT_2ADDR =
+        new Dop(DalvOps.XOR_INT_2ADDR, DalvOps.XOR_INT,
+            Form12x.THE_ONE, true, "xor-int/2addr");
+
+    public static final Dop SHL_INT_2ADDR =
+        new Dop(DalvOps.SHL_INT_2ADDR, DalvOps.SHL_INT,
+            Form12x.THE_ONE, true, "shl-int/2addr");
+
+    public static final Dop SHR_INT_2ADDR =
+        new Dop(DalvOps.SHR_INT_2ADDR, DalvOps.SHR_INT,
+            Form12x.THE_ONE, true, "shr-int/2addr");
+
+    public static final Dop USHR_INT_2ADDR =
+        new Dop(DalvOps.USHR_INT_2ADDR, DalvOps.USHR_INT,
+            Form12x.THE_ONE, true, "ushr-int/2addr");
+
+    public static final Dop ADD_LONG_2ADDR =
+        new Dop(DalvOps.ADD_LONG_2ADDR, DalvOps.ADD_LONG,
+            Form12x.THE_ONE, true, "add-long/2addr");
+
+    public static final Dop SUB_LONG_2ADDR =
+        new Dop(DalvOps.SUB_LONG_2ADDR, DalvOps.SUB_LONG,
+            Form12x.THE_ONE, true, "sub-long/2addr");
+
+    public static final Dop MUL_LONG_2ADDR =
+        new Dop(DalvOps.MUL_LONG_2ADDR, DalvOps.MUL_LONG,
+            Form12x.THE_ONE, true, "mul-long/2addr");
+
+    public static final Dop DIV_LONG_2ADDR =
+        new Dop(DalvOps.DIV_LONG_2ADDR, DalvOps.DIV_LONG,
+            Form12x.THE_ONE, true, "div-long/2addr");
+
+    public static final Dop REM_LONG_2ADDR =
+        new Dop(DalvOps.REM_LONG_2ADDR, DalvOps.REM_LONG,
+            Form12x.THE_ONE, true, "rem-long/2addr");
+
+    public static final Dop AND_LONG_2ADDR =
+        new Dop(DalvOps.AND_LONG_2ADDR, DalvOps.AND_LONG,
+            Form12x.THE_ONE, true, "and-long/2addr");
+
+    public static final Dop OR_LONG_2ADDR =
+        new Dop(DalvOps.OR_LONG_2ADDR, DalvOps.OR_LONG,
+            Form12x.THE_ONE, true, "or-long/2addr");
+
+    public static final Dop XOR_LONG_2ADDR =
+        new Dop(DalvOps.XOR_LONG_2ADDR, DalvOps.XOR_LONG,
+            Form12x.THE_ONE, true, "xor-long/2addr");
+
+    public static final Dop SHL_LONG_2ADDR =
+        new Dop(DalvOps.SHL_LONG_2ADDR, DalvOps.SHL_LONG,
+            Form12x.THE_ONE, true, "shl-long/2addr");
+
+    public static final Dop SHR_LONG_2ADDR =
+        new Dop(DalvOps.SHR_LONG_2ADDR, DalvOps.SHR_LONG,
+            Form12x.THE_ONE, true, "shr-long/2addr");
+
+    public static final Dop USHR_LONG_2ADDR =
+        new Dop(DalvOps.USHR_LONG_2ADDR, DalvOps.USHR_LONG,
+            Form12x.THE_ONE, true, "ushr-long/2addr");
+
+    public static final Dop ADD_FLOAT_2ADDR =
+        new Dop(DalvOps.ADD_FLOAT_2ADDR, DalvOps.ADD_FLOAT,
+            Form12x.THE_ONE, true, "add-float/2addr");
+
+    public static final Dop SUB_FLOAT_2ADDR =
+        new Dop(DalvOps.SUB_FLOAT_2ADDR, DalvOps.SUB_FLOAT,
+            Form12x.THE_ONE, true, "sub-float/2addr");
+
+    public static final Dop MUL_FLOAT_2ADDR =
+        new Dop(DalvOps.MUL_FLOAT_2ADDR, DalvOps.MUL_FLOAT,
+            Form12x.THE_ONE, true, "mul-float/2addr");
+
+    public static final Dop DIV_FLOAT_2ADDR =
+        new Dop(DalvOps.DIV_FLOAT_2ADDR, DalvOps.DIV_FLOAT,
+            Form12x.THE_ONE, true, "div-float/2addr");
+
+    public static final Dop REM_FLOAT_2ADDR =
+        new Dop(DalvOps.REM_FLOAT_2ADDR, DalvOps.REM_FLOAT,
+            Form12x.THE_ONE, true, "rem-float/2addr");
+
+    public static final Dop ADD_DOUBLE_2ADDR =
+        new Dop(DalvOps.ADD_DOUBLE_2ADDR, DalvOps.ADD_DOUBLE,
+            Form12x.THE_ONE, true, "add-double/2addr");
+
+    public static final Dop SUB_DOUBLE_2ADDR =
+        new Dop(DalvOps.SUB_DOUBLE_2ADDR, DalvOps.SUB_DOUBLE,
+            Form12x.THE_ONE, true, "sub-double/2addr");
+
+    public static final Dop MUL_DOUBLE_2ADDR =
+        new Dop(DalvOps.MUL_DOUBLE_2ADDR, DalvOps.MUL_DOUBLE,
+            Form12x.THE_ONE, true, "mul-double/2addr");
+
+    public static final Dop DIV_DOUBLE_2ADDR =
+        new Dop(DalvOps.DIV_DOUBLE_2ADDR, DalvOps.DIV_DOUBLE,
+            Form12x.THE_ONE, true, "div-double/2addr");
+
+    public static final Dop REM_DOUBLE_2ADDR =
+        new Dop(DalvOps.REM_DOUBLE_2ADDR, DalvOps.REM_DOUBLE,
+            Form12x.THE_ONE, true, "rem-double/2addr");
+
+    public static final Dop ADD_INT_LIT16 =
+        new Dop(DalvOps.ADD_INT_LIT16, DalvOps.ADD_INT,
+            Form22s.THE_ONE, true, "add-int/lit16");
+
+    public static final Dop RSUB_INT =
+        new Dop(DalvOps.RSUB_INT, DalvOps.RSUB_INT,
+            Form22s.THE_ONE, true, "rsub-int");
+
+    public static final Dop MUL_INT_LIT16 =
+        new Dop(DalvOps.MUL_INT_LIT16, DalvOps.MUL_INT,
+            Form22s.THE_ONE, true, "mul-int/lit16");
+
+    public static final Dop DIV_INT_LIT16 =
+        new Dop(DalvOps.DIV_INT_LIT16, DalvOps.DIV_INT,
+            Form22s.THE_ONE, true, "div-int/lit16");
+
+    public static final Dop REM_INT_LIT16 =
+        new Dop(DalvOps.REM_INT_LIT16, DalvOps.REM_INT,
+            Form22s.THE_ONE, true, "rem-int/lit16");
+
+    public static final Dop AND_INT_LIT16 =
+        new Dop(DalvOps.AND_INT_LIT16, DalvOps.AND_INT,
+            Form22s.THE_ONE, true, "and-int/lit16");
+
+    public static final Dop OR_INT_LIT16 =
+        new Dop(DalvOps.OR_INT_LIT16, DalvOps.OR_INT,
+            Form22s.THE_ONE, true, "or-int/lit16");
+
+    public static final Dop XOR_INT_LIT16 =
+        new Dop(DalvOps.XOR_INT_LIT16, DalvOps.XOR_INT,
+            Form22s.THE_ONE, true, "xor-int/lit16");
+
+    public static final Dop ADD_INT_LIT8 =
+        new Dop(DalvOps.ADD_INT_LIT8, DalvOps.ADD_INT,
+            Form22b.THE_ONE, true, "add-int/lit8");
+
+    public static final Dop RSUB_INT_LIT8 =
+        new Dop(DalvOps.RSUB_INT_LIT8, DalvOps.RSUB_INT,
+            Form22b.THE_ONE, true, "rsub-int/lit8");
+
+    public static final Dop MUL_INT_LIT8 =
+        new Dop(DalvOps.MUL_INT_LIT8, DalvOps.MUL_INT,
+            Form22b.THE_ONE, true, "mul-int/lit8");
+
+    public static final Dop DIV_INT_LIT8 =
+        new Dop(DalvOps.DIV_INT_LIT8, DalvOps.DIV_INT,
+            Form22b.THE_ONE, true, "div-int/lit8");
+
+    public static final Dop REM_INT_LIT8 =
+        new Dop(DalvOps.REM_INT_LIT8, DalvOps.REM_INT,
+            Form22b.THE_ONE, true, "rem-int/lit8");
+
+    public static final Dop AND_INT_LIT8 =
+        new Dop(DalvOps.AND_INT_LIT8, DalvOps.AND_INT,
+            Form22b.THE_ONE, true, "and-int/lit8");
+
+    public static final Dop OR_INT_LIT8 =
+        new Dop(DalvOps.OR_INT_LIT8, DalvOps.OR_INT,
+            Form22b.THE_ONE, true, "or-int/lit8");
+
+    public static final Dop XOR_INT_LIT8 =
+        new Dop(DalvOps.XOR_INT_LIT8, DalvOps.XOR_INT,
+            Form22b.THE_ONE, true, "xor-int/lit8");
+
+    public static final Dop SHL_INT_LIT8 =
+        new Dop(DalvOps.SHL_INT_LIT8, DalvOps.SHL_INT,
+            Form22b.THE_ONE, true, "shl-int/lit8");
+
+    public static final Dop SHR_INT_LIT8 =
+        new Dop(DalvOps.SHR_INT_LIT8, DalvOps.SHR_INT,
+            Form22b.THE_ONE, true, "shr-int/lit8");
+
+    public static final Dop USHR_INT_LIT8 =
+        new Dop(DalvOps.USHR_INT_LIT8, DalvOps.USHR_INT,
+            Form22b.THE_ONE, true, "ushr-int/lit8");
+
+    // END(dops)
+
+    // Static initialization.
+    static {
+        DOPS = new Dop[DalvOps.MAX_VALUE - DalvOps.MIN_VALUE + 1];
+
+        set(SPECIAL_FORMAT);
+
+        // BEGIN(dops-init); GENERATED AUTOMATICALLY BY opcode-gen
+        set(NOP);
+        set(MOVE);
+        set(MOVE_FROM16);
+        set(MOVE_16);
+        set(MOVE_WIDE);
+        set(MOVE_WIDE_FROM16);
+        set(MOVE_WIDE_16);
+        set(MOVE_OBJECT);
+        set(MOVE_OBJECT_FROM16);
+        set(MOVE_OBJECT_16);
+        set(MOVE_RESULT);
+        set(MOVE_RESULT_WIDE);
+        set(MOVE_RESULT_OBJECT);
+        set(MOVE_EXCEPTION);
+        set(RETURN_VOID);
+        set(RETURN);
+        set(RETURN_WIDE);
+        set(RETURN_OBJECT);
+        set(CONST_4);
+        set(CONST_16);
+        set(CONST);
+        set(CONST_HIGH16);
+        set(CONST_WIDE_16);
+        set(CONST_WIDE_32);
+        set(CONST_WIDE);
+        set(CONST_WIDE_HIGH16);
+        set(CONST_STRING);
+        set(CONST_STRING_JUMBO);
+        set(CONST_CLASS);
+        set(MONITOR_ENTER);
+        set(MONITOR_EXIT);
+        set(CHECK_CAST);
+        set(INSTANCE_OF);
+        set(ARRAY_LENGTH);
+        set(NEW_INSTANCE);
+        set(NEW_ARRAY);
+        set(FILLED_NEW_ARRAY);
+        set(FILLED_NEW_ARRAY_RANGE);
+        set(FILL_ARRAY_DATA);
+        set(THROW);
+        set(GOTO);
+        set(GOTO_16);
+        set(GOTO_32);
+        set(PACKED_SWITCH);
+        set(SPARSE_SWITCH);
+        set(CMPL_FLOAT);
+        set(CMPG_FLOAT);
+        set(CMPL_DOUBLE);
+        set(CMPG_DOUBLE);
+        set(CMP_LONG);
+        set(IF_EQ);
+        set(IF_NE);
+        set(IF_LT);
+        set(IF_GE);
+        set(IF_GT);
+        set(IF_LE);
+        set(IF_EQZ);
+        set(IF_NEZ);
+        set(IF_LTZ);
+        set(IF_GEZ);
+        set(IF_GTZ);
+        set(IF_LEZ);
+        set(AGET);
+        set(AGET_WIDE);
+        set(AGET_OBJECT);
+        set(AGET_BOOLEAN);
+        set(AGET_BYTE);
+        set(AGET_CHAR);
+        set(AGET_SHORT);
+        set(APUT);
+        set(APUT_WIDE);
+        set(APUT_OBJECT);
+        set(APUT_BOOLEAN);
+        set(APUT_BYTE);
+        set(APUT_CHAR);
+        set(APUT_SHORT);
+        set(IGET);
+        set(IGET_WIDE);
+        set(IGET_OBJECT);
+        set(IGET_BOOLEAN);
+        set(IGET_BYTE);
+        set(IGET_CHAR);
+        set(IGET_SHORT);
+        set(IPUT);
+        set(IPUT_WIDE);
+        set(IPUT_OBJECT);
+        set(IPUT_BOOLEAN);
+        set(IPUT_BYTE);
+        set(IPUT_CHAR);
+        set(IPUT_SHORT);
+        set(SGET);
+        set(SGET_WIDE);
+        set(SGET_OBJECT);
+        set(SGET_BOOLEAN);
+        set(SGET_BYTE);
+        set(SGET_CHAR);
+        set(SGET_SHORT);
+        set(SPUT);
+        set(SPUT_WIDE);
+        set(SPUT_OBJECT);
+        set(SPUT_BOOLEAN);
+        set(SPUT_BYTE);
+        set(SPUT_CHAR);
+        set(SPUT_SHORT);
+        set(INVOKE_VIRTUAL);
+        set(INVOKE_SUPER);
+        set(INVOKE_DIRECT);
+        set(INVOKE_STATIC);
+        set(INVOKE_INTERFACE);
+        set(INVOKE_VIRTUAL_RANGE);
+        set(INVOKE_SUPER_RANGE);
+        set(INVOKE_DIRECT_RANGE);
+        set(INVOKE_STATIC_RANGE);
+        set(INVOKE_INTERFACE_RANGE);
+        set(NEG_INT);
+        set(NOT_INT);
+        set(NEG_LONG);
+        set(NOT_LONG);
+        set(NEG_FLOAT);
+        set(NEG_DOUBLE);
+        set(INT_TO_LONG);
+        set(INT_TO_FLOAT);
+        set(INT_TO_DOUBLE);
+        set(LONG_TO_INT);
+        set(LONG_TO_FLOAT);
+        set(LONG_TO_DOUBLE);
+        set(FLOAT_TO_INT);
+        set(FLOAT_TO_LONG);
+        set(FLOAT_TO_DOUBLE);
+        set(DOUBLE_TO_INT);
+        set(DOUBLE_TO_LONG);
+        set(DOUBLE_TO_FLOAT);
+        set(INT_TO_BYTE);
+        set(INT_TO_CHAR);
+        set(INT_TO_SHORT);
+        set(ADD_INT);
+        set(SUB_INT);
+        set(MUL_INT);
+        set(DIV_INT);
+        set(REM_INT);
+        set(AND_INT);
+        set(OR_INT);
+        set(XOR_INT);
+        set(SHL_INT);
+        set(SHR_INT);
+        set(USHR_INT);
+        set(ADD_LONG);
+        set(SUB_LONG);
+        set(MUL_LONG);
+        set(DIV_LONG);
+        set(REM_LONG);
+        set(AND_LONG);
+        set(OR_LONG);
+        set(XOR_LONG);
+        set(SHL_LONG);
+        set(SHR_LONG);
+        set(USHR_LONG);
+        set(ADD_FLOAT);
+        set(SUB_FLOAT);
+        set(MUL_FLOAT);
+        set(DIV_FLOAT);
+        set(REM_FLOAT);
+        set(ADD_DOUBLE);
+        set(SUB_DOUBLE);
+        set(MUL_DOUBLE);
+        set(DIV_DOUBLE);
+        set(REM_DOUBLE);
+        set(ADD_INT_2ADDR);
+        set(SUB_INT_2ADDR);
+        set(MUL_INT_2ADDR);
+        set(DIV_INT_2ADDR);
+        set(REM_INT_2ADDR);
+        set(AND_INT_2ADDR);
+        set(OR_INT_2ADDR);
+        set(XOR_INT_2ADDR);
+        set(SHL_INT_2ADDR);
+        set(SHR_INT_2ADDR);
+        set(USHR_INT_2ADDR);
+        set(ADD_LONG_2ADDR);
+        set(SUB_LONG_2ADDR);
+        set(MUL_LONG_2ADDR);
+        set(DIV_LONG_2ADDR);
+        set(REM_LONG_2ADDR);
+        set(AND_LONG_2ADDR);
+        set(OR_LONG_2ADDR);
+        set(XOR_LONG_2ADDR);
+        set(SHL_LONG_2ADDR);
+        set(SHR_LONG_2ADDR);
+        set(USHR_LONG_2ADDR);
+        set(ADD_FLOAT_2ADDR);
+        set(SUB_FLOAT_2ADDR);
+        set(MUL_FLOAT_2ADDR);
+        set(DIV_FLOAT_2ADDR);
+        set(REM_FLOAT_2ADDR);
+        set(ADD_DOUBLE_2ADDR);
+        set(SUB_DOUBLE_2ADDR);
+        set(MUL_DOUBLE_2ADDR);
+        set(DIV_DOUBLE_2ADDR);
+        set(REM_DOUBLE_2ADDR);
+        set(ADD_INT_LIT16);
+        set(RSUB_INT);
+        set(MUL_INT_LIT16);
+        set(DIV_INT_LIT16);
+        set(REM_INT_LIT16);
+        set(AND_INT_LIT16);
+        set(OR_INT_LIT16);
+        set(XOR_INT_LIT16);
+        set(ADD_INT_LIT8);
+        set(RSUB_INT_LIT8);
+        set(MUL_INT_LIT8);
+        set(DIV_INT_LIT8);
+        set(REM_INT_LIT8);
+        set(AND_INT_LIT8);
+        set(OR_INT_LIT8);
+        set(XOR_INT_LIT8);
+        set(SHL_INT_LIT8);
+        set(SHR_INT_LIT8);
+        set(USHR_INT_LIT8);
+        // END(dops-init)
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Dops() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the {@link Dop} for the given opcode value.
+     *
+     * @param opcode {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode value
+     * @return {@code non-null;} the associated opcode instance
+     */
+    public static Dop get(int opcode) {
+        int idx = opcode - DalvOps.MIN_VALUE;
+
+        try {
+            Dop result = DOPS[idx];
+            if (result != null) {
+                return result;
+            }
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Fall through.
+        }
+
+        throw new IllegalArgumentException("bogus opcode");
+    }
+
+    /**
+     * Gets the {@link Dop} with the given family/format combination, if
+     * any.
+     *
+     * @param family {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+     * @param format {@code non-null;} the opcode's instruction format
+     * @return {@code null-ok;} the corresponding opcode, or {@code null} if
+     * there is none
+     */
+    public static Dop getOrNull(int family, InsnFormat format) {
+        if (format == null) {
+            throw new NullPointerException("format == null");
+        }
+
+        int len = DOPS.length;
+
+        // TODO: Linear search is bad.
+        for (int i = 0; i < len; i++) {
+            Dop dop = DOPS[i];
+            if ((dop != null) &&
+                (dop.getFamily() == family) &&
+                (dop.getFormat() == format)) {
+                return dop;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Puts the given opcode into the table of all ops.
+     *
+     * @param opcode {@code non-null;} the opcode
+     */
+    private static void set(Dop opcode) {
+        int idx = opcode.getOpcode() - DalvOps.MIN_VALUE;
+        DOPS[idx] = opcode;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/FixedSizeInsn.java b/dx/src/com/android/dx/dex/code/FixedSizeInsn.java
new file mode 100644
index 0000000..faed530
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/FixedSizeInsn.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Base class for instructions which are of a fixed code size and
+ * which use {@link InsnFormat} methods to write themselves. This
+ * includes most &mdash; but not all &mdash; instructions.
+ */
+public abstract class FixedSizeInsn extends DalvInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * <p><b>Note:</b> In the unlikely event that an instruction takes
+     * absolutely no registers (e.g., a {@code nop} or a
+     * no-argument no-result * static method call), then the given
+     * register list may be passed as {@link
+     * RegisterSpecList#EMPTY}.</p>
+     *
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position {@code non-null;} source position
+     * @param registers {@code non-null;} register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins or outs)
+     */
+    public FixedSizeInsn(Dop opcode, SourcePosition position,
+                         RegisterSpecList registers) {
+        super(opcode, position, registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int codeSize() {
+        return getOpcode().getFormat().codeSize();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void writeTo(AnnotatedOutput out) {
+        getOpcode().getFormat().writeTo(out, this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withRegisterOffset(int delta) {
+        return withRegisters(getRegisters().withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected final String listingString0(boolean noteIndices) {
+        return getOpcode().getFormat().listingString(this, noteIndices);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java b/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java
new file mode 100644
index 0000000..6fab094
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Combination instruction which turns into a variable number of
+ * {@code move*} instructions to move a set of registers into
+ * registers starting at {@code 0} sequentially. This is used
+ * in translating an instruction whose register requirements cannot
+ * be met using a straightforward choice of a single opcode.
+ */
+public final class HighRegisterPrefix extends VariableSizeInsn {
+    /** {@code null-ok;} cached instructions, if constructed */
+    private SimpleInsn[] insns;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     * @param registers {@code non-null;} source registers
+     */
+    public HighRegisterPrefix(SourcePosition position,
+                              RegisterSpecList registers) {
+        super(position, registers);
+
+        if (registers.size() == 0) {
+            throw new IllegalArgumentException("registers.size() == 0");
+        }
+
+        insns = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        int result = 0;
+
+        calculateInsnsIfNecessary();
+
+        for (SimpleInsn insn : insns) {
+            result += insn.codeSize();
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out) {
+        calculateInsnsIfNecessary();
+
+        for (SimpleInsn insn : insns) {
+            insn.writeTo(out);
+        }
+    }
+
+    /**
+     * Helper for {@link #codeSize} and {@link #writeTo} which sets up
+     * {@link #insns} if not already done.
+     */
+    private void calculateInsnsIfNecessary() {
+        if (insns != null) {
+            return;
+        }
+
+        RegisterSpecList registers = getRegisters();
+        int sz = registers.size();
+
+        insns = new SimpleInsn[sz];
+
+        for (int i = 0, outAt = 0; i < sz; i++) {
+            RegisterSpec src = registers.get(i);
+            insns[i] = moveInsnFor(src, outAt);
+            outAt += src.getCategory();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new HighRegisterPrefix(getPosition(), registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        RegisterSpecList registers = getRegisters();
+        int sz = registers.size();
+        StringBuffer sb = new StringBuffer(100);
+
+        for (int i = 0, outAt = 0; i < sz; i++) {
+            RegisterSpec src = registers.get(i);
+            SimpleInsn insn = moveInsnFor(src, outAt);
+
+            if (i != 0) {
+                sb.append('\n');
+            }
+
+            sb.append(insn.listingString0(noteIndices));
+
+            outAt += src.getCategory();
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns the proper move instruction for the given source spec
+     * and destination index.
+     *
+     * @param src {@code non-null;} the source register spec
+     * @param destIndex {@code >= 0;} the destination register index
+     * @return {@code non-null;} the appropriate move instruction
+     */
+    private static SimpleInsn moveInsnFor(RegisterSpec src, int destIndex) {
+        return DalvInsn.makeMove(SourcePosition.NO_INFO,
+                RegisterSpec.make(destIndex, src.getType()),
+                src);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/InsnFormat.java b/dx/src/com/android/dx/dex/code/InsnFormat.java
new file mode 100644
index 0000000..17c127f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/InsnFormat.java
@@ -0,0 +1,578 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteral64;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Base class for all instruction format handlers. Instruction format
+ * handlers know how to translate {@link DalvInsn} instances into
+ * streams of code words, as well as human-oriented listing strings
+ * representing such translations.
+ */
+public abstract class InsnFormat {
+    /**
+     * Returns the string form, suitable for inclusion in a listing
+     * dump, of the given instruction. The instruction must be of this
+     * instance's format for proper operation.
+     *
+     * @param insn {@code non-null;} the instruction
+     * @param noteIndices whether to include an explicit notation of
+     * constant pool indices
+     * @return {@code non-null;} the string form
+     */
+    public final String listingString(DalvInsn insn, boolean noteIndices) {
+        String op = insn.getOpcode().getName();
+        String arg = insnArgString(insn);
+        String comment = insnCommentString(insn, noteIndices);
+        StringBuilder sb = new StringBuilder(100);
+
+        sb.append(op);
+
+        if (arg.length() != 0) {
+            sb.append(' ');
+            sb.append(arg);
+        }
+
+        if (comment.length() != 0) {
+            sb.append(" // ");
+            sb.append(comment);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns the string form of the arguments to the given instruction.
+     * The instruction must be of this instance's format. If the instruction
+     * has no arguments, then the result should be {@code ""}, not
+     * {@code null}.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @param insn {@code non-null;} the instruction
+     * @return {@code non-null;} the string form
+     */
+    public abstract String insnArgString(DalvInsn insn);
+
+    /**
+     * Returns the associated comment for the given instruction, if any.
+     * The instruction must be of this instance's format. If the instruction
+     * has no comment, then the result should be {@code ""}, not
+     * {@code null}.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @param insn {@code non-null;} the instruction
+     * @param noteIndices whether to include an explicit notation of
+     * constant pool indices
+     * @return {@code non-null;} the string form
+     */
+    public abstract String insnCommentString(DalvInsn insn,
+            boolean noteIndices);
+
+    /**
+     * Gets the code size of instructions that use this format. The
+     * size is a number of 16-bit code units, not bytes. This should
+     * throw an exception if this format is of variable size.
+     *
+     * @return {@code >= 0;} the instruction length in 16-bit code units
+     */
+    public abstract int codeSize();
+
+    /**
+     * Returns whether or not the given instruction's arguments will
+     * fit in this instance's format. This includes such things as
+     * counting register arguments, checking register ranges, and
+     * making sure that additional arguments are of appropriate types
+     * and are in-range. If this format has a branch target but the
+     * instruction's branch offset is unknown, this method will simply
+     * not check the offset.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @param insn {@code non-null;} the instruction to check
+     * @return {@code true} iff the instruction's arguments are
+     * appropriate for this instance, or {@code false} if not
+     */
+    public abstract boolean isCompatible(DalvInsn insn);
+
+    /**
+     * Returns whether or not the given instruction's branch offset will
+     * fit in this instance's format. This always returns {@code false}
+     * for formats that don't include a branch offset.
+     *
+     * <p>The default implementation of this method always returns
+     * {@code false}. Subclasses must override this method if they
+     * include branch offsets.</p>
+     *
+     * @param insn {@code non-null;} the instruction to check
+     * @return {@code true} iff the instruction's branch offset is
+     * appropriate for this instance, or {@code false} if not
+     */
+    public boolean branchFits(TargetInsn insn) {
+        return false;
+    }
+
+    /**
+     * Returns the next instruction format to try to match an instruction
+     * with, presuming that this instance isn't compatible, if any.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @return {@code null-ok;} the next format to try, or {@code null} if
+     * there are no suitable alternatives
+     */
+    public abstract InsnFormat nextUp();
+
+    /**
+     * Writes the code units for the given instruction to the given
+     * output destination. The instruction must be of this instance's format.
+     *
+     * <p>Subclasses must override this method.</p>
+     *
+     * @param out {@code non-null;} the output destination to write to
+     * @param insn {@code non-null;} the instruction to write
+     */
+    public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
+
+    /**
+     * Helper method to return a register list string.
+     *
+     * @param list {@code non-null;} the list of registers
+     * @return {@code non-null;} the string form
+     */
+    protected static String regListString(RegisterSpecList list) {
+        int sz = list.size();
+        StringBuffer sb = new StringBuffer(sz * 5 + 2);
+
+        sb.append('{');
+
+        for (int i = 0; i < sz; i++) {
+            if (i != 0) {
+                sb.append(", ");
+            }
+            sb.append(list.get(i).regString());
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to return a literal bits argument string.
+     *
+     * @param value the value
+     * @return {@code non-null;} the string form
+     */
+    protected static String literalBitsString(CstLiteralBits value) {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append('#');
+
+        if (value instanceof CstKnownNull) {
+            sb.append("null");
+        } else {
+            sb.append(value.typeName());
+            sb.append(' ');
+            sb.append(value.toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to return a literal bits comment string.
+     *
+     * @param value the value
+     * @param width the width of the constant, in bits (used for displaying
+     * the uninterpreted bits; one of: {@code 4 8 16 32 64}
+     * @return {@code non-null;} the comment
+     */
+    protected static String literalBitsComment(CstLiteralBits value,
+            int width) {
+        StringBuffer sb = new StringBuffer(20);
+
+        sb.append("#");
+
+        long bits;
+
+        if (value instanceof CstLiteral64) {
+            bits = ((CstLiteral64) value).getLongBits();
+        } else {
+            bits = value.getIntBits();
+        }
+
+        switch (width) {
+            case 4:  sb.append(Hex.uNibble((int) bits)); break;
+            case 8:  sb.append(Hex.u1((int) bits));      break;
+            case 16: sb.append(Hex.u2((int) bits));      break;
+            case 32: sb.append(Hex.u4((int) bits));      break;
+            case 64: sb.append(Hex.u8(bits));            break;
+            default: {
+                throw new RuntimeException("shouldn't happen");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to return a branch address string.
+     *
+     * @param insn {@code non-null;} the instruction in question
+     * @return {@code non-null;} the string form of the instruction's branch target
+     */
+    protected static String branchString(DalvInsn insn) {
+        TargetInsn ti = (TargetInsn) insn;
+        int address = ti.getTargetAddress();
+
+        return (address == (char) address) ? Hex.u2(address) : Hex.u4(address);
+    }
+
+    /**
+     * Helper method to return the comment for a branch.
+     *
+     * @param insn {@code non-null;} the instruction in question
+     * @return {@code non-null;} the comment
+     */
+    protected static String branchComment(DalvInsn insn) {
+        TargetInsn ti = (TargetInsn) insn;
+        int offset = ti.getTargetOffset();
+
+        return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset);
+    }
+
+    /**
+     * Helper method to return a constant string.
+     *
+     * @param insn {@code non-null;} a constant-bearing instruction
+     * @return {@code non-null;} the string form of the contained constant
+     */
+    protected static String cstString(DalvInsn insn) {
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        return cst.toHuman();
+    }
+
+    /**
+     * Helper method to return an instruction comment for a constant.
+     *
+     * @param insn {@code non-null;} a constant-bearing instruction
+     * @return {@code non-null;} comment string representing the constant
+     */
+    protected static String cstComment(DalvInsn insn) {
+        CstInsn ci = (CstInsn) insn;
+
+        if (! ci.hasIndex()) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder(20);
+        int index = ci.getIndex();
+
+        sb.append(ci.getConstant().typeName());
+        sb.append('@');
+
+        if (index < 65536) {
+            sb.append(Hex.u2(index));
+        } else {
+            sb.append(Hex.u4(index));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to determine if a signed int value fits in a nibble.
+     *
+     * @param value the value in question
+     * @return {@code true} iff it's in the range -8..+7
+     */
+    protected static boolean signedFitsInNibble(int value) {
+        return (value >= -8) && (value <= 7);
+    }
+
+    /**
+     * Helper method to determine if an unsigned int value fits in a nibble.
+     *
+     * @param value the value in question
+     * @return {@code true} iff it's in the range 0..0xf
+     */
+    protected static boolean unsignedFitsInNibble(int value) {
+        return value == (value & 0xf);
+    }
+
+    /**
+     * Helper method to determine if a signed int value fits in a byte.
+     *
+     * @param value the value in question
+     * @return {@code true} iff it's in the range -0x80..+0x7f
+     */
+    protected static boolean signedFitsInByte(int value) {
+        return (byte) value == value;
+    }
+
+    /**
+     * Helper method to determine if an unsigned int value fits in a byte.
+     *
+     * @param value the value in question
+     * @return {@code true} iff it's in the range 0..0xff
+     */
+    protected static boolean unsignedFitsInByte(int value) {
+        return value == (value & 0xff);
+    }
+
+    /**
+     * Helper method to determine if a signed int value fits in a short.
+     *
+     * @param value the value in question
+     * @return {@code true} iff it's in the range -0x8000..+0x7fff
+     */
+    protected static boolean signedFitsInShort(int value) {
+        return (short) value == value;
+    }
+
+    /**
+     * Helper method to determine if an unsigned int value fits in a short.
+     *
+     * @param value the value in question
+     * @return {@code true} iff it's in the range 0..0xffff
+     */
+    protected static boolean unsignedFitsInShort(int value) {
+        return value == (value & 0xffff);
+    }
+
+    /**
+     * Helper method to determine if a signed int value fits in three bytes.
+     *
+     * @param value the value in question
+     * @return {@code true} iff it's in the range -0x800000..+0x7fffff
+     */
+    protected static boolean signedFitsIn3Bytes(int value) {
+        return value == ((value << 8) >> 8);
+    }
+
+    /**
+     * Helper method to extract the callout-argument index from an
+     * appropriate instruction.
+     *
+     * @param insn {@code non-null;} the instruction
+     * @return {@code >= 0;} the callout argument index
+     */
+    protected static int argIndex(DalvInsn insn) {
+        int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue();
+
+        if (arg < 0) {
+            throw new IllegalArgumentException("bogus insn");
+        }
+
+        return arg;
+    }
+
+    /**
+     * Helper method to combine an opcode and a second byte of data into
+     * the appropriate form for emitting into a code buffer.
+     *
+     * @param insn {@code non-null;} the instruction containing the opcode
+     * @param arg {@code 0..255;} arbitrary other byte value
+     * @return combined value
+     */
+    protected static short opcodeUnit(DalvInsn insn, int arg) {
+        if ((arg & 0xff) != arg) {
+            throw new IllegalArgumentException("arg out of range 0..255");
+        }
+
+        int opcode = insn.getOpcode().getOpcode();
+
+        if ((opcode & 0xff) != opcode) {
+            throw new IllegalArgumentException("opcode out of range 0..255");
+        }
+
+        return (short) (opcode | (arg << 8));
+    }
+
+    /**
+     * Helper method to combine two bytes into a code unit.
+     *
+     * @param low {@code 0..255;} low byte
+     * @param high {@code 0..255;} high byte
+     * @return combined value
+     */
+    protected static short codeUnit(int low, int high) {
+        if ((low & 0xff) != low) {
+            throw new IllegalArgumentException("low out of range 0..255");
+        }
+
+        if ((high & 0xff) != high) {
+            throw new IllegalArgumentException("high out of range 0..255");
+        }
+
+        return (short) (low | (high << 8));
+    }
+
+    /**
+     * Helper method to combine four nibbles into a code unit.
+     *
+     * @param n0 {@code 0..15;} low nibble
+     * @param n1 {@code 0..15;} medium-low nibble
+     * @param n2 {@code 0..15;} medium-high nibble
+     * @param n3 {@code 0..15;} high nibble
+     * @return combined value
+     */
+    protected static short codeUnit(int n0, int n1, int n2, int n3) {
+        if ((n0 & 0xf) != n0) {
+            throw new IllegalArgumentException("n0 out of range 0..15");
+        }
+
+        if ((n1 & 0xf) != n1) {
+            throw new IllegalArgumentException("n1 out of range 0..15");
+        }
+
+        if ((n2 & 0xf) != n2) {
+            throw new IllegalArgumentException("n2 out of range 0..15");
+        }
+
+        if ((n3 & 0xf) != n3) {
+            throw new IllegalArgumentException("n3 out of range 0..15");
+        }
+
+        return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
+    }
+
+    /**
+     * Helper method to combine two nibbles into a byte.
+     *
+     * @param low {@code 0..15;} low nibble
+     * @param high {@code 0..15;} high nibble
+     * @return {@code 0..255;} combined value
+     */
+    protected static int makeByte(int low, int high) {
+        if ((low & 0xf) != low) {
+            throw new IllegalArgumentException("low out of range 0..15");
+        }
+
+        if ((high & 0xf) != high) {
+            throw new IllegalArgumentException("high out of range 0..15");
+        }
+
+        return low | (high << 4);
+    }
+
+    /**
+     * Writes one code unit to the given output destination.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0) {
+        out.writeShort(c0);
+    }
+
+    /**
+     * Writes two code units to the given output destination.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+    }
+
+    /**
+     * Writes three code units to the given output destination.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     * @param c2 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1,
+                                short c2) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+        out.writeShort(c2);
+    }
+
+    /**
+     * Writes four code units to the given output destination.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     * @param c2 code unit to write
+     * @param c3 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1,
+                                short c2, short c3) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+        out.writeShort(c2);
+        out.writeShort(c3);
+    }
+
+    /**
+     * Writes five code units to the given output destination.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     * @param c2 code unit to write
+     * @param c3 code unit to write
+     * @param c4 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1,
+                                short c2, short c3, short c4) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+        out.writeShort(c2);
+        out.writeShort(c3);
+        out.writeShort(c4);
+    }
+
+    /**
+     * Writes six code units to the given output destination.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param c0 code unit to write
+     * @param c1 code unit to write
+     * @param c2 code unit to write
+     * @param c3 code unit to write
+     * @param c4 code unit to write
+     * @param c5 code unit to write
+     */
+    protected static void write(AnnotatedOutput out, short c0, short c1,
+                                short c2, short c3, short c4, short c5) {
+        out.writeShort(c0);
+        out.writeShort(c1);
+        out.writeShort(c2);
+        out.writeShort(c3);
+        out.writeShort(c4);
+        out.writeShort(c5);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalEnd.java b/dx/src/com/android/dx/dex/code/LocalEnd.java
new file mode 100644
index 0000000..1c2bf89
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalEnd.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to explicitly end the mapping of a
+ * register to a named local variable. That is, an instance of this
+ * class in an instruction stream indicates that starting with the
+ * subsequent instruction, the indicated variable is no longer valid.
+ */
+public final class LocalEnd extends ZeroSizeInsn {
+    /**
+     * {@code non-null;} register spec representing the local variable ended
+     * by this instance. <b>Note:</b> Technically, only the register
+     * number needs to be recorded here as the rest of the information
+     * is implicit in the ambient local variable state, but other code
+     * will check the other info for consistency.
+     */
+    private final RegisterSpec local;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     * @param local {@code non-null;} register spec representing the local
+     * variable introduced by this instance
+     */
+    public LocalEnd(SourcePosition position, RegisterSpec local) {
+        super(position);
+
+        if (local == null) {
+            throw new NullPointerException("local == null");
+        }
+
+        this.local = local;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisterOffset(int delta) {
+        return new LocalEnd(getPosition(), local.withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new LocalEnd(getPosition(), local);
+    }
+
+    /**
+     * Gets the register spec representing the local variable ended
+     * by this instance.
+     *
+     * @return {@code non-null;} the register spec
+     */
+    public RegisterSpec getLocal() {
+        return local;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return local.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        return "local-end " + LocalStart.localString(local);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalList.java b/dx/src/com/android/dx/dex/code/LocalList.java
new file mode 100644
index 0000000..94ca663
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalList.java
@@ -0,0 +1,948 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.FixedSizeList;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * List of local variables. Each local variable entry indicates a
+ * range of code which it is valid for, a register number, a name,
+ * and a type.
+ */
+public final class LocalList extends FixedSizeList {
+    /** {@code non-null;} empty instance */
+    public static final LocalList EMPTY = new LocalList(0);
+
+    /** whether to run the self-check code */
+    private static final boolean DEBUG = false;
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size {@code >= 0;} the size of the list
+     */
+    public LocalList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @return {@code non-null;} element at that index
+     */
+    public Entry get(int n) {
+        return (Entry) get0(n);
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param entry {@code non-null;} the entry to set at {@code n}
+     */
+    public void set(int n, Entry entry) {
+        set0(n, entry);
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out {@code non-null;} where to dump
+     * @param prefix {@code non-null;} prefix to attach to each line of output
+     */
+    public void debugPrint(PrintStream out, String prefix) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            out.print(prefix);
+            out.println(get(i));
+        }
+    }
+
+    /**
+     * Disposition of a local entry.
+     */
+    public static enum Disposition {
+        /** local started (introduced) */
+        START,
+
+        /** local ended without being replaced */
+        END_SIMPLY,
+
+        /** local ended because it was directly replaced */
+        END_REPLACED,
+
+        /** local ended because it was moved to a different register */
+        END_MOVED,
+
+        /**
+         * local ended because the previous local clobbered this one
+         * (because it is category-2)
+         */
+        END_CLOBBERED_BY_PREV,
+
+        /**
+         * local ended because the next local clobbered this one
+         * (because this one is a category-2)
+         */
+        END_CLOBBERED_BY_NEXT;
+    }
+
+    /**
+     * Entry in a local list.
+     */
+    public static class Entry implements Comparable<Entry> {
+        /** {@code >= 0;} address */
+        private final int address;
+
+        /** {@code non-null;} disposition of the local */
+        private final Disposition disposition;
+
+        /** {@code non-null;} register spec representing the variable */
+        private final RegisterSpec spec;
+
+        /** {@code non-null;} variable type (derived from {@code spec}) */
+        private final CstType type;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param address {@code >= 0;} address
+         * @param disposition {@code non-null;} disposition of the local
+         * @param spec {@code non-null;} register spec representing
+         * the variable
+         */
+        public Entry(int address, Disposition disposition, RegisterSpec spec) {
+            if (address < 0) {
+                throw new IllegalArgumentException("address < 0");
+            }
+
+            if (disposition == null) {
+                throw new NullPointerException("disposition == null");
+            }
+
+            try {
+                if (spec.getLocalItem() == null) {
+                    throw new NullPointerException(
+                            "spec.getLocalItem() == null");
+                }
+            } catch (NullPointerException ex) {
+                // Elucidate the exception.
+                throw new NullPointerException("spec == null");
+            }
+
+            this.address = address;
+            this.disposition = disposition;
+            this.spec = spec;
+            this.type = CstType.intern(spec.getType());
+        }
+
+        /** {@inheritDoc} */
+        public String toString() {
+            return Integer.toHexString(address) + " " + disposition + " " +
+                spec;
+        }
+
+        /** {@inheritDoc} */
+        public boolean equals(Object other) {
+            if (!(other instanceof Entry)) {
+                return false;
+            }
+
+            return (compareTo((Entry) other) == 0);
+        }
+
+        /**
+         * Compares by (in priority order) address, end then start
+         * disposition (variants of end are all consistered
+         * equivalent), and spec.
+         *
+         * @param other {@code non-null;} entry to compare to
+         * @return {@code -1..1;} standard result of comparison
+         */
+        public int compareTo(Entry other) {
+            if (address < other.address) {
+                return -1;
+            } else if (address > other.address) {
+                return 1;
+            }
+
+            boolean thisIsStart = isStart();
+            boolean otherIsStart = other.isStart();
+
+            if (thisIsStart != otherIsStart) {
+                return thisIsStart ? 1 : -1;
+            }
+
+            return spec.compareTo(other.spec);
+        }
+
+        /**
+         * Gets the address.
+         *
+         * @return {@code >= 0;} the address
+         */
+        public int getAddress() {
+            return address;
+        }
+
+        /**
+         * Gets the disposition.
+         *
+         * @return {@code non-null;} the disposition
+         */
+        public Disposition getDisposition() {
+            return disposition;
+        }
+
+        /**
+         * Gets whether this is a local start. This is just shorthand for
+         * {@code getDisposition() == Disposition.START}.
+         *
+         * @return {@code true} iff this is a start
+         */
+        public boolean isStart() {
+            return disposition == Disposition.START;
+        }
+
+        /**
+         * Gets the variable name.
+         *
+         * @return {@code null-ok;} the variable name
+         */
+        public CstUtf8 getName() {
+            return spec.getLocalItem().getName();
+        }
+
+        /**
+         * Gets the variable signature.
+         *
+         * @return {@code null-ok;} the variable signature
+         */
+        public CstUtf8 getSignature() {
+            return spec.getLocalItem().getSignature();
+        }
+
+        /**
+         * Gets the variable's type.
+         *
+         * @return {@code non-null;} the type
+         */
+        public CstType getType() {
+            return type;
+        }
+
+        /**
+         * Gets the number of the register holding the variable.
+         *
+         * @return {@code >= 0;} the number of the register holding
+         * the variable
+         */
+        public int getRegister() {
+            return spec.getReg();
+        }
+
+        /**
+         * Gets the RegisterSpec of the register holding the variable.
+         *
+         * @return {@code non-null;} RegisterSpec of the holding register.
+         */
+        public RegisterSpec getRegisterSpec() {
+            return spec;
+        }
+
+        /**
+         * Returns whether or not this instance matches the given spec.
+         *
+         * @param otherSpec {@code non-null;} the spec in question
+         * @return {@code true} iff this instance matches
+         * {@code spec}
+         */
+        public boolean matches(RegisterSpec otherSpec) {
+            return spec.equalsUsingSimpleType(otherSpec);
+        }
+
+        /**
+         * Returns whether or not this instance matches the spec in
+         * the given instance.
+         *
+         * @param other {@code non-null;} another entry
+         * @return {@code true} iff this instance's spec matches
+         * {@code other}
+         */
+        public boolean matches(Entry other) {
+            return matches(other.spec);
+        }
+
+        /**
+         * Returns an instance just like this one but with the disposition
+         * set as given.
+         *
+         * @param disposition {@code non-null;} the new disposition
+         * @return {@code non-null;} an appropriately-constructed instance
+         */
+        public Entry withDisposition(Disposition disposition) {
+            if (disposition == this.disposition) {
+                return this;
+            }
+
+            return new Entry(address, disposition, spec);
+        }
+    }
+
+    /**
+     * Constructs an instance for the given method, based on the given
+     * block order and intermediate local information.
+     *
+     * @param insns {@code non-null;} instructions to convert
+     * @return {@code non-null;} the constructed list
+     */
+    public static LocalList make(DalvInsnList insns) {
+        int sz = insns.size();
+
+        /*
+         * Go through the insn list, looking for all the local
+         * variable pseudoinstructions, splitting out LocalSnapshots
+         * into separate per-variable starts, adding explicit ends
+         * wherever a variable is replaced or moved, and collecting
+         * these and all the other local variable "activity"
+         * together into an output list (without the other insns).
+         *
+         * Note: As of this writing, this method won't be handed any
+         * insn lists that contain local ends, but I (danfuzz) expect
+         * that to change at some point, when we start feeding that
+         * info explicitly into the rop layer rather than only trying
+         * to infer it. So, given that expectation, this code is
+         * written to deal with them.
+         */
+
+        MakeState state = new MakeState(sz);
+
+        for (int i = 0; i < sz; i++) {
+            DalvInsn insn = insns.get(i);
+
+            if (insn instanceof LocalSnapshot) {
+                RegisterSpecSet snapshot =
+                    ((LocalSnapshot) insn).getLocals();
+                state.snapshot(insn.getAddress(), snapshot);
+            } else if (insn instanceof LocalStart) {
+                RegisterSpec local = ((LocalStart) insn).getLocal();
+                state.startLocal(insn.getAddress(), local);
+            } else if (insn instanceof LocalEnd) {
+                RegisterSpec local = ((LocalEnd) insn).getLocal();
+                state.endLocal(insn.getAddress(), local);
+            }
+        }
+
+        LocalList result = state.finish();
+
+        if (DEBUG) {
+            debugVerify(result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Debugging helper that verifies the constraint that a list doesn't
+     * contain any redundant local starts and that local ends that are
+     * due to replacements are properly annotated.
+     */
+    private static void debugVerify(LocalList locals) {
+        try {
+            debugVerify0(locals);
+        } catch (RuntimeException ex) {
+            int sz = locals.size();
+            for (int i = 0; i < sz; i++) {
+                System.err.println(locals.get(i));
+            }
+            throw ex;
+        }
+
+    }
+
+    /**
+     * Helper for {@link #debugVerify} which does most of the work.
+     */
+    private static void debugVerify0(LocalList locals) {
+        int sz = locals.size();
+        Entry[] active = new Entry[65536];
+
+        for (int i = 0; i < sz; i++) {
+            Entry e = locals.get(i);
+            int reg = e.getRegister();
+
+            if (e.isStart()) {
+                Entry already = active[reg];
+
+                if ((already != null) && e.matches(already)) {
+                    throw new RuntimeException("redundant start at " +
+                            Integer.toHexString(e.getAddress()) + ": got " +
+                            e + "; had " + already);
+                }
+
+                active[reg] = e;
+            } else {
+                if (active[reg] == null) {
+                    throw new RuntimeException("redundant end at " +
+                            Integer.toHexString(e.getAddress()));
+                }
+
+                int addr = e.getAddress();
+                boolean foundStart = false;
+
+                for (int j = i + 1; j < sz; j++) {
+                    Entry test = locals.get(j);
+                    if (test.getAddress() != addr) {
+                        break;
+                    }
+                    if (test.getRegisterSpec().getReg() == reg) {
+                        if (test.isStart()) {
+                            if (e.getDisposition()
+                                    != Disposition.END_REPLACED) {
+                                throw new RuntimeException(
+                                        "improperly marked end at " +
+                                        Integer.toHexString(addr));
+                            }
+                            foundStart = true;
+                        } else {
+                            throw new RuntimeException(
+                                    "redundant end at " +
+                                    Integer.toHexString(addr));
+                        }
+                    }
+                }
+
+                if (!foundStart &&
+                        (e.getDisposition() == Disposition.END_REPLACED)) {
+                    throw new RuntimeException(
+                            "improper end replacement claim at " +
+                            Integer.toHexString(addr));
+                }
+
+                active[reg] = null;
+            }
+        }
+    }
+
+    /**
+     * Intermediate state when constructing a local list.
+     */
+    public static class MakeState {
+        /** {@code non-null;} result being collected */
+        private final ArrayList<Entry> result;
+
+        /**
+         * {@code >= 0;} running count of nulled result entries, to help with
+         * sizing the final list
+         */
+        private int nullResultCount;
+
+        /** {@code null-ok;} current register mappings */
+        private RegisterSpecSet regs;
+
+        /** {@code null-ok;} result indices where local ends are stored */
+        private int[] endIndices;
+
+        /** {@code >= 0;} last address seen */
+        private int lastAddress;
+
+        /**
+         * Constructs an instance.
+         */
+        public MakeState(int initialSize) {
+            result = new ArrayList<Entry>(initialSize);
+            nullResultCount = 0;
+            regs = null;
+            endIndices = null;
+            lastAddress = 0;
+        }
+
+        /**
+         * Checks the address and other vitals as a prerequisite to
+         * further processing.
+         *
+         * @param address {@code >= 0;} address about to be processed
+         * @param reg {@code >= 0;} register number about to be processed
+         */
+        private void aboutToProcess(int address, int reg) {
+            boolean first = (endIndices == null);
+
+            if ((address == lastAddress) && !first) {
+                return;
+            }
+
+            if (address < lastAddress) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            if (first || (reg >= endIndices.length)) {
+                /*
+                 * This is the first allocation of the state set and
+                 * index array, or we need to grow. (The latter doesn't
+                 * happen much; in fact, we have only ever observed
+                 * it happening in test cases, never in "real" code.)
+                 */
+                int newSz = reg + 1;
+                RegisterSpecSet newRegs = new RegisterSpecSet(newSz);
+                int[] newEnds = new int[newSz];
+                Arrays.fill(newEnds, -1);
+
+                if (!first) {
+                    newRegs.putAll(regs);
+                    System.arraycopy(endIndices, 0, newEnds, 0,
+                            endIndices.length);
+                }
+
+                regs = newRegs;
+                endIndices = newEnds;
+            }
+        }
+
+        /**
+         * Sets the local state at the given address to the given snapshot.
+         * The first call on this instance must be to this method, so that
+         * the register state can be properly sized.
+         *
+         * @param address {@code >= 0;} the address
+         * @param specs {@code non-null;} spec set representing the locals
+         */
+        public void snapshot(int address, RegisterSpecSet specs) {
+            if (DEBUG) {
+                System.err.printf("%04x snapshot %s\n", address, specs);
+            }
+
+            int sz = specs.getMaxSize();
+            aboutToProcess(address, sz - 1);
+
+            for (int i = 0; i < sz; i++) {
+                RegisterSpec oldSpec = regs.get(i);
+                RegisterSpec newSpec = filterSpec(specs.get(i));
+
+                if (oldSpec == null) {
+                    if (newSpec != null) {
+                        startLocal(address, newSpec);
+                    }
+                } else if (newSpec == null) {
+                    endLocal(address, oldSpec);
+                } else if (! newSpec.equalsUsingSimpleType(oldSpec)) {
+                    endLocal(address, oldSpec);
+                    startLocal(address, newSpec);
+                }
+            }
+
+            if (DEBUG) {
+                System.err.printf("%04x snapshot done\n", address);
+            }
+        }
+
+        /**
+         * Starts a local at the given address.
+         *
+         * @param address {@code >= 0;} the address
+         * @param startedLocal {@code non-null;} spec representing the
+         * started local
+         */
+        public void startLocal(int address, RegisterSpec startedLocal) {
+            if (DEBUG) {
+                System.err.printf("%04x start %s\n", address, startedLocal);
+            }
+
+            int regNum = startedLocal.getReg();
+
+            startedLocal = filterSpec(startedLocal);
+            aboutToProcess(address, regNum);
+
+            RegisterSpec existingLocal = regs.get(regNum);
+
+            if (startedLocal.equalsUsingSimpleType(existingLocal)) {
+                // Silently ignore a redundant start.
+                return;
+            }
+
+            RegisterSpec movedLocal = regs.findMatchingLocal(startedLocal);
+            if (movedLocal != null) {
+                /*
+                 * The same variable was moved from one register to another.
+                 * So add an end for its old location.
+                 */
+                addOrUpdateEnd(address, Disposition.END_MOVED, movedLocal);
+            }
+
+            int endAt = endIndices[regNum];
+
+            if (existingLocal != null) {
+                /*
+                 * There is an existing (but non-matching) local.
+                 * Add an explicit end for it.
+                 */
+                add(address, Disposition.END_REPLACED, existingLocal);
+            } else if (endAt >= 0) {
+                /*
+                 * Look for an end local for the same register at the
+                 * same address. If found, then update it or delete
+                 * it, depending on whether or not it represents the
+                 * same variable as the one being started.
+                 */
+                Entry endEntry = result.get(endAt);
+                if (endEntry.getAddress() == address) {
+                    if (endEntry.matches(startedLocal)) {
+                        /*
+                         * There was already an end local for the same
+                         * variable at the same address. This turns
+                         * out to be superfluous, as we are starting
+                         * up the exact same local. This situation can
+                         * happen when a single local variable got
+                         * somehow "split up" during intermediate
+                         * processing. In any case, rather than represent
+                         * the end-then-start, just remove the old end.
+                         */
+                        result.set(endAt, null);
+                        nullResultCount++;
+                        regs.put(startedLocal);
+                        endIndices[regNum] = -1;
+                        return;
+                    } else {
+                        /*
+                         * There was a different variable ended at the
+                         * same address. Update it to indicate that
+                         * it was ended due to a replacement (rather than
+                         * ending for no particular reason).
+                         */
+                        endEntry = endEntry.withDisposition(
+                                Disposition.END_REPLACED);
+                        result.set(endAt, endEntry);
+                    }
+                }
+            }
+
+            /*
+             * The code above didn't find and remove an unnecessary
+             * local end, so we now have to add one or more entries to
+             * the output to capture the transition.
+             */
+
+            /*
+             * If the local just below (in the register set at reg-1)
+             * is of category-2, then it is ended by this new start.
+             */
+            if (regNum > 0) {
+                RegisterSpec justBelow = regs.get(regNum - 1);
+                if ((justBelow != null) && justBelow.isCategory2()) {
+                    addOrUpdateEnd(address,
+                            Disposition.END_CLOBBERED_BY_NEXT,
+                            justBelow);
+                }
+            }
+
+            /*
+             * Similarly, if this local is category-2, then the local
+             * just above (if any) is ended by the start now being
+             * emitted.
+             */
+            if (startedLocal.isCategory2()) {
+                RegisterSpec justAbove = regs.get(regNum + 1);
+                if (justAbove != null) {
+                    addOrUpdateEnd(address,
+                            Disposition.END_CLOBBERED_BY_PREV,
+                            justAbove);
+                }
+            }
+
+            /*
+             * TODO: Add an end for the same local in a different reg,
+             * if any (that is, if the local migrates from vX to vY,
+             * we should note that as a local end in vX).
+             */
+
+            add(address, Disposition.START, startedLocal);
+        }
+
+        /**
+         * Ends a local at the given address, using the disposition
+         * {@code END_SIMPLY}.
+         *
+         * @param address {@code >= 0;} the address
+         * @param endedLocal {@code non-null;} spec representing the
+         * local being ended
+         */
+        public void endLocal(int address, RegisterSpec endedLocal) {
+            endLocal(address, endedLocal, Disposition.END_SIMPLY);
+        }
+
+        /**
+         * Ends a local at the given address.
+         *
+         * @param address {@code >= 0;} the address
+         * @param endedLocal {@code non-null;} spec representing the
+         * local being ended
+         * @param disposition reason for the end
+         */
+        public void endLocal(int address, RegisterSpec endedLocal,
+                Disposition disposition) {
+            if (DEBUG) {
+                System.err.printf("%04x end %s\n", address, endedLocal);
+            }
+
+            int regNum = endedLocal.getReg();
+
+            endedLocal = filterSpec(endedLocal);
+            aboutToProcess(address, regNum);
+
+            int endAt = endIndices[regNum];
+
+            if (endAt >= 0) {
+                /*
+                 * The local in the given register is already ended.
+                 * Silently return without adding anything to the result.
+                 */
+                return;
+            }
+
+            // Check for start and end at the same address.
+            if (checkForEmptyRange(address, endedLocal)) {
+                return;
+            }
+
+            add(address, disposition, endedLocal);
+        }
+
+        /**
+         * Helper for {@link #endLocal}, which handles the cases where
+         * and end local is issued at the same address as a start local
+         * for the same register. If this case is found, then this
+         * method will remove the start (as the local was never actually
+         * active), update the {@link #endIndices} to be accurate, and
+         * if needed update the newly-active end to reflect an altered
+         * disposition.
+         *
+         * @param address {@code >= 0;} the address
+         * @param endedLocal {@code non-null;} spec representing the
+         * local being ended
+         * @return {@code true} iff this method found the case in question
+         * and adjusted things accordingly
+         */
+        private boolean checkForEmptyRange(int address,
+                RegisterSpec endedLocal) {
+            int at = result.size() - 1;
+            Entry entry;
+
+            // Look for a previous entry at the same address.
+            for (/*at*/; at >= 0; at--) {
+                entry = result.get(at);
+
+                if (entry == null) {
+                    continue;
+                }
+
+                if (entry.getAddress() != address) {
+                    // We didn't find any match at the same address.
+                    return false;
+                }
+
+                if (entry.matches(endedLocal)) {
+                    break;
+                }
+            }
+
+            /*
+             * In fact, we found that the endedLocal had started at the
+             * same address, so do all the requisite cleanup.
+             */
+
+            regs.remove(endedLocal);
+            result.set(at, null);
+            nullResultCount++;
+
+            int regNum = endedLocal.getReg();
+            boolean found = false;
+            entry = null;
+
+            // Now look back further to update where the register ended.
+            for (at--; at >= 0; at--) {
+                entry = result.get(at);
+
+                if (entry == null) {
+                    continue;
+                }
+
+                if (entry.getRegisterSpec().getReg() == regNum) {
+                    found = true;
+                    break;
+                }
+            }
+
+            if (found) {
+                // We found an end for the same register.
+                endIndices[regNum] = at;
+
+                if (entry.getAddress() == address) {
+                    /*
+                     * It's still the same address, so update the
+                     * disposition.
+                     */
+                    result.set(at,
+                            entry.withDisposition(Disposition.END_SIMPLY));
+                }
+            }
+
+            return true;
+        }
+
+        /**
+         * Converts a given spec into the form acceptable for use in a
+         * local list. This, in particular, transforms the "known
+         * null" type into simply {@code Object}. This method needs to
+         * be called for any spec that is on its way into a locals
+         * list.
+         *
+         * <p>This isn't necessarily the cleanest way to achieve the
+         * goal of not representing known nulls in a locals list, but
+         * it gets the job done.</p>
+         *
+         * @param orig {@code null-ok;} the original spec
+         * @return {@code null-ok;} an appropriately modified spec, or the
+         * original if nothing needs to be done
+         */
+        private static RegisterSpec filterSpec(RegisterSpec orig) {
+            if ((orig != null) && (orig.getType() == Type.KNOWN_NULL)) {
+                return orig.withType(Type.OBJECT);
+            }
+
+            return orig;
+        }
+
+        /**
+         * Adds an entry to the result, updating the adjunct tables
+         * accordingly.
+         *
+         * @param address {@code >= 0;} the address
+         * @param disposition {@code non-null;} the disposition
+         * @param spec {@code non-null;} spec representing the local
+         */
+        private void add(int address, Disposition disposition,
+                RegisterSpec spec) {
+            int regNum = spec.getReg();
+
+            result.add(new Entry(address, disposition, spec));
+
+            if (disposition == Disposition.START) {
+                regs.put(spec);
+                endIndices[regNum] = -1;
+            } else {
+                regs.remove(spec);
+                endIndices[regNum] = result.size() - 1;
+            }
+        }
+
+        /**
+         * Adds or updates an end local (changing its disposition). If
+         * this would cause an empty range for a local, this instead
+         * removes the local entirely.
+         *
+         * @param address {@code >= 0;} the address
+         * @param disposition {@code non-null;} the disposition
+         * @param spec {@code non-null;} spec representing the local
+         */
+        private void addOrUpdateEnd(int address, Disposition disposition,
+                RegisterSpec spec) {
+            if (disposition == Disposition.START) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            int regNum = spec.getReg();
+            int endAt = endIndices[regNum];
+
+            if (endAt >= 0) {
+                // There is a previous end.
+                Entry endEntry = result.get(endAt);
+                if ((endEntry.getAddress() == address) &&
+                        endEntry.getRegisterSpec().equals(spec)) {
+                    /*
+                     * The end is for the right address and variable, so
+                     * update it.
+                     */
+                    result.set(endAt, endEntry.withDisposition(disposition));
+                    regs.remove(spec); // TODO: Is this line superfluous?
+                    return;
+                }
+            }
+
+            endLocal(address, spec, disposition);
+        }
+
+        /**
+         * Finishes processing altogether and gets the result.
+         *
+         * @return {@code non-null;} the result list
+         */
+        public LocalList finish() {
+            aboutToProcess(Integer.MAX_VALUE, 0);
+
+            int resultSz = result.size();
+            int finalSz = resultSz - nullResultCount;
+
+            if (finalSz == 0) {
+                return EMPTY;
+            }
+
+            /*
+             * Collect an array of only the non-null entries, and then
+             * sort it to get a consistent order for everything: Local
+             * ends and starts for a given address could come in any
+             * order, but we want ends before starts as well as
+             * registers in order (within ends or starts).
+             */
+
+            Entry[] resultArr = new Entry[finalSz];
+
+            if (resultSz == finalSz) {
+                result.toArray(resultArr);
+            } else {
+                int at = 0;
+                for (Entry e : result) {
+                    if (e != null) {
+                        resultArr[at++] = e;
+                    }
+                }
+            }
+
+            Arrays.sort(resultArr);
+
+            LocalList resultList = new LocalList(finalSz);
+
+            for (int i = 0; i < finalSz; i++) {
+                resultList.set(i, resultArr[i]);
+            }
+
+            resultList.setImmutable();
+            return resultList;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalSnapshot.java b/dx/src/com/android/dx/dex/code/LocalSnapshot.java
new file mode 100644
index 0000000..baeab4c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalSnapshot.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to hold a snapshot of the
+ * state of local variable name mappings that exists immediately after
+ * the instance in an instruction array.
+ */
+public final class LocalSnapshot extends ZeroSizeInsn {
+    /** {@code non-null;} local state associated with this instance */
+    private final RegisterSpecSet locals;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     * @param locals {@code non-null;} associated local variable state
+     */
+    public LocalSnapshot(SourcePosition position, RegisterSpecSet locals) {
+        super(position);
+
+        if (locals == null) {
+            throw new NullPointerException("locals == null");
+        }
+
+        this.locals = locals;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisterOffset(int delta) {
+        return new LocalSnapshot(getPosition(), locals.withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new LocalSnapshot(getPosition(), locals);
+    }
+
+    /**
+     * Gets the local state associated with this instance.
+     *
+     * @return {@code non-null;} the state
+     */
+    public RegisterSpecSet getLocals() {
+        return locals;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return locals.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        int sz = locals.size();
+        int max = locals.getMaxSize();
+        StringBuffer sb = new StringBuffer(100 + sz * 40);
+
+        sb.append("local-snapshot");
+
+        for (int i = 0; i < max; i++) {
+            RegisterSpec spec = locals.get(i);
+            if (spec != null) {
+                sb.append("\n  ");
+                sb.append(LocalStart.localString(spec));
+            }
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalStart.java b/dx/src/com/android/dx/dex/code/LocalStart.java
new file mode 100644
index 0000000..9a17c5b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalStart.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to introduce a new local variable. That
+ * is, an instance of this class in an instruction stream indicates that
+ * starting with the subsequent instruction, the indicated variable
+ * is bound.
+ */
+public final class LocalStart extends ZeroSizeInsn {
+    /**
+     * {@code non-null;} register spec representing the local variable introduced
+     * by this instance
+     */
+    private final RegisterSpec local;
+
+    /**
+     * Returns the local variable listing string for a single register spec.
+     *
+     * @param spec {@code non-null;} the spec to convert
+     * @return {@code non-null;} the string form
+     */
+    public static String localString(RegisterSpec spec) {
+        return spec.regString() + ' ' + spec.getLocalItem().toString() + ": " +
+            spec.getTypeBearer().toHuman();
+    }
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     * @param local {@code non-null;} register spec representing the local
+     * variable introduced by this instance
+     */
+    public LocalStart(SourcePosition position, RegisterSpec local) {
+        super(position);
+
+        if (local == null) {
+            throw new NullPointerException("local == null");
+        }
+
+        this.local = local;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisterOffset(int delta) {
+        return new LocalStart(getPosition(), local.withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new LocalStart(getPosition(), local);
+    }
+
+    /**
+     * Gets the register spec representing the local variable introduced
+     * by this instance.
+     *
+     * @return {@code non-null;} the register spec
+     */
+    public RegisterSpec getLocal() {
+        return local;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return local.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        return "local-start " + localString(local);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/OddSpacer.java b/dx/src/com/android/dx/dex/code/OddSpacer.java
new file mode 100644
index 0000000..756a0e2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OddSpacer.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Pseudo-instruction which either turns into a {@code nop} or
+ * nothingness, in order to make the subsequent instruction have an
+ * even address. This is used to align (subsequent) instructions that
+ * require it.
+ */
+public final class OddSpacer extends VariableSizeInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     */
+    public OddSpacer(SourcePosition position) {
+        super(position, RegisterSpecList.EMPTY);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return (getAddress() & 1);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out) {
+        if (codeSize() != 0) {
+            out.writeShort(InsnFormat.codeUnit(DalvOps.NOP, 0));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new OddSpacer(getPosition());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        if (codeSize() == 0) {
+            return null;
+        }
+
+        return "nop // spacer";
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/OutputCollector.java b/dx/src/com/android/dx/dex/code/OutputCollector.java
new file mode 100644
index 0000000..d78e5fc
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OutputCollector.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import java.util.ArrayList;
+
+/**
+ * Destination for {@link DalvInsn} instances being output. This class
+ * receives and collects instructions in two pieces &mdash; a primary
+ * list and a suffix (generally consisting of adjunct data referred to
+ * by the primary list, such as switch case tables) &mdash; which it
+ * merges and emits back out in the form of a {@link DalvInsnList}
+ * instance.
+ */
+public final class OutputCollector {
+    /**
+     * {@code non-null;} the associated finisher (which holds the instruction
+     * list in-progress)
+     */
+    private final OutputFinisher finisher;
+
+    /**
+     * {@code null-ok;} suffix for the output, or {@code null} if the suffix
+     * has been appended to the main output (by {@link #appendSuffixToOutput})
+     */
+    private ArrayList<DalvInsn> suffix;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param initialCapacity {@code >= 0;} initial capacity of the output list
+     * @param suffixInitialCapacity {@code >= 0;} initial capacity of the output
+     * suffix
+     * @param regCount {@code >= 0;} register count for the method
+     */
+    public OutputCollector(int initialCapacity, int suffixInitialCapacity,
+            int regCount) {
+        this.finisher = new OutputFinisher(initialCapacity, regCount);
+        this.suffix = new ArrayList<DalvInsn>(suffixInitialCapacity);
+    }
+
+    /**
+     * Adds an instruction to the output.
+     *
+     * @param insn {@code non-null;} the instruction to add
+     */
+    public void add(DalvInsn insn) {
+        finisher.add(insn);
+    }
+
+    /**
+     * Reverses a branch which is buried a given number of instructions
+     * backward in the output. It is illegal to call this unless the
+     * indicated instruction really is a reversible branch.
+     *
+     * @param which how many instructions back to find the branch;
+     * {@code 0} is the most recently added instruction,
+     * {@code 1} is the instruction before that, etc.
+     * @param newTarget {@code non-null;} the new target for the reversed branch
+     */
+    public void reverseBranch(int which, CodeAddress newTarget) {
+        finisher.reverseBranch(which, newTarget);
+    }
+
+    /**
+     * Adds an instruction to the output suffix.
+     *
+     * @param insn {@code non-null;} the instruction to add
+     */
+    public void addSuffix(DalvInsn insn) {
+        suffix.add(insn);
+    }
+
+    /**
+     * Gets the results of all the calls on this instance, in the form of
+     * an {@link OutputFinisher}.
+     *
+     * @return {@code non-null;} the output finisher
+     * @throws UnsupportedOperationException if this method has
+     * already been called
+     */
+    public OutputFinisher getFinisher() {
+        if (suffix == null) {
+            throw new UnsupportedOperationException("already processed");
+        }
+
+        appendSuffixToOutput();
+        return finisher;
+    }
+
+    /**
+     * Helper for {@link #getFinisher}, which appends the suffix to
+     * the primary output.
+     */
+    private void appendSuffixToOutput() {
+        int size = suffix.size();
+
+        for (int i = 0; i < size; i++) {
+            finisher.add(suffix.get(i));
+        }
+
+        suffix = null;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/OutputFinisher.java b/dx/src/com/android/dx/dex/code/OutputFinisher.java
new file mode 100644
index 0000000..4a51198
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OutputFinisher.java
@@ -0,0 +1,737 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Processor for instruction lists, which takes a "first cut" of
+ * instruction selection as a basis and produces a "final cut" in the
+ * form of a {@link DalvInsnList} instance.
+ */
+public final class OutputFinisher {
+    /**
+     * {@code >= 0;} register count for the method, not including any extra
+     * "reserved" registers needed to translate "difficult" instructions
+     */
+    private final int unreservedRegCount;
+
+    /** {@code non-null;} the list of instructions, per se */
+    private ArrayList<DalvInsn> insns;
+
+    /** whether any instruction has position info */
+    private boolean hasAnyPositionInfo;
+
+    /** whether any instruction has local variable info */
+    private boolean hasAnyLocalInfo;
+
+    /**
+     * {@code >= 0;} the count of reserved registers (low-numbered
+     * registers used when expanding instructions that can't be
+     * represented simply); becomes valid after a call to {@link
+     * #massageInstructions}
+     */
+    private int reservedCount;
+
+    /**
+     * Constructs an instance. It initially contains no instructions.
+     *
+     * @param regCount {@code >= 0;} register count for the method
+     * @param initialCapacity {@code >= 0;} initial capacity of the instructions
+     * list
+     */
+    public OutputFinisher(int initialCapacity, int regCount) {
+        this.unreservedRegCount = regCount;
+        this.insns = new ArrayList<DalvInsn>(initialCapacity);
+        this.reservedCount = -1;
+        this.hasAnyPositionInfo = false;
+        this.hasAnyLocalInfo = false;
+    }
+
+    /**
+     * Returns whether any of the instructions added to this instance
+     * come with position info.
+     *
+     * @return whether any of the instructions added to this instance
+     * come with position info
+     */
+    public boolean hasAnyPositionInfo() {
+        return hasAnyPositionInfo;
+    }
+
+    /**
+     * Returns whether this instance has any local variable information.
+     *
+     * @return whether this instance has any local variable information
+     */
+    public boolean hasAnyLocalInfo() {
+        return hasAnyLocalInfo;
+    }
+
+    /**
+     * Helper for {@link #add} which scrutinizes a single
+     * instruction for local variable information.
+     *
+     * @param insn {@code non-null;} instruction to scrutinize
+     * @return {@code true} iff the instruction refers to any
+     * named locals
+     */
+    private static boolean hasLocalInfo(DalvInsn insn) {
+        if (insn instanceof LocalSnapshot) {
+            RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
+            int size = specs.size();
+            for (int i = 0; i < size; i++) {
+                if (hasLocalInfo(specs.get(i))) {
+                    return true;
+                }
+            }
+        } else if (insn instanceof LocalStart) {
+            RegisterSpec spec = ((LocalStart) insn).getLocal();
+            if (hasLocalInfo(spec)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single
+     * register spec.
+     *
+     * @param spec {@code non-null;} spec to scrutinize
+     * @return {@code true} iff the spec refers to any
+     * named locals
+     */
+    private static boolean hasLocalInfo(RegisterSpec spec) {
+        return (spec != null)
+            && (spec.getLocalItem().getName() != null);
+    }
+
+    /**
+     * Returns the set of all constants referred to by instructions added
+     * to this instance.
+     *
+     * @return {@code non-null;} the set of constants
+     */
+    public HashSet<Constant> getAllConstants() {
+        HashSet<Constant> result = new HashSet<Constant>(20);
+
+        for (DalvInsn insn : insns) {
+            addConstants(result, insn);
+        }
+
+        return result;
+    }
+
+    /**
+     * Helper for {@link #getAllConstants} which adds all the info for
+     * a single instruction.
+     *
+     * @param result {@code non-null;} result set to add to
+     * @param insn {@code non-null;} instruction to scrutinize
+     */
+    private static void addConstants(HashSet<Constant> result,
+            DalvInsn insn) {
+        if (insn instanceof CstInsn) {
+            Constant cst = ((CstInsn) insn).getConstant();
+            result.add(cst);
+        } else if (insn instanceof LocalSnapshot) {
+            RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
+            int size = specs.size();
+            for (int i = 0; i < size; i++) {
+                addConstants(result, specs.get(i));
+            }
+        } else if (insn instanceof LocalStart) {
+            RegisterSpec spec = ((LocalStart) insn).getLocal();
+            addConstants(result, spec);
+        }
+    }
+
+    /**
+     * Helper for {@link #getAllConstants} which adds all the info for
+     * a single {@code RegisterSpec}.
+     *
+     * @param result {@code non-null;} result set to add to
+     * @param spec {@code null-ok;} register spec to add
+     */
+    private static void addConstants(HashSet<Constant> result,
+            RegisterSpec spec) {
+        if (spec == null) {
+            return;
+        }
+
+        LocalItem local = spec.getLocalItem();
+        CstUtf8 name = local.getName();
+        CstUtf8 signature = local.getSignature();
+        Type type = spec.getType();
+
+        if (type != Type.KNOWN_NULL) {
+            result.add(CstType.intern(type));
+        }
+
+        if (name != null) {
+            result.add(name);
+        }
+
+        if (signature != null) {
+            result.add(signature);
+        }
+    }
+
+    /**
+     * Adds an instruction to the output.
+     *
+     * @param insn {@code non-null;} the instruction to add
+     */
+    public void add(DalvInsn insn) {
+        insns.add(insn);
+        updateInfo(insn);
+    }
+
+    /**
+     * Inserts an instruction in the output at the given offset.
+     *
+     * @param at {@code >= 0;} what index to insert at
+     * @param insn {@code non-null;} the instruction to insert
+     */
+    public void insert(int at, DalvInsn insn) {
+        insns.add(at, insn);
+        updateInfo(insn);
+    }
+
+    /**
+     * Helper for {@link #add} and {@link #insert},
+     * which updates the position and local info flags.
+     *
+     * @param insn {@code non-null;} an instruction that was just introduced
+     */
+    private void updateInfo(DalvInsn insn) {
+        if (! hasAnyPositionInfo) {
+            SourcePosition pos = insn.getPosition();
+            if (pos.getLine() >= 0) {
+                hasAnyPositionInfo = true;
+            }
+        }
+
+        if (! hasAnyLocalInfo) {
+            if (hasLocalInfo(insn)) {
+                hasAnyLocalInfo = true;
+            }
+        }
+    }
+
+    /**
+     * Reverses a branch which is buried a given number of instructions
+     * backward in the output. It is illegal to call this unless the
+     * indicated instruction really is a reversible branch.
+     *
+     * @param which how many instructions back to find the branch;
+     * {@code 0} is the most recently added instruction,
+     * {@code 1} is the instruction before that, etc.
+     * @param newTarget {@code non-null;} the new target for the reversed branch
+     */
+    public void reverseBranch(int which, CodeAddress newTarget) {
+        int size = insns.size();
+        int index = size - which - 1;
+        TargetInsn targetInsn;
+
+        try {
+            targetInsn = (TargetInsn) insns.get(index);
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("too few instructions");
+        } catch (ClassCastException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("non-reversible instruction");
+        }
+
+        /*
+         * No need to call this.set(), since the format and other info
+         * are the same.
+         */
+        insns.set(index, targetInsn.withNewTargetAndReversed(newTarget));
+    }
+
+    /**
+     * Assigns indices in all instructions that need them, using the
+     * given callback to perform lookups. This should be called before
+     * calling {@link #finishProcessingAndGetList}.
+     *
+     * @param callback {@code non-null;} callback object
+     */
+    public void assignIndices(DalvCode.AssignIndicesCallback callback) {
+        for (DalvInsn insn : insns) {
+            if (insn instanceof CstInsn) {
+                assignIndices((CstInsn) insn, callback);
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #assignIndices} which does assignment for one
+     * instruction.
+     *
+     * @param insn {@code non-null;} the instruction
+     * @param callback {@code non-null;} the callback
+     */
+    private static void assignIndices(CstInsn insn,
+            DalvCode.AssignIndicesCallback callback) {
+        Constant cst = insn.getConstant();
+        int index = callback.getIndex(cst);
+
+        if (index >= 0) {
+            insn.setIndex(index);
+        }
+
+        if (cst instanceof CstMemberRef) {
+            CstMemberRef member = (CstMemberRef) cst;
+            CstType definer = member.getDefiningClass();
+            index = callback.getIndex(definer);
+            if (index >= 0) {
+                insn.setClassIndex(index);
+            }
+        }
+    }
+
+    /**
+     * Does final processing on this instance and gets the output as
+     * a {@link DalvInsnList}. Final processing consists of:
+     *
+     * <ul>
+     *   <li>optionally renumbering registers (to make room as needed for
+     *   expanded instructions)</li>
+     *   <li>picking a final opcode for each instruction</li>
+     *   <li>rewriting instructions, because of register number,
+     *   constant pool index, or branch target size issues</li>
+     *   <li>assigning final addresses</li>
+     * </ul>
+     *
+     * <p><b>Note:</b> This method may only be called once per instance
+     * of this class.</p>
+     *
+     * @return {@code non-null;} the output list
+     * @throws UnsupportedOperationException if this method has
+     * already been called
+     */
+    public DalvInsnList finishProcessingAndGetList() {
+        if (reservedCount >= 0) {
+            throw new UnsupportedOperationException("already processed");
+        }
+
+        InsnFormat[] formats = makeFormatsArray();
+        reserveRegisters(formats);
+        massageInstructions(formats);
+        assignAddressesAndFixBranches();
+
+        return DalvInsnList.makeImmutable(insns,
+                reservedCount + unreservedRegCount);
+    }
+
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which extracts
+     * the format out of each instruction into a separate array, to be
+     * further manipulated as things progress.
+     *
+     * @return {@code non-null;} the array of formats
+     */
+    private InsnFormat[] makeFormatsArray() {
+        int size = insns.size();
+        InsnFormat[] result = new InsnFormat[size];
+
+        for (int i = 0; i < size; i++) {
+            result[i] = insns.get(i).getOpcode().getFormat();
+        }
+
+        return result;
+    }
+
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which figures
+     * out how many reserved registers are required and then reserving
+     * them. It also updates the given {@code formats} array so
+     * as to avoid extra work when constructing the massaged
+     * instruction list.
+     *
+     * @param formats {@code non-null;} array of per-instruction format selections
+     */
+    private void reserveRegisters(InsnFormat[] formats) {
+        int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount;
+
+        /*
+         * Call calculateReservedCount() and then perform register
+         * reservation, repeatedly until no new reservations happen.
+         */
+        for (;;) {
+            int newReservedCount = calculateReservedCount(formats);
+            if (oldReservedCount >= newReservedCount) {
+                break;
+            }
+
+            int reservedDifference = newReservedCount - oldReservedCount;
+            int size = insns.size();
+
+            for (int i = 0; i < size; i++) {
+                /*
+                 * CodeAddress instance identity is used to link
+                 * TargetInsns to their targets, so it is
+                 * inappropriate to make replacements, and they don't
+                 * have registers in any case. Hence, the instanceof
+                 * test below.
+                 */
+                DalvInsn insn = insns.get(i);
+                if (!(insn instanceof CodeAddress)) {
+                    /*
+                     * No need to call this.set() since the format and
+                     * other info are the same.
+                     */
+                    insns.set(i, insn.withRegisterOffset(reservedDifference));
+                }
+            }
+
+            oldReservedCount = newReservedCount;
+        }
+
+        reservedCount = oldReservedCount;
+    }
+
+    /**
+     * Helper for {@link #reserveRegisters}, which does one
+     * pass over the instructions, calculating the number of
+     * registers that need to be reserved. It also updates the
+     * {@code formats} list to help avoid extra work in future
+     * register reservation passes.
+     *
+     * @param formats {@code non-null;} array of per-instruction format selections
+     * @return {@code >= 0;} the count of reserved registers
+     */
+    private int calculateReservedCount(InsnFormat[] formats) {
+        int size = insns.size();
+
+        /*
+         * Potential new value of reservedCount, which gets updated in the
+         * following loop. It starts out with the existing reservedCount
+         * and gets increased if it turns out that additional registers
+         * need to be reserved.
+         */
+        int newReservedCount = reservedCount;
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            InsnFormat originalFormat = formats[i];
+            InsnFormat newFormat = findFormatForInsn(insn, originalFormat);
+
+            if (originalFormat == newFormat) {
+                continue;
+            }
+
+            if (newFormat == null) {
+                /*
+                 * The instruction will need to be expanded, so reserve
+                 * registers for it.
+                 */
+                int reserve = insn.getMinimumRegisterRequirement();
+                if (reserve > newReservedCount) {
+                    newReservedCount = reserve;
+                }
+            }
+
+            formats[i] = newFormat;
+        }
+
+        return newReservedCount;
+    }
+
+    /**
+     * Attempts to fit the given instruction into a format, returning
+     * either a format that the instruction fits into or {@code null}
+     * to indicate that the instruction will need to be expanded. This
+     * fitting process starts with the given format as a first "best
+     * guess" and then pessimizes from there if necessary.
+     *
+     * @param insn {@code non-null;} the instruction in question
+     * @param format {@code null-ok;} the current guess as to the best instruction
+     * format to use; {@code null} means that no simple format fits
+     * @return {@code null-ok;} a possibly-different format, which is either a
+     * good fit or {@code null} to indicate that no simple format
+     * fits
+     */
+    private InsnFormat findFormatForInsn(DalvInsn insn, InsnFormat format) {
+        if (format == null) {
+            // The instruction is already known not to fit any simple format.
+            return format;
+        }
+
+        if (format.isCompatible(insn)) {
+            // The instruction already fits in the current best-known format.
+            return format;
+        }
+
+        Dop dop = insn.getOpcode();
+        int family = dop.getFamily();
+
+        for (;;) {
+            format = format.nextUp();
+            if ((format == null) ||
+                    (format.isCompatible(insn) &&
+                     (Dops.getOrNull(family, format) != null))) {
+                break;
+            }
+        }
+
+        return format;
+    }
+
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which goes
+     * through each instruction in the output, making sure its opcode
+     * can accomodate its arguments. In cases where the opcode is
+     * unable to do so, this replaces the instruction with a larger
+     * instruction with identical semantics that <i>will</i> work.
+     *
+     * <p>This method may also reserve a number of low-numbered
+     * registers, renumbering the instructions' original registers, in
+     * order to have register space available in which to move
+     * very-high registers when expanding instructions into
+     * multi-instruction sequences. This expansion is done when no
+     * simple instruction format can be found for a given instruction that
+     * is able to accomodate that instruction's registers.</p>
+     *
+     * <p>This method ignores issues of branch target size, since
+     * final addresses aren't known at the point that this method is
+     * called.</p>
+     *
+     * @param formats {@code non-null;} array of per-instruction format selections
+     */
+    private void massageInstructions(InsnFormat[] formats) {
+        if (reservedCount == 0) {
+            /*
+             * The easy common case: No registers were reserved, so we
+             * merely need to replace any instructions whose format changed
+             * during the reservation pass, but all instructions will stay
+             * at their original indices, and the instruction list doesn't
+             * grow.
+             */
+            int size = insns.size();
+
+            for (int i = 0; i < size; i++) {
+                DalvInsn insn = insns.get(i);
+                Dop dop = insn.getOpcode();
+                InsnFormat format = formats[i];
+
+                if (format != dop.getFormat()) {
+                    dop = Dops.getOrNull(dop.getFamily(), format);
+                    insns.set(i, insn.withOpcode(dop));
+                }
+            }
+        } else {
+            /*
+             * The difficult uncommon case: Some instructions have to be
+             * expanded to deal with high registers.
+             */
+            insns = performExpansion(formats);
+        }
+    }
+
+    /**
+     * Helper for {@link #massageInstructions}, which constructs a
+     * replacement list, where each {link DalvInsn} instance that
+     * couldn't be represented simply (due to register representation
+     * problems) is expanded into a series of instances that together
+     * perform the proper function.
+     *
+     * @param formats {@code non-null;} array of per-instruction format selections
+     * @return {@code non-null;} the replacement list
+     */
+    private ArrayList<DalvInsn> performExpansion(InsnFormat[] formats) {
+        int size = insns.size();
+        ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            Dop dop = insn.getOpcode();
+            InsnFormat originalFormat = dop.getFormat();
+            InsnFormat currentFormat = formats[i];
+            DalvInsn prefix;
+            DalvInsn suffix;
+
+            if (currentFormat != null) {
+                // No expansion is necessary.
+                prefix = null;
+                suffix = null;
+            } else {
+                // Expansion is required.
+                prefix = insn.hrPrefix();
+                suffix = insn.hrSuffix();
+
+                /*
+                 * Get the initial guess as to the hr version, but then
+                 * let findFormatForInsn() pick a better format, if any.
+                 */
+                insn = insn.hrVersion();
+                originalFormat = insn.getOpcode().getFormat();
+                currentFormat = findFormatForInsn(insn, originalFormat);
+            }
+
+            if (prefix != null) {
+                result.add(prefix);
+            }
+
+            if (currentFormat != originalFormat) {
+                dop = Dops.getOrNull(dop.getFamily(), currentFormat);
+                insn = insn.withOpcode(dop);
+            }
+            result.add(insn);
+
+            if (suffix != null) {
+                result.add(suffix);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Helper for {@link #finishProcessingAndGetList}, which assigns
+     * addresses to each instruction, possibly rewriting branches to
+     * fix ones that wouldn't otherwise be able to reach their
+     * targets.
+     */
+    private void assignAddressesAndFixBranches() {
+        for (;;) {
+            assignAddresses();
+            if (!fixBranches()) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #assignAddressesAndFixBranches}, which
+     * assigns an address to each instruction, in order.
+     */
+    private void assignAddresses() {
+        int address = 0;
+        int size = insns.size();
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            insn.setAddress(address);
+            address += insn.codeSize();
+        }
+    }
+
+    /**
+     * Helper for {@link #assignAddressesAndFixBranches}, which checks
+     * the branch target size requirement of each branch instruction
+     * to make sure it fits. For instructions that don't fit, this
+     * rewrites them to use a {@code goto} of some sort. In the
+     * case of a conditional branch that doesn't fit, the sense of the
+     * test is reversed in order to branch around a {@code goto}
+     * to the original target.
+     *
+     * @return whether any branches had to be fixed
+     */
+    private boolean fixBranches() {
+        int size = insns.size();
+        boolean anyFixed = false;
+
+        for (int i = 0; i < size; i++) {
+            DalvInsn insn = insns.get(i);
+            if (!(insn instanceof TargetInsn)) {
+                // This loop only needs to inspect TargetInsns.
+                continue;
+            }
+
+            Dop dop = insn.getOpcode();
+            InsnFormat format = dop.getFormat();
+            TargetInsn target = (TargetInsn) insn;
+
+            if (format.branchFits(target)) {
+                continue;
+            }
+
+            if (dop.getFamily() == DalvOps.GOTO) {
+                // It is a goto; widen it if possible.
+                InsnFormat newFormat = findFormatForInsn(insn, format);
+                if (newFormat == null) {
+                    /*
+                     * The branch is already maximally large. This should
+                     * only be possible if a method somehow manages to have
+                     * more than 2^31 code units.
+                     */
+                    throw new UnsupportedOperationException("method too long");
+                }
+                dop = Dops.getOrNull(dop.getFamily(), newFormat);
+                insn = insn.withOpcode(dop);
+                insns.set(i, insn);
+            } else {
+                /*
+                 * It is a conditional: Reverse its sense, and arrange for
+                 * it to branch around an absolute goto to the original
+                 * branch target.
+                 *
+                 * Note: An invariant of the list being processed is
+                 * that every TargetInsn is followed by a CodeAddress.
+                 * Hence, it is always safe to get the next element
+                 * after a TargetInsn and cast it to CodeAddress, as
+                 * is happening a few lines down.
+                 *
+                 * Also note: Size gets incremented by one here, as we
+                 * have -- in the net -- added one additional element
+                 * to the list, so we increment i to match. The added
+                 * and changed elements will be inspected by a repeat
+                 * call to this method after this invocation returns.
+                 */
+                CodeAddress newTarget;
+                try {
+                    newTarget = (CodeAddress) insns.get(i + 1);
+                } catch (IndexOutOfBoundsException ex) {
+                    // The TargetInsn / CodeAddress invariant was violated.
+                    throw new IllegalStateException(
+                            "unpaired TargetInsn (dangling)");
+                } catch (ClassCastException ex) {
+                    // The TargetInsn / CodeAddress invariant was violated.
+                    throw new IllegalStateException("unpaired TargetInsn");
+                }
+                TargetInsn gotoInsn =
+                    new TargetInsn(Dops.GOTO, target.getPosition(),
+                            RegisterSpecList.EMPTY, target.getTarget());
+                insns.set(i, gotoInsn);
+                insns.add(i, target.withNewTargetAndReversed(newTarget));
+                size++;
+                i++;
+            }
+
+            anyFixed = true;
+        }
+
+        return anyFixed;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/PositionList.java b/dx/src/com/android/dx/dex/code/PositionList.java
new file mode 100644
index 0000000..1e07e46
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/PositionList.java
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of source position entries. This class includes a utility
+ * method to extract an instance out of a {@link DalvInsnList}.
+ */
+public final class PositionList extends FixedSizeList {
+    /** {@code non-null;} empty instance */
+    public static final PositionList EMPTY = new PositionList(0);
+
+    /**
+     * constant for {@link #make} to indicate that no actual position
+     * information should be returned
+     */
+    public static final int NONE = 1;
+
+    /**
+     * constant for {@link #make} to indicate that only line number
+     * transitions should be returned
+     */
+    public static final int LINES = 2;
+
+    /**
+     * constant for {@link #make} to indicate that only "important" position
+     * information should be returned. This includes block starts and
+     * instructions that might throw.
+     */
+    public static final int IMPORTANT = 3;
+
+    /**
+     * Extracts and returns the source position information out of an
+     * instruction list.
+     *
+     * @param insns {@code non-null;} instructions to convert
+     * @param howMuch how much information should be included; one of the
+     * static constants defined by this class
+     * @return {@code non-null;} the positions list
+     */
+    public static PositionList make(DalvInsnList insns, int howMuch) {
+        switch (howMuch) {
+            case NONE: {
+                return EMPTY;
+            }
+            case LINES:
+            case IMPORTANT: {
+                // Valid.
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException("bogus howMuch");
+            }
+        }
+
+        SourcePosition noInfo = SourcePosition.NO_INFO;
+        SourcePosition cur = noInfo;
+        int sz = insns.size();
+        PositionList.Entry[] arr = new PositionList.Entry[sz];
+        boolean lastWasTarget = false;
+        int at = 0;
+
+        for (int i = 0; i < sz; i++) {
+            DalvInsn insn = insns.get(i);
+
+            if (insn instanceof CodeAddress) {
+                lastWasTarget = true;;
+                continue;
+            }
+
+            SourcePosition pos = insn.getPosition();
+
+            if (pos.equals(noInfo) || pos.sameLine(cur)) {
+                continue;
+            }
+
+            if ((howMuch == IMPORTANT) && !lastWasTarget) {
+                continue;
+            }
+
+            cur = pos;
+            arr[at] = new PositionList.Entry(insn.getAddress(), pos);
+            at++;
+
+            lastWasTarget = false;
+        }
+
+        PositionList result = new PositionList(at);
+        for (int i = 0; i < at; i++) {
+            result.set(i, arr[i]);
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size {@code >= 0;} the size of the list
+     */
+    public PositionList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @return {@code non-null;} element at that index
+     */
+    public Entry get(int n) {
+        return (Entry) get0(n);
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param entry {@code non-null;} the entry to set at {@code n}
+     */
+    public void set(int n, Entry entry) {
+        set0(n, entry);
+    }
+
+    /**
+     * Entry in a position list.
+     */
+    public static class Entry {
+        /** {@code >= 0;} address of this entry */
+        private final int address;
+
+        /** {@code non-null;} corresponding source position information */
+        private final SourcePosition position;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param address {@code >= 0;} address of this entry
+         * @param position {@code non-null;} corresponding source position information
+         */
+        public Entry (int address, SourcePosition position) {
+            if (address < 0) {
+                throw new IllegalArgumentException("address < 0");
+            }
+
+            if (position == null) {
+                throw new NullPointerException("position == null");
+            }
+
+            this.address = address;
+            this.position = position;
+        }
+
+        /**
+         * Gets the address.
+         *
+         * @return {@code >= 0;} the address
+         */
+        public int getAddress() {
+            return address;
+        }
+
+        /**
+         * Gets the source position information.
+         *
+         * @return {@code non-null;} the position information
+         */
+        public SourcePosition getPosition() {
+            return position;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/RopToDop.java b/dx/src/com/android/dx/dex/code/RopToDop.java
new file mode 100644
index 0000000..d8fa1cc
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopToDop.java
@@ -0,0 +1,415 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+
+import java.util.HashMap;
+
+/**
+ * Translator from rop-level {@link Insn} instances to corresponding
+ * {@link Dop} instances.
+ */
+public final class RopToDop {
+    /** {@code non-null;} map from all the common rops to dalvik opcodes */
+    private static final HashMap<Rop, Dop> MAP;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private RopToDop() {
+        // This space intentionally left blank.
+    }
+
+    static {
+        /*
+         * Note: The choices made here are to pick the optimistically
+         * smallest Dalvik opcode, and leave it to later processing to
+         * pessimize.
+         */
+        MAP = new HashMap<Rop, Dop>(400);
+        MAP.put(Rops.NOP,               Dops.NOP);
+        MAP.put(Rops.MOVE_INT,          Dops.MOVE);
+        MAP.put(Rops.MOVE_LONG,         Dops.MOVE_WIDE);
+        MAP.put(Rops.MOVE_FLOAT,        Dops.MOVE);
+        MAP.put(Rops.MOVE_DOUBLE,       Dops.MOVE_WIDE);
+        MAP.put(Rops.MOVE_OBJECT,       Dops.MOVE_OBJECT);
+        MAP.put(Rops.MOVE_PARAM_INT,    Dops.MOVE);
+        MAP.put(Rops.MOVE_PARAM_LONG,   Dops.MOVE_WIDE);
+        MAP.put(Rops.MOVE_PARAM_FLOAT,  Dops.MOVE);
+        MAP.put(Rops.MOVE_PARAM_DOUBLE, Dops.MOVE_WIDE);
+        MAP.put(Rops.MOVE_PARAM_OBJECT, Dops.MOVE_OBJECT);
+
+        /*
+         * Note: No entry for MOVE_EXCEPTION, since it varies by
+         * exception type. (That is, there is no unique instance to
+         * add to the map.)
+         */
+
+        MAP.put(Rops.CONST_INT,         Dops.CONST_4);
+        MAP.put(Rops.CONST_LONG,        Dops.CONST_WIDE_16);
+        MAP.put(Rops.CONST_FLOAT,       Dops.CONST_4);
+        MAP.put(Rops.CONST_DOUBLE,      Dops.CONST_WIDE_16);
+
+        /*
+         * Note: No entry for CONST_OBJECT, since it needs to turn
+         * into either CONST_STRING or CONST_CLASS.
+         */
+
+        /*
+         * TODO: I think the only case of this is for null, and
+         * const/4 should cover that.
+         */
+        MAP.put(Rops.CONST_OBJECT_NOTHROW, Dops.CONST_4);
+
+        MAP.put(Rops.GOTO,                 Dops.GOTO);
+        MAP.put(Rops.IF_EQZ_INT,           Dops.IF_EQZ);
+        MAP.put(Rops.IF_NEZ_INT,           Dops.IF_NEZ);
+        MAP.put(Rops.IF_LTZ_INT,           Dops.IF_LTZ);
+        MAP.put(Rops.IF_GEZ_INT,           Dops.IF_GEZ);
+        MAP.put(Rops.IF_LEZ_INT,           Dops.IF_LEZ);
+        MAP.put(Rops.IF_GTZ_INT,           Dops.IF_GTZ);
+        MAP.put(Rops.IF_EQZ_OBJECT,        Dops.IF_EQZ);
+        MAP.put(Rops.IF_NEZ_OBJECT,        Dops.IF_NEZ);
+        MAP.put(Rops.IF_EQ_INT,            Dops.IF_EQ);
+        MAP.put(Rops.IF_NE_INT,            Dops.IF_NE);
+        MAP.put(Rops.IF_LT_INT,            Dops.IF_LT);
+        MAP.put(Rops.IF_GE_INT,            Dops.IF_GE);
+        MAP.put(Rops.IF_LE_INT,            Dops.IF_LE);
+        MAP.put(Rops.IF_GT_INT,            Dops.IF_GT);
+        MAP.put(Rops.IF_EQ_OBJECT,         Dops.IF_EQ);
+        MAP.put(Rops.IF_NE_OBJECT,         Dops.IF_NE);
+        MAP.put(Rops.SWITCH,               Dops.SPARSE_SWITCH);
+        MAP.put(Rops.ADD_INT,              Dops.ADD_INT_2ADDR);
+        MAP.put(Rops.ADD_LONG,             Dops.ADD_LONG_2ADDR);
+        MAP.put(Rops.ADD_FLOAT,            Dops.ADD_FLOAT_2ADDR);
+        MAP.put(Rops.ADD_DOUBLE,           Dops.ADD_DOUBLE_2ADDR);
+        MAP.put(Rops.SUB_INT,              Dops.SUB_INT_2ADDR);
+        MAP.put(Rops.SUB_LONG,             Dops.SUB_LONG_2ADDR);
+        MAP.put(Rops.SUB_FLOAT,            Dops.SUB_FLOAT_2ADDR);
+        MAP.put(Rops.SUB_DOUBLE,           Dops.SUB_DOUBLE_2ADDR);
+        MAP.put(Rops.MUL_INT,              Dops.MUL_INT_2ADDR);
+        MAP.put(Rops.MUL_LONG,             Dops.MUL_LONG_2ADDR);
+        MAP.put(Rops.MUL_FLOAT,            Dops.MUL_FLOAT_2ADDR);
+        MAP.put(Rops.MUL_DOUBLE,           Dops.MUL_DOUBLE_2ADDR);
+        MAP.put(Rops.DIV_INT,              Dops.DIV_INT_2ADDR);
+        MAP.put(Rops.DIV_LONG,             Dops.DIV_LONG_2ADDR);
+        MAP.put(Rops.DIV_FLOAT,            Dops.DIV_FLOAT_2ADDR);
+        MAP.put(Rops.DIV_DOUBLE,           Dops.DIV_DOUBLE_2ADDR);
+        MAP.put(Rops.REM_INT,              Dops.REM_INT_2ADDR);
+        MAP.put(Rops.REM_LONG,             Dops.REM_LONG_2ADDR);
+        MAP.put(Rops.REM_FLOAT,            Dops.REM_FLOAT_2ADDR);
+        MAP.put(Rops.REM_DOUBLE,           Dops.REM_DOUBLE_2ADDR);
+        MAP.put(Rops.NEG_INT,              Dops.NEG_INT);
+        MAP.put(Rops.NEG_LONG,             Dops.NEG_LONG);
+        MAP.put(Rops.NEG_FLOAT,            Dops.NEG_FLOAT);
+        MAP.put(Rops.NEG_DOUBLE,           Dops.NEG_DOUBLE);
+        MAP.put(Rops.AND_INT,              Dops.AND_INT_2ADDR);
+        MAP.put(Rops.AND_LONG,             Dops.AND_LONG_2ADDR);
+        MAP.put(Rops.OR_INT,               Dops.OR_INT_2ADDR);
+        MAP.put(Rops.OR_LONG,              Dops.OR_LONG_2ADDR);
+        MAP.put(Rops.XOR_INT,              Dops.XOR_INT_2ADDR);
+        MAP.put(Rops.XOR_LONG,             Dops.XOR_LONG_2ADDR);
+        MAP.put(Rops.SHL_INT,              Dops.SHL_INT_2ADDR);
+        MAP.put(Rops.SHL_LONG,             Dops.SHL_LONG_2ADDR);
+        MAP.put(Rops.SHR_INT,              Dops.SHR_INT_2ADDR);
+        MAP.put(Rops.SHR_LONG,             Dops.SHR_LONG_2ADDR);
+        MAP.put(Rops.USHR_INT,             Dops.USHR_INT_2ADDR);
+        MAP.put(Rops.USHR_LONG,            Dops.USHR_LONG_2ADDR);
+        MAP.put(Rops.NOT_INT,              Dops.NOT_INT);
+        MAP.put(Rops.NOT_LONG,             Dops.NOT_LONG);
+
+        MAP.put(Rops.ADD_CONST_INT,        Dops.ADD_INT_LIT8);
+        // Note: No dalvik ops for other types of add_const.
+
+        /*
+         * Note: No dalvik ops for any type of sub_const; there's a
+         * *reverse* sub (constant - reg) for ints, though, but that
+         * should end up getting handled at optimization time.
+         */
+
+        MAP.put(Rops.MUL_CONST_INT,        Dops.MUL_INT_LIT8);
+        // Note: No dalvik ops for other types of mul_const.
+
+        MAP.put(Rops.DIV_CONST_INT,        Dops.DIV_INT_LIT8);
+        // Note: No dalvik ops for other types of div_const.
+
+        MAP.put(Rops.REM_CONST_INT,        Dops.REM_INT_LIT8);
+        // Note: No dalvik ops for other types of rem_const.
+
+        MAP.put(Rops.AND_CONST_INT,        Dops.AND_INT_LIT8);
+        // Note: No dalvik op for and_const_long.
+
+        MAP.put(Rops.OR_CONST_INT,         Dops.OR_INT_LIT8);
+        // Note: No dalvik op for or_const_long.
+
+        MAP.put(Rops.XOR_CONST_INT,        Dops.XOR_INT_LIT8);
+        // Note: No dalvik op for xor_const_long.
+
+        MAP.put(Rops.SHL_CONST_INT,        Dops.SHL_INT_LIT8);
+        // Note: No dalvik op for shl_const_long.
+
+        MAP.put(Rops.SHR_CONST_INT,        Dops.SHR_INT_LIT8);
+        // Note: No dalvik op for shr_const_long.
+
+        MAP.put(Rops.USHR_CONST_INT,       Dops.USHR_INT_LIT8);
+        // Note: No dalvik op for shr_const_long.
+
+        MAP.put(Rops.CMPL_LONG,            Dops.CMP_LONG);
+        MAP.put(Rops.CMPL_FLOAT,           Dops.CMPL_FLOAT);
+        MAP.put(Rops.CMPL_DOUBLE,          Dops.CMPL_DOUBLE);
+        MAP.put(Rops.CMPG_FLOAT,           Dops.CMPG_FLOAT);
+        MAP.put(Rops.CMPG_DOUBLE,          Dops.CMPG_DOUBLE);
+        MAP.put(Rops.CONV_L2I,             Dops.LONG_TO_INT);
+        MAP.put(Rops.CONV_F2I,             Dops.FLOAT_TO_INT);
+        MAP.put(Rops.CONV_D2I,             Dops.DOUBLE_TO_INT);
+        MAP.put(Rops.CONV_I2L,             Dops.INT_TO_LONG);
+        MAP.put(Rops.CONV_F2L,             Dops.FLOAT_TO_LONG);
+        MAP.put(Rops.CONV_D2L,             Dops.DOUBLE_TO_LONG);
+        MAP.put(Rops.CONV_I2F,             Dops.INT_TO_FLOAT);
+        MAP.put(Rops.CONV_L2F,             Dops.LONG_TO_FLOAT);
+        MAP.put(Rops.CONV_D2F,             Dops.DOUBLE_TO_FLOAT);
+        MAP.put(Rops.CONV_I2D,             Dops.INT_TO_DOUBLE);
+        MAP.put(Rops.CONV_L2D,             Dops.LONG_TO_DOUBLE);
+        MAP.put(Rops.CONV_F2D,             Dops.FLOAT_TO_DOUBLE);
+        MAP.put(Rops.TO_BYTE,              Dops.INT_TO_BYTE);
+        MAP.put(Rops.TO_CHAR,              Dops.INT_TO_CHAR);
+        MAP.put(Rops.TO_SHORT,             Dops.INT_TO_SHORT);
+        MAP.put(Rops.RETURN_VOID,          Dops.RETURN_VOID);
+        MAP.put(Rops.RETURN_INT,           Dops.RETURN);
+        MAP.put(Rops.RETURN_LONG,          Dops.RETURN_WIDE);
+        MAP.put(Rops.RETURN_FLOAT,         Dops.RETURN);
+        MAP.put(Rops.RETURN_DOUBLE,        Dops.RETURN_WIDE);
+        MAP.put(Rops.RETURN_OBJECT,        Dops.RETURN_OBJECT);
+        MAP.put(Rops.ARRAY_LENGTH,         Dops.ARRAY_LENGTH);
+        MAP.put(Rops.THROW,                Dops.THROW);
+        MAP.put(Rops.MONITOR_ENTER,        Dops.MONITOR_ENTER);
+        MAP.put(Rops.MONITOR_EXIT,         Dops.MONITOR_EXIT);
+        MAP.put(Rops.AGET_INT,             Dops.AGET);
+        MAP.put(Rops.AGET_LONG,            Dops.AGET_WIDE);
+        MAP.put(Rops.AGET_FLOAT,           Dops.AGET);
+        MAP.put(Rops.AGET_DOUBLE,          Dops.AGET_WIDE);
+        MAP.put(Rops.AGET_OBJECT,          Dops.AGET_OBJECT);
+        MAP.put(Rops.AGET_BOOLEAN,         Dops.AGET_BOOLEAN);
+        MAP.put(Rops.AGET_BYTE,            Dops.AGET_BYTE);
+        MAP.put(Rops.AGET_CHAR,            Dops.AGET_CHAR);
+        MAP.put(Rops.AGET_SHORT,           Dops.AGET_SHORT);
+        MAP.put(Rops.APUT_INT,             Dops.APUT);
+        MAP.put(Rops.APUT_LONG,            Dops.APUT_WIDE);
+        MAP.put(Rops.APUT_FLOAT,           Dops.APUT);
+        MAP.put(Rops.APUT_DOUBLE,          Dops.APUT_WIDE);
+        MAP.put(Rops.APUT_OBJECT,          Dops.APUT_OBJECT);
+        MAP.put(Rops.APUT_BOOLEAN,         Dops.APUT_BOOLEAN);
+        MAP.put(Rops.APUT_BYTE,            Dops.APUT_BYTE);
+        MAP.put(Rops.APUT_CHAR,            Dops.APUT_CHAR);
+        MAP.put(Rops.APUT_SHORT,           Dops.APUT_SHORT);
+        MAP.put(Rops.NEW_INSTANCE,         Dops.NEW_INSTANCE);
+        MAP.put(Rops.CHECK_CAST,           Dops.CHECK_CAST);
+        MAP.put(Rops.INSTANCE_OF,          Dops.INSTANCE_OF);
+
+        MAP.put(Rops.GET_FIELD_LONG,       Dops.IGET_WIDE);
+        MAP.put(Rops.GET_FIELD_FLOAT,      Dops.IGET);
+        MAP.put(Rops.GET_FIELD_DOUBLE,     Dops.IGET_WIDE);
+        MAP.put(Rops.GET_FIELD_OBJECT,     Dops.IGET_OBJECT);
+        /*
+         * Note: No map entries for get_field_* for non-long integral types,
+         * since they need to be handled specially (see dopFor() below).
+         */
+
+        MAP.put(Rops.GET_STATIC_LONG,      Dops.SGET_WIDE);
+        MAP.put(Rops.GET_STATIC_FLOAT,     Dops.SGET);
+        MAP.put(Rops.GET_STATIC_DOUBLE,    Dops.SGET_WIDE);
+        MAP.put(Rops.GET_STATIC_OBJECT,    Dops.SGET_OBJECT);
+        /*
+         * Note: No map entries for get_static* for non-long integral types,
+         * since they need to be handled specially (see dopFor() below).
+         */
+
+        MAP.put(Rops.PUT_FIELD_LONG,       Dops.IPUT_WIDE);
+        MAP.put(Rops.PUT_FIELD_FLOAT,      Dops.IPUT);
+        MAP.put(Rops.PUT_FIELD_DOUBLE,     Dops.IPUT_WIDE);
+        MAP.put(Rops.PUT_FIELD_OBJECT,     Dops.IPUT_OBJECT);
+        /*
+         * Note: No map entries for put_field_* for non-long integral types,
+         * since they need to be handled specially (see dopFor() below).
+         */
+
+        MAP.put(Rops.PUT_STATIC_LONG,      Dops.SPUT_WIDE);
+        MAP.put(Rops.PUT_STATIC_FLOAT,     Dops.SPUT);
+        MAP.put(Rops.PUT_STATIC_DOUBLE,    Dops.SPUT_WIDE);
+        MAP.put(Rops.PUT_STATIC_OBJECT,    Dops.SPUT_OBJECT);
+        /*
+         * Note: No map entries for put_static* for non-long integral types,
+         * since they need to be handled specially (see dopFor() below).
+         */
+
+        /*
+         * Note: No map entries for invoke*, new_array, and
+         * filled_new_array, since they need to be handled specially
+         * (see dopFor() below).
+         */
+    }
+
+    /**
+     * Returns the dalvik opcode appropriate for the given register-based
+     * instruction.
+     *
+     * @param insn {@code non-null;} the original instruction
+     * @return the corresponding dalvik opcode; one of the constants in
+     * {@link Dops}
+     */
+    public static Dop dopFor(Insn insn) {
+        Rop rop = insn.getOpcode();
+
+        /*
+         * First, just try looking up the rop in the MAP of easy
+         * cases.
+         */
+        Dop result = MAP.get(rop);
+        if (result != null) {
+            return result;
+        }
+
+        /*
+         * There was no easy case for the rop, so look up the opcode, and
+         * do something special for each:
+         *
+         * The move_exception, new_array, filled_new_array, and
+         * invoke* opcodes won't be found in MAP, since they'll each
+         * have different source and/or result register types / lists.
+         *
+         * The get* and put* opcodes for (non-long) integral types
+         * aren't in the map, since the type signatures aren't
+         * sufficient to distinguish between the types (the salient
+         * source or result will always be just "int").
+         *
+         * And const instruction need to distinguish between strings and
+         * classes.
+         */
+
+        switch (rop.getOpcode()) {
+            case RegOps.MOVE_EXCEPTION:   return Dops.MOVE_EXCEPTION;
+            case RegOps.INVOKE_STATIC:    return Dops.INVOKE_STATIC;
+            case RegOps.INVOKE_VIRTUAL:   return Dops.INVOKE_VIRTUAL;
+            case RegOps.INVOKE_SUPER:     return Dops.INVOKE_SUPER;
+            case RegOps.INVOKE_DIRECT:    return Dops.INVOKE_DIRECT;
+            case RegOps.INVOKE_INTERFACE: return Dops.INVOKE_INTERFACE;
+            case RegOps.NEW_ARRAY:        return Dops.NEW_ARRAY;
+            case RegOps.FILLED_NEW_ARRAY: return Dops.FILLED_NEW_ARRAY;
+            case RegOps.FILL_ARRAY_DATA:  return Dops.FILL_ARRAY_DATA;
+            case RegOps.MOVE_RESULT: {
+                RegisterSpec resultReg = insn.getResult();
+
+                if (resultReg == null) {
+                    return Dops.NOP;
+                } else {
+                    switch (resultReg.getBasicType()) {
+                        case Type.BT_INT:
+                        case Type.BT_FLOAT:
+                        case Type.BT_BOOLEAN:
+                        case Type.BT_BYTE:
+                        case Type.BT_CHAR:
+                        case Type.BT_SHORT:
+                            return Dops.MOVE_RESULT;
+                        case Type.BT_LONG:
+                        case Type.BT_DOUBLE:
+                            return Dops.MOVE_RESULT_WIDE;
+                        case Type.BT_OBJECT:
+                            return Dops.MOVE_RESULT_OBJECT;
+                        default: {
+                            throw new RuntimeException("Unexpected basic type");
+                        }
+                    }
+                }
+            }
+
+            case RegOps.GET_FIELD: {
+                CstFieldRef ref =
+                    (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+                int basicType = ref.getBasicType();
+                switch (basicType) {
+                    case Type.BT_BOOLEAN: return Dops.IGET_BOOLEAN;
+                    case Type.BT_BYTE:    return Dops.IGET_BYTE;
+                    case Type.BT_CHAR:    return Dops.IGET_CHAR;
+                    case Type.BT_SHORT:   return Dops.IGET_SHORT;
+                    case Type.BT_INT:     return Dops.IGET;
+                }
+                break;
+            }
+            case RegOps.PUT_FIELD: {
+                CstFieldRef ref =
+                    (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+                int basicType = ref.getBasicType();
+                switch (basicType) {
+                    case Type.BT_BOOLEAN: return Dops.IPUT_BOOLEAN;
+                    case Type.BT_BYTE:    return Dops.IPUT_BYTE;
+                    case Type.BT_CHAR:    return Dops.IPUT_CHAR;
+                    case Type.BT_SHORT:   return Dops.IPUT_SHORT;
+                    case Type.BT_INT:     return Dops.IPUT;
+                }
+                break;
+            }
+            case RegOps.GET_STATIC: {
+                CstFieldRef ref =
+                    (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+                int basicType = ref.getBasicType();
+                switch (basicType) {
+                    case Type.BT_BOOLEAN: return Dops.SGET_BOOLEAN;
+                    case Type.BT_BYTE:    return Dops.SGET_BYTE;
+                    case Type.BT_CHAR:    return Dops.SGET_CHAR;
+                    case Type.BT_SHORT:   return Dops.SGET_SHORT;
+                    case Type.BT_INT:     return Dops.SGET;
+                }
+                break;
+            }
+            case RegOps.PUT_STATIC: {
+                CstFieldRef ref =
+                    (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+                int basicType = ref.getBasicType();
+                switch (basicType) {
+                    case Type.BT_BOOLEAN: return Dops.SPUT_BOOLEAN;
+                    case Type.BT_BYTE:    return Dops.SPUT_BYTE;
+                    case Type.BT_CHAR:    return Dops.SPUT_CHAR;
+                    case Type.BT_SHORT:   return Dops.SPUT_SHORT;
+                    case Type.BT_INT:     return Dops.SPUT;
+                }
+                break;
+            }
+            case RegOps.CONST: {
+                Constant cst = ((ThrowingCstInsn) insn).getConstant();
+                if (cst instanceof CstType) {
+                    return Dops.CONST_CLASS;
+                } else if (cst instanceof CstString) {
+                    return Dops.CONST_STRING;
+                }
+                break;
+            }
+        }
+
+        throw new RuntimeException("unknown rop: " + rop);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/RopTranslator.java b/dx/src/com/android/dx/dex/code/RopTranslator.java
new file mode 100644
index 0000000..a38ea11
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopTranslator.java
@@ -0,0 +1,872 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.FillArrayDataInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.LocalVariableInfo;
+import com.android.dx.rop.code.PlainCstInsn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+
+/**
+ * Translator from {@link RopMethod} to {@link DalvCode}. The {@link
+ * #translate} method is the thing to call on this class.
+ */
+public final class RopTranslator {
+    /** {@code non-null;} method to translate */
+    private final RopMethod method;
+
+    /**
+     * how much position info to preserve; one of the static
+     * constants in {@link PositionList}
+     */
+    private final int positionInfo;
+
+    /** {@code null-ok;} local variable info to use */
+    private final LocalVariableInfo locals;
+
+    /** {@code non-null;} container for all the address objects for the method */
+    private final BlockAddresses addresses;
+
+    /** {@code non-null;} list of output instructions in-progress */
+    private final OutputCollector output;
+
+    /** {@code non-null;} visitor to use during translation */
+    private final TranslationVisitor translationVisitor;
+
+    /** {@code >= 0;} register count for the method */
+    private final int regCount;
+
+    /** {@code null-ok;} block output order; becomes non-null in {@link #pickOrder} */
+    private int[] order;
+
+    /** size, in register units, of all the parameters to this method */
+    private final int paramSize;
+
+    /**
+     * true if the parameters to this method happen to be in proper order
+     * at the end of the frame (as the optimizer emits them)
+     */
+    private boolean paramsAreInOrder;
+
+    /**
+     * Translates a {@link RopMethod}. This may modify the given
+     * input.
+     *
+     * @param method {@code non-null;} the original method
+     * @param positionInfo how much position info to preserve; one of the
+     * static constants in {@link PositionList}
+     * @param locals {@code null-ok;} local variable information to use
+     * @param paramSize size, in register units, of all the parameters to
+     * this method
+     * @return {@code non-null;} the translated version
+     */
+    public static DalvCode translate(RopMethod method, int positionInfo,
+                                     LocalVariableInfo locals, int paramSize) {
+        RopTranslator translator =
+            new RopTranslator(method, positionInfo, locals,
+                    paramSize);
+        return translator.translateAndGetResult();
+    }
+
+    /**
+     * Constructs an instance. This method is private. Use {@link #translate}.
+     *
+     * @param method {@code non-null;} the original method
+     * @param positionInfo how much position info to preserve; one of the
+     * static constants in {@link PositionList}
+     * @param locals {@code null-ok;} local variable information to use
+     * @param paramSize size, in register units, of all the parameters to
+     * this method
+     */
+    private RopTranslator(RopMethod method, int positionInfo,
+                          LocalVariableInfo locals, int paramSize) {
+        this.method = method;
+        this.positionInfo = positionInfo;
+        this.locals = locals;
+        this.addresses = new BlockAddresses(method);
+        this.paramSize = paramSize;
+        this.order = null;
+        this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize);
+
+        BasicBlockList blocks = method.getBlocks();
+        int bsz = blocks.size();
+
+        /*
+         * Max possible instructions includes three code address
+         * objects per basic block (to the first and last instruction,
+         * and just past the end of the block), and the possibility of
+         * an extra goto at the end of each basic block.
+         */
+        int maxInsns = (bsz * 3) + blocks.getInstructionCount();
+
+        if (locals != null) {
+            /*
+             * If we're tracking locals, then there's could be another
+             * extra instruction per block (for the locals state at the
+             * start of the block) as well as one for each interblock
+             * local introduction.
+             */
+            maxInsns += bsz + locals.getAssignmentCount();
+        }
+
+        /*
+         * If params are not in order, we will need register space
+         * for them before this is all over...
+         */
+        this.regCount = blocks.getRegCount()
+                + (paramsAreInOrder ? 0 : this.paramSize);
+
+        this.output = new OutputCollector(maxInsns, bsz * 3, regCount);
+
+        if (locals != null) {
+            this.translationVisitor =
+                new LocalVariableAwareTranslationVisitor(output, locals);
+        } else {
+            this.translationVisitor = new TranslationVisitor(output);
+        }
+    }
+
+    /**
+     * Checks to see if the move-param instructions that occur in this
+     * method happen to slot the params in an order at the top of the
+     * stack frame that matches dalvik's calling conventions. This will
+     * alway result in "true" for methods that have run through the
+     * SSA optimizer.
+     *
+     * @param paramSize size, in register units, of all the parameters
+     * to this method
+     */
+    private static boolean calculateParamsAreInOrder(RopMethod method,
+            final int paramSize) {
+        final boolean[] paramsAreInOrder = { true };
+        final int initialRegCount = method.getBlocks().getRegCount();
+
+        /*
+         * We almost could just check the first block here, but the
+         * {@code cf} layer will put in a second move-param in a
+         * subsequent block in the case of synchronized methods.
+         */
+        method.getBlocks().forEachInsn(new Insn.BaseVisitor() {
+            public void visitPlainCstInsn(PlainCstInsn insn) {
+                if (insn.getOpcode().getOpcode()== RegOps.MOVE_PARAM) {
+                    int param =
+                        ((CstInteger) insn.getConstant()).getValue();
+
+                    paramsAreInOrder[0] = paramsAreInOrder[0]
+                            && ((initialRegCount - paramSize + param)
+                                == insn.getResult().getReg());
+                }
+            }
+        });
+
+        return paramsAreInOrder[0];
+    }
+
+    /**
+     * Does the translation and returns the result.
+     *
+     * @return {@code non-null;} the result
+     */
+    private DalvCode translateAndGetResult() {
+        pickOrder();
+        outputInstructions();
+
+        StdCatchBuilder catches =
+            new StdCatchBuilder(method, order, addresses);
+
+        return new DalvCode(positionInfo, output.getFinisher(), catches);
+    }
+
+    /**
+     * Performs initial creation of output instructions based on the
+     * original blocks.
+     */
+    private void outputInstructions() {
+        BasicBlockList blocks = method.getBlocks();
+        int[] order = this.order;
+        int len = order.length;
+
+        // Process the blocks in output order.
+        for (int i = 0; i < len; i++) {
+            int nextI = i + 1;
+            int nextLabel = (nextI == order.length) ? -1 : order[nextI];
+            outputBlock(blocks.labelToBlock(order[i]), nextLabel);
+        }
+    }
+
+    /**
+     * Helper for {@link #outputInstructions}, which does the processing
+     * and output of one block.
+     *
+     * @param block {@code non-null;} the block to process and output
+     * @param nextLabel {@code >= -1;} the next block that will be processed, or
+     * {@code -1} if there is no next block
+     */
+    private void outputBlock(BasicBlock block, int nextLabel) {
+        // Append the code address for this block.
+        CodeAddress startAddress = addresses.getStart(block);
+        output.add(startAddress);
+
+        // Append the local variable state for the block.
+        if (locals != null) {
+            RegisterSpecSet starts = locals.getStarts(block);
+            output.add(new LocalSnapshot(startAddress.getPosition(),
+                                         starts));
+        }
+
+        /*
+         * Choose and append an output instruction for each original
+         * instruction.
+         */
+        translationVisitor.setBlock(block, addresses.getLast(block));
+        block.getInsns().forEach(translationVisitor);
+
+        // Insert the block end code address.
+        output.add(addresses.getEnd(block));
+
+        // Set up for end-of-block activities.
+
+        int succ = block.getPrimarySuccessor();
+        Insn lastInsn = block.getLastInsn();
+
+        /*
+         * Check for (and possibly correct for) a non-optimal choice of
+         * which block will get output next.
+         */
+
+        if ((succ >= 0) && (succ != nextLabel)) {
+            /*
+             * The block has a "primary successor" and that primary
+             * successor isn't the next block to be output.
+             */
+            Rop lastRop = lastInsn.getOpcode();
+            if ((lastRop.getBranchingness() == Rop.BRANCH_IF) &&
+                    (block.getSecondarySuccessor() == nextLabel)) {
+                /*
+                 * The block ends with an "if" of some sort, and its
+                 * secondary successor (the "then") is in fact the
+                 * next block to output. So, reverse the sense of
+                 * the test, so that we can just emit the next block
+                 * without an interstitial goto.
+                 */
+                output.reverseBranch(1, addresses.getStart(succ));
+            } else {
+                /*
+                 * Our only recourse is to add a goto here to get the
+                 * flow to be correct.
+                 */
+                TargetInsn insn =
+                    new TargetInsn(Dops.GOTO, lastInsn.getPosition(),
+                            RegisterSpecList.EMPTY,
+                            addresses.getStart(succ));
+                output.add(insn);
+            }
+        }
+    }
+
+    /**
+     * Picks an order for the blocks by doing "trace" analysis.
+     */
+    private void pickOrder() {
+        BasicBlockList blocks = method.getBlocks();
+        int sz = blocks.size();
+        int maxLabel = blocks.getMaxLabel();
+        int[] workSet = Bits.makeBitSet(maxLabel);
+        int[] tracebackSet = Bits.makeBitSet(maxLabel);
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = blocks.get(i);
+            Bits.set(workSet, one.getLabel());
+        }
+
+        int[] order = new int[sz];
+        int at = 0;
+
+        /*
+         * Starting with the designated "first label" (that is, the
+         * first block of the method), add that label to the order,
+         * and then pick its first as-yet unordered successor to
+         * immediately follow it, giving top priority to the primary
+         * (aka default) successor (if any). Keep following successors
+         * until the trace runs out of possibilities. Then, continue
+         * by finding an unordered chain containing the first as-yet
+         * unordered block, and adding it to the order, and so on.
+         */
+        for (int label = method.getFirstLabel();
+             label != -1;
+             label = Bits.findFirst(workSet, 0)) {
+
+            /*
+             * Attempt to trace backward from the chosen block to an
+             * as-yet unordered predecessor which lists the chosen
+             * block as its primary successor, and so on, until we
+             * fail to find such an unordered predecessor. Start the
+             * trace with that block. Note that the first block in the
+             * method has no predecessors, so in that case this loop
+             * will simply terminate with zero iterations and without
+             * picking a new starter block.
+             */
+            traceBack:
+            for (;;) {
+                IntList preds = method.labelToPredecessors(label);
+                int psz = preds.size();
+
+                for (int i = 0; i < psz; i++) {
+                    int predLabel = preds.get(i);
+
+                    if (Bits.get(tracebackSet, predLabel)) {
+                        /*
+                         * We found a predecessor loop; stop tracing back
+                         * from here.
+                         */
+                        break;
+                    }
+
+                    if (!Bits.get(workSet, predLabel)) {
+                        // This one's already ordered.
+                        continue;
+                    }
+
+                    BasicBlock pred = blocks.labelToBlock(predLabel);
+                    if (pred.getPrimarySuccessor() == label) {
+                        // Found one!
+                        label = predLabel;
+                        Bits.set(tracebackSet, label);
+                        continue traceBack;
+                    }
+                }
+
+                // Failed to find a better block to start the trace.
+                break;
+            }
+
+            /*
+             * Trace a path from the chosen block to one of its
+             * unordered successors (hopefully the primary), and so
+             * on, until we run out of unordered successors.
+             */
+            while (label != -1) {
+                Bits.clear(workSet, label);
+                Bits.clear(tracebackSet, label);
+                order[at] = label;
+                at++;
+
+                BasicBlock one = blocks.labelToBlock(label);
+                BasicBlock preferredBlock = blocks.preferredSuccessorOf(one);
+
+                if (preferredBlock == null) {
+                    break;
+                }
+
+                int preferred = preferredBlock.getLabel();
+                int primary = one.getPrimarySuccessor();
+
+                if (Bits.get(workSet, preferred)) {
+                    /*
+                     * Order the current block's preferred successor
+                     * next, as it has yet to be scheduled.
+                     */
+                    label = preferred;
+                } else if ((primary != preferred) && (primary >= 0)
+                        && Bits.get(workSet, primary)) {
+                    /*
+                     * The primary is available, so use that.
+                     */
+                    label = primary;
+                } else {
+                    /*
+                     * There's no obvious candidate, so pick the first
+                     * one that's available, if any.
+                     */
+                    IntList successors = one.getSuccessors();
+                    int ssz = successors.size();
+                    label = -1;
+                    for (int i = 0; i < ssz; i++) {
+                        int candidate = successors.get(i);
+                        if (Bits.get(workSet, candidate)) {
+                            label = candidate;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (at != sz) {
+            // There was a duplicate block label.
+            throw new RuntimeException("shouldn't happen");
+        }
+
+        this.order = order;
+    }
+
+    /**
+     * Gets the complete register list (result and sources) out of a
+     * given rop instruction. For insns that are commutative, have
+     * two register sources, and have a source equal to the result,
+     * place that source first.
+     *
+     * @param insn {@code non-null;} instruction in question
+     * @return {@code non-null;} the instruction's complete register list
+     */
+    private static RegisterSpecList getRegs(Insn insn) {
+        return getRegs(insn, insn.getResult());
+    }
+
+    /**
+     * Gets the complete register list (result and sources) out of a
+     * given rop instruction. For insns that are commutative, have
+     * two register sources, and have a source equal to the result,
+     * place that source first.
+     *
+     * @param insn {@code non-null;} instruction in question
+     * @param resultReg {@code null-ok;} the real result to use (ignore the insn's)
+     * @return {@code non-null;} the instruction's complete register list
+     */
+    private static RegisterSpecList getRegs(Insn insn,
+            RegisterSpec resultReg) {
+        RegisterSpecList regs = insn.getSources();
+
+        if (insn.getOpcode().isCommutative()
+                && (regs.size() == 2)
+                && (resultReg.getReg() == regs.get(1).getReg())) {
+
+            /*
+             * For commutative ops which have two register sources,
+             * if the second source is the same register as the result,
+             * swap the sources so that an opcode of form 12x can be selected
+             * instead of one of form 23x
+             */
+
+            regs = RegisterSpecList.make(regs.get(1), regs.get(0));
+        }
+
+        if (resultReg == null) {
+            return regs;
+        }
+
+        return regs.withFirst(resultReg);
+    }
+
+    /**
+     * Instruction visitor class for doing the instruction translation per se.
+     */
+    private class TranslationVisitor implements Insn.Visitor {
+        /** {@code non-null;} list of output instructions in-progress */
+        private final OutputCollector output;
+
+        /** {@code non-null;} basic block being worked on */
+        private BasicBlock block;
+
+        /**
+         * {@code null-ok;} code address for the salient last instruction of the
+         * block (used before switches and throwing instructions)
+         */
+        private CodeAddress lastAddress;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param output {@code non-null;} destination for instruction output
+         */
+        public TranslationVisitor(OutputCollector output) {
+            this.output = output;
+        }
+
+        /**
+         * Sets the block currently being worked on.
+         *
+         * @param block {@code non-null;} the block
+         * @param lastAddress {@code non-null;} code address for the salient
+         * last instruction of the block
+         */
+        public void setBlock(BasicBlock block, CodeAddress lastAddress) {
+            this.block = block;
+            this.lastAddress = lastAddress;
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainInsn(PlainInsn insn) {
+            Rop rop = insn.getOpcode();
+            if (rop.getOpcode() == RegOps.MARK_LOCAL) {
+                /*
+                 * Ignore these. They're dealt with by
+                 * the LocalVariableAwareTranslationVisitor
+                 */
+                return;
+            }
+            if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
+                // These get skipped
+                return;
+            }
+
+            SourcePosition pos = insn.getPosition();
+            Dop opcode = RopToDop.dopFor(insn);
+            DalvInsn di;
+
+            switch (rop.getBranchingness()) {
+                case Rop.BRANCH_NONE:
+                case Rop.BRANCH_RETURN:
+                case Rop.BRANCH_THROW: {
+                    di = new SimpleInsn(opcode, pos, getRegs(insn));
+                    break;
+                }
+                case Rop.BRANCH_GOTO: {
+                    /*
+                     * Code in the main translation loop will emit a
+                     * goto if necessary (if the branch isn't to the
+                     * immediately subsequent block).
+                     */
+                    return;
+                }
+                case Rop.BRANCH_IF: {
+                    int target = block.getSuccessors().get(1);
+                    di = new TargetInsn(opcode, pos, getRegs(insn),
+                                        addresses.getStart(target));
+                    break;
+                }
+                default: {
+                    throw new RuntimeException("shouldn't happen");
+                }
+            }
+
+            addOutput(di);
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainCstInsn(PlainCstInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            Dop opcode = RopToDop.dopFor(insn);
+            Rop rop = insn.getOpcode();
+            int ropOpcode = rop.getOpcode();
+            DalvInsn di;
+
+            if (rop.getBranchingness() != Rop.BRANCH_NONE) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            if (ropOpcode == RegOps.MOVE_PARAM) {
+                if (!paramsAreInOrder) {
+                    /*
+                     * Parameters are not in order at the top of the reg space.
+                     * We need to add moves.
+                     */
+
+                    RegisterSpec dest = insn.getResult();
+                    int param =
+                        ((CstInteger) insn.getConstant()).getValue();
+                    RegisterSpec source =
+                        RegisterSpec.make(regCount - paramSize + param,
+                                dest.getType());
+                    di = new SimpleInsn(opcode, pos,
+                                        RegisterSpecList.make(dest, source));
+                    addOutput(di);
+                }
+            } else {
+                // No moves required for the parameters
+                RegisterSpecList regs = getRegs(insn);
+                di = new CstInsn(opcode, pos, regs, insn.getConstant());
+                addOutput(di);
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitchInsn(SwitchInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            IntList cases = insn.getCases();
+            IntList successors = block.getSuccessors();
+            int casesSz = cases.size();
+            int succSz = successors.size();
+            int primarySuccessor = block.getPrimarySuccessor();
+
+            /*
+             * Check the assumptions that the number of cases is one
+             * less than the number of successors and that the last
+             * successor in the list is the primary (in this case, the
+             * default). This test is here to guard against forgetting
+             * to change this code if the way switch instructions are
+             * constructed also gets changed.
+             */
+            if ((casesSz != (succSz - 1)) ||
+                (primarySuccessor != successors.get(casesSz))) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            CodeAddress[] switchTargets = new CodeAddress[casesSz];
+
+            for (int i = 0; i < casesSz; i++) {
+                int label = successors.get(i);
+                switchTargets[i] = addresses.getStart(label);
+            }
+
+            CodeAddress dataAddress = new CodeAddress(pos);
+            SwitchData dataInsn =
+                new SwitchData(pos, lastAddress, cases, switchTargets);
+            Dop opcode = dataInsn.isPacked() ?
+                Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH;
+            TargetInsn switchInsn =
+                new TargetInsn(opcode, pos, getRegs(insn), dataAddress);
+
+            addOutput(lastAddress);
+            addOutput(switchInsn);
+
+            addOutputSuffix(new OddSpacer(pos));
+            addOutputSuffix(dataAddress);
+            addOutputSuffix(dataInsn);
+        }
+
+        /**
+         * Looks forward to the current block's primary successor, returning
+         * the RegisterSpec of the result of the move-result-pseudo at the
+         * top of that block or null if none.
+         *
+         * @return {@code null-ok;} result of move-result-pseudo at the beginning of
+         * primary successor
+         */
+        private RegisterSpec getNextMoveResultPseudo()
+        {
+            int label = block.getPrimarySuccessor();
+
+            if (label < 0) {
+                return null;
+            }
+
+            Insn insn
+                    = method.getBlocks().labelToBlock(label).getInsns().get(0);
+
+            if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) {
+                return null;
+            } else {
+                return insn.getResult();
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            Dop opcode = RopToDop.dopFor(insn);
+            Rop rop = insn.getOpcode();
+            Constant cst = insn.getConstant();
+
+            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            addOutput(lastAddress);
+
+            if (rop.isCallLike()) {
+                RegisterSpecList regs = insn.getSources();
+                DalvInsn di = new CstInsn(opcode, pos, regs, cst);
+
+                addOutput(di);
+            } else {
+                RegisterSpec realResult = getNextMoveResultPseudo();
+
+                RegisterSpecList regs = getRegs(insn, realResult);
+                DalvInsn di;
+
+                boolean hasResult = opcode.hasResult()
+                        || (rop.getOpcode() == RegOps.CHECK_CAST);
+
+                if (hasResult != (realResult != null)) {
+                    throw new RuntimeException(
+                            "Insn with result/move-result-pseudo mismatch " +
+                            insn);
+                }
+
+                if ((rop.getOpcode() == RegOps.NEW_ARRAY) &&
+                    (opcode.getOpcode() != DalvOps.NEW_ARRAY)) {
+                    /*
+                     * It's a type-specific new-array-<primitive>, and
+                     * so it should be turned into a SimpleInsn (no
+                     * constant ref as it's implicit).
+                     */
+                    di = new SimpleInsn(opcode, pos, regs);
+                } else {
+                    /*
+                     * This is the general case for constant-bearing
+                     * instructions.
+                     */
+                    di = new CstInsn(opcode, pos, regs, cst);
+                }
+
+                addOutput(di);
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingInsn(ThrowingInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            Dop opcode = RopToDop.dopFor(insn);
+            Rop rop = insn.getOpcode();
+            RegisterSpec realResult;
+
+            if (rop.getBranchingness() != Rop.BRANCH_THROW) {
+                throw new RuntimeException("shouldn't happen");
+            }
+
+            realResult = getNextMoveResultPseudo();
+
+            if (opcode.hasResult() != (realResult != null)) {
+                throw new RuntimeException(
+                        "Insn with result/move-result-pseudo mismatch" + insn);
+            }
+
+            addOutput(lastAddress);
+
+            DalvInsn di = new SimpleInsn(opcode, pos,
+                    getRegs(insn, realResult));
+
+            addOutput(di);
+        }
+
+        /** {@inheritDoc} */
+        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+            SourcePosition pos = insn.getPosition();
+            Constant cst = insn.getConstant();
+            ArrayList<Constant> values = insn.getInitValues();
+            Rop rop = insn.getOpcode();
+
+            if (rop.getBranchingness() != Rop.BRANCH_NONE) {
+                throw new RuntimeException("shouldn't happen");
+            }
+            CodeAddress dataAddress = new CodeAddress(pos);
+            ArrayData dataInsn =
+                new ArrayData(pos, lastAddress, values, cst);
+
+            TargetInsn fillArrayDataInsn =
+                new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn),
+                        dataAddress);
+
+            addOutput(lastAddress);
+            addOutput(fillArrayDataInsn);
+
+            addOutputSuffix(new OddSpacer(pos));
+            addOutputSuffix(dataAddress);
+            addOutputSuffix(dataInsn);
+        }
+
+        /**
+         * Adds to the output.
+         *
+         * @param insn {@code non-null;} instruction to add
+         */
+        protected void addOutput(DalvInsn insn) {
+            output.add(insn);
+        }
+
+        /**
+         * Adds to the output suffix.
+         *
+         * @param insn {@code non-null;} instruction to add
+         */
+        protected void addOutputSuffix(DalvInsn insn) {
+            output.addSuffix(insn);
+        }
+    }
+
+    /**
+     * Instruction visitor class for doing instruction translation with
+     * local variable tracking
+     */
+    private class LocalVariableAwareTranslationVisitor
+            extends TranslationVisitor {
+        /** {@code non-null;} local variable info */
+        private LocalVariableInfo locals;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param output {@code non-null;} destination for instruction output
+         * @param locals {@code non-null;} the local variable info
+         */
+        public LocalVariableAwareTranslationVisitor(OutputCollector output,
+                                                    LocalVariableInfo locals) {
+            super(output);
+            this.locals = locals;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitPlainInsn(PlainInsn insn) {
+            super.visitPlainInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitPlainCstInsn(PlainCstInsn insn) {
+            super.visitPlainCstInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitSwitchInsn(SwitchInsn insn) {
+            super.visitSwitchInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+            super.visitThrowingCstInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void visitThrowingInsn(ThrowingInsn insn) {
+            super.visitThrowingInsn(insn);
+            addIntroductionIfNecessary(insn);
+        }
+
+        /**
+         * Adds a {@link LocalStart} to the output if the given
+         * instruction in fact introduces a local variable.
+         *
+         * @param insn {@code non-null;} instruction in question
+         */
+        public void addIntroductionIfNecessary(Insn insn) {
+            RegisterSpec spec = locals.getAssignment(insn);
+
+            if (spec != null) {
+                addOutput(new LocalStart(insn.getPosition(), spec));
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/SimpleInsn.java b/dx/src/com/android/dx/dex/code/SimpleInsn.java
new file mode 100644
index 0000000..8cdcc55
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/SimpleInsn.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Instruction which has no extra info beyond the basics provided for in
+ * the base class.
+ */
+public final class SimpleInsn extends FixedSizeInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position {@code non-null;} source position
+     * @param registers {@code non-null;} register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins or outs)
+     */
+    public SimpleInsn(Dop opcode, SourcePosition position,
+                      RegisterSpecList registers) {
+        super(opcode, position, registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withOpcode(Dop opcode) {
+        return new SimpleInsn(opcode, getPosition(), getRegisters());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new SimpleInsn(getOpcode(), getPosition(), registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        return null;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/StdCatchBuilder.java b/dx/src/com/android/dx/dex/code/StdCatchBuilder.java
new file mode 100644
index 0000000..1e7612e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/StdCatchBuilder.java
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Constructor of {@link CatchTable} instances from {@link RopMethod}
+ * and associated data.
+ */
+public final class StdCatchBuilder implements CatchBuilder {
+    /** the maximum range of a single catch handler, in code units */
+    private static final int MAX_CATCH_RANGE = 65535;
+
+    /** {@code non-null;} method to build the list for */
+    private final RopMethod method;
+
+    /** {@code non-null;} block output order */
+    private final int[] order;
+
+    /** {@code non-null;} address objects for each block */
+    private final BlockAddresses addresses;
+
+    /**
+     * Constructs an instance. It merely holds onto its parameters for
+     * a subsequent call to {@link #build}.
+     *
+     * @param method {@code non-null;} method to build the list for
+     * @param order {@code non-null;} block output order
+     * @param addresses {@code non-null;} address objects for each block
+     */
+    public StdCatchBuilder(RopMethod method, int[] order,
+            BlockAddresses addresses) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        if (order == null) {
+            throw new NullPointerException("order == null");
+        }
+
+        if (addresses == null) {
+            throw new NullPointerException("addresses == null");
+        }
+
+        this.method = method;
+        this.order = order;
+        this.addresses = addresses;
+    }
+
+    /** {@inheritDoc} */
+    public CatchTable build() {
+        return build(method, order, addresses);
+    }
+
+    /** {@inheritDoc} */
+    public boolean hasAnyCatches() {
+        BasicBlockList blocks = method.getBlocks();
+        int size = blocks.size();
+
+        for (int i = 0; i < size; i++) {
+            BasicBlock block = blocks.get(i);
+            TypeList catches = block.getLastInsn().getCatches();
+            if (catches.size() != 0) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public HashSet<Type> getCatchTypes() {
+        HashSet<Type> result = new HashSet<Type>(20);
+        BasicBlockList blocks = method.getBlocks();
+        int size = blocks.size();
+
+        for (int i = 0; i < size; i++) {
+            BasicBlock block = blocks.get(i);
+            TypeList catches = block.getLastInsn().getCatches();
+            int catchSize = catches.size();
+
+            for (int j = 0; j < catchSize; j++) {
+                result.add(catches.getType(j));
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Builds and returns the catch table for a given method.
+     *
+     * @param method {@code non-null;} method to build the list for
+     * @param order {@code non-null;} block output order
+     * @param addresses {@code non-null;} address objects for each block
+     * @return {@code non-null;} the constructed table
+     */
+    public static CatchTable build(RopMethod method, int[] order,
+            BlockAddresses addresses) {
+        int len = order.length;
+        BasicBlockList blocks = method.getBlocks();
+        ArrayList<CatchTable.Entry> resultList =
+            new ArrayList<CatchTable.Entry>(len);
+        CatchHandlerList currentHandlers = CatchHandlerList.EMPTY;
+        BasicBlock currentStartBlock = null;
+        BasicBlock currentEndBlock = null;
+
+        for (int i = 0; i < len; i++) {
+            BasicBlock block = blocks.labelToBlock(order[i]);
+
+            if (!block.canThrow()) {
+                /*
+                 * There is no need to concern ourselves with the
+                 * placement of blocks that can't throw with respect
+                 * to the blocks that *can* throw.
+                 */
+                continue;
+            }
+
+            CatchHandlerList handlers = handlersFor(block, addresses);
+
+            if (currentHandlers.size() == 0) {
+                // This is the start of a new catch range.
+                currentStartBlock = block;
+                currentEndBlock = block;
+                currentHandlers = handlers;
+                continue;
+            }
+
+            if (currentHandlers.equals(handlers)
+                    && rangeIsValid(currentStartBlock, block, addresses)) {
+                /*
+                 * The block we are looking at now has the same handlers
+                 * as the block that started the currently open catch
+                 * range, and adding it to the currently open range won't
+                 * cause it to be too long.
+                 */
+                currentEndBlock = block;
+                continue;
+            }
+
+            /*
+             * The block we are looking at now has incompatible handlers,
+             * so we need to finish off the last entry and start a new
+             * one. Note: We only emit an entry if it has associated handlers.
+             */
+            if (currentHandlers.size() != 0) {
+                CatchTable.Entry entry =
+                    makeEntry(currentStartBlock, currentEndBlock,
+                            currentHandlers, addresses);
+                resultList.add(entry);
+            }
+
+            currentStartBlock = block;
+            currentEndBlock = block;
+            currentHandlers = handlers;
+        }
+
+        if (currentHandlers.size() != 0) {
+            // Emit an entry for the range that was left hanging.
+            CatchTable.Entry entry =
+                makeEntry(currentStartBlock, currentEndBlock,
+                        currentHandlers, addresses);
+            resultList.add(entry);
+        }
+
+        // Construct the final result.
+
+        int resultSz = resultList.size();
+
+        if (resultSz == 0) {
+            return CatchTable.EMPTY;
+        }
+
+        CatchTable result = new CatchTable(resultSz);
+
+        for (int i = 0; i < resultSz; i++) {
+            result.set(i, resultList.get(i));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Makes the {@link CatchHandlerList} for the given basic block.
+     *
+     * @param block {@code non-null;} block to get entries for
+     * @param addresses {@code non-null;} address objects for each block
+     * @return {@code non-null;} array of entries
+     */
+    private static CatchHandlerList handlersFor(BasicBlock block,
+            BlockAddresses addresses) {
+        IntList successors = block.getSuccessors();
+        int succSize = successors.size();
+        int primary = block.getPrimarySuccessor();
+        TypeList catches = block.getLastInsn().getCatches();
+        int catchSize = catches.size();
+
+        if (catchSize == 0) {
+            return CatchHandlerList.EMPTY;
+        }
+
+        if (((primary == -1) && (succSize != catchSize))
+                || ((primary != -1) &&
+                        ((succSize != (catchSize + 1))
+                                || (primary != successors.get(catchSize))))) {
+            /*
+             * Blocks that throw are supposed to list their primary
+             * successor -- if any -- last in the successors list, but
+             * that constraint appears to be violated here.
+             */
+            throw new RuntimeException(
+                    "shouldn't happen: weird successors list");
+        }
+
+        /*
+         * Reduce the effective catchSize if we spot a catch-all that
+         * isn't at the end.
+         */
+        for (int i = 0; i < catchSize; i++) {
+            Type type = catches.getType(i);
+            if (type.equals(Type.OBJECT)) {
+                catchSize = i + 1;
+                break;
+            }
+        }
+
+        CatchHandlerList result = new CatchHandlerList(catchSize);
+
+        for (int i = 0; i < catchSize; i++) {
+            CstType oneType = new CstType(catches.getType(i));
+            CodeAddress oneHandler = addresses.getStart(successors.get(i));
+            result.set(i, oneType, oneHandler.getAddress());
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Makes a {@link CatchTable#Entry} for the given block range and
+     * handlers.
+     *
+     * @param start {@code non-null;} the start block for the range (inclusive)
+     * @param end {@code non-null;} the start block for the range (also inclusive)
+     * @param handlers {@code non-null;} the handlers for the range
+     * @param addresses {@code non-null;} address objects for each block
+     */
+    private static CatchTable.Entry makeEntry(BasicBlock start,
+            BasicBlock end, CatchHandlerList handlers,
+            BlockAddresses addresses) {
+        /*
+         * We start at the *last* instruction of the start block, since
+         * that's the instruction that can throw...
+         */
+        CodeAddress startAddress = addresses.getLast(start);
+
+        // ...And we end *after* the last instruction of the end block.
+        CodeAddress endAddress = addresses.getEnd(end);
+
+        return new CatchTable.Entry(startAddress.getAddress(),
+                endAddress.getAddress(), handlers);
+    }
+
+    /**
+     * Gets whether the address range for the given two blocks is valid
+     * for a catch handler. This is true as long as the covered range is
+     * under 65536 code units.
+     *
+     * @param start {@code non-null;} the start block for the range (inclusive)
+     * @param end {@code non-null;} the start block for the range (also inclusive)
+     * @param addresses {@code non-null;} address objects for each block
+     * @return {@code true} if the range is valid as a catch range
+     */
+    private static boolean rangeIsValid(BasicBlock start, BasicBlock end,
+            BlockAddresses addresses) {
+        if (start == null) {
+            throw new NullPointerException("start == null");
+        }
+
+        if (end == null) {
+            throw new NullPointerException("end == null");
+        }
+
+        // See above about selection of instructions.
+        int startAddress = addresses.getLast(start).getAddress();
+        int endAddress = addresses.getEnd(end).getAddress();
+
+        return (endAddress - startAddress) <= MAX_CATCH_RANGE;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/SwitchData.java b/dx/src/com/android/dx/dex/code/SwitchData.java
new file mode 100644
index 0000000..27a6342
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/SwitchData.java
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * Pseudo-instruction which holds switch data. The switch data is
+ * a map of values to target addresses, and this class writes the data
+ * in either a "packed" or "sparse" form.
+ */
+public final class SwitchData extends VariableSizeInsn {
+    /**
+     * {@code non-null;} address representing the instruction that uses this
+     * instance
+     */
+    private final CodeAddress user;
+
+    /** {@code non-null;} sorted list of switch cases (keys) */
+    private final IntList cases;
+
+    /**
+     * {@code non-null;} corresponding list of code addresses; the branch
+     * target for each case
+     */
+    private final CodeAddress[] targets;
+
+    /** whether the output table will be packed (vs. sparse) */
+    private final boolean packed;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     * @param user {@code non-null;} address representing the instruction that
+     * uses this instance
+     * @param cases {@code non-null;} sorted list of switch cases (keys)
+     * @param targets {@code non-null;} corresponding list of code addresses; the
+     * branch target for each case
+     */
+    public SwitchData(SourcePosition position, CodeAddress user,
+                      IntList cases, CodeAddress[] targets) {
+        super(position, RegisterSpecList.EMPTY);
+
+        if (user == null) {
+            throw new NullPointerException("user == null");
+        }
+
+        if (cases == null) {
+            throw new NullPointerException("cases == null");
+        }
+
+        if (targets == null) {
+            throw new NullPointerException("targets == null");
+        }
+
+        int sz = cases.size();
+
+        if (sz != targets.length) {
+            throw new IllegalArgumentException("cases / targets mismatch");
+        }
+
+        if (sz > 65535) {
+            throw new IllegalArgumentException("too many cases");
+        }
+
+        this.user = user;
+        this.cases = cases;
+        this.targets = targets;
+        this.packed = shouldPack(cases);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return packed ? (int) packedCodeSize(cases) :
+            (int) sparseCodeSize(cases);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out) {
+        int baseAddress = user.getAddress();
+        int defaultTarget = Dops.PACKED_SWITCH.getFormat().codeSize();
+        int sz = targets.length;
+
+        if (packed) {
+            int firstCase = (sz == 0) ? 0 : cases.get(0);
+            int lastCase = (sz == 0) ? 0 : cases.get(sz - 1);
+            int outSz = lastCase - firstCase + 1;
+
+            out.writeShort(0x100 | DalvOps.NOP);
+            out.writeShort(outSz);
+            out.writeInt(firstCase);
+
+            int caseAt = 0;
+            for (int i = 0; i < outSz; i++) {
+                int outCase = firstCase + i;
+                int oneCase = cases.get(caseAt);
+                int relTarget;
+
+                if (oneCase > outCase) {
+                    relTarget = defaultTarget;
+                } else {
+                    relTarget = targets[caseAt].getAddress() - baseAddress;
+                    caseAt++;
+                }
+
+                out.writeInt(relTarget);
+            }
+        } else {
+            out.writeShort(0x200 | DalvOps.NOP);
+            out.writeShort(sz);
+
+            for (int i = 0; i < sz; i++) {
+                out.writeInt(cases.get(i));
+            }
+
+            for (int i = 0; i < sz; i++) {
+                int relTarget = targets[i].getAddress() - baseAddress;
+                out.writeInt(relTarget);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new SwitchData(getPosition(), user, cases, targets);
+    }
+
+    /**
+     * Returns whether or not this instance's data will be output as packed.
+     *
+     * @return {@code true} iff the data is to be packed
+     */
+    public boolean isPacked() {
+        return packed;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        int sz = targets.length;
+        for (int i = 0; i < sz; i++) {
+            sb.append("\n    ");
+            sb.append(cases.get(i));
+            sb.append(": ");
+            sb.append(targets[i]);
+        }
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String listingString0(boolean noteIndices) {
+        int baseAddress = user.getAddress();
+        StringBuffer sb = new StringBuffer(100);
+        int sz = targets.length;
+
+        sb.append(packed ? "packed" : "sparse");
+        sb.append("-switch-data // for switch @ ");
+        sb.append(Hex.u2(baseAddress));
+
+        for (int i = 0; i < sz; i++) {
+            int absTarget = targets[i].getAddress();
+            int relTarget = absTarget - baseAddress;
+            sb.append("\n  ");
+            sb.append(cases.get(i));
+            sb.append(": ");
+            sb.append(Hex.u4(absTarget));
+            sb.append(" // ");
+            sb.append(Hex.s4(relTarget));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the size of a packed table for the given cases, in 16-bit code
+     * units.
+     *
+     * @param cases {@code non-null;} sorted list of cases
+     * @return {@code >= -1;} the packed table size or {@code -1} if the
+     * cases couldn't possibly be represented as a packed table
+     */
+    private static long packedCodeSize(IntList cases) {
+        int sz = cases.size();
+        long low = cases.get(0);
+        long high = cases.get(sz - 1);
+        long result = ((high - low + 1)) * 2 + 4;
+
+        return (result <= 0x7fffffff) ? result : -1;
+    }
+
+    /**
+     * Gets the size of a sparse table for the given cases, in 16-bit code
+     * units.
+     *
+     * @param cases {@code non-null;} sorted list of cases
+     * @return {@code > 0;} the sparse table size
+     */
+    private static long sparseCodeSize(IntList cases) {
+        int sz = cases.size();
+
+        return (sz * 4L) + 2;
+    }
+
+    /**
+     * Determines whether the given list of cases warrant being packed.
+     *
+     * @param cases {@code non-null;} sorted list of cases
+     * @return {@code true} iff the table encoding the cases
+     * should be packed
+     */
+    private static boolean shouldPack(IntList cases) {
+        int sz = cases.size();
+
+        if (sz < 2) {
+            return true;
+        }
+
+        long packedSize = packedCodeSize(cases);
+        long sparseSize = sparseCodeSize(cases);
+
+        /*
+         * We pick the packed representation if it is possible and
+         * would be as small or smaller than 5/4 of the sparse
+         * representation. That is, we accept some size overhead on
+         * the packed representation, since that format is faster to
+         * execute at runtime.
+         */
+        return (packedSize >= 0) && (packedSize <= ((sparseSize * 5) / 4));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/TargetInsn.java b/dx/src/com/android/dx/dex/code/TargetInsn.java
new file mode 100644
index 0000000..cbb5ff9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/TargetInsn.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Instruction which has a single branch target.
+ */
+public final class TargetInsn extends FixedSizeInsn {
+    /** {@code non-null;} the branch target */
+    private CodeAddress target;
+
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}), and the target is initially
+     * {@code null}.
+     *
+     * @param opcode the opcode; one of the constants from {@link Dops}
+     * @param position {@code non-null;} source position
+     * @param registers {@code non-null;} register list, including a
+     * result register if appropriate (that is, registers may be either
+     * ins or outs)
+     * @param target {@code non-null;} the branch target
+     */
+    public TargetInsn(Dop opcode, SourcePosition position,
+                      RegisterSpecList registers, CodeAddress target) {
+        super(opcode, position, registers);
+
+        if (target == null) {
+            throw new NullPointerException("target == null");
+        }
+
+        this.target = target;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withOpcode(Dop opcode) {
+        return new TargetInsn(opcode, getPosition(), getRegisters(), target);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisters(RegisterSpecList registers) {
+        return new TargetInsn(getOpcode(), getPosition(), registers, target);
+    }
+
+    /**
+     * Returns an instance that is just like this one, except that its
+     * opcode has the opposite sense (as a test; e.g. a
+     * {@code lt} test becomes a {@code ge}), and its branch
+     * target is replaced by the one given, and all set-once values
+     * associated with the class (such as its address) are reset.
+     *
+     * @param target {@code non-null;} the new branch target
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public TargetInsn withNewTargetAndReversed(CodeAddress target) {
+        Dop opcode = getOpcode().getOppositeTest();
+
+        return new TargetInsn(opcode, getPosition(), getRegisters(), target);
+    }
+
+    /**
+     * Gets the unique branch target of this instruction.
+     *
+     * @return {@code non-null;} the branch target
+     */
+    public CodeAddress getTarget() {
+        return target;
+    }
+
+    /**
+     * Gets the target address of this instruction. This is only valid
+     * to call if the target instruction has been assigned an address,
+     * and it is merely a convenient shorthand for
+     * {@code getTarget().getAddress()}.
+     *
+     * @return {@code >= 0;} the target address
+     */
+    public int getTargetAddress() {
+        return target.getAddress();
+    }
+
+    /**
+     * Gets the branch offset of this instruction. This is only valid to
+     * call if both this and the target instruction each has been assigned
+     * an address, and it is merely a convenient shorthand for
+     * {@code getTargetAddress() - getAddress()}.
+     *
+     * @return the branch offset
+     */
+    public int getTargetOffset() {
+        return target.getAddress() - getAddress();
+    }
+
+    /**
+     * Returns whether the target offset is known.
+     *
+     * @return {@code true} if the target offset is known or
+     * {@code false} if not
+     */
+    public boolean hasTargetOffset() {
+        return hasAddress() && target.hasAddress();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String argString() {
+        if (target == null) {
+            return "????";
+        }
+
+        return target.identifierString();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/VariableSizeInsn.java b/dx/src/com/android/dx/dex/code/VariableSizeInsn.java
new file mode 100644
index 0000000..06b40f7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/VariableSizeInsn.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction base class for variable-sized instructions.
+ */
+public abstract class VariableSizeInsn extends DalvInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     * @param registers {@code non-null;} source registers
+     */
+    public VariableSizeInsn(SourcePosition position,
+                            RegisterSpecList registers) {
+        super(Dops.SPECIAL_FORMAT, position, registers);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withOpcode(Dop opcode) {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withRegisterOffset(int delta) {
+        return withRegisters(getRegisters().withOffset(delta));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java b/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java
new file mode 100644
index 0000000..2cc157b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Pseudo-instruction base class for zero-size (no code emitted)
+ * instructions, which are generally used for tracking metainformation
+ * about the code they are adjacent to.
+ */
+public abstract class ZeroSizeInsn extends DalvInsn {
+    /**
+     * Constructs an instance. The output address of this instance is initially
+     * unknown ({@code -1}).
+     *
+     * @param position {@code non-null;} source position
+     */
+    public ZeroSizeInsn(SourcePosition position) {
+        super(Dops.SPECIAL_FORMAT, position, RegisterSpecList.EMPTY);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int codeSize() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void writeTo(AnnotatedOutput out) {
+        // Nothing to do here, for this class.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final DalvInsn withOpcode(Dop opcode) {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DalvInsn withRegisterOffset(int delta) {
+        return withRegisters(getRegisters().withOffset(delta));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form10t.java b/dx/src/com/android/dx/dex/code/form/Form10t.java
new file mode 100644
index 0000000..82b731d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form10t.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 10t}. See the instruction format spec
+ * for details.
+ */
+public final class Form10t extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form10t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form10t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        return branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!((insn instanceof TargetInsn) &&
+              (insn.getRegisters().size() == 0))) {
+            return false;
+        }
+
+        TargetInsn ti = (TargetInsn) insn;
+        return ti.hasTargetOffset() ? branchFits(ti) : true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        int offset = insn.getTargetOffset();
+
+        // Note: A zero offset would fit, but it is prohibited by the spec.
+        return (offset != 0) && signedFitsInByte(offset);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form20t.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, (offset & 0xff)));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form10x.java b/dx/src/com/android/dx/dex/code/form/Form10x.java
new file mode 100644
index 0000000..c7a22a6
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form10x.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 10x}. See the instruction format spec
+ * for details.
+ */
+public final class Form10x extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form10x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form10x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        // This format has no arguments.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        return (insn instanceof SimpleInsn) &&
+            (insn.getRegisters().size() == 0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        write(out, opcodeUnit(insn, 0));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form11n.java b/dx/src/com/android/dx/dex/code/form/Form11n.java
new file mode 100644
index 0000000..d63fc6f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form11n.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 11n}. See the instruction format spec
+ * for details.
+ */
+public final class Form11n extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form11n();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form11n() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 4);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInNibble(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        return cb.fitsInInt() && signedFitsInNibble(cb.getIntBits());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form21s.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn, makeByte(regs.get(0).getReg(), value & 0xf)));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form11x.java b/dx/src/com/android/dx/dex/code/form/Form11x.java
new file mode 100644
index 0000000..b4acc1a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form11x.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 11x}. See the instruction format spec
+ * for details.
+ */
+public final class Form11x extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form11x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form11x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return (insn instanceof SimpleInsn) &&
+            (regs.size() == 1) &&
+            unsignedFitsInByte(regs.get(0).getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        write(out, opcodeUnit(insn, regs.get(0).getReg()));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form12x.java b/dx/src/com/android/dx/dex/code/form/Form12x.java
new file mode 100644
index 0000000..c7754be
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form12x.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.HighRegisterPrefix;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 12x}. See the instruction format spec
+ * for details.
+ */
+public final class Form12x extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form12x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form12x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+
+        /*
+         * The (sz - 2) and (sz - 1) below makes this code work for
+         * both the two- and three-register ops. (See "case 3" in
+         * isCompatible(), below.)
+         */
+
+        return regs.get(sz - 2).regString() + ", " +
+            regs.get(sz - 1).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof SimpleInsn)) {
+            return false;
+        }
+
+        RegisterSpecList regs = insn.getRegisters();
+        RegisterSpec rs1;
+        RegisterSpec rs2;
+
+        switch (regs.size()) {
+            case 2: {
+                rs1 = regs.get(0);
+                rs2 = regs.get(1);
+                break;
+            }
+            case 3: {
+                /*
+                 * This format is allowed for ops that are effectively
+                 * 3-arg but where the first two args are identical.
+                 */
+                rs1 = regs.get(1);
+                rs2 = regs.get(2);
+                if (rs1.getReg() != regs.get(0).getReg()) {
+                    return false;
+                }
+                break;
+            }
+            default: {
+                return false;
+            }
+        }
+
+        return unsignedFitsInNibble(rs1.getReg()) &&
+            unsignedFitsInNibble(rs2.getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form22x.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+
+        /*
+         * The (sz - 2) and (sz - 1) below makes this code work for
+         * both the two- and three-register ops. (See "case 3" in
+         * isCompatible(), above.)
+         */
+
+        write(out, opcodeUnit(insn,
+                              makeByte(regs.get(sz - 2).getReg(),
+                                       regs.get(sz - 1).getReg())));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form20t.java b/dx/src/com/android/dx/dex/code/form/Form20t.java
new file mode 100644
index 0000000..0b5a3b2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form20t.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 20t}. See the instruction format spec
+ * for details.
+ */
+public final class Form20t extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form20t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form20t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        return branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!((insn instanceof TargetInsn) &&
+              (insn.getRegisters().size() == 0))) {
+            return false;
+        }
+
+        TargetInsn ti = (TargetInsn) insn;
+        return ti.hasTargetOffset() ? branchFits(ti) : true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        int offset = insn.getTargetOffset();
+
+        // Note: A zero offset would fit, but it is prohibited by the spec.
+        return (offset != 0) && signedFitsInShort(offset);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form30t.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, 0), (short) offset);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21c.java b/dx/src/com/android/dx/dex/code/form/Form21c.java
new file mode 100644
index 0000000..ed1ec3c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21c.java
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21c}. See the instruction format spec
+ * for details.
+ */
+public final class Form21c extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form21c();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form21c() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + cstString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof CstInsn)) {
+            return false;
+        }
+
+        RegisterSpecList regs = insn.getRegisters();
+        RegisterSpec reg;
+
+        switch (regs.size()) {
+            case 1: {
+                reg = regs.get(0);
+                break;
+            }
+            case 2: {
+                /*
+                 * This format is allowed for ops that are effectively
+                 * 2-arg but where the two args are identical.
+                 */
+                reg = regs.get(0);
+                if (reg.getReg() != regs.get(1).getReg()) {
+                    return false;
+                }
+                break;
+            }
+            default: {
+                return false;
+            }
+        }
+
+        if (!unsignedFitsInByte(reg.getReg())) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        int cpi = ci.getIndex();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        Constant cst = ci.getConstant();
+        return (cst instanceof CstType) ||
+            (cst instanceof CstFieldRef) ||
+            (cst instanceof CstString);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form31c.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int cpi = ((CstInsn) insn).getIndex();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) cpi);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21h.java b/dx/src/com/android/dx/dex/code/form/Form21h.java
new file mode 100644
index 0000000..e0bd751
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21h.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21h}. See the instruction format spec
+ * for details.
+ */
+public final class Form21h extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form21h();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form21h() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return
+            literalBitsComment(value,
+                    (regs.get(0).getCategory() == 1) ? 32 : 64);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        // Where the high bits are depends on the category of the target.
+        if (regs.get(0).getCategory() == 1) {
+            int bits = cb.getIntBits();
+            return ((bits & 0xffff) == 0);
+        } else {
+            long bits = cb.getLongBits();
+            return ((bits & 0xffffffffffffL) == 0);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form31i.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits cb = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        short bits;
+
+        // Where the high bits are depends on the category of the target.
+        if (regs.get(0).getCategory() == 1) {
+            bits = (short) (cb.getIntBits() >>> 16);
+        } else {
+            bits = (short) (cb.getLongBits() >>> 48);
+        }
+
+        write(out, opcodeUnit(insn, regs.get(0).getReg()), bits);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21s.java b/dx/src/com/android/dx/dex/code/form/Form21s.java
new file mode 100644
index 0000000..a03ee43
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21s.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21s}. See the instruction format spec
+ * for details.
+ */
+public final class Form21s extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form21s();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form21s() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 16);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        return cb.fitsInInt() && signedFitsInShort(cb.getIntBits());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form21h.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) value);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21t.java b/dx/src/com/android/dx/dex/code/form/Form21t.java
new file mode 100644
index 0000000..f0ce644
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21t.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21t}. See the instruction format spec
+ * for details.
+ */
+public final class Form21t extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form21t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form21t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        if (!((insn instanceof TargetInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        TargetInsn ti = (TargetInsn) insn;
+        return ti.hasTargetOffset() ? branchFits(ti) : true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        int offset = insn.getTargetOffset();
+
+        // Note: A zero offset would fit, but it is prohibited by the spec.
+        return (offset != 0) && signedFitsInShort(offset);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form31t.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) offset);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22b.java b/dx/src/com/android/dx/dex/code/form/Form22b.java
new file mode 100644
index 0000000..2884fbb
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22b.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22b}. See the instruction format spec
+ * for details.
+ */
+public final class Form22b extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22b();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22b() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + regs.get(1).regString() +
+            ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 8);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 2) &&
+              unsignedFitsInByte(regs.get(0).getReg()) &&
+              unsignedFitsInByte(regs.get(1).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        return cb.fitsInInt() && signedFitsInByte(cb.getIntBits());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form22s.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              codeUnit(regs.get(1).getReg(), value & 0xff));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22c.java b/dx/src/com/android/dx/dex/code/form/Form22c.java
new file mode 100644
index 0000000..423ccc8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22c.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22c}. See the instruction format spec
+ * for details.
+ */
+public final class Form22c extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22c();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22c() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString() +
+            ", " + cstString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 2) &&
+              unsignedFitsInNibble(regs.get(0).getReg()) &&
+              unsignedFitsInNibble(regs.get(1).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        int cpi = ci.getIndex();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        Constant cst = ci.getConstant();
+        return (cst instanceof CstType) ||
+            (cst instanceof CstFieldRef);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int cpi = ((CstInsn) insn).getIndex();
+
+        write(out,
+              opcodeUnit(insn,
+                         makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+              (short) cpi);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22s.java b/dx/src/com/android/dx/dex/code/form/Form22s.java
new file mode 100644
index 0000000..5964217
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22s.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22s}. See the instruction format spec
+ * for details.
+ */
+public final class Form22s extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22s();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22s() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + regs.get(1).regString()
+            + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 16);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 2) &&
+              unsignedFitsInNibble(regs.get(0).getReg()) &&
+              unsignedFitsInNibble(regs.get(1).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        CstLiteralBits cb = (CstLiteralBits) cst;
+
+        return cb.fitsInInt() && signedFitsInShort(cb.getIntBits());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn,
+                         makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+              (short) value);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22t.java b/dx/src/com/android/dx/dex/code/form/Form22t.java
new file mode 100644
index 0000000..1577803
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22t.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22t}. See the instruction format spec
+ * for details.
+ */
+public final class Form22t extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString() +
+            ", " + branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        if (!((insn instanceof TargetInsn) &&
+              (regs.size() == 2) &&
+              unsignedFitsInNibble(regs.get(0).getReg()) &&
+              unsignedFitsInNibble(regs.get(1).getReg()))) {
+            return false;
+        }
+
+        TargetInsn ti = (TargetInsn) insn;
+        return ti.hasTargetOffset() ? branchFits(ti) : true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        int offset = insn.getTargetOffset();
+
+        // Note: A zero offset would fit, but it is prohibited by the spec.
+        return (offset != 0) && signedFitsInShort(offset);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out,
+              opcodeUnit(insn,
+                         makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+              (short) offset);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22x.java b/dx/src/com/android/dx/dex/code/form/Form22x.java
new file mode 100644
index 0000000..b7ce4e7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22x.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22x}. See the instruction format spec
+ * for details.
+ */
+public final class Form22x extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form22x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form22x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        return (insn instanceof SimpleInsn) &&
+            (regs.size() == 2) &&
+            unsignedFitsInByte(regs.get(0).getReg()) &&
+            unsignedFitsInShort(regs.get(1).getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form23x.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) regs.get(1).getReg());
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form23x.java b/dx/src/com/android/dx/dex/code/form/Form23x.java
new file mode 100644
index 0000000..64dd6b0
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form23x.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 23x}. See the instruction format spec
+ * for details.
+ */
+public final class Form23x extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form23x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form23x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString() +
+            ", " + regs.get(2).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        return (insn instanceof SimpleInsn) &&
+            (regs.size() == 3) &&
+            unsignedFitsInByte(regs.get(0).getReg()) &&
+            unsignedFitsInByte(regs.get(1).getReg()) &&
+            unsignedFitsInByte(regs.get(2).getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form32x.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              codeUnit(regs.get(1).getReg(), regs.get(2).getReg()));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form30t.java b/dx/src/com/android/dx/dex/code/form/Form30t.java
new file mode 100644
index 0000000..af0a699
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form30t.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 30t}. See the instruction format spec
+ * for details.
+ */
+public final class Form30t extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form30t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form30t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        return branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!((insn instanceof TargetInsn) &&
+              (insn.getRegisters().size() == 0))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, 0),
+                (short) offset,
+                (short) (offset >> 16));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31c.java b/dx/src/com/android/dx/dex/code/form/Form31c.java
new file mode 100644
index 0000000..0c983c5
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31c.java
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31c}. See the instruction format spec
+ * for details.
+ */
+public final class Form31c extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form31c();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form31c() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + cstString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof CstInsn)) {
+            return false;
+        }
+
+        RegisterSpecList regs = insn.getRegisters();
+        RegisterSpec reg;
+
+        switch (regs.size()) {
+            case 1: {
+                reg = regs.get(0);
+                break;
+            }
+            case 2: {
+                /*
+                 * This format is allowed for ops that are effectively
+                 * 2-arg but where the two args are identical.
+                 */
+                reg = regs.get(0);
+                if (reg.getReg() != regs.get(1).getReg()) {
+                    return false;
+                }
+                break;
+            }
+            default: {
+                return false;
+            }
+        }
+
+        if (!unsignedFitsInByte(reg.getReg())) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        return ((cst instanceof CstType) ||
+                (cst instanceof CstFieldRef) ||
+                (cst instanceof CstString));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int cpi = ((CstInsn) insn).getIndex();
+
+        write(out,
+                opcodeUnit(insn, regs.get(0).getReg()),
+                (short) cpi,
+                (short) (cpi >> 16));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31i.java b/dx/src/com/android/dx/dex/code/form/Form31i.java
new file mode 100644
index 0000000..c893a12
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31i.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31i}. See the instruction format spec
+ * for details.
+ */
+public final class Form31i extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form31i();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form31i() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 32);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        if (!(cst instanceof CstLiteralBits)) {
+            return false;
+        }
+
+        return ((CstLiteralBits) cst).fitsInInt();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form51l.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int value =
+            ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) value,
+              (short) (value >> 16));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31t.java b/dx/src/com/android/dx/dex/code/form/Form31t.java
new file mode 100644
index 0000000..682408c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31t.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31t}. See the instruction format spec
+ * for details.
+ */
+public final class Form31t extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form31t();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form31t() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + branchString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        return branchComment(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        if (!((insn instanceof TargetInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean branchFits(TargetInsn insn) {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int offset = ((TargetInsn) insn).getTargetOffset();
+
+        write(out, opcodeUnit(insn, regs.get(0).getReg()),
+                (short) offset,
+                (short) (offset >> 16));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form32x.java b/dx/src/com/android/dx/dex/code/form/Form32x.java
new file mode 100644
index 0000000..4a981ee
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form32x.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 32x}. See the instruction format spec
+ * for details.
+ */
+public final class Form32x extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form32x();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form32x() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return regs.get(0).regString() + ", " + regs.get(1).regString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        // This format has no comment.
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        return (insn instanceof SimpleInsn) &&
+            (regs.size() == 2) &&
+            unsignedFitsInShort(regs.get(0).getReg()) &&
+            unsignedFitsInShort(regs.get(1).getReg());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+
+        write(out,
+              opcodeUnit(insn, 0),
+              (short) regs.get(0).getReg(),
+              (short) regs.get(1).getReg());
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form35c.java b/dx/src/com/android/dx/dex/code/form/Form35c.java
new file mode 100644
index 0000000..31b127d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form35c.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 35c}. See the instruction format spec
+ * for details.
+ */
+public final class Form35c extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form35c();
+
+    /** Maximal number of operands */
+    private static final int MAX_NUM_OPS = 5;
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form35c() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = explicitize(insn.getRegisters());
+        return regListString(regs) + ", " + cstString(insn);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof CstInsn)) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        int cpi = ci.getIndex();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        Constant cst = ci.getConstant();
+        if (!((cst instanceof CstMethodRef) ||
+              (cst instanceof CstType))) {
+            return false;
+        }
+
+        RegisterSpecList regs = ci.getRegisters();
+        return (wordCount(regs) >= 0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return Form3rc.THE_ONE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        int cpi = ((CstInsn) insn).getIndex();
+        RegisterSpecList regs = explicitize(insn.getRegisters());
+        int sz = regs.size();
+        int r0 = (sz > 0) ? regs.get(0).getReg() : 0;
+        int r1 = (sz > 1) ? regs.get(1).getReg() : 0;
+        int r2 = (sz > 2) ? regs.get(2).getReg() : 0;
+        int r3 = (sz > 3) ? regs.get(3).getReg() : 0;
+        int r4 = (sz > 4) ? regs.get(4).getReg() : 0;
+
+        write(out,
+              opcodeUnit(insn,
+                         makeByte(r4, sz)), // encode the fifth operand here
+              (short) cpi,
+              codeUnit(r0, r1, r2, r3));
+    }
+
+    /**
+     * Gets the number of words required for the given register list, where
+     * category-2 values count as two words. Return {@code -1} if the
+     * list requires more than five words or contains registers that need
+     * more than a nibble to identify them.
+     *
+     * @param regs {@code non-null;} the register list in question
+     * @return {@code >= -1;} the number of words required, or {@code -1}
+     * if the list couldn't possibly fit in this format
+     */
+    private static int wordCount(RegisterSpecList regs) {
+        int sz = regs.size();
+
+        if (sz > MAX_NUM_OPS) {
+            // It can't possibly fit.
+            return -1;
+        }
+
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = regs.get(i);
+            result += one.getCategory();
+            /*
+             * The check below adds (category - 1) to the register, to
+             * account for the fact that the second half of a
+             * category-2 register has to be represented explicitly in
+             * the result.
+             */
+            if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) {
+                return -1;
+            }
+        }
+
+        return (result <= MAX_NUM_OPS) ? result : -1;
+    }
+
+    /**
+     * Returns a register list which is equivalent to the given one,
+     * except that it splits category-2 registers into two explicit
+     * entries. This returns the original list if no modification is
+     * required
+     *
+     * @param orig {@code non-null;} the original list
+     * @return {@code non-null;} the list with the described transformation
+     */
+    private static RegisterSpecList explicitize(RegisterSpecList orig) {
+        int wordCount = wordCount(orig);
+        int sz = orig.size();
+
+        if (wordCount == sz) {
+            return orig;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(wordCount);
+        int wordAt = 0;
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = orig.get(i);
+            result.set(wordAt, one);
+            if (one.getCategory() == 2) {
+                result.set(wordAt + 1,
+                           RegisterSpec.make(one.getReg() + 1, Type.VOID));
+                wordAt += 2;
+            } else {
+                wordAt++;
+            }
+        }
+
+        result.setImmutable();
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form3rc.java b/dx/src/com/android/dx/dex/code/form/Form3rc.java
new file mode 100644
index 0000000..755ad76
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form3rc.java
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 3rc}. See the instruction format spec
+ * for details.
+ */
+public final class Form3rc extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form3rc();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form3rc() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int size = regs.size();
+        StringBuilder sb = new StringBuilder(30);
+
+        sb.append("{");
+
+        switch (size) {
+            case 0: {
+                // Nothing to do.
+                break;
+            }
+            case 1: {
+                sb.append(regs.get(0).regString());
+                break;
+            }
+            default: {
+                RegisterSpec lastReg = regs.get(size - 1);
+                if (lastReg.getCategory() == 2) {
+                    /*
+                     * Add one to properly represent a list-final
+                     * category-2 register.
+                     */
+                    lastReg = lastReg.withOffset(1);
+                }
+
+                sb.append(regs.get(0).regString());
+                sb.append("..");
+                sb.append(lastReg.regString());
+            }
+        }
+
+        sb.append("}, ");
+        sb.append(cstString(insn));
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        if (noteIndices) {
+            return cstComment(insn);
+        } else {
+            return "";
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        if (!(insn instanceof CstInsn)) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        int cpi = ci.getIndex();
+
+        if (! unsignedFitsInShort(cpi)) {
+            return false;
+        }
+
+        Constant cst = ci.getConstant();
+        if (!((cst instanceof CstMethodRef) ||
+              (cst instanceof CstType))) {
+            return false;
+        }
+
+        RegisterSpecList regs = ci.getRegisters();
+        int sz = regs.size();
+
+        if (sz == 0) {
+            return true;
+        }
+
+        int first = regs.get(0).getReg();
+        int next = first;
+
+        if (!unsignedFitsInShort(first)) {
+            return false;
+        }
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = regs.get(i);
+            if (one.getReg() != next) {
+                return false;
+            }
+            next += one.getCategory();
+        }
+
+        return unsignedFitsInByte(next - first);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        int sz = regs.size();
+        int cpi = ((CstInsn) insn).getIndex();
+        int firstReg;
+        int count;
+
+        if (sz == 0) {
+            firstReg = 0;
+            count = 0;
+        } else {
+            int lastReg = regs.get(sz - 1).getNextReg();
+            firstReg = regs.get(0).getReg();
+            count = lastReg - firstReg;
+        }
+
+        write(out,
+              opcodeUnit(insn, count),
+              (short) cpi,
+              (short) firstReg);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form51l.java b/dx/src/com/android/dx/dex/code/form/Form51l.java
new file mode 100644
index 0000000..9e3ab6a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form51l.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteral64;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 51l}. See the instruction format spec
+ * for details.
+ */
+public final class Form51l extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new Form51l();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private Form51l() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+        return regs.get(0).regString() + ", " + literalBitsString(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+        return literalBitsComment(value, 64);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        return 5;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        if (!((insn instanceof CstInsn) &&
+              (regs.size() == 1) &&
+              unsignedFitsInByte(regs.get(0).getReg()))) {
+            return false;
+        }
+
+        CstInsn ci = (CstInsn) insn;
+        Constant cst = ci.getConstant();
+
+        return (cst instanceof CstLiteral64);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        RegisterSpecList regs = insn.getRegisters();
+        long value =
+            ((CstLiteral64) ((CstInsn) insn).getConstant()).getLongBits();
+
+        write(out,
+              opcodeUnit(insn, regs.get(0).getReg()),
+              (short) value,
+              (short) (value >> 16),
+              (short) (value >> 32),
+              (short) (value >> 48));
+    }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/SpecialFormat.java b/dx/src/com/android/dx/dex/code/form/SpecialFormat.java
new file mode 100644
index 0000000..8a2e5ed
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/SpecialFormat.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.DalvOps;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format for nonstandard format instructions, which aren't
+ * generally real instructions but do end up appearing in instruction
+ * lists. Most of the overridden methods on this class end up throwing
+ * exceptions, as code should know (implicitly or explicitly) to avoid
+ * using this class. The one exception is {@link #isCompatible}, which
+ * always returns {@code true}.
+ */
+public final class SpecialFormat extends InsnFormat {
+    /** {@code non-null;} unique instance of this class */
+    public static final InsnFormat THE_ONE = new SpecialFormat();
+
+    /**
+     * Constructs an instance. This class is not publicly
+     * instantiable. Use {@link #THE_ONE}.
+     */
+    private SpecialFormat() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnArgString(DalvInsn insn) {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int codeSize() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCompatible(DalvInsn insn) {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InsnFormat nextUp() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+        throw new RuntimeException("unsupported");
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationItem.java b/dx/src/com/android/dx/dex/file/AnnotationItem.java
new file mode 100644
index 0000000..1febd9e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationItem.java
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Single annotation, which consists of a type and a set of name-value
+ * element pairs.
+ */
+public final class AnnotationItem extends OffsettedItem {
+    /** annotation visibility constant: visible at build time only */
+    private static final int VISIBILITY_BUILD = 0;
+
+    /** annotation visibility constant: visible at runtime */
+    private static final int VISIBILITY_RUNTIME = 1;
+
+    /** annotation visibility constant: visible at runtime only to system */
+    private static final int VISIBILITY_SYSTEM = 2;
+
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 1;
+
+    /** {@code non-null;} unique instance of {@link #TypeIdSorter} */
+    private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
+
+    /** {@code non-null;} the annotation to represent */
+    private final Annotation annotation;
+
+    /**
+     * {@code null-ok;} type reference for the annotation type; set during
+     * {@link #addContents}
+     */
+    private TypeIdItem type;
+
+    /**
+     * {@code null-ok;} encoded form, ready for writing to a file; set during
+     * {@link #place0}
+     */
+    private byte[] encodedForm;
+
+    /**
+     * Comparator that sorts (outer) instances by type id index.
+     */
+    private static class TypeIdSorter implements Comparator<AnnotationItem> {
+        /** {@inheritDoc} */
+        public int compare(AnnotationItem item1, AnnotationItem item2) {
+            int index1 = item1.type.getIndex();
+            int index2 = item2.type.getIndex();
+
+            if (index1 < index2) {
+                return -1;
+            } else if (index1 > index2) {
+                return 1;
+            }
+
+            return 0;
+        }
+    }
+
+    /**
+     * Sorts an array of instances, in place, by type id index,
+     * ignoring all other aspects of the elements. This is only valid
+     * to use after type id indices are known.
+     *
+     * @param array {@code non-null;} array to sort
+     */
+    public static void sortByTypeIdIndex(AnnotationItem[] array) {
+        Arrays.sort(array, TYPE_ID_SORTER);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param annotation {@code non-null;} annotation to represent
+     */
+    public AnnotationItem(Annotation annotation) {
+        /*
+         * The write size isn't known up-front because (the variable-lengthed)
+         * leb128 type is used to represent some things.
+         */
+        super(ALIGNMENT, -1);
+
+        if (annotation == null) {
+            throw new NullPointerException("annotation == null");
+        }
+
+        this.annotation = annotation;
+        this.type = null;
+        this.encodedForm = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ANNOTATION_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return annotation.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        AnnotationItem otherAnnotation = (AnnotationItem) other;
+
+        return annotation.compareTo(otherAnnotation.annotation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return annotation.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        type = file.getTypeIds().intern(annotation.getType());
+        ValueEncoder.addContents(file, annotation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Encode the data and note the size.
+
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+        ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
+
+        encoder.writeAnnotation(annotation, false);
+        encodedForm = out.toByteArray();
+
+        // Add one for the visibility byte in front of the encoded annotation.
+        setWriteSize(encodedForm.length + 1);
+    }
+
+    /**
+     * Write a (listing file) annotation for this instance to the given
+     * output, that consumes no bytes of output. This is for annotating
+     * a reference to this instance at the point of the reference.
+     *
+     * @param out {@code non-null;} where to output to
+     * @param prefix {@code non-null;} prefix for each line of output
+     */
+    public void annotateTo(AnnotatedOutput out, String prefix) {
+        out.annotate(0, prefix + "visibility: " +
+                annotation.getVisibility().toHuman());
+        out.annotate(0, prefix + "type: " + annotation.getType().toHuman());
+
+        for (NameValuePair pair : annotation.getNameValuePairs()) {
+            CstUtf8 name = pair.getName();
+            Constant value = pair.getValue();
+
+            out.annotate(0, prefix + name.toHuman() + ": " +
+                    ValueEncoder.constantToHuman(value));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        AnnotationVisibility visibility = annotation.getVisibility();
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " annotation");
+            out.annotate(1, "  visibility: VISBILITY_" + visibility);
+        }
+
+        switch (visibility) {
+            case BUILD:   out.writeByte(VISIBILITY_BUILD); break;
+            case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break;
+            case SYSTEM:  out.writeByte(VISIBILITY_SYSTEM); break;
+            default: {
+                // EMBEDDED shouldn't appear at the top level.
+                throw new RuntimeException("shouldn't happen");
+            }
+        }
+
+        if (annotates) {
+            /*
+             * The output is to be annotated, so redo the work previously
+             * done by place0(), except this time annotations will actually
+             * get emitted.
+             */
+            ValueEncoder encoder = new ValueEncoder(file, out);
+            encoder.writeAnnotation(annotation, true);
+        } else {
+            out.write(encodedForm);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationSetItem.java b/dx/src/com/android/dx/dex/file/AnnotationSetItem.java
new file mode 100644
index 0000000..2187700
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationSetItem.java
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Set of annotations, where no annotation type appears more than once.
+ */
+public final class AnnotationSetItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 4;
+
+    /** the size of an entry int the set: one {@code uint} */
+    private static final int ENTRY_WRITE_SIZE = 4;
+
+    /** {@code non-null;} the set of annotations */
+    private final Annotations annotations;
+
+    /**
+     * {@code non-null;} set of annotations as individual items in an array.
+     * <b>Note:</b> The contents have to get sorted by type id before
+     * writing.
+     */
+    private final AnnotationItem[] items;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param annotations {@code non-null;} set of annotations
+     */
+    public AnnotationSetItem(Annotations annotations) {
+        super(ALIGNMENT, writeSize(annotations));
+
+        this.annotations = annotations;
+        this.items = new AnnotationItem[annotations.size()];
+
+        int at = 0;
+        for (Annotation a : annotations.getAnnotations()) {
+            items[at] = new AnnotationItem(a);
+            at++;
+        }
+    }
+
+    /**
+     * Gets the write size for the given set.
+     *
+     * @param annotations {@code non-null;} the set
+     * @return {@code > 0;} the write size
+     */
+    private static int writeSize(Annotations annotations) {
+        // This includes an int size at the start of the list.
+
+        try {
+            return (annotations.size() * ENTRY_WRITE_SIZE) + 4;
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("list == null");
+        }
+    }
+
+    /**
+     * Gets the underlying annotations of this instance
+     *
+     * @return {@code non-null;} the annotations
+     */
+    public Annotations getAnnotations() {
+        return annotations;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return annotations.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        AnnotationSetItem otherSet = (AnnotationSetItem) other;
+
+        return annotations.compareTo(otherSet.annotations);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ANNOTATION_SET_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return annotations.toString();
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MixedItemSection byteData = file.getByteData();
+        int size = items.length;
+
+        for (int i = 0; i < size; i++) {
+            items[i] = byteData.intern(items[i]);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Sort the array to be in type id index order.
+        AnnotationItem.sortByTypeIdIndex(items);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        int size = items.length;
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " annotation set");
+            out.annotate(4, "  size: " + Hex.u4(size));
+        }
+
+        out.writeInt(size);
+
+        for (int i = 0; i < size; i++) {
+            AnnotationItem item = items[i];
+            int offset = item.getAbsoluteOffset();
+
+            if (annotates) {
+                out.annotate(4, "  entries[" + Integer.toHexString(i) + "]: " +
+                        Hex.u4(offset));
+                items[i].annotateTo(out, "    ");
+            }
+
+            out.writeInt(offset);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java b/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java
new file mode 100644
index 0000000..53072d8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Indirect reference to an {@link AnnotationSetItem}.
+ */
+public final class AnnotationSetRefItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 4;
+
+    /** write size of this class, in bytes */
+    private static final int WRITE_SIZE = 4;
+
+    /** {@code non-null;} the annotation set to refer to */
+    private AnnotationSetItem annotations;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param annotations {@code non-null;} the annotation set to refer to
+     */
+    public AnnotationSetRefItem(AnnotationSetItem annotations) {
+        super(ALIGNMENT, WRITE_SIZE);
+
+        if (annotations == null) {
+            throw new NullPointerException("annotations == null");
+        }
+
+        this.annotations = annotations;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ANNOTATION_SET_REF_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MixedItemSection wordData = file.getWordData();
+
+        annotations = wordData.intern(annotations);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return annotations.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        int annotationsOff = annotations.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "  annotations_off: " + Hex.u4(annotationsOff));
+        }
+
+        out.writeInt(annotationsOff);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationUtils.java b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
new file mode 100644
index 0000000..d500ec4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+import java.util.ArrayList;
+
+import static com.android.dx.rop.annotation.AnnotationVisibility.*;
+
+/**
+ * Utility class for dealing with annotations.
+ */
+public final class AnnotationUtils {
+    /** {@code non-null;} type for {@code AnnotationDefault} annotations */
+    private static final CstType ANNOTATION_DEFAULT_TYPE =
+        CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;"));
+
+    /** {@code non-null;} type for {@code EnclosingClass} annotations */
+    private static final CstType ENCLOSING_CLASS_TYPE =
+        CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;"));
+
+    /** {@code non-null;} type for {@code EnclosingMethod} annotations */
+    private static final CstType ENCLOSING_METHOD_TYPE =
+        CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;"));
+
+    /** {@code non-null;} type for {@code InnerClass} annotations */
+    private static final CstType INNER_CLASS_TYPE =
+        CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;"));
+
+    /** {@code non-null;} type for {@code MemberClasses} annotations */
+    private static final CstType MEMBER_CLASSES_TYPE =
+        CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;"));
+
+    /** {@code non-null;} type for {@code Signature} annotations */
+    private static final CstType SIGNATURE_TYPE =
+        CstType.intern(Type.intern("Ldalvik/annotation/Signature;"));
+
+    /** {@code non-null;} type for {@code Throws} annotations */
+    private static final CstType THROWS_TYPE =
+        CstType.intern(Type.intern("Ldalvik/annotation/Throws;"));
+
+    /** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */
+    private static final CstUtf8 ACCESS_FLAGS_UTF = new CstUtf8("accessFlags");
+
+    /** {@code non-null;} the UTF-8 constant {@code "name"} */
+    private static final CstUtf8 NAME_UTF = new CstUtf8("name");
+
+    /** {@code non-null;} the UTF-8 constant {@code "value"} */
+    private static final CstUtf8 VALUE_UTF = new CstUtf8("value");
+
+    /**
+     * This class is uninstantiable.
+     */
+    private AnnotationUtils() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Constructs a standard {@code AnnotationDefault} annotation.
+     *
+     * @param defaults {@code non-null;} the defaults, itself as an annotation
+     * @return {@code non-null;} the constructed annotation
+     */
+    public static Annotation makeAnnotationDefault(Annotation defaults) {
+        Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM);
+
+        result.put(new NameValuePair(VALUE_UTF, new CstAnnotation(defaults)));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard {@code EnclosingClass} annotation.
+     *
+     * @param clazz {@code non-null;} the enclosing class
+     * @return {@code non-null;} the annotation
+     */
+    public static Annotation makeEnclosingClass(CstType clazz) {
+        Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM);
+
+        result.put(new NameValuePair(VALUE_UTF, clazz));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard {@code EnclosingMethod} annotation.
+     *
+     * @param method {@code non-null;} the enclosing method
+     * @return {@code non-null;} the annotation
+     */
+    public static Annotation makeEnclosingMethod(CstMethodRef method) {
+        Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM);
+
+        result.put(new NameValuePair(VALUE_UTF, method));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard {@code InnerClass} annotation.
+     *
+     * @param name {@code null-ok;} the original name of the class, or
+     * {@code null} to represent an anonymous class
+     * @param accessFlags the original access flags
+     * @return {@code non-null;} the annotation
+     */
+    public static Annotation makeInnerClass(CstUtf8 name, int accessFlags) {
+        Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM);
+        Constant nameCst =
+            (name != null) ? new CstString(name) : CstKnownNull.THE_ONE;
+
+        result.put(new NameValuePair(NAME_UTF, nameCst));
+        result.put(new NameValuePair(ACCESS_FLAGS_UTF,
+                        CstInteger.make(accessFlags)));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard {@code MemberClasses} annotation.
+     *
+     * @param types {@code non-null;} the list of (the types of) the member classes
+     * @return {@code non-null;} the annotation
+     */
+    public static Annotation makeMemberClasses(TypeList types) {
+        CstArray array = makeCstArray(types);
+        Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM);
+        result.put(new NameValuePair(VALUE_UTF, array));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard {@code Signature} annotation.
+     *
+     * @param signature {@code non-null;} the signature string
+     * @return {@code non-null;} the annotation
+     */
+    public static Annotation makeSignature(CstUtf8 signature) {
+        Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM);
+
+        /*
+         * Split the string into pieces that are likely to be common
+         * across many signatures and the rest of the file.
+         */
+
+        String raw = signature.getString();
+        int rawLength = raw.length();
+        ArrayList<String> pieces = new ArrayList<String>(20);
+
+        for (int at = 0; at < rawLength; /*at*/) {
+            char c = raw.charAt(at);
+            int endAt = at + 1;
+            if (c == 'L') {
+                // Scan to ';' or '<'. Consume ';' but not '<'.
+                while (endAt < rawLength) {
+                    c = raw.charAt(endAt);
+                    if (c == ';') {
+                        endAt++;
+                        break;
+                    } else if (c == '<') {
+                        break;
+                    }
+                    endAt++;
+                }
+            } else {
+                // Scan to 'L' without consuming it.
+                while (endAt < rawLength) {
+                    c = raw.charAt(endAt);
+                    if (c == 'L') {
+                        break;
+                    }
+                    endAt++;
+                }
+            }
+
+            pieces.add(raw.substring(at, endAt));
+            at = endAt;
+        }
+
+        int size = pieces.size();
+        CstArray.List list = new CstArray.List(size);
+
+        for (int i = 0; i < size; i++) {
+            list.set(i, new CstString(pieces.get(i)));
+        }
+
+        list.setImmutable();
+
+        result.put(new NameValuePair(VALUE_UTF, new CstArray(list)));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs a standard {@code Throws} annotation.
+     *
+     * @param types {@code non-null;} the list of thrown types
+     * @return {@code non-null;} the annotation
+     */
+    public static Annotation makeThrows(TypeList types) {
+        CstArray array = makeCstArray(types);
+        Annotation result = new Annotation(THROWS_TYPE, SYSTEM);
+        result.put(new NameValuePair(VALUE_UTF, array));
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Converts a {@link TypeList} to a {@link CstArray}.
+     *
+     * @param types {@code non-null;} the type list
+     * @return {@code non-null;} the corresponding array constant
+     */
+    private static CstArray makeCstArray(TypeList types) {
+        int size = types.size();
+        CstArray.List list = new CstArray.List(size);
+
+        for (int i = 0; i < size; i++) {
+            list.set(i, CstType.intern(types.getType(i)));
+        }
+
+        list.setImmutable();
+        return new CstArray(list);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java b/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java
new file mode 100644
index 0000000..972d4e6
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java
@@ -0,0 +1,385 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Per-class directory of annotations.
+ */
+public final class AnnotationsDirectoryItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 4;
+
+    /** write size of this class's header, in bytes */
+    private static final int HEADER_SIZE = 16;
+
+    /** write size of a list element, in bytes */
+    private static final int ELEMENT_SIZE = 8;
+
+    /** {@code null-ok;} the class-level annotations, if any */
+    private AnnotationSetItem classAnnotations;
+
+    /** {@code null-ok;} the annotated fields, if any */
+    private ArrayList<FieldAnnotationStruct> fieldAnnotations;
+
+    /** {@code null-ok;} the annotated methods, if any */
+    private ArrayList<MethodAnnotationStruct> methodAnnotations;
+
+    /** {@code null-ok;} the annotated parameters, if any */
+    private ArrayList<ParameterAnnotationStruct> parameterAnnotations;
+
+    /**
+     * Constructs an empty instance.
+     */
+    public AnnotationsDirectoryItem() {
+        super(ALIGNMENT, -1);
+
+        classAnnotations = null;
+        fieldAnnotations = null;
+        methodAnnotations = null;
+        parameterAnnotations = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
+    }
+
+    /**
+     * Returns whether this item is empty (has no contents).
+     *
+     * @return {@code true} if this item is empty, or {@code false}
+     * if not
+     */
+    public boolean isEmpty() {
+        return (classAnnotations == null) &&
+            (fieldAnnotations == null) &&
+            (methodAnnotations == null) &&
+            (parameterAnnotations == null);
+    }
+
+    /**
+     * Returns whether this item is a candidate for interning. The only
+     * interning candidates are ones that <i>only</i> have a non-null
+     * set of class annotations, with no other lists.
+     *
+     * @return {@code true} if this is an interning candidate, or
+     * {@code false} if not
+     */
+    public boolean isInternable() {
+        return (classAnnotations != null) &&
+            (fieldAnnotations == null) &&
+            (methodAnnotations == null) &&
+            (parameterAnnotations == null);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        if (classAnnotations == null) {
+            return 0;
+        }
+
+        return classAnnotations.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p><b>Note:</b>: This throws an exception if this item is not
+     * internable.</p>
+     *
+     * @see #isInternable
+     */
+    @Override
+    public int compareTo0(OffsettedItem other) {
+        if (! isInternable()) {
+            throw new UnsupportedOperationException("uninternable instance");
+        }
+
+        AnnotationsDirectoryItem otherDirectory =
+            (AnnotationsDirectoryItem) other;
+        return classAnnotations.compareTo(otherDirectory.classAnnotations);
+    }
+
+    /**
+     * Sets the direct annotations on this instance. These are annotations
+     * made on the class, per se, as opposed to on one of its members.
+     * It is only valid to call this method at most once per instance.
+     *
+     * @param annotations {@code non-null;} annotations to set for this class
+     */
+    public void setClassAnnotations(Annotations annotations) {
+        if (annotations == null) {
+            throw new NullPointerException("annotations == null");
+        }
+
+        if (classAnnotations != null) {
+            throw new UnsupportedOperationException(
+                    "class annotations already set");
+        }
+
+        classAnnotations = new AnnotationSetItem(annotations);
+    }
+
+    /**
+     * Adds a field annotations item to this instance.
+     *
+     * @param field {@code non-null;} field in question
+     * @param annotations {@code non-null;} associated annotations to add
+     */
+    public void addFieldAnnotations(CstFieldRef field,
+            Annotations annotations) {
+        if (fieldAnnotations == null) {
+            fieldAnnotations = new ArrayList<FieldAnnotationStruct>();
+        }
+
+        fieldAnnotations.add(new FieldAnnotationStruct(field,
+                        new AnnotationSetItem(annotations)));
+    }
+
+    /**
+     * Adds a method annotations item to this instance.
+     *
+     * @param method {@code non-null;} method in question
+     * @param annotations {@code non-null;} associated annotations to add
+     */
+    public void addMethodAnnotations(CstMethodRef method,
+            Annotations annotations) {
+        if (methodAnnotations == null) {
+            methodAnnotations = new ArrayList<MethodAnnotationStruct>();
+        }
+
+        methodAnnotations.add(new MethodAnnotationStruct(method,
+                        new AnnotationSetItem(annotations)));
+    }
+
+    /**
+     * Adds a parameter annotations item to this instance.
+     *
+     * @param method {@code non-null;} method in question
+     * @param list {@code non-null;} associated list of annotation sets to add
+     */
+    public void addParameterAnnotations(CstMethodRef method,
+            AnnotationsList list) {
+        if (parameterAnnotations == null) {
+            parameterAnnotations = new ArrayList<ParameterAnnotationStruct>();
+        }
+
+        parameterAnnotations.add(new ParameterAnnotationStruct(method, list));
+    }
+
+    /**
+     * Gets the method annotations for a given method, if any. This is
+     * meant for use by debugging / dumping code.
+     *
+     * @param method {@code non-null;} the method
+     * @return {@code null-ok;} the method annotations, if any
+     */
+    public Annotations getMethodAnnotations(CstMethodRef method) {
+        if (methodAnnotations == null) {
+            return null;
+        }
+
+        for (MethodAnnotationStruct item : methodAnnotations) {
+            if (item.getMethod().equals(method)) {
+                return item.getAnnotations();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the parameter annotations for a given method, if any. This is
+     * meant for use by debugging / dumping code.
+     *
+     * @param method {@code non-null;} the method
+     * @return {@code null-ok;} the parameter annotations, if any
+     */
+    public AnnotationsList getParameterAnnotations(CstMethodRef method) {
+        if (parameterAnnotations == null) {
+            return null;
+        }
+
+        for (ParameterAnnotationStruct item : parameterAnnotations) {
+            if (item.getMethod().equals(method)) {
+                return item.getAnnotationsList();
+            }
+        }
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MixedItemSection wordData = file.getWordData();
+
+        if (classAnnotations != null) {
+            classAnnotations = wordData.intern(classAnnotations);
+        }
+
+        if (fieldAnnotations != null) {
+            for (FieldAnnotationStruct item : fieldAnnotations) {
+                item.addContents(file);
+            }
+        }
+
+        if (methodAnnotations != null) {
+            for (MethodAnnotationStruct item : methodAnnotations) {
+                item.addContents(file);
+            }
+        }
+
+        if (parameterAnnotations != null) {
+            for (ParameterAnnotationStruct item : parameterAnnotations) {
+                item.addContents(file);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // We just need to set the write size here.
+
+        int elementCount = listSize(fieldAnnotations)
+            + listSize(methodAnnotations) + listSize(parameterAnnotations);
+        setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations);
+        int fieldsSize = listSize(fieldAnnotations);
+        int methodsSize = listSize(methodAnnotations);
+        int parametersSize = listSize(parameterAnnotations);
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " annotations directory");
+            out.annotate(4, "  class_annotations_off: " + Hex.u4(classOff));
+            out.annotate(4, "  fields_size:           " +
+                    Hex.u4(fieldsSize));
+            out.annotate(4, "  methods_size:          " +
+                    Hex.u4(methodsSize));
+            out.annotate(4, "  parameters_size:       " +
+                    Hex.u4(parametersSize));
+        }
+
+        out.writeInt(classOff);
+        out.writeInt(fieldsSize);
+        out.writeInt(methodsSize);
+        out.writeInt(parametersSize);
+
+        if (fieldsSize != 0) {
+            Collections.sort(fieldAnnotations);
+            if (annotates) {
+                out.annotate(0, "  fields:");
+            }
+            for (FieldAnnotationStruct item : fieldAnnotations) {
+                item.writeTo(file, out);
+            }
+        }
+
+        if (methodsSize != 0) {
+            Collections.sort(methodAnnotations);
+            if (annotates) {
+                out.annotate(0, "  methods:");
+            }
+            for (MethodAnnotationStruct item : methodAnnotations) {
+                item.writeTo(file, out);
+            }
+        }
+
+        if (parametersSize != 0) {
+            Collections.sort(parameterAnnotations);
+            if (annotates) {
+                out.annotate(0, "  parameters:");
+            }
+            for (ParameterAnnotationStruct item : parameterAnnotations) {
+                item.writeTo(file, out);
+            }
+        }
+    }
+
+    /**
+     * Gets the list size of the given list, or {@code 0} if given
+     * {@code null}.
+     *
+     * @param list {@code null-ok;} the list in question
+     * @return {@code >= 0;} its size
+     */
+    private static int listSize(ArrayList<?> list) {
+        if (list == null) {
+            return 0;
+        }
+
+        return list.size();
+    }
+
+    /**
+     * Prints out the contents of this instance, in a debugging-friendly
+     * way. This is meant to be called from {@link ClassDefItem#debugPrint}.
+     *
+     * @param out {@code non-null;} where to output to
+     */
+    /*package*/ void debugPrint(PrintWriter out) {
+        if (classAnnotations != null) {
+            out.println("  class annotations: " + classAnnotations);
+        }
+
+        if (fieldAnnotations != null) {
+            out.println("  field annotations:");
+            for (FieldAnnotationStruct item : fieldAnnotations) {
+                out.println("    " + item.toHuman());
+            }
+        }
+
+        if (methodAnnotations != null) {
+            out.println("  method annotations:");
+            for (MethodAnnotationStruct item : methodAnnotations) {
+                out.println("    " + item.toHuman());
+            }
+        }
+
+        if (parameterAnnotations != null) {
+            out.println("  parameter annotations:");
+            for (ParameterAnnotationStruct item : parameterAnnotations) {
+                out.println("    " + item.toHuman());
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/CatchStructs.java b/dx/src/com/android/dx/dex/file/CatchStructs.java
new file mode 100644
index 0000000..e07ec29
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/CatchStructs.java
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.CatchHandlerList;
+import com.android.dx.dex.code.CatchTable;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * List of exception handlers (tuples of covered range, catch type,
+ * handler address) for a particular piece of code. Instances of this
+ * class correspond to a {@code try_item[]} and a
+ * {@code catch_handler_item[]}.
+ */
+public final class CatchStructs {
+    /**
+     * the size of a {@code try_item}: a {@code uint}
+     * and two {@code ushort}s
+     */
+    private static final int TRY_ITEM_WRITE_SIZE = 4 + (2 * 2);
+
+    /** {@code non-null;} code that contains the catches */
+    private final DalvCode code;
+
+    /**
+     * {@code null-ok;} the underlying table; set in
+     * {@link #finishProcessingIfNecessary}
+     */
+    private CatchTable table;
+
+    /**
+     * {@code null-ok;} the encoded handler list, if calculated; set in
+     * {@link #encode}
+     */
+    private byte[] encodedHandlers;
+
+    /**
+     * length of the handlers header (encoded size), if known; used for
+     * annotation
+     */
+    private int encodedHandlerHeaderSize;
+
+    /**
+     * {@code null-ok;} map from handler lists to byte offsets, if calculated; set in
+     * {@link #encode}
+     */
+    private TreeMap<CatchHandlerList, Integer> handlerOffsets;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param code {@code non-null;} code that contains the catches
+     */
+    public CatchStructs(DalvCode code) {
+        this.code = code;
+        this.table = null;
+        this.encodedHandlers = null;
+        this.encodedHandlerHeaderSize = 0;
+        this.handlerOffsets = null;
+    }
+
+    /**
+     * Finish processing the catches, if necessary.
+     */
+    private void finishProcessingIfNecessary() {
+        if (table == null) {
+            table = code.getCatches();
+        }
+    }
+
+    /**
+     * Gets the size of the tries list, in entries.
+     *
+     * @return {@code >= 0;} the tries list size
+     */
+    public int triesSize() {
+        finishProcessingIfNecessary();
+        return table.size();
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out {@code non-null;} where to dump
+     * @param prefix {@code non-null;} prefix to attach to each line of output
+     */
+    public void debugPrint(PrintWriter out, String prefix) {
+        annotateEntries(prefix, out, null);
+    }
+
+    /**
+     * Encodes the handler lists.
+     *
+     * @param file {@code non-null;} file this instance is part of
+     */
+    public void encode(DexFile file) {
+        finishProcessingIfNecessary();
+
+        TypeIdsSection typeIds = file.getTypeIds();
+        int size = table.size();
+
+        handlerOffsets = new TreeMap<CatchHandlerList, Integer>();
+
+        /*
+         * First add a map entry for each unique list. The tree structure
+         * will ensure they are sorted when we reiterate later.
+         */
+        for (int i = 0; i < size; i++) {
+            handlerOffsets.put(table.get(i).getHandlers(), null);
+        }
+
+        if (handlerOffsets.size() > 65535) {
+            throw new UnsupportedOperationException(
+                    "too many catch handlers");
+        }
+
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+
+        // Write out the handlers "header" consisting of its size in entries.
+        encodedHandlerHeaderSize =
+            out.writeUnsignedLeb128(handlerOffsets.size());
+
+        // Now write the lists out in order, noting the offset of each.
+        for (Map.Entry<CatchHandlerList, Integer> mapping :
+                 handlerOffsets.entrySet()) {
+            CatchHandlerList list = mapping.getKey();
+            int listSize = list.size();
+            boolean catchesAll = list.catchesAll();
+
+            // Set the offset before we do any writing.
+            mapping.setValue(out.getCursor());
+
+            if (catchesAll) {
+                // A size <= 0 means that the list ends with a catch-all.
+                out.writeSignedLeb128(-(listSize - 1));
+                listSize--;
+            } else {
+                out.writeSignedLeb128(listSize);
+            }
+
+            for (int i = 0; i < listSize; i++) {
+                CatchHandlerList.Entry entry = list.get(i);
+                out.writeUnsignedLeb128(
+                        typeIds.indexOf(entry.getExceptionType()));
+                out.writeUnsignedLeb128(entry.getHandler());
+            }
+
+            if (catchesAll) {
+                out.writeUnsignedLeb128(list.get(listSize).getHandler());
+            }
+        }
+
+        encodedHandlers = out.toByteArray();
+    }
+
+    /**
+     * Gets the write size of this instance, in bytes.
+     *
+     * @return {@code >= 0;} the write size
+     */
+    public int writeSize() {
+        return (triesSize() * TRY_ITEM_WRITE_SIZE) +
+                + encodedHandlers.length;
+    }
+
+    /**
+     * Writes this instance to the given stream.
+     *
+     * @param file {@code non-null;} file this instance is part of
+     * @param out {@code non-null;} where to write to
+     */
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        finishProcessingIfNecessary();
+
+        if (out.annotates()) {
+            annotateEntries("  ", null, out);
+        }
+
+        int tableSize = table.size();
+        for (int i = 0; i < tableSize; i++) {
+            CatchTable.Entry one = table.get(i);
+            int start = one.getStart();
+            int end = one.getEnd();
+            int insnCount = end - start;
+
+            if (insnCount >= 65536) {
+                throw new UnsupportedOperationException(
+                        "bogus exception range: " + Hex.u4(start) + ".." +
+                        Hex.u4(end));
+            }
+
+            out.writeInt(start);
+            out.writeShort(insnCount);
+            out.writeShort(handlerOffsets.get(one.getHandlers()));
+        }
+
+        out.write(encodedHandlers);
+    }
+
+    /**
+     * Helper method to annotate or simply print the exception handlers.
+     * Only one of {@code printTo} or {@code annotateTo} should
+     * be non-null.
+     *
+     * @param prefix {@code non-null;} prefix for each line
+     * @param printTo {@code null-ok;} where to print to
+     * @param annotateTo {@code null-ok;} where to consume bytes and annotate to
+     */
+    private void annotateEntries(String prefix, PrintWriter printTo,
+            AnnotatedOutput annotateTo) {
+        finishProcessingIfNecessary();
+
+        boolean consume = (annotateTo != null);
+        int amt1 = consume ? 6 : 0;
+        int amt2 = consume ? 2 : 0;
+        int size = table.size();
+        String subPrefix = prefix + "  ";
+
+        if (consume) {
+            annotateTo.annotate(0, prefix + "tries:");
+        } else {
+            printTo.println(prefix + "tries:");
+        }
+
+        for (int i = 0; i < size; i++) {
+            CatchTable.Entry entry = table.get(i);
+            CatchHandlerList handlers = entry.getHandlers();
+            String s1 = subPrefix + "try " + Hex.u2or4(entry.getStart())
+                + ".." + Hex.u2or4(entry.getEnd());
+            String s2 = handlers.toHuman(subPrefix, "");
+
+            if (consume) {
+                annotateTo.annotate(amt1, s1);
+                annotateTo.annotate(amt2, s2);
+            } else {
+                printTo.println(s1);
+                printTo.println(s2);
+            }
+        }
+
+        if (! consume) {
+            // Only emit the handler lists if we are consuming bytes.
+            return;
+        }
+
+        annotateTo.annotate(0, prefix + "handlers:");
+        annotateTo.annotate(encodedHandlerHeaderSize,
+                subPrefix + "size: " + Hex.u2(handlerOffsets.size()));
+
+        int lastOffset = 0;
+        CatchHandlerList lastList = null;
+
+        for (Map.Entry<CatchHandlerList, Integer> mapping :
+                 handlerOffsets.entrySet()) {
+            CatchHandlerList list = mapping.getKey();
+            int offset = mapping.getValue();
+
+            if (lastList != null) {
+                annotateAndConsumeHandlers(lastList, lastOffset,
+                        offset - lastOffset, subPrefix, printTo, annotateTo);
+            }
+
+            lastList = list;
+            lastOffset = offset;
+        }
+
+        annotateAndConsumeHandlers(lastList, lastOffset,
+                encodedHandlers.length - lastOffset,
+                subPrefix, printTo, annotateTo);
+    }
+
+    /**
+     * Helper for {@link #annotateEntries} to annotate a catch handler list
+     * while consuming it.
+     *
+     * @param handlers {@code non-null;} handlers to annotate
+     * @param offset {@code >= 0;} the offset of this handler
+     * @param size {@code >= 1;} the number of bytes the handlers consume
+     * @param prefix {@code non-null;} prefix for each line
+     * @param printTo {@code null-ok;} where to print to
+     * @param annotateTo {@code non-null;} where to annotate to
+     */
+    private static void annotateAndConsumeHandlers(CatchHandlerList handlers,
+            int offset, int size, String prefix, PrintWriter printTo,
+            AnnotatedOutput annotateTo) {
+        String s = handlers.toHuman(prefix, Hex.u2(offset) + ": ");
+
+        if (printTo != null) {
+            printTo.println(s);
+        }
+
+        annotateTo.annotate(size, s);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDataItem.java b/dx/src/com/android/dx/dex/file/ClassDataItem.java
new file mode 100644
index 0000000..275ae99
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDataItem.java
@@ -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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.Zeroes;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Writers;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.HashMap;
+
+/**
+ * Representation of all the parts of a Dalvik class that are generally
+ * "inflated" into an in-memory representation at runtime. Instances of
+ * this class are represented in a compact streamable form in a
+ * {@code dex} file, as opposed to a random-access form.
+ */
+public final class ClassDataItem extends OffsettedItem {
+    /** {@code non-null;} what class this data is for, just for listing generation */
+    private final CstType thisClass;
+
+    /** {@code non-null;} list of static fields */
+    private final ArrayList<EncodedField> staticFields;
+
+    /** {@code non-null;} list of initial values for static fields */
+    private final HashMap<EncodedField, Constant> staticValues;
+
+    /** {@code non-null;} list of instance fields */
+    private final ArrayList<EncodedField> instanceFields;
+
+    /** {@code non-null;} list of direct methods */
+    private final ArrayList<EncodedMethod> directMethods;
+
+    /** {@code non-null;} list of virtual methods */
+    private final ArrayList<EncodedMethod> virtualMethods;
+
+    /** {@code null-ok;} static initializer list; set in {@link #addContents} */
+    private CstArray staticValuesConstant;
+
+    /**
+     * {@code null-ok;} encoded form, ready for writing to a file; set during
+     * {@link #place0}
+     */
+    private byte[] encodedForm;
+
+    /**
+     * Constructs an instance. Its sets of members are initially
+     * empty.
+     *
+     * @param thisClass {@code non-null;} what class this data is for, just
+     * for listing generation
+     */
+    public ClassDataItem(CstType thisClass) {
+        super(1, -1);
+
+        if (thisClass == null) {
+            throw new NullPointerException("thisClass == null");
+        }
+
+        this.thisClass = thisClass;
+        this.staticFields = new ArrayList<EncodedField>(20);
+        this.staticValues = new HashMap<EncodedField, Constant>(40);
+        this.instanceFields = new ArrayList<EncodedField>(20);
+        this.directMethods = new ArrayList<EncodedMethod>(20);
+        this.virtualMethods = new ArrayList<EncodedMethod>(20);
+        this.staticValuesConstant = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_CLASS_DATA_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return toString();
+    }
+
+    /**
+     * Returns whether this instance is empty.
+     *
+     * @return {@code true} if this instance is empty or
+     * {@code false} if at least one element has been added to it
+     */
+    public boolean isEmpty() {
+        return staticFields.isEmpty() && instanceFields.isEmpty()
+            && directMethods.isEmpty() && virtualMethods.isEmpty();
+    }
+
+    /**
+     * Adds a static field.
+     *
+     * @param field {@code non-null;} the field to add
+     * @param value {@code null-ok;} initial value for the field, if any
+     */
+    public void addStaticField(EncodedField field, Constant value) {
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        if (staticValuesConstant != null) {
+            throw new UnsupportedOperationException(
+                    "static fields already sorted");
+        }
+
+        staticFields.add(field);
+        staticValues.put(field, value);
+    }
+
+    /**
+     * Adds an instance field.
+     *
+     * @param field {@code non-null;} the field to add
+     */
+    public void addInstanceField(EncodedField field) {
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        instanceFields.add(field);
+    }
+
+    /**
+     * Adds a direct ({@code static} and/or {@code private}) method.
+     *
+     * @param method {@code non-null;} the method to add
+     */
+    public void addDirectMethod(EncodedMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        directMethods.add(method);
+    }
+
+    /**
+     * Adds a virtual method.
+     *
+     * @param method {@code non-null;} the method to add
+     */
+    public void addVirtualMethod(EncodedMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        virtualMethods.add(method);
+    }
+
+    /**
+     * Gets all the methods in this class. The returned list is not linked
+     * in any way to the underlying lists contained in this instance, but
+     * the objects contained in the list are shared.
+     *
+     * @return {@code non-null;} list of all methods
+     */
+    public ArrayList<EncodedMethod> getMethods() {
+        int sz = directMethods.size() + virtualMethods.size();
+        ArrayList<EncodedMethod> result = new ArrayList<EncodedMethod>(sz);
+
+        result.addAll(directMethods);
+        result.addAll(virtualMethods);
+
+        return result;
+    }
+
+
+    /**
+     * Prints out the contents of this instance, in a debugging-friendly
+     * way.
+     *
+     * @param out {@code non-null;} where to output to
+     * @param verbose whether to be verbose with the output
+     */
+    public void debugPrint(Writer out, boolean verbose) {
+        PrintWriter pw = Writers.printWriterFor(out);
+
+        int sz = staticFields.size();
+        for (int i = 0; i < sz; i++) {
+            pw.println("  sfields[" + i + "]: " + staticFields.get(i));
+        }
+
+        sz = instanceFields.size();
+        for (int i = 0; i < sz; i++) {
+            pw.println("  ifields[" + i + "]: " + instanceFields.get(i));
+        }
+
+        sz = directMethods.size();
+        for (int i = 0; i < sz; i++) {
+            pw.println("  dmeths[" + i + "]:");
+            directMethods.get(i).debugPrint(pw, verbose);
+        }
+
+        sz = virtualMethods.size();
+        for (int i = 0; i < sz; i++) {
+            pw.println("  vmeths[" + i + "]:");
+            virtualMethods.get(i).debugPrint(pw, verbose);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        if (!staticFields.isEmpty()) {
+            getStaticValuesConstant(); // Force the fields to be sorted.
+            for (EncodedField field : staticFields) {
+                field.addContents(file);
+            }
+        }
+
+        if (!instanceFields.isEmpty()) {
+            Collections.sort(instanceFields);
+            for (EncodedField field : instanceFields) {
+                field.addContents(file);
+            }
+        }
+
+        if (!directMethods.isEmpty()) {
+            Collections.sort(directMethods);
+            for (EncodedMethod method : directMethods) {
+                method.addContents(file);
+            }
+        }
+
+        if (!virtualMethods.isEmpty()) {
+            Collections.sort(virtualMethods);
+            for (EncodedMethod method : virtualMethods) {
+                method.addContents(file);
+            }
+        }
+    }
+
+    /**
+     * Gets a {@link CstArray} corresponding to {@link #staticValues} if
+     * it contains any non-zero non-{@code null} values.
+     *
+     * @return {@code null-ok;} the corresponding constant or {@code null} if
+     * there are no values to encode
+     */
+    public CstArray getStaticValuesConstant() {
+        if ((staticValuesConstant == null) && (staticFields.size() != 0)) {
+            staticValuesConstant = makeStaticValuesConstant();
+        }
+
+        return staticValuesConstant;
+    }
+
+    /**
+     * Gets a {@link CstArray} corresponding to {@link #staticValues} if
+     * it contains any non-zero non-{@code null} values.
+     *
+     * @return {@code null-ok;} the corresponding constant or {@code null} if
+     * there are no values to encode
+     */
+    private CstArray makeStaticValuesConstant() {
+        // First sort the statics into their final order.
+        Collections.sort(staticFields);
+
+        /*
+         * Get the size of staticValues minus any trailing zeros/nulls (both
+         * nulls per se as well as instances of CstKnownNull).
+         */
+
+        int size = staticFields.size();
+        while (size > 0) {
+            EncodedField field = staticFields.get(size - 1);
+            Constant cst = staticValues.get(field);
+            if (cst instanceof CstLiteralBits) {
+                // Note: CstKnownNull extends CstLiteralBits.
+                if (((CstLiteralBits) cst).getLongBits() != 0) {
+                    break;
+                }
+            } else if (cst != null) {
+                break;
+            }
+            size--;
+        }
+
+        if (size == 0) {
+            return null;
+        }
+
+        // There is something worth encoding, so build up a result.
+
+        CstArray.List list = new CstArray.List(size);
+        for (int i = 0; i < size; i++) {
+            EncodedField field = staticFields.get(i);
+            Constant cst = staticValues.get(field);
+            if (cst == null) {
+                cst = Zeroes.zeroFor(field.getRef().getType());
+            }
+            list.set(i, cst);
+        }
+        list.setImmutable();
+
+        return new CstArray(list);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Encode the data and note the size.
+
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+
+        encodeOutput(addedTo.getFile(), out);
+        encodedForm = out.toByteArray();
+        setWriteSize(encodedForm.length);
+    }
+
+    /**
+     * Writes out the encoded form of this instance.
+     *
+     * @param file {@code non-null;} file this instance is part of
+     * @param out {@code non-null;} where to write to
+     */
+    private void encodeOutput(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " class data for " +
+                    thisClass.toHuman());
+        }
+
+        encodeSize(file, out, "static_fields", staticFields.size());
+        encodeSize(file, out, "instance_fields", instanceFields.size());
+        encodeSize(file, out, "direct_methods", directMethods.size());
+        encodeSize(file, out, "virtual_methods", virtualMethods.size());
+
+        encodeList(file, out, "static_fields", staticFields);
+        encodeList(file, out, "instance_fields", instanceFields);
+        encodeList(file, out, "direct_methods", directMethods);
+        encodeList(file, out, "virtual_methods", virtualMethods);
+
+        if (annotates) {
+            out.endAnnotation();
+        }
+    }
+
+    /**
+     * Helper for {@link #encodeOutput}, which writes out the given
+     * size value, annotating it as well (if annotations are enabled).
+     *
+     * @param file {@code non-null;} file this instance is part of
+     * @param out {@code non-null;} where to write to
+     * @param label {@code non-null;} the label for the purposes of annotation
+     * @param size {@code >= 0;} the size to write
+     */
+    private static void encodeSize(DexFile file, AnnotatedOutput out,
+            String label, int size) {
+        if (out.annotates()) {
+            out.annotate(String.format("  %-21s %08x", label + "_size:",
+                            size));
+        }
+
+        out.writeUnsignedLeb128(size);
+    }
+
+    /**
+     * Helper for {@link #encodeOutput}, which writes out the given
+     * list. It also annotates the items (if any and if annotations
+     * are enabled).
+     *
+     * @param file {@code non-null;} file this instance is part of
+     * @param out {@code non-null;} where to write to
+     * @param label {@code non-null;} the label for the purposes of annotation
+     * @param list {@code non-null;} the list in question
+     */
+    private static void encodeList(DexFile file, AnnotatedOutput out,
+            String label, ArrayList<? extends EncodedMember> list) {
+        int size = list.size();
+        int lastIndex = 0;
+
+        if (size == 0) {
+            return;
+        }
+
+        if (out.annotates()) {
+            out.annotate(0, "  " + label + ":");
+        }
+
+        for (int i = 0; i < size; i++) {
+            lastIndex = list.get(i).encode(file, out, lastIndex, i);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+
+        if (annotates) {
+            /*
+             * The output is to be annotated, so redo the work previously
+             * done by place0(), except this time annotations will actually
+             * get emitted.
+             */
+            encodeOutput(file, out);
+        } else {
+            out.write(encodedForm);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDefItem.java b/dx/src/com/android/dx/dex/file/ClassDefItem.java
new file mode 100644
index 0000000..4d1719b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDefItem.java
@@ -0,0 +1,410 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Writers;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.TreeSet;
+
+/**
+ * Representation of a Dalvik class, which is basically a set of
+ * members (fields or methods) along with a few more pieces of
+ * information.
+ */
+public final class ClassDefItem extends IndexedItem {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 32;
+
+    /** {@code non-null;} type constant for this class */
+    private final CstType thisClass;
+
+    /** access flags */
+    private final int accessFlags;
+
+    /**
+     * {@code null-ok;} superclass or {@code null} if this class is a/the
+     * root class
+     */
+    private final CstType superclass;
+
+    /** {@code null-ok;} list of implemented interfaces */
+    private TypeListItem interfaces;
+
+    /** {@code null-ok;} source file name or {@code null} if unknown */
+    private final CstUtf8 sourceFile;
+
+    /** {@code non-null;} associated class data object */
+    private final ClassDataItem classData;
+
+    /**
+     * {@code null-ok;} item wrapper for the static values, initialized
+     * in {@link #addContents}
+     */
+    private EncodedArrayItem staticValuesItem;
+
+    /** {@code non-null;} annotations directory */
+    private AnnotationsDirectoryItem annotationsDirectory;
+
+    /**
+     * Constructs an instance. Its sets of members and annotations are
+     * initially empty.
+     *
+     * @param thisClass {@code non-null;} type constant for this class
+     * @param accessFlags access flags
+     * @param superclass {@code null-ok;} superclass or {@code null} if
+     * this class is a/the root class
+     * @param interfaces {@code non-null;} list of implemented interfaces
+     * @param sourceFile {@code null-ok;} source file name or
+     * {@code null} if unknown
+     */
+    public ClassDefItem(CstType thisClass, int accessFlags,
+            CstType superclass, TypeList interfaces, CstUtf8 sourceFile) {
+        if (thisClass == null) {
+            throw new NullPointerException("thisClass == null");
+        }
+
+        /*
+         * TODO: Maybe check accessFlags and superclass, at
+         * least for easily-checked stuff?
+         */
+
+        if (interfaces == null) {
+            throw new NullPointerException("interfaces == null");
+        }
+
+        this.thisClass = thisClass;
+        this.accessFlags = accessFlags;
+        this.superclass = superclass;
+        this.interfaces =
+            (interfaces.size() == 0) ? null :  new TypeListItem(interfaces);
+        this.sourceFile = sourceFile;
+        this.classData = new ClassDataItem(thisClass);
+        this.staticValuesItem = null;
+        this.annotationsDirectory = new AnnotationsDirectoryItem();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_CLASS_DEF_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        MixedItemSection byteData = file.getByteData();
+        MixedItemSection wordData = file.getWordData();
+        MixedItemSection typeLists = file.getTypeLists();
+        StringIdsSection stringIds = file.getStringIds();
+
+        typeIds.intern(thisClass);
+
+        if (!classData.isEmpty()) {
+            MixedItemSection classDataSection = file.getClassData();
+            classDataSection.add(classData);
+
+            CstArray staticValues = classData.getStaticValuesConstant();
+            if (staticValues != null) {
+                staticValuesItem =
+                    byteData.intern(new EncodedArrayItem(staticValues));
+            }
+        }
+
+        if (superclass != null) {
+            typeIds.intern(superclass);
+        }
+
+        if (interfaces != null) {
+            interfaces = typeLists.intern(interfaces);
+        }
+
+        if (sourceFile != null) {
+            stringIds.intern(sourceFile);
+        }
+
+        if (! annotationsDirectory.isEmpty()) {
+            if (annotationsDirectory.isInternable()) {
+                annotationsDirectory = wordData.intern(annotationsDirectory);
+            } else {
+                wordData.add(annotationsDirectory);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        TypeIdsSection typeIds = file.getTypeIds();
+        int classIdx = typeIds.indexOf(thisClass);
+        int superIdx = (superclass == null) ? -1 :
+            typeIds.indexOf(superclass);
+        int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces);
+        int annoOff = annotationsDirectory.isEmpty() ? 0 :
+            annotationsDirectory.getAbsoluteOffset();
+        int sourceFileIdx = (sourceFile == null) ? -1 :
+            file.getStringIds().indexOf(sourceFile);
+        int dataOff = classData.isEmpty()? 0 : classData.getAbsoluteOffset();
+        int staticValuesOff =
+            OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem);
+
+        if (annotates) {
+            out.annotate(0, indexString() + ' ' + thisClass.toHuman());
+            out.annotate(4, "  class_idx:           " + Hex.u4(classIdx));
+            out.annotate(4, "  access_flags:        " +
+                         AccessFlags.classString(accessFlags));
+            out.annotate(4, "  superclass_idx:      " + Hex.u4(superIdx) +
+                         " // " + ((superclass == null) ? "<none>" :
+                          superclass.toHuman()));
+            out.annotate(4, "  interfaces_off:      " + Hex.u4(interOff));
+            if (interOff != 0) {
+                TypeList list = interfaces.getList();
+                int sz = list.size();
+                for (int i = 0; i < sz; i++) {
+                    out.annotate(0, "    " + list.getType(i).toHuman());
+                }
+            }
+            out.annotate(4, "  source_file_idx:     " + Hex.u4(sourceFileIdx) +
+                         " // " + ((sourceFile == null) ? "<none>" :
+                          sourceFile.toHuman()));
+            out.annotate(4, "  annotations_off:     " + Hex.u4(annoOff));
+            out.annotate(4, "  class_data_off:      " + Hex.u4(dataOff));
+            out.annotate(4, "  static_values_off:   " +
+                    Hex.u4(staticValuesOff));
+        }
+
+        out.writeInt(classIdx);
+        out.writeInt(accessFlags);
+        out.writeInt(superIdx);
+        out.writeInt(interOff);
+        out.writeInt(sourceFileIdx);
+        out.writeInt(annoOff);
+        out.writeInt(dataOff);
+        out.writeInt(staticValuesOff);
+    }
+
+    /**
+     * Gets the constant corresponding to this class.
+     *
+     * @return {@code non-null;} the constant
+     */
+    public CstType getThisClass() {
+        return thisClass;
+    }
+
+    /**
+     * Gets the access flags.
+     *
+     * @return the access flags
+     */
+    public int getAccessFlags() {
+        return accessFlags;
+    }
+
+    /**
+     * Gets the superclass.
+     *
+     * @return {@code null-ok;} the superclass or {@code null} if
+     * this class is a/the root class
+     */
+    public CstType getSuperclass() {
+        return superclass;
+    }
+
+    /**
+     * Gets the list of interfaces implemented.
+     *
+     * @return {@code non-null;} the interfaces list
+     */
+    public TypeList getInterfaces() {
+        if (interfaces == null) {
+            return StdTypeList.EMPTY;
+        }
+
+        return interfaces.getList();
+    }
+
+    /**
+     * Gets the source file name.
+     *
+     * @return {@code null-ok;} the source file name or {@code null} if unknown
+     */
+    public CstUtf8 getSourceFile() {
+        return sourceFile;
+    }
+
+    /**
+     * Adds a static field.
+     *
+     * @param field {@code non-null;} the field to add
+     * @param value {@code null-ok;} initial value for the field, if any
+     */
+    public void addStaticField(EncodedField field, Constant value) {
+        classData.addStaticField(field, value);
+    }
+
+    /**
+     * Adds an instance field.
+     *
+     * @param field {@code non-null;} the field to add
+     */
+    public void addInstanceField(EncodedField field) {
+        classData.addInstanceField(field);
+    }
+
+    /**
+     * Adds a direct ({@code static} and/or {@code private}) method.
+     *
+     * @param method {@code non-null;} the method to add
+     */
+    public void addDirectMethod(EncodedMethod method) {
+        classData.addDirectMethod(method);
+    }
+
+    /**
+     * Adds a virtual method.
+     *
+     * @param method {@code non-null;} the method to add
+     */
+    public void addVirtualMethod(EncodedMethod method) {
+        classData.addVirtualMethod(method);
+    }
+
+    /**
+     * Gets all the methods in this class. The returned list is not linked
+     * in any way to the underlying lists contained in this instance, but
+     * the objects contained in the list are shared.
+     *
+     * @return {@code non-null;} list of all methods
+     */
+    public ArrayList<EncodedMethod> getMethods() {
+        return classData.getMethods();
+    }
+
+    /**
+     * Sets the direct annotations on this class. These are annotations
+     * made on the class, per se, as opposed to on one of its members.
+     * It is only valid to call this method at most once per instance.
+     *
+     * @param annotations {@code non-null;} annotations to set for this class
+     */
+    public void setClassAnnotations(Annotations annotations) {
+        annotationsDirectory.setClassAnnotations(annotations);
+    }
+
+    /**
+     * Adds a field annotations item to this class.
+     *
+     * @param field {@code non-null;} field in question
+     * @param annotations {@code non-null;} associated annotations to add
+     */
+    public void addFieldAnnotations(CstFieldRef field,
+            Annotations annotations) {
+        annotationsDirectory.addFieldAnnotations(field, annotations);
+    }
+
+    /**
+     * Adds a method annotations item to this class.
+     *
+     * @param method {@code non-null;} method in question
+     * @param annotations {@code non-null;} associated annotations to add
+     */
+    public void addMethodAnnotations(CstMethodRef method,
+            Annotations annotations) {
+        annotationsDirectory.addMethodAnnotations(method, annotations);
+    }
+
+    /**
+     * Adds a parameter annotations item to this class.
+     *
+     * @param method {@code non-null;} method in question
+     * @param list {@code non-null;} associated list of annotation sets to add
+     */
+    public void addParameterAnnotations(CstMethodRef method,
+            AnnotationsList list) {
+        annotationsDirectory.addParameterAnnotations(method, list);
+    }
+
+    /**
+     * Gets the method annotations for a given method, if any. This is
+     * meant for use by debugging / dumping code.
+     *
+     * @param method {@code non-null;} the method
+     * @return {@code null-ok;} the method annotations, if any
+     */
+    public Annotations getMethodAnnotations(CstMethodRef method) {
+        return annotationsDirectory.getMethodAnnotations(method);
+    }
+
+    /**
+     * Gets the parameter annotations for a given method, if any. This is
+     * meant for use by debugging / dumping code.
+     *
+     * @param method {@code non-null;} the method
+     * @return {@code null-ok;} the parameter annotations, if any
+     */
+    public AnnotationsList getParameterAnnotations(CstMethodRef method) {
+        return annotationsDirectory.getParameterAnnotations(method);
+    }
+
+    /**
+     * Prints out the contents of this instance, in a debugging-friendly
+     * way.
+     *
+     * @param out {@code non-null;} where to output to
+     * @param verbose whether to be verbose with the output
+     */
+    public void debugPrint(Writer out, boolean verbose) {
+        PrintWriter pw = Writers.printWriterFor(out);
+
+        pw.println(getClass().getName() + " {");
+        pw.println("  accessFlags: " + Hex.u2(accessFlags));
+        pw.println("  superclass: " + superclass);
+        pw.println("  interfaces: " +
+                ((interfaces == null) ? "<none>" : interfaces));
+        pw.println("  sourceFile: " +
+                ((sourceFile == null) ? "<none>" : sourceFile.toQuoted()));
+
+        classData.debugPrint(out, verbose);
+        annotationsDirectory.debugPrint(pw);
+
+        pw.println("}");
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDefsSection.java b/dx/src/com/android/dx/dex/file/ClassDefsSection.java
new file mode 100644
index 0000000..1ca391f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDefsSection.java
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Class definitions list section of a {@code .dex} file.
+ */
+public final class ClassDefsSection extends UniformItemSection {
+    /**
+     * {@code non-null;} map from type constants for classes to {@link
+     * ClassDefItem} instances that define those classes
+     */
+    private final TreeMap<Type, ClassDefItem> classDefs;
+
+    /** {@code null-ok;} ordered list of classes; set in {@link #orderItems} */
+    private ArrayList<ClassDefItem> orderedDefs;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param file {@code non-null;} file that this instance is part of
+     */
+    public ClassDefsSection(DexFile file) {
+        super("class_defs", file, 4);
+
+        classDefs = new TreeMap<Type, ClassDefItem>();
+        orderedDefs = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        if (orderedDefs != null) {
+            return orderedDefs;
+        }
+
+        return classDefs.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        Type type = ((CstType) cst).getClassType();
+        IndexedItem result = classDefs.get(type);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     *
+     * @param out {@code non-null;} where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = classDefs.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "class_defs_size: " + Hex.u4(sz));
+            out.annotate(4, "class_defs_off:  " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Adds an element to this instance. It is illegal to attempt to add more
+     * than one class with the same name.
+     *
+     * @param clazz {@code non-null;} the class def to add
+     */
+    public void add(ClassDefItem clazz) {
+        Type type;
+
+        try {
+            type = clazz.getThisClass().getClassType();
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("clazz == null");
+        }
+
+        throwIfPrepared();
+
+        if (classDefs.get(type) != null) {
+            throw new IllegalArgumentException("already added: " + type);
+        }
+
+        classDefs.put(type, clazz);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int sz = classDefs.size();
+        int idx = 0;
+
+        orderedDefs = new ArrayList<ClassDefItem>(sz);
+
+        /*
+         * Iterate over all the classes, recursively assigning an
+         * index to each, implicitly skipping the ones that have
+         * already been assigned by the time this (top-level)
+         * iteration reaches them.
+         */
+        for (Type type : classDefs.keySet()) {
+            idx = orderItems0(type, idx, sz - idx);
+        }
+    }
+
+    /**
+     * Helper for {@link #orderItems}, which recursively assigns indices
+     * to classes.
+     *
+     * @param type {@code null-ok;} type ref to assign, if any
+     * @param idx {@code >= 0;} the next index to assign
+     * @param maxDepth maximum recursion depth; if negative, this will
+     * throw an exception indicating class definition circularity
+     * @return {@code >= 0;} the next index to assign
+     */
+    private int orderItems0(Type type, int idx, int maxDepth) {
+        ClassDefItem c = classDefs.get(type);
+
+        if ((c == null) || (c.hasIndex())) {
+            return idx;
+        }
+
+        if (maxDepth < 0) {
+            throw new RuntimeException("class circularity with " + type);
+        }
+
+        maxDepth--;
+
+        CstType superclassCst = c.getSuperclass();
+        if (superclassCst != null) {
+            Type superclass = superclassCst.getClassType();
+            idx = orderItems0(superclass, idx, maxDepth);
+        }
+
+        TypeList interfaces = c.getInterfaces();
+        int sz = interfaces.size();
+        for (int i = 0; i < sz; i++) {
+            idx = orderItems0(interfaces.getType(i), idx, maxDepth);
+        }
+
+        c.setIndex(idx);
+        orderedDefs.add(c);
+        return idx + 1;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/CodeItem.java b/dx/src/com/android/dx/dex/file/CodeItem.java
new file mode 100644
index 0000000..a47f68a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/CodeItem.java
@@ -0,0 +1,334 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.CatchTable;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.DalvInsnList;
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+
+/**
+ * Representation of all the parts needed for concrete methods in a
+ * {@code dex} file.
+ */
+public final class CodeItem extends OffsettedItem {
+    /** file alignment of this class, in bytes */
+    private static final int ALIGNMENT = 4;
+
+    /** write size of the header of this class, in bytes */
+    private static final int HEADER_SIZE = 16;
+
+    /** {@code non-null;} method that this code implements */
+    private final CstMethodRef ref;
+
+    /** {@code non-null;} the bytecode instructions and associated data */
+    private final DalvCode code;
+
+    /** {@code null-ok;} the catches, if needed; set in {@link #addContents} */
+    private CatchStructs catches;
+
+    /** whether this instance is for a {@code static} method */
+    private final boolean isStatic;
+
+    /**
+     * {@code non-null;} list of possibly-thrown exceptions; just used in
+     * generating debugging output (listings)
+     */
+    private final TypeList throwsList;
+
+    /**
+     * {@code null-ok;} the debug info or {@code null} if there is none;
+     * set in {@link #addContents}
+     */
+    private DebugInfoItem debugInfo;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param ref {@code non-null;} method that this code implements
+     * @param code {@code non-null;} the underlying code
+     * @param isStatic whether this instance is for a {@code static}
+     * method
+     * @param throwsList {@code non-null;} list of possibly-thrown exceptions,
+     * just used in generating debugging output (listings)
+     */
+    public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic,
+            TypeList throwsList) {
+        super(ALIGNMENT, -1);
+
+        if (ref == null) {
+            throw new NullPointerException("ref == null");
+        }
+
+        if (code == null) {
+            throw new NullPointerException("code == null");
+        }
+
+        if (throwsList == null) {
+            throw new NullPointerException("throwsList == null");
+        }
+
+        this.ref = ref;
+        this.code = code;
+        this.isStatic = isStatic;
+        this.throwsList = throwsList;
+        this.catches = null;
+        this.debugInfo = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_CODE_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MixedItemSection byteData = file.getByteData();
+        TypeIdsSection typeIds = file.getTypeIds();
+
+        if (code.hasPositions() || code.hasLocals()) {
+            debugInfo = new DebugInfoItem(code, isStatic, ref);
+            byteData.add(debugInfo);
+        }
+
+        if (code.hasAnyCatches()) {
+            for (Type type : code.getCatchTypes()) {
+                typeIds.intern(type);
+            }
+            catches = new CatchStructs(code);
+        }
+
+        for (Constant c : code.getInsnConstants()) {
+            file.internIfAppropriate(c);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "CodeItem{" + toHuman() + "}";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return ref.toHuman();
+    }
+
+    /**
+     * Gets the reference to the method this instance implements.
+     *
+     * @return {@code non-null;} the method reference
+     */
+    public CstMethodRef getRef() {
+        return ref;
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out {@code non-null;} where to dump
+     * @param prefix {@code non-null;} per-line prefix to use
+     * @param verbose whether to be verbose with the output
+     */
+    public void debugPrint(PrintWriter out, String prefix, boolean verbose) {
+        out.println(ref.toHuman() + ":");
+
+        DalvInsnList insns = code.getInsns();
+        out.println("regs: " + Hex.u2(getRegistersSize()) +
+                "; ins: " + Hex.u2(getInsSize()) + "; outs: " +
+                Hex.u2(getOutsSize()));
+
+        insns.debugPrint(out, prefix, verbose);
+
+        String prefix2 = prefix + "  ";
+
+        if (catches != null) {
+            out.print(prefix);
+            out.println("catches");
+            catches.debugPrint(out, prefix2);
+        }
+
+        if (debugInfo != null) {
+            out.print(prefix);
+            out.println("debug info");
+            debugInfo.debugPrint(out, prefix2);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        final DexFile file = addedTo.getFile();
+        int catchesSize;
+
+        /*
+         * In order to get the catches and insns, all the code's
+         * constants need to be assigned indices.
+         */
+        code.assignIndices(new DalvCode.AssignIndicesCallback() {
+                public int getIndex(Constant cst) {
+                    IndexedItem item = file.findItemOrNull(cst);
+                    if (item == null) {
+                        return -1;
+                    }
+                    return item.getIndex();
+                }
+            });
+
+        if (catches != null) {
+            catches.encode(file);
+            catchesSize = catches.writeSize();
+        } else {
+            catchesSize = 0;
+        }
+
+        /*
+         * The write size includes the header, two bytes per code
+         * unit, post-code padding if necessary, and however much
+         * space the catches need.
+         */
+
+        int insnsSize = code.getInsns().codeSize();
+        if ((insnsSize & 1) != 0) {
+            insnsSize++;
+        }
+
+        setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        int regSz = getRegistersSize();
+        int outsSz = getOutsSize();
+        int insSz = getInsSize();
+        int insnsSz = code.getInsns().codeSize();
+        boolean needPadding = (insnsSz & 1) != 0;
+        int triesSz = (catches == null) ? 0 : catches.triesSize();
+        int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset();
+
+        if (annotates) {
+            out.annotate(0, offsetString() + ' ' + ref.toHuman());
+            out.annotate(2, "  registers_size: " + Hex.u2(regSz));
+            out.annotate(2, "  ins_size:       " + Hex.u2(insSz));
+            out.annotate(2, "  outs_size:      " + Hex.u2(outsSz));
+            out.annotate(2, "  tries_size:     " + Hex.u2(triesSz));
+            out.annotate(4, "  debug_off:      " + Hex.u4(debugOff));
+            out.annotate(4, "  insns_size:     " + Hex.u4(insnsSz));
+
+            // This isn't represented directly here, but it is useful to see.
+            int size = throwsList.size();
+            if (size != 0) {
+                out.annotate(0, "  throws " + StdTypeList.toHuman(throwsList));
+            }
+        }
+
+        out.writeShort(regSz);
+        out.writeShort(insSz);
+        out.writeShort(outsSz);
+        out.writeShort(triesSz);
+        out.writeInt(debugOff);
+        out.writeInt(insnsSz);
+
+        writeCodes(file, out);
+
+        if (catches != null) {
+            if (needPadding) {
+                if (annotates) {
+                    out.annotate(2, "  padding: 0");
+                }
+                out.writeShort(0);
+            }
+
+            catches.writeTo(file, out);
+        }
+
+        if (annotates) {
+            /*
+             * These are pointed at in the code header (above), but it's less
+             * distracting to expand on them at the bottom of the code.
+             */
+            if (debugInfo != null) {
+                out.annotate(0, "  debug info");
+                debugInfo.annotateTo(file, out, "    ");
+            }
+        }
+    }
+
+    /**
+     * Helper for {@link #writeTo0} which writes out the actual bytecode.
+     *
+     * @param file {@code non-null;} file we are part of
+     * @param out {@code non-null;} where to write to
+     */
+    private void writeCodes(DexFile file, AnnotatedOutput out) {
+        DalvInsnList insns = code.getInsns();
+
+        try {
+            insns.writeTo(out);
+        } catch (RuntimeException ex) {
+            throw ExceptionWithContext.withContext(ex, "...while writing " +
+                    "instructions for " + ref.toHuman());
+        }
+    }
+
+    /**
+     * Get the in registers count.
+     *
+     * @return the count
+     */
+    private int getInsSize() {
+        return ref.getParameterWordCount(isStatic);
+    }
+
+    /**
+     * Get the out registers count.
+     *
+     * @return the count
+     */
+    private int getOutsSize() {
+        return code.getInsns().getOutsSize();
+    }
+
+    /**
+     * Get the total registers count.
+     *
+     * @return the count
+     */
+    private int getRegistersSize() {
+        return code.getInsns().getRegistersSize();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoConstants.java b/dx/src/com/android/dx/dex/file/DebugInfoConstants.java
new file mode 100644
index 0000000..78b6b04
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoConstants.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * Constants for the dex debug info state machine format.
+ */
+public interface DebugInfoConstants {
+
+    /*
+     * normal opcodes
+     */
+
+    /**
+     * Terminates a debug info sequence for a method.<p>
+     * Args: none
+     *
+     */
+    static final int DBG_END_SEQUENCE = 0x00;
+
+    /**
+     * Advances the program counter/address register without emitting
+     * a positions entry.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; amount to advance pc by
+     * </ol>
+     */
+    static final int DBG_ADVANCE_PC = 0x01;
+
+    /**
+     * Advances the line register without emitting
+     * a positions entry.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Signed LEB128 &mdash; amount to change line register by.
+     * </ol>
+     */
+    static final int DBG_ADVANCE_LINE = 0x02;
+
+    /**
+     * Introduces a local variable at the current address.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; register that will contain local.
+     * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of local name.
+     * <li>Unsigned LEB128 &mdash; type index (shifted by 1) of type.
+     * </ol>
+     */
+    static final int DBG_START_LOCAL = 0x03;
+
+    /**
+     * Introduces a local variable at the current address with a type
+     * signature specified.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; register that will contain local.
+     * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of local name.
+     * <li>Unsigned LEB128 &mdash; type index (shifted by 1) of type.
+     * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of
+     * type signature.
+     * </ol>
+     */
+    static final int DBG_START_LOCAL_EXTENDED = 0x04;
+
+    /**
+     * Marks a currently-live local variable as out of scope at the
+     * current address.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; register that contained local
+     * </ol>
+     */
+    static final int DBG_END_LOCAL = 0x05;
+
+    /**
+     * Re-introduces a local variable at the current address. The name
+     * and type are the same as the last local that was live in the specified
+     * register.<p>
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; register to re-start.
+     * </ol>
+     */
+    static final int DBG_RESTART_LOCAL = 0x06;
+
+
+    /**
+     * Sets the "prologue_end" state machine register, indicating that the
+     * next position entry that is added should be considered the end of
+     * a method prologue (an appropriate place for a method breakpoint).<p>
+     *
+     * The prologue_end register is cleared by any special
+     * ({@code >= OPCODE_BASE}) opcode.
+     */
+    static final int DBG_SET_PROLOGUE_END = 0x07;
+
+    /**
+     * Sets the "epilogue_begin" state machine register, indicating that the
+     * next position entry that is added should be considered the beginning of
+     * a method epilogue (an appropriate place to suspend execution before
+     * method exit).<p>
+     *
+     * The epilogue_begin register is cleared by any special
+     * ({@code >= OPCODE_BASE}) opcode.
+     */
+    static final int DBG_SET_EPILOGUE_BEGIN = 0x08;
+
+    /**
+     * Sets the current file that that line numbers refer to. All subsequent
+     * line number entries make reference to this source file name, instead
+     * of the default name specified in code_item.
+     *
+     * Args:
+     * <ol>
+     * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of source
+     * file name.
+     * </ol>
+     */
+    static final int DBG_SET_FILE = 0x09;
+
+    /* IF YOU ADD A NEW OPCODE, increase OPCODE_BASE */
+
+    /*
+     * "special opcode" configuration, essentially what's found in
+     * the line number program header in DWARFv3, Section 6.2.4
+     */
+
+    /** the smallest value a special opcode can take */
+    static final int DBG_FIRST_SPECIAL = 0x0a;
+    static final int DBG_LINE_BASE = -4;
+    static final int DBG_LINE_RANGE = 15;
+    // MIN_INSN_LENGTH is always 1
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
new file mode 100644
index 0000000..e823816
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
@@ -0,0 +1,653 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.DalvInsnList;
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.dx.dex.file.DebugInfoConstants.*;
+
+/**
+ * A decoder for the dex debug info state machine format.
+ * This code exists mostly as a reference implementation and test for
+ * for the {@code DebugInfoEncoder}
+ */
+public class DebugInfoDecoder {
+    /** encoded debug info */
+    private final byte[] encoded;
+
+    /** positions decoded */
+    private final ArrayList<PositionEntry> positions;
+
+    /** locals decoded */
+    private final ArrayList<LocalEntry> locals;
+
+    /** size of code block in code units */
+    private final int codesize;
+
+    /** indexed by register, the last local variable live in a reg */
+    private final LocalEntry[] lastEntryForReg;
+
+    /** method descriptor of method this debug info is for */
+    private final Prototype desc;
+
+    /** true if method is static */
+    private final boolean isStatic;
+
+    /** dex file this debug info will be stored in */
+    private final DexFile file;
+
+    /**
+     * register size, in register units, of the register space
+     * used by this method
+     */
+    private final int regSize;
+
+    /** current decoding state: line number */
+    private int line = 1;
+
+    /** current decoding state: bytecode address */
+    private int address = 0;
+
+    /** string index of the string "this" */
+    private final int thisStringIdx;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param encoded encoded debug info
+     * @param codesize size of code block in code units
+     * @param regSize register size, in register units, of the register space
+     * used by this method
+     * @param isStatic true if method is static
+     * @param ref method descriptor of method this debug info is for
+     * @param file dex file this debug info will be stored in
+     */
+    DebugInfoDecoder(byte[] encoded, int codesize, int regSize,
+            boolean isStatic, CstMethodRef ref, DexFile file) {
+        if (encoded == null) {
+            throw new NullPointerException("encoded == null");
+        }
+
+        this.encoded = encoded;
+        this.isStatic = isStatic;
+        this.desc = ref.getPrototype();
+        this.file = file;
+        this.regSize = regSize;
+
+        positions = new ArrayList<PositionEntry>();
+        locals = new ArrayList<LocalEntry>();
+        this.codesize = codesize;
+        lastEntryForReg = new LocalEntry[regSize];
+
+        int idx = -1;
+
+        try {
+            idx = file.getStringIds().indexOf(new CstUtf8("this"));
+        } catch (IllegalArgumentException ex) {
+            /*
+             * Silently tolerate not finding "this". It just means that
+             * no method has local variable info that looks like
+             * a standard instance method.
+             */
+        }
+
+        thisStringIdx = idx;
+    }
+
+    /**
+     * An entry in the resulting postions table
+     */
+    static private class PositionEntry {
+        /** bytecode address */
+        public int address;
+
+        /** line number */
+        public int line;
+
+        public PositionEntry(int address, int line) {
+            this.address = address;
+            this.line = line;
+        }
+    }
+
+    /**
+     * An entry in the resulting locals table
+     */
+    static private class LocalEntry {
+        /** address of event */
+        public int address;
+
+        /** {@code true} iff it's a local start */
+        public boolean isStart;
+
+        /** register number */
+        public int reg;
+
+        /** index of name in strings table */
+        public int nameIndex;
+
+        /** index of type in types table */
+        public int typeIndex;
+
+        /** index of type signature in strings table */
+        public int signatureIndex;
+
+        public LocalEntry(int address, boolean isStart, int reg, int nameIndex,
+                int typeIndex, int signatureIndex) {
+            this.address        = address;
+            this.isStart        = isStart;
+            this.reg            = reg;
+            this.nameIndex      = nameIndex;
+            this.typeIndex      = typeIndex;
+            this.signatureIndex = signatureIndex;
+        }
+
+        public String toString() {
+            return String.format("[%x %s v%d %04x %04x %04x]",
+                    address, isStart ? "start" : "end", reg,
+                    nameIndex, typeIndex, signatureIndex);
+        }
+    }
+
+    /**
+     * Gets the decoded positions list.
+     * Valid after calling {@code decode}.
+     *
+     * @return positions list in ascending address order.
+     */
+    public List<PositionEntry> getPositionList() {
+        return positions;
+    }
+
+    /**
+     * Gets the decoded locals list, in ascending start-address order.
+     * Valid after calling {@code decode}.
+     *
+     * @return locals list in ascending address order.
+     */
+    public List<LocalEntry> getLocals() {
+        return locals;
+    }
+
+    /**
+     * Decodes the debug info sequence.
+     */
+    public void decode() {
+        try {
+            decode0();
+        } catch (Exception ex) {
+            throw ExceptionWithContext.withContext(ex,
+                    "...while decoding debug info");
+        }
+    }
+
+    /**
+     * Reads a string index. String indicies are offset by 1, and a 0 value
+     * in the stream (-1 as returned by this method) means "null"
+     *
+     * @param bs
+     * @return index into file's string ids table, -1 means null
+     * @throws IOException
+     */
+    private int readStringIndex(InputStream bs) throws IOException {
+        int offsetIndex = readUnsignedLeb128(bs);
+
+        return offsetIndex - 1;
+    }
+
+    /**
+     * Gets the register that begins the method's parameter range (including
+     * the 'this' parameter for non-static methods). The range continues until
+     * {@code regSize}
+     *
+     * @return register as noted above.
+     */
+    private int getParamBase() {
+        return regSize
+                - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
+    }
+
+    private void decode0() throws IOException {
+        ByteArrayInputStream bs = new ByteArrayInputStream(encoded);
+
+        line = readUnsignedLeb128(bs);
+        int szParams = readUnsignedLeb128(bs);
+        StdTypeList params = desc.getParameterTypes();
+        int curReg = getParamBase();
+
+        if (szParams != params.size()) {
+            throw new RuntimeException(
+                    "Mismatch between parameters_size and prototype");
+        }
+
+        if (!isStatic) {
+            // Start off with implicit 'this' entry
+            LocalEntry thisEntry =
+                new LocalEntry(0, true, curReg, thisStringIdx, 0, 0);
+            locals.add(thisEntry);
+            lastEntryForReg[curReg] = thisEntry;
+            curReg++;
+        }
+
+        for (int i = 0; i < szParams; i++) {
+            Type paramType = params.getType(i);
+            LocalEntry le;
+
+            int nameIdx = readStringIndex(bs);
+
+            if (nameIdx == -1) {
+                /*
+                 * Unnamed parameter; often but not always filled in by an
+                 * extended start op after the prologue
+                 */
+                le = new LocalEntry(0, true, curReg, -1, 0, 0);
+            } else {
+                // TODO: Final 0 should be idx of paramType.getDescriptor().
+                le = new LocalEntry(0, true, curReg, nameIdx, 0, 0);
+            }
+
+            locals.add(le);
+            lastEntryForReg[curReg] = le;
+            curReg += paramType.getCategory();
+        }
+
+        for (;;) {
+            int opcode = bs.read();
+
+            if (opcode < 0) {
+                throw new RuntimeException
+                        ("Reached end of debug stream without "
+                                + "encountering end marker");
+            }
+
+            switch (opcode) {
+                case DBG_START_LOCAL: {
+                    int reg = readUnsignedLeb128(bs);
+                    int nameIdx = readStringIndex(bs);
+                    int typeIdx = readStringIndex(bs);
+                    LocalEntry le = new LocalEntry(
+                            address, true, reg, nameIdx, typeIdx, 0);
+
+                    locals.add(le);
+                    lastEntryForReg[reg] = le;
+                }
+                break;
+
+                case DBG_START_LOCAL_EXTENDED: {
+                    int reg = readUnsignedLeb128(bs);
+                    int nameIdx = readStringIndex(bs);
+                    int typeIdx = readStringIndex(bs);
+                    int sigIdx = readStringIndex(bs);
+                    LocalEntry le = new LocalEntry(
+                            address, true, reg, nameIdx, typeIdx, sigIdx);
+
+                    locals.add(le);
+                    lastEntryForReg[reg] = le;
+                }
+                break;
+
+                case DBG_RESTART_LOCAL: {
+                    int reg = readUnsignedLeb128(bs);
+                    LocalEntry prevle;
+                    LocalEntry le;
+
+                    try {
+                        prevle = lastEntryForReg[reg];
+
+                        if (prevle.isStart) {
+                            throw new RuntimeException("nonsensical "
+                                    + "RESTART_LOCAL on live register v"
+                                    + reg);
+                        }
+
+                        le = new LocalEntry(address, true, reg,
+                                prevle.nameIndex, prevle.typeIndex, 0);
+                    } catch (NullPointerException ex) {
+                        throw new RuntimeException(
+                                "Encountered RESTART_LOCAL on new v" + reg);
+                    }
+
+                    locals.add(le);
+                    lastEntryForReg[reg] = le;
+                }
+                break;
+
+                case DBG_END_LOCAL: {
+                    int reg = readUnsignedLeb128(bs);
+                    LocalEntry prevle;
+                    LocalEntry le;
+
+                    try {
+                        prevle = lastEntryForReg[reg];
+
+                        if (!prevle.isStart) {
+                            throw new RuntimeException("nonsensical "
+                                    + "END_LOCAL on dead register v" + reg);
+                        }
+
+                        le = new LocalEntry(address, false, reg,
+                                prevle.nameIndex, prevle.typeIndex,
+                                prevle.signatureIndex);
+                    } catch (NullPointerException ex) {
+                        throw new RuntimeException(
+                                "Encountered END_LOCAL on new v" + reg);
+                    }
+
+                    locals.add(le);
+                    lastEntryForReg[reg] = le;
+                }
+                break;
+
+                case DBG_END_SEQUENCE:
+                    // all done
+                return;
+
+                case DBG_ADVANCE_PC:
+                    address += readUnsignedLeb128(bs);
+                break;
+
+                case DBG_ADVANCE_LINE:
+                    line += readSignedLeb128(bs);
+                break;
+
+                case DBG_SET_PROLOGUE_END:
+                    //TODO do something with this.
+                break;
+
+                case DBG_SET_EPILOGUE_BEGIN:
+                    //TODO do something with this.
+                break;
+
+                case DBG_SET_FILE:
+                    //TODO do something with this.
+                break;
+
+                default:
+                    if (opcode < DBG_FIRST_SPECIAL) {
+                        throw new RuntimeException(
+                                "Invalid extended opcode encountered "
+                                        + opcode);
+                    }
+
+                    int adjopcode = opcode - DBG_FIRST_SPECIAL;
+
+                    address += adjopcode / DBG_LINE_RANGE;
+                    line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+
+                    positions.add(new PositionEntry(address, line));
+                break;
+
+            }
+        }
+    }
+
+    /**
+     * Validates an encoded debug info stream against data used to encode it,
+     * throwing an exception if they do not match. Used to validate the
+     * encoder.
+     *
+     * @param info encoded debug info
+     * @param file {@code non-null;} file to refer to during decoding
+     * @param ref {@code non-null;} method whose info is being decoded
+     * @param code {@code non-null;} original code object that was encoded
+     * @param isStatic whether the method is static
+     */
+    public static void validateEncode(byte[] info, DexFile file,
+            CstMethodRef ref, DalvCode code, boolean isStatic) {
+        PositionList pl = code.getPositions();
+        LocalList ll = code.getLocals();
+        DalvInsnList insns = code.getInsns();
+        int codeSize = insns.codeSize();
+        int countRegisters = insns.getRegistersSize();
+
+        try {
+            validateEncode0(info, codeSize, countRegisters,
+                    isStatic, ref, file, pl, ll);
+        } catch (RuntimeException ex) {
+            System.err.println("instructions:");
+            insns.debugPrint(System.err, "  ", true);
+            System.err.println("local list:");
+            ll.debugPrint(System.err, "  ");
+            throw ExceptionWithContext.withContext(ex,
+                    "while processing " + ref.toHuman());
+        }
+    }
+
+    private static void validateEncode0(byte[] info, int codeSize,
+            int countRegisters, boolean isStatic, CstMethodRef ref,
+            DexFile file, PositionList pl, LocalList ll) {
+        DebugInfoDecoder decoder
+                = new DebugInfoDecoder(info, codeSize, countRegisters,
+                    isStatic, ref, file);
+
+        decoder.decode();
+
+        /*
+         * Go through the decoded position entries, matching up
+         * with original entries.
+         */
+
+        List<PositionEntry> decodedEntries = decoder.getPositionList();
+
+        if (decodedEntries.size() != pl.size()) {
+            throw new RuntimeException(
+                    "Decoded positions table not same size was "
+                    + decodedEntries.size() + " expected " + pl.size());
+        }
+
+        for (PositionEntry entry : decodedEntries) {
+            boolean found = false;
+            for (int i = pl.size() - 1; i >= 0; i--) {
+                PositionList.Entry ple = pl.get(i);
+
+                if (entry.line == ple.getPosition().getLine()
+                        && entry.address == ple.getAddress()) {
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found) {
+                throw new RuntimeException ("Could not match position entry: "
+                        + entry.address + ", " + entry.line);
+            }
+        }
+
+        /*
+         * Go through the original local list, in order, matching up
+         * with decoded entries.
+         */
+
+        List<LocalEntry> decodedLocals = decoder.getLocals();
+        int thisStringIdx = decoder.thisStringIdx;
+        int decodedSz = decodedLocals.size();
+        int paramBase = decoder.getParamBase();
+
+        /*
+         * Preflight to fill in any parameters that were skipped in
+         * the prologue (including an implied "this") but then
+         * identified by full signature.
+         */
+        for (int i = 0; i < decodedSz; i++) {
+            LocalEntry entry = decodedLocals.get(i);
+            int idx = entry.nameIndex;
+
+            if ((idx < 0) || (idx == thisStringIdx)) {
+                for (int j = i + 1; j < decodedSz; j++) {
+                    LocalEntry e2 = decodedLocals.get(j);
+                    if (e2.address != 0) {
+                        break;
+                    }
+                    if ((entry.reg == e2.reg) && e2.isStart) {
+                        decodedLocals.set(i, e2);
+                        decodedLocals.remove(j);
+                        decodedSz--;
+                        break;
+                    }
+                }
+            }
+        }
+
+        int origSz = ll.size();
+        int decodeAt = 0;
+        boolean problem = false;
+
+        for (int i = 0; i < origSz; i++) {
+            LocalList.Entry origEntry = ll.get(i);
+
+            if (origEntry.getDisposition()
+                    == LocalList.Disposition.END_REPLACED) {
+                /*
+                 * The encoded list doesn't represent replacements, so
+                 * ignore them for the sake of comparison.
+                 */
+                continue;
+            }
+
+            LocalEntry decodedEntry;
+
+            do {
+                decodedEntry = decodedLocals.get(decodeAt);
+                if (decodedEntry.nameIndex >= 0) {
+                    break;
+                }
+                /*
+                 * A negative name index means this is an anonymous
+                 * parameter, and we shouldn't expect to see it in the
+                 * original list. So, skip it.
+                 */
+                decodeAt++;
+            } while (decodeAt < decodedSz);
+
+            int decodedAddress = decodedEntry.address;
+
+            if (decodedEntry.reg != origEntry.getRegister()) {
+                System.err.println("local register mismatch at orig " + i +
+                        " / decoded " + decodeAt);
+                problem = true;
+                break;
+            }
+
+            if (decodedEntry.isStart != origEntry.isStart()) {
+                System.err.println("local start/end mismatch at orig " + i +
+                        " / decoded " + decodeAt);
+                problem = true;
+                break;
+            }
+
+            /*
+             * The secondary check here accounts for the fact that a
+             * parameter might not be marked as starting at 0 in the
+             * original list.
+             */
+            if ((decodedAddress != origEntry.getAddress())
+                    && !((decodedAddress == 0)
+                            && (decodedEntry.reg >= paramBase))) {
+                System.err.println("local address mismatch at orig " + i +
+                        " / decoded " + decodeAt);
+                problem = true;
+                break;
+            }
+
+            decodeAt++;
+        }
+
+        if (problem) {
+            System.err.println("decoded locals:");
+            for (LocalEntry e : decodedLocals) {
+                System.err.println("  " + e);
+            }
+            throw new RuntimeException("local table problem");
+        }
+    }
+
+    /**
+     * Reads a DWARFv3-style signed LEB128 integer to the specified stream.
+     * See DWARF v3 section 7.6. An invalid sequence produces an IOException.
+     *
+     * @param bs stream to input from
+     * @return read value
+     * @throws IOException on invalid sequence in addition to
+     * those caused by the InputStream
+     */
+    public static int readSignedLeb128(InputStream bs) throws IOException {
+        int result = 0;
+        int cur;
+        int count = 0;
+        int signBits = -1;
+
+        do {
+            cur = bs.read();
+            result |= (cur & 0x7f) << (count * 7);
+            signBits <<= 7;
+            count++;
+        } while (((cur & 0x80) == 0x80) && count < 5);
+
+        if ((cur & 0x80) == 0x80) {
+            throw new IOException ("invalid LEB128 sequence");
+        }
+
+        // Sign extend if appropriate
+        if (((signBits >> 1) & result) != 0 ) {
+            result |= signBits;
+        }
+
+        return result;
+    }
+
+    /**
+     * Reads a DWARFv3-style unsigned LEB128 integer to the specified stream.
+     * See DWARF v3 section 7.6. An invalid sequence produces an IOException.
+     *
+     * @param bs stream to input from
+     * @return read value, which should be treated as an unsigned value.
+     * @throws IOException on invalid sequence in addition to
+     * those caused by the InputStream
+     */
+    public static int readUnsignedLeb128(InputStream bs) throws IOException {
+        int result = 0;
+        int cur;
+        int count = 0;
+
+        do {
+            cur = bs.read();
+            result |= (cur & 0x7f) << (count * 7);
+            count++;
+        } while (((cur & 0x80) == 0x80) && count < 5);
+
+        if ((cur & 0x80) == 0x80) {
+            throw new IOException ("invalid LEB128 sequence");
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
new file mode 100644
index 0000000..08b6637
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
@@ -0,0 +1,920 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.BitSet;
+
+import static com.android.dx.dex.file.DebugInfoConstants.*;
+
+/**
+ * An encoder for the dex debug info state machine format. The format
+ * for each method enrty is as follows:
+ * <ol>
+ * <li> signed LEB128: initial value for line register.
+ * <li> n instances of signed LEB128: string indicies (offset by 1)
+ * for each method argument in left-to-right order
+ * with {@code this} excluded. A value of '0' indicates "no name"
+ * <li> A sequence of special or normal opcodes as defined in
+ * {@code DebugInfoConstants}.
+ * <li> A single terminating {@code OP_END_SEQUENCE}
+ * </ol>
+ */
+public final class DebugInfoEncoder {
+    private static final boolean DEBUG = false;
+
+    /** {@code null-ok;} positions (line numbers) to encode */
+    private final PositionList positions;
+
+    /** {@code null-ok;} local variables to encode */
+    private final LocalList locals;
+
+    private final ByteArrayAnnotatedOutput output;
+    private final DexFile file;
+    private final int codeSize;
+    private final int regSize;
+
+    private final Prototype desc;
+    private final boolean isStatic;
+
+    /** current encoding state: bytecode address */
+    private int address = 0;
+
+    /** current encoding state: line number */
+    private int line = 1;
+
+    /**
+     * if non-null: the output to write annotations to. No normal
+     * output is written to this.
+     */
+    private AnnotatedOutput annotateTo;
+
+    /** if non-null: another possible output for annotations */
+    private PrintWriter debugPrint;
+
+    /** if non-null: the prefix for each annotation or debugPrint line */
+    private String prefix;
+
+    /** true if output should be consumed during annotation */
+    private boolean shouldConsume;
+
+    /** indexed by register; last local alive in register */
+    private final LocalList.Entry[] lastEntryForReg;
+
+    /**
+     * Creates an instance.
+     *
+     * @param positions {@code null-ok;} positions (line numbers) to encode
+     * @param locals {@code null-ok;} local variables to encode
+     * @param file {@code null-ok;} may only be {@code null} if simply using
+     * this class to do a debug print
+     * @param codeSize
+     * @param regSize
+     * @param isStatic
+     * @param ref
+     */
+    public DebugInfoEncoder(PositionList positions, LocalList locals,
+            DexFile file, int codeSize, int regSize,
+            boolean isStatic, CstMethodRef ref) {
+        this.positions = positions;
+        this.locals = locals;
+        this.file = file;
+        this.desc = ref.getPrototype();
+        this.isStatic = isStatic;
+        this.codeSize = codeSize;
+        this.regSize = regSize;
+
+        output = new ByteArrayAnnotatedOutput();
+        lastEntryForReg = new LocalList.Entry[regSize];
+    }
+
+    /**
+     * Annotates or writes a message to the {@code debugPrint} writer
+     * if applicable.
+     *
+     * @param length the number of bytes associated with this message
+     * @param message the message itself
+     */
+    private void annotate(int length, String message) {
+        if (prefix != null) {
+            message = prefix + message;
+        }
+
+        if (annotateTo != null) {
+            annotateTo.annotate(shouldConsume ? length : 0, message);
+        }
+
+        if (debugPrint != null) {
+            debugPrint.println(message);
+        }
+    }
+
+    /**
+     * Converts this (PositionList, LocalList) pair into a state machine
+     * sequence.
+     *
+     * @return {@code non-null;} encoded byte sequence without padding and
+     * terminated with a {@code 0x00} byte
+     */
+    public byte[] convert() {
+        try {
+            byte[] ret;
+            ret = convert0();
+
+            if (DEBUG) {
+                for (int i = 0 ; i < ret.length; i++) {
+                    System.err.printf("byte %02x\n", (0xff & ret[i]));
+                }
+            }
+
+            return ret;
+        } catch (IOException ex) {
+            throw ExceptionWithContext
+                    .withContext(ex, "...while encoding debug info");
+        }
+    }
+
+    /**
+     * Converts and produces annotations on a stream. Does not write
+     * actual bits to the {@code AnnotatedOutput}.
+     *
+     * @param prefix {@code null-ok;} prefix to attach to each line of output
+     * @param debugPrint {@code null-ok;} if specified, an alternate output for
+     * annotations
+     * @param out {@code null-ok;} if specified, where annotations should go
+     * @param consume whether to claim to have consumed output for
+     * {@code out}
+     * @return {@code non-null;} encoded output
+     */
+    public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint,
+            AnnotatedOutput out, boolean consume) {
+        this.prefix = prefix;
+        this.debugPrint = debugPrint;
+        annotateTo = out;
+        shouldConsume = consume;
+
+        byte[] result = convert();
+
+        return result;
+    }
+
+    private byte[] convert0() throws IOException {
+        ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions();
+        ArrayList<LocalList.Entry> methodArgs = extractMethodArguments();
+
+        emitHeader(sortedPositions, methodArgs);
+
+        // TODO: Make this mark be the actual prologue end.
+        output.writeByte(DBG_SET_PROLOGUE_END);
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(1, String.format("%04x: prologue end",address));
+        }
+
+        int positionsSz = sortedPositions.size();
+        int localsSz = locals.size();
+
+        // Current index in sortedPositions
+        int curPositionIdx = 0;
+        // Current index in locals
+        int curLocalIdx = 0;
+
+        for (;;) {
+            /*
+             * Emit any information for the current address.
+             */
+
+            curLocalIdx = emitLocalsAtAddress(curLocalIdx);
+            curPositionIdx =
+                emitPositionsAtAddress(curPositionIdx, sortedPositions);
+
+            /*
+             * Figure out what the next important address is.
+             */
+
+            int nextAddrL = Integer.MAX_VALUE; // local variable
+            int nextAddrP = Integer.MAX_VALUE; // position (line number)
+
+            if (curLocalIdx < localsSz) {
+                nextAddrL = locals.get(curLocalIdx).getAddress();
+            }
+
+            if (curPositionIdx < positionsSz) {
+                nextAddrP = sortedPositions.get(curPositionIdx).getAddress();
+            }
+
+            int next = Math.min(nextAddrP, nextAddrL);
+
+            // No next important address == done.
+            if (next == Integer.MAX_VALUE) {
+                break;
+            }
+
+            /*
+             * If the only work remaining are local ends at the end of the
+             * block, stop here. Those are implied anyway.
+             */
+            if (next == codeSize
+                    && nextAddrL == Integer.MAX_VALUE
+                    && nextAddrP == Integer.MAX_VALUE) {
+                break;
+            }
+
+            if (next == nextAddrP) {
+                // Combined advance PC + position entry
+                emitPosition(sortedPositions.get(curPositionIdx++));
+            } else {
+                emitAdvancePc(next - address);
+            }
+        }
+
+        emitEndSequence();
+
+        return output.toByteArray();
+    }
+
+    /**
+     * Emits all local variable activity that occurs at the current
+     * {@link #address} starting at the given index into {@code
+     * locals} and including all subsequent activity at the same
+     * address.
+     *
+     * @param curLocalIdx Current index in locals
+     * @return new value for {@code curLocalIdx}
+     * @throws IOException
+     */
+    private int emitLocalsAtAddress(int curLocalIdx)
+            throws IOException {
+        int sz = locals.size();
+
+        // TODO: Don't emit ends implied by starts.
+
+        while ((curLocalIdx < sz)
+                && (locals.get(curLocalIdx).getAddress() == address)) {
+            LocalList.Entry entry = locals.get(curLocalIdx++);
+            int reg = entry.getRegister();
+            LocalList.Entry prevEntry = lastEntryForReg[reg];
+
+            if (entry == prevEntry) {
+                /*
+                 * Here we ignore locals entries for parameters,
+                 * which have already been represented and placed in the
+                 * lastEntryForReg array.
+                 */
+                continue;
+            }
+
+            // At this point we have a new entry one way or another.
+            lastEntryForReg[reg] = entry;
+
+            if (entry.isStart()) {
+                if ((prevEntry != null) && entry.matches(prevEntry)) {
+                    /*
+                     * The previous local in this register has the same
+                     * name and type as the one being introduced now, so
+                     * use the more efficient "restart" form.
+                     */
+                    if (prevEntry.isStart()) {
+                        /*
+                         * We should never be handed a start when a
+                         * a matching local is already active.
+                         */
+                        throw new RuntimeException("shouldn't happen");
+                    }
+                    emitLocalRestart(entry);
+                } else {
+                    emitLocalStart(entry);
+                }
+            } else {
+                /*
+                 * Only emit a local end if it is *not* due to a direct
+                 * replacement. Direct replacements imply an end of the
+                 * previous local in the same register.
+                 *
+                 * TODO: Make sure the runtime can deal with implied
+                 * local ends from category-2 interactions, and when so,
+                 * also stop emitting local ends for those cases.
+                 */
+                if (entry.getDisposition()
+                        != LocalList.Disposition.END_REPLACED) {
+                    emitLocalEnd(entry);
+                }
+            }
+        }
+
+        return curLocalIdx;
+    }
+
+    /**
+     * Emits all positions that occur at the current {@code address}
+     *
+     * @param curPositionIdx Current index in sortedPositions
+     * @param sortedPositions positions, sorted by ascending address
+     * @return new value for {@code curPositionIdx}
+     * @throws IOException
+     */
+    private int emitPositionsAtAddress(int curPositionIdx,
+            ArrayList<PositionList.Entry> sortedPositions)
+            throws IOException {
+        int positionsSz = sortedPositions.size();
+        while ((curPositionIdx < positionsSz)
+                && (sortedPositions.get(curPositionIdx).getAddress()
+                        == address)) {
+            emitPosition(sortedPositions.get(curPositionIdx++));
+        }
+        return curPositionIdx;
+    }
+
+    /**
+     * Emits the header sequence, which consists of LEB128-encoded initial
+     * line number and string indicies for names of all non-"this" arguments.
+     *
+     * @param sortedPositions positions, sorted by ascending address
+     * @param methodArgs local list entries for method argumens arguments,
+     * in left-to-right order omitting "this"
+     * @throws IOException
+     */
+    private void emitHeader(ArrayList<PositionList.Entry> sortedPositions,
+            ArrayList<LocalList.Entry> methodArgs) throws IOException {
+        boolean annotate = (annotateTo != null) || (debugPrint != null);
+        int mark = output.getCursor();
+
+        // Start by initializing the line number register.
+        if (sortedPositions.size() > 0) {
+            PositionList.Entry entry = sortedPositions.get(0);
+            line = entry.getPosition().getLine();
+        }
+        output.writeUnsignedLeb128(line);
+
+        if (annotate) {
+            annotate(output.getCursor() - mark, "line_start: " + line);
+        }
+
+        int curParam = getParamBase();
+        // paramTypes will not include 'this'
+        StdTypeList paramTypes = desc.getParameterTypes();
+        int szParamTypes = paramTypes.size();
+
+        /*
+         * Initialize lastEntryForReg to have an initial
+         * entry for the 'this' pointer.
+         */
+        if (!isStatic) {
+            for (LocalList.Entry arg : methodArgs) {
+                if (curParam == arg.getRegister()) {
+                    lastEntryForReg[curParam] = arg;
+                    break;
+                }
+            }
+            curParam++;
+        }
+
+        // Write out the number of parameter entries that will follow.
+        mark = output.getCursor();
+        output.writeUnsignedLeb128(szParamTypes);
+
+        if (annotate) {
+            annotate(output.getCursor() - mark,
+                    String.format("parameters_size: %04x", szParamTypes));
+        }
+
+        /*
+         * Then emit the string indicies of all the method parameters.
+         * Note that 'this', if applicable, is excluded.
+         */
+        for (int i = 0; i < szParamTypes; i++) {
+            Type pt = paramTypes.get(i);
+            LocalList.Entry found = null;
+
+            mark = output.getCursor();
+
+            for (LocalList.Entry arg : methodArgs) {
+                if (curParam == arg.getRegister()) {
+                    found = arg;
+
+                    if (arg.getSignature() != null) {
+                        /*
+                         * Parameters with signatures will be re-emitted
+                         * in complete as LOCAL_START_EXTENDED's below.
+                         */
+                        emitStringIndex(null);
+                    } else {
+                        emitStringIndex(arg.getName());
+                    }
+                    lastEntryForReg[curParam] = arg;
+
+                    break;
+                }
+            }
+
+            if (found == null) {
+                /*
+                 * Emit a null symbol for "unnamed." This is common
+                 * for, e.g., synthesized methods and inner-class
+                 * this$0 arguments.
+                 */
+                emitStringIndex(null);
+            }
+
+            if (annotate) {
+                String parameterName
+                        = (found == null || found.getSignature() != null)
+                                ? "<unnamed>" : found.getName().toHuman();
+                annotate(output.getCursor() - mark,
+                        "parameter " + parameterName + " "
+                                + RegisterSpec.PREFIX + curParam);
+            }
+
+            curParam += pt.getCategory();
+        }
+
+        /*
+         * If anything emitted above has a type signature, emit it again as
+         * a LOCAL_RESTART_EXTENDED
+         */
+
+        for (LocalList.Entry arg : lastEntryForReg) {
+            if (arg == null) {
+                continue;
+            }
+
+            CstUtf8 signature = arg.getSignature();
+
+            if (signature != null) {
+                emitLocalStartExtended(arg);
+            }
+        }
+    }
+
+    /**
+     * Builds a list of position entries, sorted by ascending address.
+     *
+     * @return A sorted positions list
+     */
+    private ArrayList<PositionList.Entry> buildSortedPositions() {
+        int sz = (positions == null) ? 0 : positions.size();
+        ArrayList<PositionList.Entry> result = new ArrayList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            result.add(positions.get(i));
+        }
+
+        // Sort ascending by address.
+        Collections.sort (result, new Comparator<PositionList.Entry>() {
+            public int compare (PositionList.Entry a, PositionList.Entry b) {
+                return a.getAddress() - b.getAddress();
+            }
+
+            public boolean equals (Object obj) {
+               return obj == this;
+            }
+        });
+        return result;
+    }
+
+    /**
+     * Gets the register that begins the method's parameter range (including
+     * the 'this' parameter for non-static methods). The range continues until
+     * {@code regSize}
+     *
+     * @return register as noted above
+     */
+    private int getParamBase() {
+        return regSize
+                - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
+    }
+
+    /**
+     * Extracts method arguments from a locals list. These will be collected
+     * from the input list and sorted by ascending register in the
+     * returned list.
+     *
+     * @return list of non-{@code this} method argument locals,
+     * sorted by ascending register
+     */
+    private ArrayList<LocalList.Entry> extractMethodArguments() {
+        ArrayList<LocalList.Entry> result
+                = new ArrayList(desc.getParameterTypes().size());
+        int argBase = getParamBase();
+        BitSet seen = new BitSet(regSize - argBase);
+        int sz = locals.size();
+
+        for (int i = 0; i < sz; i++) {
+            LocalList.Entry e = locals.get(i);
+            int reg = e.getRegister();
+
+            if (reg < argBase) {
+                continue;
+            }
+
+            // only the lowest-start-address entry is included.
+            if (seen.get(reg - argBase)) {
+                continue;
+            }
+
+            seen.set(reg - argBase);
+            result.add(e);
+        }
+
+        // Sort by ascending register.
+        Collections.sort(result, new Comparator<LocalList.Entry>() {
+            public int compare(LocalList.Entry a, LocalList.Entry b) {
+                return a.getRegister() - b.getRegister();
+            }
+
+            public boolean equals(Object obj) {
+               return obj == this;
+            }
+        });
+
+        return result;
+    }
+
+    /**
+     * Returns a string representation of this LocalList entry that is
+     * appropriate for emitting as an annotation.
+     *
+     * @param e {@code non-null;} entry
+     * @return {@code non-null;} annotation string
+     */
+    private String entryAnnotationString(LocalList.Entry e) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(RegisterSpec.PREFIX);
+        sb.append(e.getRegister());
+        sb.append(' ');
+
+        CstUtf8 name = e.getName();
+        if (name == null) {
+            sb.append("null");
+        } else {
+            sb.append(name.toHuman());
+        }
+        sb.append(' ');
+
+        CstType type = e.getType();
+        if (type == null) {
+            sb.append("null");
+        } else {
+            sb.append(type.toHuman());
+        }
+
+        CstUtf8 signature = e.getSignature();
+
+        if (signature != null) {
+            sb.append(' ');
+            sb.append(signature.toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL}
+     * sequence.
+     *
+     * @param entry entry associated with this restart
+     * @throws IOException
+     */
+    private void emitLocalRestart(LocalList.Entry entry)
+            throws IOException {
+
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_RESTART_LOCAL);
+        emitUnsignedLeb128(entry.getRegister());
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: +local restart %s",
+                            address, entryAnnotationString(entry)));
+        }
+
+        if (DEBUG) {
+            System.err.println("emit local restart");
+        }
+    }
+
+    /**
+     * Emits a string index as an unsigned LEB128. The actual value written
+     * is shifted by 1, so that the '0' value is reserved for "null". The
+     * null symbol is used in some cases by the parameter name list
+     * at the beginning of the sequence.
+     *
+     * @param string {@code null-ok;} string to emit
+     * @throws IOException
+     */
+    private void emitStringIndex(CstUtf8 string) throws IOException {
+        if ((string == null) || (file == null)) {
+            output.writeUnsignedLeb128(0);
+        } else {
+            output.writeUnsignedLeb128(
+                1 + file.getStringIds().indexOf(string));
+        }
+
+        if (DEBUG) {
+            System.err.printf("Emit string %s\n",
+                    string == null ? "<null>" : string.toQuoted());
+        }
+    }
+
+    /**
+     * Emits a type index as an unsigned LEB128. The actual value written
+     * is shifted by 1, so that the '0' value is reserved for "null".
+     *
+     * @param type {@code null-ok;} type to emit
+     * @throws IOException
+     */
+    private void emitTypeIndex(CstType type) throws IOException {
+        if ((type == null) || (file == null)) {
+            output.writeUnsignedLeb128(0);
+        } else {
+            output.writeUnsignedLeb128(
+                1 + file.getTypeIds().indexOf(type));
+        }
+
+        if (DEBUG) {
+            System.err.printf("Emit type %s\n",
+                    type == null ? "<null>" : type.toHuman());
+        }
+    }
+
+    /**
+     * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or
+     * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
+     * DBG_START_LOCAL_EXTENDED} sequence.
+     *
+     * @param entry entry to emit
+     * @throws IOException
+     */
+    private void emitLocalStart(LocalList.Entry entry)
+        throws IOException {
+
+        if (entry.getSignature() != null) {
+            emitLocalStartExtended(entry);
+            return;
+        }
+
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_START_LOCAL);
+
+        emitUnsignedLeb128(entry.getRegister());
+        emitStringIndex(entry.getName());
+        emitTypeIndex(entry.getType());
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: +local %s", address,
+                            entryAnnotationString(entry)));
+        }
+
+        if (DEBUG) {
+            System.err.println("emit local start");
+        }
+    }
+
+    /**
+     * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
+     * DBG_START_LOCAL_EXTENDED} sequence.
+     *
+     * @param entry entry to emit
+     * @throws IOException
+     */
+    private void emitLocalStartExtended(LocalList.Entry entry)
+        throws IOException {
+
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_START_LOCAL_EXTENDED);
+
+        emitUnsignedLeb128(entry.getRegister());
+        emitStringIndex(entry.getName());
+        emitTypeIndex(entry.getType());
+        emitStringIndex(entry.getSignature());
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: +localx %s", address,
+                            entryAnnotationString(entry)));
+        }
+
+        if (DEBUG) {
+            System.err.println("emit local start");
+        }
+    }
+
+    /**
+     * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence.
+     *
+     * @param entry {@code entry non-null;} entry associated with end.
+     * @throws IOException
+     */
+    private void emitLocalEnd(LocalList.Entry entry)
+            throws IOException {
+
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_END_LOCAL);
+        output.writeUnsignedLeb128(entry.getRegister());
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: -local %s", address,
+                            entryAnnotationString(entry)));
+        }
+
+        if (DEBUG) {
+            System.err.println("emit local end");
+        }
+    }
+
+    /**
+     * Emits the necessary byte sequences to emit the given position table
+     * entry. This will typically be a single special opcode, although
+     * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE.
+     *
+     * @param entry position entry to emit.
+     * @throws IOException
+     */
+    private void emitPosition(PositionList.Entry entry)
+            throws IOException {
+
+        SourcePosition pos = entry.getPosition();
+        int newLine = pos.getLine();
+        int newAddress = entry.getAddress();
+
+        int opcode;
+
+        int deltaLines = newLine - line;
+        int deltaAddress = newAddress - address;
+
+        if (deltaAddress < 0) {
+            throw new RuntimeException(
+                    "Position entries must be in ascending address order");
+        }
+
+        if ((deltaLines < DBG_LINE_BASE)
+                || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) {
+            emitAdvanceLine(deltaLines);
+            deltaLines = 0;
+        }
+
+        opcode = computeOpcode (deltaLines, deltaAddress);
+
+        if ((opcode & ~0xff) > 0) {
+            emitAdvancePc(deltaAddress);
+            deltaAddress = 0;
+            opcode = computeOpcode (deltaLines, deltaAddress);
+
+            if ((opcode & ~0xff) > 0) {
+                emitAdvanceLine(deltaLines);
+                deltaLines = 0;
+                opcode = computeOpcode (deltaLines, deltaAddress);
+            }
+        }
+
+        output.writeByte(opcode);
+
+        line += deltaLines;
+        address += deltaAddress;
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(1,
+                    String.format("%04x: line %d", address, line));
+        }
+    }
+
+    /**
+     * Computes a special opcode that will encode the given position change.
+     * If the return value is > 0xff, then the request cannot be fulfilled.
+     * Essentially the same as described in "DWARF Debugging Format Version 3"
+     * section 6.2.5.1.
+     *
+     * @param deltaLines {@code >= DBG_LINE_BASE, <= DBG_LINE_BASE +
+     * DBG_LINE_RANGE;} the line change to encode
+     * @param deltaAddress {@code >= 0;} the address change to encode
+     * @return {@code <= 0xff} if in range, otherwise parameters are out
+     * of range
+     */
+    private static int computeOpcode(int deltaLines, int deltaAddress) {
+        if (deltaLines < DBG_LINE_BASE
+                || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) {
+
+            throw new RuntimeException("Parameter out of range");
+        }
+
+        return (deltaLines - DBG_LINE_BASE)
+            + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL;
+    }
+
+    /**
+     * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE}
+     * sequence.
+     *
+     * @param deltaLines amount to change line number register by
+     * @throws IOException
+     */
+    private void emitAdvanceLine(int deltaLines) throws IOException {
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_ADVANCE_LINE);
+        output.writeSignedLeb128(deltaLines);
+        line += deltaLines;
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("line = %d", line));
+        }
+
+        if (DEBUG) {
+            System.err.printf("Emitting advance_line for %d\n", deltaLines);
+        }
+    }
+
+    /**
+     * Emits an  {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC}
+     * sequence.
+     *
+     * @param deltaAddress {@code >= 0;} amount to change program counter by
+     * @throws IOException
+     */
+    private void emitAdvancePc(int deltaAddress) throws IOException {
+        int mark = output.getCursor();
+
+        output.writeByte(DBG_ADVANCE_PC);
+        output.writeUnsignedLeb128(deltaAddress);
+        address += deltaAddress;
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(output.getCursor() - mark,
+                    String.format("%04x: advance pc", address));
+        }
+
+        if (DEBUG) {
+            System.err.printf("Emitting advance_pc for %d\n", deltaAddress);
+        }
+    }
+
+    /**
+     * Emits an unsigned LEB128 value.
+     *
+     * @param n {@code >= 0;} value to emit. Note that, although this can
+     * represent integers larger than Integer.MAX_VALUE, we currently don't
+     * allow that.
+     * @throws IOException
+     */
+    private void emitUnsignedLeb128(int n) throws IOException {
+        // We'll never need the top end of the unsigned range anyway.
+        if (n < 0) {
+            throw new RuntimeException(
+                    "Signed value where unsigned required: " + n);
+        }
+
+        output.writeUnsignedLeb128(n);
+    }
+
+    /**
+     * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE}
+     * bytecode.
+     */
+    private void emitEndSequence() {
+        output.writeByte(DBG_END_SEQUENCE);
+
+        if (annotateTo != null || debugPrint != null) {
+            annotate(1, "end sequence");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoItem.java b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
new file mode 100644
index 0000000..6a41b18
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.DalvInsnList;
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.PrintWriter;
+
+public class DebugInfoItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 1;
+
+    private static final boolean ENABLE_ENCODER_SELF_CHECK = false;
+
+    /** {@code non-null;} the code this item represents */
+    private final DalvCode code;
+
+    private byte[] encoded;
+
+    private final boolean isStatic;
+    private final CstMethodRef ref;
+
+    public DebugInfoItem(DalvCode code, boolean isStatic, CstMethodRef ref) {
+        // We don't know the write size yet.
+        super (ALIGNMENT, -1);
+
+        if (code == null) {
+            throw new NullPointerException("code == null");
+        }
+
+        this.code = code;
+        this.isStatic = isStatic;
+        this.ref = ref;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_DEBUG_INFO_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        // No contents to add.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Encode the data and note the size.
+
+        try {
+            encoded = encode(addedTo.getFile(), null, null, null, false);
+            setWriteSize(encoded.length);
+        } catch (RuntimeException ex) {
+            throw ExceptionWithContext.withContext(ex,
+                    "...while placing debug info for " + ref.toHuman());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /**
+     * Writes annotations for the elements of this list, as
+     * zero-length. This is meant to be used for dumping this instance
+     * directly after a code dump (with the real local list actually
+     * existing elsewhere in the output).
+     *
+     * @param file {@code non-null;} the file to use for referencing other sections
+     * @param out {@code non-null;} where to annotate to
+     * @param prefix {@code null-ok;} prefix to attach to each line of output
+     */
+    public void annotateTo(DexFile file, AnnotatedOutput out, String prefix) {
+        encode(file, prefix, null, out, false);
+    }
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out {@code non-null;} where to dump
+     * @param prefix {@code non-null;} prefix to attach to each line of output
+     */
+    public void debugPrint(PrintWriter out, String prefix) {
+        encode(null, prefix, out, null, false);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        if (out.annotates()) {
+            /*
+             * Re-run the encoder to generate the annotations,
+             * but write the bits from the original encode
+             */
+
+            out.annotate(offsetString() + " debug info");
+            encode(file, null, null, out, true);
+        }
+
+        out.write(encoded);
+    }
+
+    /**
+     * Performs debug info encoding.
+     *
+     * @param file {@code null-ok;} file to refer to during encoding
+     * @param prefix {@code null-ok;} prefix to attach to each line of output
+     * @param debugPrint {@code null-ok;} if specified, an alternate output for
+     * annotations
+     * @param out {@code null-ok;} if specified, where annotations should go
+     * @param consume whether to claim to have consumed output for
+     * {@code out}
+     * @return {@code non-null;} the encoded array
+     */
+    private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint,
+            AnnotatedOutput out, boolean consume) {
+        byte[] result = encode0(file, prefix, debugPrint, out, consume);
+
+        if (ENABLE_ENCODER_SELF_CHECK && (file != null)) {
+            try {
+                DebugInfoDecoder.validateEncode(result, file, ref, code,
+                        isStatic);
+            } catch (RuntimeException ex) {
+                // Reconvert, annotating to System.err.
+                encode0(file, "", new PrintWriter(System.err, true), null,
+                        false);
+                throw ex;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Helper for {@link #encode} to do most of the work.
+     *
+     * @param file {@code null-ok;} file to refer to during encoding
+     * @param prefix {@code null-ok;} prefix to attach to each line of output
+     * @param debugPrint {@code null-ok;} if specified, an alternate output for
+     * annotations
+     * @param out {@code null-ok;} if specified, where annotations should go
+     * @param consume whether to claim to have consumed output for
+     * {@code out}
+     * @return {@code non-null;} the encoded array
+     */
+    private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint,
+            AnnotatedOutput out, boolean consume) {
+        PositionList positions = code.getPositions();
+        LocalList locals = code.getLocals();
+        DalvInsnList insns = code.getInsns();
+        int codeSize = insns.codeSize();
+        int regSize = insns.getRegistersSize();
+
+        DebugInfoEncoder encoder =
+            new DebugInfoEncoder(positions, locals,
+                    file, codeSize, regSize, isStatic, ref);
+
+        byte[] result;
+
+        if ((debugPrint == null) && (out == null)) {
+            result = encoder.convert();
+        } else {
+            result = encoder.convertAndAnnotate(prefix, debugPrint, out,
+                    consume);
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/DexFile.java b/dx/src/com/android/dx/dex/file/DexFile.java
new file mode 100644
index 0000000..1cc9358
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DexFile.java
@@ -0,0 +1,647 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.zip.Adler32;
+
+import static com.android.dx.dex.file.MixedItemSection.SortType;
+
+/**
+ * Representation of an entire {@code .dex} (Dalvik EXecutable)
+ * file, which itself consists of a set of Dalvik classes.
+ */
+public final class DexFile {
+    /** {@code non-null;} word data section */
+    private final MixedItemSection wordData;
+
+    /**
+     * {@code non-null;} type lists section. This is word data, but separating
+     * it from {@link #wordData} helps break what would otherwise be a
+     * circular dependency between the that and {@link #protoIds}.
+     */
+    private final MixedItemSection typeLists;
+
+    /**
+     * {@code non-null;} map section. The map needs to be in a section by itself
+     * for the self-reference mechanics to work in a reasonably
+     * straightforward way. See {@link MapItem#addMap} for more detail.
+     */
+    private final MixedItemSection map;
+
+    /** {@code non-null;} string data section */
+    private final MixedItemSection stringData;
+
+    /** {@code non-null;} string identifiers section */
+    private final StringIdsSection stringIds;
+
+    /** {@code non-null;} type identifiers section */
+    private final TypeIdsSection typeIds;
+
+    /** {@code non-null;} prototype identifiers section */
+    private final ProtoIdsSection protoIds;
+
+    /** {@code non-null;} field identifiers section */
+    private final FieldIdsSection fieldIds;
+
+    /** {@code non-null;} method identifiers section */
+    private final MethodIdsSection methodIds;
+
+    /** {@code non-null;} class definitions section */
+    private final ClassDefsSection classDefs;
+
+    /** {@code non-null;} class data section */
+    private final MixedItemSection classData;
+
+    /** {@code non-null;} byte data section */
+    private final MixedItemSection byteData;
+
+    /** {@code non-null;} file header */
+    private final HeaderSection header;
+
+    /**
+     * {@code non-null;} array of sections in the order they will appear in the
+     * final output file
+     */
+    private final Section[] sections;
+
+    /** {@code >= -1;} total file size or {@code -1} if unknown */
+    private int fileSize;
+
+    /** {@code >= 40;} maximum width of the file dump */
+    private int dumpWidth;
+
+    /**
+     * Constructs an instance. It is initially empty.
+     */
+    public DexFile() {
+        header = new HeaderSection(this);
+        typeLists = new MixedItemSection(null, this, 4, SortType.NONE);
+        wordData = new MixedItemSection("word_data", this, 4, SortType.TYPE);
+        stringData =
+            new MixedItemSection("string_data", this, 1, SortType.INSTANCE);
+        classData = new MixedItemSection(null, this, 1, SortType.NONE);
+        byteData = new MixedItemSection("byte_data", this, 1, SortType.TYPE);
+        stringIds = new StringIdsSection(this);
+        typeIds = new TypeIdsSection(this);
+        protoIds = new ProtoIdsSection(this);
+        fieldIds = new FieldIdsSection(this);
+        methodIds = new MethodIdsSection(this);
+        classDefs = new ClassDefsSection(this);
+        map = new MixedItemSection("map", this, 4, SortType.NONE);
+
+        /*
+         * This is the list of sections in the order they appear in
+         * the final output.
+         */
+        sections = new Section[] {
+            header, stringIds, typeIds, protoIds, fieldIds, methodIds,
+            classDefs, wordData, typeLists, stringData, byteData,
+            classData, map };
+
+        fileSize = -1;
+        dumpWidth = 79;
+    }
+
+    /**
+     * Adds a class to this instance. It is illegal to attempt to add more
+     * than one class with the same name.
+     *
+     * @param clazz {@code non-null;} the class to add
+     */
+    public void add(ClassDefItem clazz) {
+        classDefs.add(clazz);
+    }
+
+    /**
+     * Gets the class definition with the given name, if any.
+     *
+     * @param name {@code non-null;} the class name to look for
+     * @return {@code null-ok;} the class with the given name, or {@code null}
+     * if there is no such class
+     */
+    public ClassDefItem getClassOrNull(String name) {
+        try {
+            Type type = Type.internClassName(name);
+            return (ClassDefItem) classDefs.get(new CstType(type));
+        } catch (IllegalArgumentException ex) {
+            // Translate exception, per contract.
+            return null;
+        }
+    }
+
+    /**
+     * Writes the contents of this instance as either a binary or a
+     * human-readable form, or both.
+     *
+     * @param out {@code null-ok;} where to write to
+     * @param humanOut {@code null-ok;} where to write human-oriented output to
+     * @param verbose whether to be verbose when writing human-oriented output
+     */
+    public void writeTo(OutputStream out, Writer humanOut, boolean verbose)
+        throws IOException {
+        boolean annotate = (humanOut != null);
+        ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
+
+        if (out != null) {
+            out.write(result.getArray());
+        }
+
+        if (annotate) {
+            result.writeAnnotationsTo(humanOut);
+        }
+    }
+
+    /**
+     * Returns the contents of this instance as a {@code .dex} file,
+     * in {@code byte[]} form.
+     *
+     * @param humanOut {@code null-ok;} where to write human-oriented output to
+     * @param verbose whether to be verbose when writing human-oriented output
+     * @return {@code non-null;} a {@code .dex} file for this instance
+     */
+    public byte[] toDex(Writer humanOut, boolean verbose)
+        throws IOException {
+        boolean annotate = (humanOut != null);
+        ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
+
+        if (annotate) {
+            result.writeAnnotationsTo(humanOut);
+        }
+
+        return result.getArray();
+    }
+
+    /**
+     * Sets the maximum width of the human-oriented dump of the instance.
+     *
+     * @param dumpWidth {@code >= 40;} the width
+     */
+    public void setDumpWidth(int dumpWidth) {
+        if (dumpWidth < 40) {
+            throw new IllegalArgumentException("dumpWidth < 40");
+        }
+
+        this.dumpWidth = dumpWidth;
+    }
+
+    /**
+     * Gets the total file size, if known.
+     *
+     * <p>This is package-scope in order to allow
+     * the {@link HeaderSection} to set itself up properly.</p>
+     *
+     * @return {@code >= 0;} the total file size
+     * @throws RuntimeException thrown if the file size is not yet known
+     */
+    /*package*/ int getFileSize() {
+        if (fileSize < 0) {
+            throw new RuntimeException("file size not yet known");
+        }
+
+        return fileSize;
+    }
+
+    /**
+     * Gets the string data section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the string data section
+     */
+    /*package*/ MixedItemSection getStringData() {
+        return stringData;
+    }
+
+    /**
+     * Gets the word data section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the word data section
+     */
+    /*package*/ MixedItemSection getWordData() {
+        return wordData;
+    }
+
+    /**
+     * Gets the type lists section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the word data section
+     */
+    /*package*/ MixedItemSection getTypeLists() {
+        return typeLists;
+    }
+
+    /**
+     * Gets the map section.
+     *
+     * <p>This is package-scope in order to allow the header section
+     * to query it.</p>
+     *
+     * @return {@code non-null;} the map section
+     */
+    /*package*/ MixedItemSection getMap() {
+        return map;
+    }
+
+    /**
+     * Gets the string identifiers section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the string identifiers section
+     */
+    /*package*/ StringIdsSection getStringIds() {
+        return stringIds;
+    }
+
+    /**
+     * Gets the class definitions section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the class definitions section
+     */
+    /*package*/ ClassDefsSection getClassDefs() {
+        return classDefs;
+    }
+
+    /**
+     * Gets the class data section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the class data section
+     */
+    /*package*/ MixedItemSection getClassData() {
+        return classData;
+    }
+
+    /**
+     * Gets the type identifiers section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the class identifiers section
+     */
+    /*package*/ TypeIdsSection getTypeIds() {
+        return typeIds;
+    }
+
+    /**
+     * Gets the prototype identifiers section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the prototype identifiers section
+     */
+    /*package*/ ProtoIdsSection getProtoIds() {
+        return protoIds;
+    }
+
+    /**
+     * Gets the field identifiers section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the field identifiers section
+     */
+    /*package*/ FieldIdsSection getFieldIds() {
+        return fieldIds;
+    }
+
+    /**
+     * Gets the method identifiers section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the method identifiers section
+     */
+    /*package*/ MethodIdsSection getMethodIds() {
+        return methodIds;
+    }
+
+    /**
+     * Gets the byte data section.
+     *
+     * <p>This is package-scope in order to allow
+     * the various {@link Item} instances to add items to the
+     * instance.</p>
+     *
+     * @return {@code non-null;} the byte data section
+     */
+    /*package*/ MixedItemSection getByteData() {
+        return byteData;
+    }
+
+    /**
+     * Gets the first section of the file that is to be considered
+     * part of the data section.
+     *
+     * <p>This is package-scope in order to allow the header section
+     * to query it.</p>
+     *
+     * @return {@code non-null;} the section
+     */
+    /*package*/ Section getFirstDataSection() {
+        return wordData;
+    }
+
+    /**
+     * Gets the last section of the file that is to be considered
+     * part of the data section.
+     *
+     * <p>This is package-scope in order to allow the header section
+     * to query it.</p>
+     *
+     * @return {@code non-null;} the section
+     */
+    /*package*/ Section getLastDataSection() {
+        return map;
+    }
+
+    /**
+     * Interns the given constant in the appropriate section of this
+     * instance, or do nothing if the given constant isn't the sort
+     * that should be interned.
+     *
+     * @param cst {@code non-null;} constant to possibly intern
+     */
+    /*package*/ void internIfAppropriate(Constant cst) {
+        if (cst instanceof CstString) {
+            stringIds.intern((CstString) cst);
+        } else if (cst instanceof CstUtf8) {
+            stringIds.intern((CstUtf8) cst);
+        } else if (cst instanceof CstType) {
+            typeIds.intern((CstType) cst);
+        } else if (cst instanceof CstBaseMethodRef) {
+            methodIds.intern((CstBaseMethodRef) cst);
+        } else if (cst instanceof CstFieldRef) {
+            fieldIds.intern((CstFieldRef) cst);
+        } else if (cst instanceof CstEnumRef) {
+            fieldIds.intern(((CstEnumRef) cst).getFieldRef());
+        } else if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+    }
+
+    /**
+     * Gets the {@link IndexedItem} corresponding to the given constant,
+     * if it is a constant that has such a correspondence, or return
+     * {@code null} if it isn't such a constant. This will throw
+     * an exception if the given constant <i>should</i> have been found
+     * but wasn't.
+     *
+     * @param cst {@code non-null;} the constant to look up
+     * @return {@code null-ok;} its corresponding item, if it has a corresponding
+     * item, or {@code null} if it's not that sort of constant
+     */
+    /*package*/ IndexedItem findItemOrNull(Constant cst) {
+        IndexedItem item;
+
+        if (cst instanceof CstString) {
+            return stringIds.get(cst);
+        } else if (cst instanceof CstType) {
+            return typeIds.get(cst);
+        } else if (cst instanceof CstBaseMethodRef) {
+            return methodIds.get(cst);
+        } else if (cst instanceof CstFieldRef) {
+            return fieldIds.get(cst);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the contents of this instance as a {@code .dex} file,
+     * in a {@link ByteArrayAnnotatedOutput} instance.
+     *
+     * @param annotate whether or not to keep annotations
+     * @param verbose if annotating, whether to be verbose
+     * @return {@code non-null;} a {@code .dex} file for this instance
+     */
+    private ByteArrayAnnotatedOutput toDex0(boolean annotate,
+            boolean verbose) {
+        /*
+         * The following is ordered so that the prepare() calls which
+         * add items happen before the calls to the sections that get
+         * added to.
+         */
+
+        classDefs.prepare();
+        classData.prepare();
+        wordData.prepare();
+        byteData.prepare();
+        methodIds.prepare();
+        fieldIds.prepare();
+        protoIds.prepare();
+        typeLists.prepare();
+        typeIds.prepare();
+        stringIds.prepare();
+        stringData.prepare();
+        header.prepare();
+
+        // Place the sections within the file.
+
+        int count = sections.length;
+        int offset = 0;
+
+        for (int i = 0; i < count; i++) {
+            Section one = sections[i];
+            int placedAt = one.setFileOffset(offset);
+            if (placedAt < offset) {
+                throw new RuntimeException("bogus placement for section " + i);
+            }
+
+            try {
+                if (one == map) {
+                    /*
+                     * Inform the map of all the sections, and add it
+                     * to the file. This can only be done after all
+                     * the other items have been sorted and placed.
+                     */
+                    MapItem.addMap(sections, map);
+                    map.prepare();
+                }
+
+                if (one instanceof MixedItemSection) {
+                    /*
+                     * Place the items of a MixedItemSection that just
+                     * got placed.
+                     */
+                    ((MixedItemSection) one).placeItems();
+                }
+
+                offset = placedAt + one.writeSize();
+            } catch (RuntimeException ex) {
+                throw ExceptionWithContext.withContext(ex,
+                        "...while writing section " + i);
+            }
+        }
+
+        // Write out all the sections.
+
+        fileSize = offset;
+        byte[] barr = new byte[fileSize];
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(barr);
+
+        if (annotate) {
+            out.enableAnnotations(dumpWidth, verbose);
+        }
+
+        for (int i = 0; i < count; i++) {
+            try {
+                Section one = sections[i];
+                int zeroCount = one.getFileOffset() - out.getCursor();
+                if (zeroCount < 0) {
+                    throw new ExceptionWithContext("excess write of " +
+                            (-zeroCount));
+                }
+                out.writeZeroes(one.getFileOffset() - out.getCursor());
+                one.writeTo(out);
+            } catch (RuntimeException ex) {
+                ExceptionWithContext ec;
+                if (ex instanceof ExceptionWithContext) {
+                    ec = (ExceptionWithContext) ex;
+                } else {
+                    ec = new ExceptionWithContext(ex);
+                }
+                ec.addContext("...while writing section " + i);
+                throw ec;
+            }
+        }
+
+        if (out.getCursor() != fileSize) {
+            throw new RuntimeException("foreshortened write");
+        }
+
+        // Perform final bookkeeping.
+
+        calcSignature(barr);
+        calcChecksum(barr);
+
+        if (annotate) {
+            wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM,
+                    "\nmethod code index:\n\n");
+            getStatistics().writeAnnotation(out);
+            out.finishAnnotating();
+        }
+
+        return out;
+    }
+
+    /**
+     * Generates and returns statistics for all the items in the file.
+     *
+     * @return {@code non-null;} the statistics
+     */
+    public Statistics getStatistics() {
+        Statistics stats = new Statistics();
+
+        for (Section s : sections) {
+            stats.addAll(s);
+        }
+
+        return stats;
+    }
+
+    /**
+     * Calculates the signature for the {@code .dex} file in the
+     * given array, and modify the array to contain it.
+     *
+     * @param bytes {@code non-null;} the bytes of the file
+     */
+    private static void calcSignature(byte[] bytes) {
+        MessageDigest md;
+
+        try {
+            md = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException ex) {
+            throw new RuntimeException(ex);
+        }
+
+        md.update(bytes, 32, bytes.length - 32);
+
+        try {
+            int amt = md.digest(bytes, 12, 20);
+            if (amt != 20) {
+                throw new RuntimeException("unexpected digest write: " + amt +
+                                           " bytes");
+            }
+        } catch (DigestException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Calculates the checksum for the {@code .dex} file in the
+     * given array, and modify the array to contain it.
+     *
+     * @param bytes {@code non-null;} the bytes of the file
+     */
+    private static void calcChecksum(byte[] bytes) {
+        Adler32 a32 = new Adler32();
+
+        a32.update(bytes, 12, bytes.length - 12);
+
+        int sum = (int) a32.getValue();
+
+        bytes[8]  = (byte) sum;
+        bytes[9]  = (byte) (sum >> 8);
+        bytes[10] = (byte) (sum >> 16);
+        bytes[11] = (byte) (sum >> 24);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedArrayItem.java b/dx/src/com/android/dx/dex/file/EncodedArrayItem.java
new file mode 100644
index 0000000..4d904e7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedArrayItem.java
@@ -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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Encoded array of constant values.
+ */
+public final class EncodedArrayItem extends OffsettedItem {
+    /** the required alignment for instances of this class */
+    private static final int ALIGNMENT = 1;
+
+    /** {@code non-null;} the array to represent */
+    private final CstArray array;
+
+    /**
+     * {@code null-ok;} encoded form, ready for writing to a file; set during
+     * {@link #place0}
+     */
+    private byte[] encodedForm;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param array {@code non-null;} array to represent
+     */
+    public EncodedArrayItem(CstArray array) {
+        /*
+         * The write size isn't known up-front because (the variable-lengthed)
+         * leb128 type is used to represent some things.
+         */
+        super(ALIGNMENT, -1);
+
+        if (array == null) {
+            throw new NullPointerException("array == null");
+        }
+
+        this.array = array;
+        this.encodedForm = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_ENCODED_ARRAY_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return array.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        EncodedArrayItem otherArray = (EncodedArrayItem) other;
+
+        return array.compareTo(otherArray.array);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return array.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        ValueEncoder.addContents(file, array);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        // Encode the data and note the size.
+
+        ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+        ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
+
+        encoder.writeArray(array, false);
+        encodedForm = out.toByteArray();
+        setWriteSize(encodedForm.length);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+
+        if (annotates) {
+            out.annotate(0, offsetString() + " encoded array");
+
+            /*
+             * The output is to be annotated, so redo the work previously
+             * done by place0(), except this time annotations will actually
+             * get emitted.
+             */
+            ValueEncoder encoder = new ValueEncoder(file, out);
+            encoder.writeArray(array, true);
+        } else {
+            out.write(encodedForm);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedField.java b/dx/src/com/android/dx/dex/file/EncodedField.java
new file mode 100644
index 0000000..f2a8184
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedField.java
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+import java.io.PrintWriter;
+
+/**
+ * Representation of a field of a class, of any sort.
+ */
+public final class EncodedField extends EncodedMember
+        implements Comparable<EncodedField> {
+    /** {@code non-null;} constant for the field */
+    private final CstFieldRef field;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param field {@code non-null;} constant for the field
+     * @param accessFlags access flags
+     */
+    public EncodedField(CstFieldRef field, int accessFlags) {
+        super(accessFlags);
+
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        /*
+         * TODO: Maybe check accessFlags, at least for
+         * easily-checked stuff?
+         */
+
+        this.field = field;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return field.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof EncodedField)) {
+            return false;
+        }
+
+        return compareTo((EncodedField) other) == 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p><b>Note:</b> This compares the method constants only,
+     * ignoring any associated code, because it should never be the
+     * case that two different items with the same method constant
+     * ever appear in the same list (or same file, even).</p>
+     */
+    public int compareTo(EncodedField other) {
+        return field.compareTo(other.field);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append('{');
+        sb.append(Hex.u2(getAccessFlags()));
+        sb.append(' ');
+        sb.append(field);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        FieldIdsSection fieldIds = file.getFieldIds();
+        fieldIds.intern(field);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public CstUtf8 getName() {
+        return field.getNat().getName();
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return field.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void debugPrint(PrintWriter out, boolean verbose) {
+        // TODO: Maybe put something better here?
+        out.println(toString());
+    }
+
+    /**
+     * Gets the constant for the field.
+     *
+     * @return {@code non-null;} the constant
+     */
+    public CstFieldRef getRef() {
+        return field;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int encode(DexFile file, AnnotatedOutput out,
+            int lastIndex, int dumpSeq) {
+        int fieldIdx = file.getFieldIds().indexOf(field);
+        int diff = fieldIdx - lastIndex;
+        int accessFlags = getAccessFlags();
+
+        if (out.annotates()) {
+            out.annotate(0, String.format("  [%x] %s", dumpSeq,
+                            field.toHuman()));
+            out.annotate(Leb128Utils.unsignedLeb128Size(diff),
+                    "    field_idx:    " + Hex.u4(fieldIdx));
+            out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
+                    "    access_flags: " +
+                    AccessFlags.fieldString(accessFlags));
+        }
+
+        out.writeUnsignedLeb128(diff);
+        out.writeUnsignedLeb128(accessFlags);
+
+        return fieldIdx;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedMember.java b/dx/src/com/android/dx/dex/file/EncodedMember.java
new file mode 100644
index 0000000..5099325
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedMember.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ToHuman;
+
+import java.io.PrintWriter;
+
+/**
+ * Representation of a member (field or method) of a class, for the
+ * purposes of encoding it inside a {@link ClassDataItem}.
+ */
+public abstract class EncodedMember implements ToHuman {
+    /** access flags */
+    private final int accessFlags;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param accessFlags access flags for the member
+     */
+    public EncodedMember(int accessFlags) {
+        this.accessFlags = accessFlags;
+    }
+
+    /**
+     * Gets the access flags.
+     *
+     * @return the access flags
+     */
+    public final int getAccessFlags() {
+        return accessFlags;
+    }
+
+    /**
+     * Gets the name.
+     *
+     * @return {@code non-null;} the name
+     */
+    public abstract CstUtf8 getName();
+
+    /**
+     * Does a human-friendly dump of this instance.
+     *
+     * @param out {@code non-null;} where to dump
+     * @param verbose whether to be verbose with the output
+     */
+    public abstract void debugPrint(PrintWriter out, boolean verbose);
+
+    /**
+     * Populates a {@link DexFile} with items from within this instance.
+     *
+     * @param file {@code non-null;} the file to populate
+     */
+    public abstract void addContents(DexFile file);
+
+    /**
+     * Encodes this instance to the given output.
+     *
+     * @param file {@code non-null;} file this instance is part of
+     * @param out {@code non-null;} where to write to
+     * @param lastIndex {@code >= 0;} the previous member index value encoded, or
+     * {@code 0} if this is the first element to encode
+     * @param dumpSeq {@code >= 0;} sequence number of this instance for
+     * annotation purposes
+     * @return {@code >= 0;} the member index value that was encoded
+     */
+    public abstract int encode(DexFile file, AnnotatedOutput out,
+            int lastIndex, int dumpSeq);
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedMethod.java b/dx/src/com/android/dx/dex/file/EncodedMethod.java
new file mode 100644
index 0000000..1b0770f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedMethod.java
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+import java.io.PrintWriter;
+
+/**
+ * Class that representats a method of a class.
+ */
+public final class EncodedMethod extends EncodedMember
+        implements Comparable<EncodedMethod> {
+    /** {@code non-null;} constant for the method */
+    private final CstMethodRef method;
+
+    /**
+     * {@code null-ok;} code for the method, if the method is neither
+     * {@code abstract} nor {@code native}
+     */
+    private final CodeItem code;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method {@code non-null;} constant for the method
+     * @param accessFlags access flags
+     * @param code {@code null-ok;} code for the method, if it is neither
+     * {@code abstract} nor {@code native}
+     * @param throwsList {@code non-null;} list of possibly-thrown exceptions,
+     * just used in generating debugging output (listings)
+     */
+    public EncodedMethod(CstMethodRef method, int accessFlags,
+            DalvCode code, TypeList throwsList) {
+        super(accessFlags);
+
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        this.method = method;
+
+        if (code == null) {
+            this.code = null;
+        } else {
+            boolean isStatic = (accessFlags & AccessFlags.ACC_STATIC) != 0;
+            this.code = new CodeItem(method, code, isStatic, throwsList);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof EncodedMethod)) {
+            return false;
+        }
+
+        return compareTo((EncodedMethod) other) == 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p><b>Note:</b> This compares the method constants only,
+     * ignoring any associated code, because it should never be the
+     * case that two different items with the same method constant
+     * ever appear in the same list (or same file, even).</p>
+     */
+    public int compareTo(EncodedMethod other) {
+        return method.compareTo(other.method);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append('{');
+        sb.append(Hex.u2(getAccessFlags()));
+        sb.append(' ');
+        sb.append(method);
+
+        if (code != null) {
+            sb.append(' ');
+            sb.append(code);
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        MethodIdsSection methodIds = file.getMethodIds();
+        MixedItemSection wordData = file.getWordData();
+
+        methodIds.intern(method);
+
+        if (code != null) {
+            wordData.add(code);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public final String toHuman() {
+        return method.toHuman();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final CstUtf8 getName() {
+        return method.getNat().getName();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void debugPrint(PrintWriter out, boolean verbose) {
+        if (code == null) {
+            out.println(getRef().toHuman() + ": abstract or native");
+        } else {
+            code.debugPrint(out, "  ", verbose);
+        }
+    }
+
+    /**
+     * Gets the constant for the method.
+     *
+     * @return {@code non-null;} the constant
+     */
+    public final CstMethodRef getRef() {
+        return method;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int encode(DexFile file, AnnotatedOutput out,
+            int lastIndex, int dumpSeq) {
+        int methodIdx = file.getMethodIds().indexOf(method);
+        int diff = methodIdx - lastIndex;
+        int accessFlags = getAccessFlags();
+        int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code);
+        boolean hasCode = (codeOff != 0);
+        boolean shouldHaveCode = (accessFlags &
+                (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0;
+
+        /*
+         * Verify that code appears if and only if a method is
+         * declared to have it.
+         */
+        if (hasCode != shouldHaveCode) {
+            throw new UnsupportedOperationException(
+                    "code vs. access_flags mismatch");
+        }
+
+        if (out.annotates()) {
+            out.annotate(0, String.format("  [%x] %s", dumpSeq,
+                            method.toHuman()));
+            out.annotate(Leb128Utils.unsignedLeb128Size(diff),
+                    "    method_idx:   " + Hex.u4(methodIdx));
+            out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
+                    "    access_flags: " +
+                    AccessFlags.methodString(accessFlags));
+            out.annotate(Leb128Utils.unsignedLeb128Size(codeOff),
+                    "    code_off:     " + Hex.u4(codeOff));
+        }
+
+        out.writeUnsignedLeb128(diff);
+        out.writeUnsignedLeb128(accessFlags);
+        out.writeUnsignedLeb128(codeOff);
+
+        return methodIdx;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java b/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java
new file mode 100644
index 0000000..f363d41
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Association of a field and its annotations.
+ */
+public final class FieldAnnotationStruct
+        implements ToHuman, Comparable<FieldAnnotationStruct> {
+    /** {@code non-null;} the field in question */
+    private final CstFieldRef field;
+
+    /** {@code non-null;} the associated annotations */
+    private AnnotationSetItem annotations;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param field {@code non-null;} the field in question
+     * @param annotations {@code non-null;} the associated annotations
+     */
+    public FieldAnnotationStruct(CstFieldRef field,
+            AnnotationSetItem annotations) {
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        if (annotations == null) {
+            throw new NullPointerException("annotations == null");
+        }
+
+        this.field = field;
+        this.annotations = annotations;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return field.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof FieldAnnotationStruct)) {
+            return false;
+        }
+
+        return field.equals(((FieldAnnotationStruct) other).field);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(FieldAnnotationStruct other) {
+        return field.compareTo(other.field);
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        FieldIdsSection fieldIds = file.getFieldIds();
+        MixedItemSection wordData = file.getWordData();
+
+        fieldIds.intern(field);
+        annotations = wordData.intern(annotations);
+    }
+
+    /** {@inheritDoc} */
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int fieldIdx = file.getFieldIds().indexOf(field);
+        int annotationsOff = annotations.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(0, "    " + field.toHuman());
+            out.annotate(4, "      field_idx:       " + Hex.u4(fieldIdx));
+            out.annotate(4, "      annotations_off: " +
+                    Hex.u4(annotationsOff));
+        }
+
+        out.writeInt(fieldIdx);
+        out.writeInt(annotationsOff);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return field.toHuman() + ": " + annotations;
+    }
+
+    /**
+     * Gets the field this item is for.
+     *
+     * @return {@code non-null;} the field
+     */
+    public CstFieldRef getField() {
+        return field;
+    }
+
+    /**
+     * Gets the associated annotations.
+     *
+     * @return {@code non-null;} the annotations
+     */
+    public Annotations getAnnotations() {
+        return annotations.getAnnotations();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldIdItem.java b/dx/src/com/android/dx/dex/file/FieldIdItem.java
new file mode 100644
index 0000000..ecb1d3d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldIdItem.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstFieldRef;
+
+/**
+ * Representation of a field reference inside a Dalvik file.
+ */
+public final class FieldIdItem extends MemberIdItem {
+    /**
+     * Constructs an instance.
+     *
+     * @param field {@code non-null;} the constant for the field
+     */
+    public FieldIdItem(CstFieldRef field) {
+        super(field);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_FIELD_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        super.addContents(file);
+
+        TypeIdsSection typeIds = file.getTypeIds();
+        typeIds.intern(getFieldRef().getType());
+    }
+
+    /**
+     * Gets the field constant.
+     *
+     * @return {@code non-null;} the constant
+     */
+    public CstFieldRef getFieldRef() {
+        return (CstFieldRef) getRef();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int getTypoidIdx(DexFile file) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        return typeIds.indexOf(getFieldRef().getType());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String getTypoidName() {
+        return "type_idx";
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldIdsSection.java b/dx/src/com/android/dx/dex/file/FieldIdsSection.java
new file mode 100644
index 0000000..c320731
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldIdsSection.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Field refs list section of a {@code .dex} file.
+ */
+public final class FieldIdsSection extends MemberIdsSection {
+    /**
+     * {@code non-null;} map from field constants to {@link
+     * FieldIdItem} instances
+     */
+    private final TreeMap<CstFieldRef, FieldIdItem> fieldIds;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param file {@code non-null;} file that this instance is part of
+     */
+    public FieldIdsSection(DexFile file) {
+        super("field_ids", file);
+
+        fieldIds = new TreeMap<CstFieldRef, FieldIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return fieldIds.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        IndexedItem result = fieldIds.get((CstFieldRef) cst);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     *
+     * @param out {@code non-null;} where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = fieldIds.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "field_ids_size:  " + Hex.u4(sz));
+            out.annotate(4, "field_ids_off:   " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param field {@code non-null;} the reference to intern
+     * @return {@code non-null;} the interned reference
+     */
+    public FieldIdItem intern(CstFieldRef field) {
+        if (field == null) {
+            throw new NullPointerException("field == null");
+        }
+
+        throwIfPrepared();
+
+        FieldIdItem result = fieldIds.get(field);
+
+        if (result == null) {
+            result = new FieldIdItem(field);
+            fieldIds.put(field, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the index of the given reference, which must have been added
+     * to this instance.
+     *
+     * @param ref {@code non-null;} the reference to look up
+     * @return {@code >= 0;} the reference's index
+     */
+    public int indexOf(CstFieldRef ref) {
+        if (ref == null) {
+            throw new NullPointerException("ref == null");
+        }
+
+        throwIfNotPrepared();
+
+        FieldIdItem item = fieldIds.get(ref);
+
+        if (item == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return item.getIndex();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/HeaderItem.java b/dx/src/com/android/dx/dex/file/HeaderItem.java
new file mode 100644
index 0000000..f95ff44
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/HeaderItem.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * File header section of a {@code .dex} file.
+ */
+public final class HeaderItem extends IndexedItem {
+    /**
+     * {@code non-null;} the file format magic number, represented as the
+     * low-order bytes of a string
+     */
+    private static final String MAGIC = "dex\n035\0";
+
+    /** size of this section, in bytes */
+    private static final int HEADER_SIZE = 0x70;
+
+    /** the endianness tag */
+    private static final int ENDIAN_TAG = 0x12345678;
+
+    /**
+     * Constructs an instance.
+     */
+    public HeaderItem() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_HEADER_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return HEADER_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        // Nothing to do here.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int mapOff = file.getMap().getFileOffset();
+        Section firstDataSection = file.getFirstDataSection();
+        Section lastDataSection = file.getLastDataSection();
+        int dataOff = firstDataSection.getFileOffset();
+        int dataSize = lastDataSection.getFileOffset() +
+            lastDataSection.writeSize() - dataOff;
+
+        if (out.annotates()) {
+            out.annotate(8, "magic: " + new CstUtf8(MAGIC).toQuoted());
+            out.annotate(4, "checksum");
+            out.annotate(20, "signature");
+            out.annotate(4, "file_size:       " +
+                         Hex.u4(file.getFileSize()));
+            out.annotate(4, "header_size:     " + Hex.u4(HEADER_SIZE));
+            out.annotate(4, "endian_tag:      " + Hex.u4(ENDIAN_TAG));
+            out.annotate(4, "link_size:       0");
+            out.annotate(4, "link_off:        0");
+            out.annotate(4, "map_off:         " + Hex.u4(mapOff));
+        }
+
+        // Write the magic number.
+        for (int i = 0; i < 8; i++) {
+            out.writeByte(MAGIC.charAt(i));
+        }
+
+        // Leave space for the checksum and signature.
+        out.writeZeroes(24);
+
+        out.writeInt(file.getFileSize());
+        out.writeInt(HEADER_SIZE);
+        out.writeInt(ENDIAN_TAG);
+
+        /*
+         * Write zeroes for the link size and data, as the output
+         * isn't a staticly linked file.
+         */
+        out.writeZeroes(8);
+
+        out.writeInt(mapOff);
+
+        // Write out each section's respective header part.
+        file.getStringIds().writeHeaderPart(out);
+        file.getTypeIds().writeHeaderPart(out);
+        file.getProtoIds().writeHeaderPart(out);
+        file.getFieldIds().writeHeaderPart(out);
+        file.getMethodIds().writeHeaderPart(out);
+        file.getClassDefs().writeHeaderPart(out);
+
+        if (out.annotates()) {
+            out.annotate(4, "data_size:       " + Hex.u4(dataSize));
+            out.annotate(4, "data_off:        " + Hex.u4(dataOff));
+        }
+
+        out.writeInt(dataSize);
+        out.writeInt(dataOff);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/HeaderSection.java b/dx/src/com/android/dx/dex/file/HeaderSection.java
new file mode 100644
index 0000000..21da488
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/HeaderSection.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * File header section of a {@code .dex} file.
+ */
+public final class HeaderSection extends UniformItemSection {
+    /** {@code non-null;} the list of the one item in the section */
+    private final List<HeaderItem> list;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param file {@code non-null;} file that this instance is part of
+     */
+    public HeaderSection(DexFile file) {
+        super(null, file, 4);
+
+        HeaderItem item = new HeaderItem();
+        item.setIndex(0);
+
+        this.list = Collections.singletonList(item);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return list;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        // Nothing to do here.
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/IdItem.java b/dx/src/com/android/dx/dex/file/IdItem.java
new file mode 100644
index 0000000..1bd2b5f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/IdItem.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Representation of a reference to an item inside a Dalvik file.
+ */
+public abstract class IdItem extends IndexedItem {
+    /**
+     * {@code non-null;} the type constant for the defining class of
+     * the reference
+     */
+    private final CstType type;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param type {@code non-null;} the type constant for the defining
+     * class of the reference
+     */
+    public IdItem(CstType type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        this.type = type;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        typeIds.intern(type);
+    }
+
+    /**
+     * Gets the type constant for the defining class of the
+     * reference.
+     *
+     * @return {@code non-null;} the type constant
+     */
+    public final CstType getDefiningClass() {
+        return type;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/IndexedItem.java b/dx/src/com/android/dx/dex/file/IndexedItem.java
new file mode 100644
index 0000000..9ba4783
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/IndexedItem.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * An item in a Dalvik file which is referenced by index.
+ */
+public abstract class IndexedItem extends Item {
+    /** {@code >= -1;} assigned index of the item, or {@code -1} if not
+     * yet assigned */
+    private int index;
+
+    /**
+     * Constructs an instance. The index is initially unassigned.
+     */
+    public IndexedItem() {
+        index = -1;
+    }
+
+    /**
+     * Gets whether or not this instance has been assigned an index.
+     *
+     * @return {@code true} iff this instance has been assigned an index
+     */
+    public final boolean hasIndex() {
+        return (index >= 0);
+    }
+
+    /**
+     * Gets the item index.
+     *
+     * @return {@code >= 0;} the index
+     * @throws RuntimeException thrown if the item index is not yet assigned
+     */
+    public final int getIndex() {
+        if (index < 0) {
+            throw new RuntimeException("index not yet set");
+        }
+
+        return index;
+    }
+
+    /**
+     * Sets the item index. This method may only ever be called once
+     * per instance, and this will throw a {@code RuntimeException} if
+     * called a second (or subsequent) time.
+     *
+     * @param index {@code >= 0;} the item index
+     */
+    public final void setIndex(int index) {
+        if (this.index != -1) {
+            throw new RuntimeException("index already set");
+        }
+
+        this.index = index;
+    }
+
+    /**
+     * Gets the index of this item as a string, suitable for including in
+     * annotations.
+     *
+     * @return {@code non-null;} the index string
+     */
+    public final String indexString() {
+        return '[' + Integer.toHexString(index) + ']';
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/Item.java b/dx/src/com/android/dx/dex/file/Item.java
new file mode 100644
index 0000000..cf2b380
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Item.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Base class for any structurally-significant and (potentially)
+ * repeated piece of a Dalvik file.
+ */
+public abstract class Item {
+    /**
+     * Constructs an instance.
+     */
+    public Item() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Returns the item type for this instance.
+     *
+     * @return {@code non-null;} the item type
+     */
+    public abstract ItemType itemType();
+
+    /**
+     * Returns the human name for the particular type of item this
+     * instance is.
+     *
+     * @return {@code non-null;} the name
+     */
+    public final String typeName() {
+        return itemType().toHuman();
+    }
+
+    /**
+     * Gets the size of this instance when written, in bytes.
+     *
+     * @return {@code >= 0;} the write size
+     */
+    public abstract int writeSize();
+
+    /**
+     * Populates a {@link DexFile} with items from within this instance.
+     * This will <i>not</i> add an item to the file for this instance itself
+     * (which should have been done by whatever refers to this instance).
+     *
+     * <p><b>Note:</b> Subclasses must override this to do something
+     * appropriate.</p>
+     *
+     * @param file {@code non-null;} the file to populate
+     */
+    public abstract void addContents(DexFile file);
+
+    /**
+     * Writes the representation of this instance to the given data section,
+     * using the given {@link DexFile} to look things up as needed.
+     * If this instance keeps track of its offset, then this method will
+     * note the written offset and will also throw an exception if this
+     * instance has already been written.
+     *
+     * @param file {@code non-null;} the file to use for reference
+     * @param out {@code non-null;} where to write to
+     */
+    public abstract void writeTo(DexFile file, AnnotatedOutput out);
+}
diff --git a/dx/src/com/android/dx/dex/file/ItemType.java b/dx/src/com/android/dx/dex/file/ItemType.java
new file mode 100644
index 0000000..2fe97ab
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ItemType.java
@@ -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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Enumeration of all the top-level item types.
+ */
+public enum ItemType implements ToHuman {
+    TYPE_HEADER_ITEM(               0x0000, "header_item"),
+    TYPE_STRING_ID_ITEM(            0x0001, "string_id_item"),
+    TYPE_TYPE_ID_ITEM(              0x0002, "type_id_item"),
+    TYPE_PROTO_ID_ITEM(             0x0003, "proto_id_item"),
+    TYPE_FIELD_ID_ITEM(             0x0004, "field_id_item"),
+    TYPE_METHOD_ID_ITEM(            0x0005, "method_id_item"),
+    TYPE_CLASS_DEF_ITEM(            0x0006, "class_def_item"),
+    TYPE_MAP_LIST(                  0x1000, "map_list"),
+    TYPE_TYPE_LIST(                 0x1001, "type_list"),
+    TYPE_ANNOTATION_SET_REF_LIST(   0x1002, "annotation_set_ref_list"),
+    TYPE_ANNOTATION_SET_ITEM(       0x1003, "annotation_set_item"),
+    TYPE_CLASS_DATA_ITEM(           0x2000, "class_data_item"),
+    TYPE_CODE_ITEM(                 0x2001, "code_item"),
+    TYPE_STRING_DATA_ITEM(          0x2002, "string_data_item"),
+    TYPE_DEBUG_INFO_ITEM(           0x2003, "debug_info_item"),
+    TYPE_ANNOTATION_ITEM(           0x2004, "annotation_item"),
+    TYPE_ENCODED_ARRAY_ITEM(        0x2005, "encoded_array_item"),
+    TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, "annotations_directory_item"),
+    TYPE_MAP_ITEM(                  -1,     "map_item"),
+    TYPE_TYPE_ITEM(                 -1,     "type_item"),
+    TYPE_EXCEPTION_HANDLER_ITEM(    -1,     "exception_handler_item"),
+    TYPE_ANNOTATION_SET_REF_ITEM(   -1,     "annotation_set_ref_item");
+
+    /** value when represented in a {@link MapItem} */
+    private final int mapValue;
+
+    /** {@code non-null;} name of the type */
+    private final String typeName;
+
+    /** {@code non-null;} the short human name */
+    private final String humanName;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param mapValue value when represented in a {@link MapItem}
+     * @param typeName {@code non-null;} name of the type
+     */
+    private ItemType(int mapValue, String typeName) {
+        this.mapValue = mapValue;
+        this.typeName = typeName;
+
+        // Make the human name.
+        String human = typeName;
+        if (human.endsWith("_item")) {
+            human = human.substring(0, human.length() - 5);
+        }
+        this.humanName = human.replace('_', ' ');
+    }
+
+    /**
+     * Gets the map value.
+     *
+     * @return the map value
+     */
+    public int getMapValue() {
+        return mapValue;
+    }
+
+    /**
+     * Gets the type name.
+     *
+     * @return {@code non-null;} the type name
+     */
+    public String getTypeName() {
+        return typeName;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return humanName;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MapItem.java b/dx/src/com/android/dx/dex/file/MapItem.java
new file mode 100644
index 0000000..d78dc91
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MapItem.java
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+
+/**
+ * Class that represents a map item.
+ */
+public final class MapItem extends OffsettedItem {
+    /** file alignment of this class, in bytes */
+    private static final int ALIGNMENT = 4;
+
+    /** write size of this class, in bytes: three {@code uint}s */
+    private static final int WRITE_SIZE = (4 * 3);
+
+    /** {@code non-null;} item type this instance covers */
+    private final ItemType type;
+
+    /** {@code non-null;} section this instance covers */
+    private final Section section;
+
+    /**
+     * {@code null-ok;} first item covered or {@code null} if this is
+     * a self-reference
+     */
+    private final Item firstItem;
+
+    /**
+     * {@code null-ok;} last item covered or {@code null} if this is
+     * a self-reference
+     */
+    private final Item lastItem;
+
+    /**
+     * {@code > 0;} count of items covered; {@code 1} if this
+     * is a self-reference
+     */
+    private final int itemCount;
+
+    /**
+     * Constructs a list item with instances of this class representing
+     * the contents of the given array of sections, adding it to the
+     * given map section.
+     *
+     * @param sections {@code non-null;} the sections
+     * @param mapSection {@code non-null;} the section that the resulting map
+     * should be added to; it should be empty on entry to this method
+     */
+    public static void addMap(Section[] sections,
+            MixedItemSection mapSection) {
+        if (sections == null) {
+            throw new NullPointerException("sections == null");
+        }
+
+        if (mapSection.items().size() != 0) {
+            throw new IllegalArgumentException(
+                    "mapSection.items().size() != 0");
+        }
+
+        ArrayList<MapItem> items = new ArrayList<MapItem>(50);
+
+        for (Section section : sections) {
+            ItemType currentType = null;
+            Item firstItem = null;
+            Item lastItem = null;
+            int count = 0;
+
+            for (Item item : section.items()) {
+                ItemType type = item.itemType();
+                if (type != currentType) {
+                    if (count != 0) {
+                        items.add(new MapItem(currentType, section,
+                                        firstItem, lastItem, count));
+                    }
+                    currentType = type;
+                    firstItem = item;
+                    count = 0;
+                }
+                lastItem = item;
+                count++;
+            }
+
+            if (count != 0) {
+                // Add a MapItem for the final items in the section.
+                items.add(new MapItem(currentType, section,
+                                firstItem, lastItem, count));
+            } else if (section == mapSection) {
+                // Add a MapItem for the self-referential section.
+                items.add(new MapItem(mapSection));
+            }
+        }
+
+        mapSection.add(
+                new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items));
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param type {@code non-null;} item type this instance covers
+     * @param section {@code non-null;} section this instance covers
+     * @param firstItem {@code non-null;} first item covered
+     * @param lastItem {@code non-null;} last item covered
+     * @param itemCount {@code > 0;} count of items covered
+     */
+    private MapItem(ItemType type, Section section, Item firstItem,
+            Item lastItem, int itemCount) {
+        super(ALIGNMENT, WRITE_SIZE);
+
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        if (section == null) {
+            throw new NullPointerException("section == null");
+        }
+
+        if (firstItem == null) {
+            throw new NullPointerException("firstItem == null");
+        }
+
+        if (lastItem == null) {
+            throw new NullPointerException("lastItem == null");
+        }
+
+        if (itemCount <= 0) {
+            throw new IllegalArgumentException("itemCount <= 0");
+        }
+
+        this.type = type;
+        this.section = section;
+        this.firstItem = firstItem;
+        this.lastItem = lastItem;
+        this.itemCount = itemCount;
+    }
+
+    /**
+     * Constructs a self-referential instance. This instance is meant to
+     * represent the section containing the {@code map_list}.
+     *
+     * @param section {@code non-null;} section this instance covers
+     */
+    private MapItem(Section section) {
+        super(ALIGNMENT, WRITE_SIZE);
+
+        if (section == null) {
+            throw new NullPointerException("section == null");
+        }
+
+        this.type = ItemType.TYPE_MAP_LIST;
+        this.section = section;
+        this.firstItem = null;
+        this.lastItem = null;
+        this.itemCount = 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_MAP_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append('{');
+        sb.append(section.toString());
+        sb.append(' ');
+        sb.append(type.toHuman());
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        // We have nothing to add.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String toHuman() {
+        return toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        int value = type.getMapValue();
+        int offset;
+
+        if (firstItem == null) {
+            offset = section.getFileOffset();
+        } else {
+            offset = section.getAbsoluteItemOffset(firstItem);
+        }
+
+        if (out.annotates()) {
+            out.annotate(0, offsetString() + ' ' + type.getTypeName() +
+                    " map");
+            out.annotate(2, "  type:   " + Hex.u2(value) + " // " +
+                    type.toString());
+            out.annotate(2, "  unused: 0");
+            out.annotate(4, "  size:   " + Hex.u4(itemCount));
+            out.annotate(4, "  offset: " + Hex.u4(offset));
+        }
+
+        out.writeShort(value);
+        out.writeShort(0); // unused
+        out.writeInt(itemCount);
+        out.writeInt(offset);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MemberIdItem.java b/dx/src/com/android/dx/dex/file/MemberIdItem.java
new file mode 100644
index 0000000..d3a61d4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MemberIdItem.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a member (field or method) reference inside a
+ * Dalvik file.
+ */
+public abstract class MemberIdItem extends IdItem {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 8;
+
+    /** {@code non-null;} the constant for the member */
+    private final CstMemberRef cst;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cst {@code non-null;} the constant for the member
+     */
+    public MemberIdItem(CstMemberRef cst) {
+        super(cst.getDefiningClass());
+
+        this.cst = cst;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        super.addContents(file);
+
+        StringIdsSection stringIds = file.getStringIds();
+        stringIds.intern(getRef().getNat().getName());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void writeTo(DexFile file, AnnotatedOutput out) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        StringIdsSection stringIds = file.getStringIds();
+        CstNat nat = cst.getNat();
+        int classIdx = typeIds.indexOf(getDefiningClass());
+        int nameIdx = stringIds.indexOf(nat.getName());
+        int typoidIdx = getTypoidIdx(file);
+
+        if (out.annotates()) {
+            out.annotate(0, indexString() + ' ' + cst.toHuman());
+            out.annotate(2, "  class_idx: " + Hex.u2(classIdx));
+            out.annotate(2, String.format("  %-10s %s", getTypoidName() + ':',
+                            Hex.u2(typoidIdx)));
+            out.annotate(4, "  name_idx:  " + Hex.u4(nameIdx));
+        }
+
+        out.writeShort(classIdx);
+        out.writeShort(typoidIdx);
+        out.writeInt(nameIdx);
+    }
+
+    /**
+     * Returns the index of the type-like thing associated with
+     * this item, in order that it may be written out. Subclasses must
+     * override this to get whatever it is they need to store.
+     *
+     * @param file {@code non-null;} the file being written
+     * @return the index in question
+     */
+    protected abstract int getTypoidIdx(DexFile file);
+
+    /**
+     * Returns the field name of the type-like thing associated with
+     * this item, for listing-generating purposes. Subclasses must override
+     * this.
+     *
+     * @return {@code non-null;} the name in question
+     */
+    protected abstract String getTypoidName();
+
+    /**
+     * Gets the member constant.
+     *
+     * @return {@code non-null;} the constant
+     */
+    public final CstMemberRef getRef() {
+        return cst;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MemberIdsSection.java b/dx/src/com/android/dx/dex/file/MemberIdsSection.java
new file mode 100644
index 0000000..ef0d8cd
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MemberIdsSection.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * Member (field or method) refs list section of a {@code .dex} file.
+ */
+public abstract class MemberIdsSection extends UniformItemSection {
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param name {@code null-ok;} the name of this instance, for annotation
+     * purposes
+     * @param file {@code non-null;} file that this instance is part of
+     */
+    public MemberIdsSection(String name, DexFile file) {
+        super(name, file, 4);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int idx = 0;
+
+        for (Object i : items()) {
+            ((MemberIdItem) i).setIndex(idx);
+            idx++;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java b/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java
new file mode 100644
index 0000000..38f7ce4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Association of a method and its annotations.
+ */
+public final class MethodAnnotationStruct
+        implements ToHuman, Comparable<MethodAnnotationStruct> {
+    /** {@code non-null;} the method in question */
+    private final CstMethodRef method;
+
+    /** {@code non-null;} the associated annotations */
+    private AnnotationSetItem annotations;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method {@code non-null;} the method in question
+     * @param annotations {@code non-null;} the associated annotations
+     */
+    public MethodAnnotationStruct(CstMethodRef method,
+            AnnotationSetItem annotations) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        if (annotations == null) {
+            throw new NullPointerException("annotations == null");
+        }
+
+        this.method = method;
+        this.annotations = annotations;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return method.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof MethodAnnotationStruct)) {
+            return false;
+        }
+
+        return method.equals(((MethodAnnotationStruct) other).method);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(MethodAnnotationStruct other) {
+        return method.compareTo(other.method);
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MethodIdsSection methodIds = file.getMethodIds();
+        MixedItemSection wordData = file.getWordData();
+
+        methodIds.intern(method);
+        annotations = wordData.intern(annotations);
+    }
+
+    /** {@inheritDoc} */
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int methodIdx = file.getMethodIds().indexOf(method);
+        int annotationsOff = annotations.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(0, "    " + method.toHuman());
+            out.annotate(4, "      method_idx:      " + Hex.u4(methodIdx));
+            out.annotate(4, "      annotations_off: " +
+                    Hex.u4(annotationsOff));
+        }
+
+        out.writeInt(methodIdx);
+        out.writeInt(annotationsOff);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return method.toHuman() + ": " + annotations;
+    }
+
+    /**
+     * Gets the method this item is for.
+     *
+     * @return {@code non-null;} the method
+     */
+    public CstMethodRef getMethod() {
+        return method;
+    }
+
+    /**
+     * Gets the associated annotations.
+     *
+     * @return {@code non-null;} the annotations
+     */
+    public Annotations getAnnotations() {
+        return annotations.getAnnotations();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodIdItem.java b/dx/src/com/android/dx/dex/file/MethodIdItem.java
new file mode 100644
index 0000000..f2ff4f9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodIdItem.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstBaseMethodRef;
+
+/**
+ * Representation of a method reference inside a Dalvik file.
+ */
+public final class MethodIdItem extends MemberIdItem {
+    /**
+     * Constructs an instance.
+     *
+     * @param method {@code non-null;} the constant for the method
+     */
+    public MethodIdItem(CstBaseMethodRef method) {
+        super(method);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_METHOD_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        super.addContents(file);
+
+        ProtoIdsSection protoIds = file.getProtoIds();
+        protoIds.intern(getMethodRef().getPrototype());
+    }
+
+    /**
+     * Gets the method constant.
+     *
+     * @return {@code non-null;} the constant
+     */
+    public CstBaseMethodRef getMethodRef() {
+        return (CstBaseMethodRef) getRef();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int getTypoidIdx(DexFile file) {
+        ProtoIdsSection protoIds = file.getProtoIds();
+        return protoIds.indexOf(getMethodRef().getPrototype());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String getTypoidName() {
+        return "proto_idx";
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodIdsSection.java b/dx/src/com/android/dx/dex/file/MethodIdsSection.java
new file mode 100644
index 0000000..fa0cd3c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodIdsSection.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Method refs list section of a {@code .dex} file.
+ */
+public final class MethodIdsSection extends MemberIdsSection {
+    /**
+     * {@code non-null;} map from method constants to {@link
+     * MethodIdItem} instances
+     */
+    private final TreeMap<CstBaseMethodRef, MethodIdItem> methodIds;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param file {@code non-null;} file that this instance is part of
+     */
+    public MethodIdsSection(DexFile file) {
+        super("method_ids", file);
+
+        methodIds = new TreeMap<CstBaseMethodRef, MethodIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return methodIds.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        IndexedItem result = methodIds.get((CstBaseMethodRef) cst);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     *
+     * @param out {@code non-null;} where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = methodIds.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "method_ids_size: " + Hex.u4(sz));
+            out.annotate(4, "method_ids_off:  " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param method {@code non-null;} the reference to intern
+     * @return {@code non-null;} the interned reference
+     */
+    public MethodIdItem intern(CstBaseMethodRef method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        throwIfPrepared();
+
+        MethodIdItem result = methodIds.get(method);
+
+        if (result == null) {
+            result = new MethodIdItem(method);
+            methodIds.put(method, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the index of the given reference, which must have been added
+     * to this instance.
+     *
+     * @param ref {@code non-null;} the reference to look up
+     * @return {@code >= 0;} the reference's index
+     */
+    public int indexOf(CstBaseMethodRef ref) {
+        if (ref == null) {
+            throw new NullPointerException("ref == null");
+        }
+
+        throwIfNotPrepared();
+
+        MethodIdItem item = methodIds.get(ref);
+
+        if (item == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return item.getIndex();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/MixedItemSection.java b/dx/src/com/android/dx/dex/file/MixedItemSection.java
new file mode 100644
index 0000000..b885306
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MixedItemSection.java
@@ -0,0 +1,362 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.TreeMap;
+
+/**
+ * A section of a {@code .dex} file which consists of a sequence of
+ * {@link OffsettedItem} objects, which may each be of a different concrete
+ * class and/or size.
+ *
+ * <b>Note:</b> It is invalid for an item in an instance of this class to
+ * have a larger alignment requirement than the alignment of this instance.
+ */
+public final class MixedItemSection extends Section {
+    static enum SortType {
+        /** no sorting */
+        NONE,
+
+        /** sort by type only */
+        TYPE,
+
+        /** sort in class-major order, with instances sorted per-class */
+        INSTANCE;
+    };
+
+    /** {@code non-null;} sorter which sorts instances by type */
+    private static final Comparator<OffsettedItem> TYPE_SORTER =
+        new Comparator<OffsettedItem>() {
+        public int compare(OffsettedItem item1, OffsettedItem item2) {
+            ItemType type1 = item1.itemType();
+            ItemType type2 = item2.itemType();
+            return type1.compareTo(type2);
+        }
+    };
+
+    /** {@code non-null;} the items in this part */
+    private final ArrayList<OffsettedItem> items;
+
+    /** {@code non-null;} items that have been explicitly interned */
+    private final HashMap<OffsettedItem, OffsettedItem> interns;
+
+    /** {@code non-null;} how to sort the items */
+    private final SortType sort;
+
+    /**
+     * {@code >= -1;} the current size of this part, in bytes, or {@code -1}
+     * if not yet calculated
+     */
+    private int writeSize;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param name {@code null-ok;} the name of this instance, for annotation
+     * purposes
+     * @param file {@code non-null;} file that this instance is part of
+     * @param alignment {@code > 0;} alignment requirement for the final output;
+     * must be a power of 2
+     * @param sort how the items should be sorted in the final output
+     */
+    public MixedItemSection(String name, DexFile file, int alignment,
+            SortType sort) {
+        super(name, file, alignment);
+
+        this.items = new ArrayList<OffsettedItem>(100);
+        this.interns = new HashMap<OffsettedItem, OffsettedItem>(100);
+        this.sort = sort;
+        this.writeSize = -1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return items;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        throwIfNotPrepared();
+        return writeSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getAbsoluteItemOffset(Item item) {
+        OffsettedItem oi = (OffsettedItem) item;
+        return oi.getAbsoluteOffset();
+    }
+
+    /**
+     * Gets the size of this instance, in items.
+     *
+     * @return {@code >= 0;} the size
+     */
+    public int size() {
+        return items.size();
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     *
+     * @param out {@code non-null;} where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        if (writeSize == -1) {
+            throw new RuntimeException("write size not yet set");
+        }
+
+        int sz = writeSize;
+        int offset = (sz == 0) ? 0 : getFileOffset();
+        String name = getName();
+
+        if (name == null) {
+            name = "<unnamed>";
+        }
+
+        int spaceCount = 15 - name.length();
+        char[] spaceArr = new char[spaceCount];
+        Arrays.fill(spaceArr, ' ');
+        String spaces = new String(spaceArr);
+
+        if (out.annotates()) {
+            out.annotate(4, name + "_size:" + spaces + Hex.u4(sz));
+            out.annotate(4, name + "_off: " + spaces + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Adds an item to this instance. This will in turn tell the given item
+     * that it has been added to this instance. It is invalid to add the
+     * same item to more than one instance, nor to add the same items
+     * multiple times to a single instance.
+     *
+     * @param item {@code non-null;} the item to add
+     */
+    public void add(OffsettedItem item) {
+        throwIfPrepared();
+
+        try {
+            if (item.getAlignment() > getAlignment()) {
+                throw new IllegalArgumentException(
+                        "incompatible item alignment");
+            }
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("item == null");
+        }
+
+        items.add(item);
+    }
+
+    /**
+     * Interns an item in this instance, returning the interned instance
+     * (which may not be the one passed in). This will add the item if no
+     * equal item has been added.
+     *
+     * @param item {@code non-null;} the item to intern
+     * @return {@code non-null;} the equivalent interned instance
+     */
+    public <T extends OffsettedItem> T intern(T item) {
+        throwIfPrepared();
+
+        OffsettedItem result = interns.get(item);
+
+        if (result != null) {
+            return (T) result;
+        }
+
+        add(item);
+        interns.put(item, item);
+        return item;
+    }
+
+    /**
+     * Gets an item which was previously interned.
+     *
+     * @param item {@code non-null;} the item to look for
+     * @return {@code non-null;} the equivalent already-interned instance
+     */
+    public <T extends OffsettedItem> T get(T item) {
+        throwIfNotPrepared();
+
+        OffsettedItem result = interns.get(item);
+
+        if (result != null) {
+            return (T) result;
+        }
+
+        throw new NoSuchElementException(item.toString());
+    }
+
+    /**
+     * Writes an index of contents of the items in this instance of the
+     * given type. If there are none, this writes nothing. If there are any,
+     * then the index is preceded by the given intro string.
+     *
+     * @param out {@code non-null;} where to write to
+     * @param itemType {@code non-null;} the item type of interest
+     * @param intro {@code non-null;} the introductory string for non-empty indices
+     */
+    public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType,
+            String intro) {
+        throwIfNotPrepared();
+
+        TreeMap<String, OffsettedItem> index =
+            new TreeMap<String, OffsettedItem>();
+
+        for (OffsettedItem item : items) {
+            if (item.itemType() == itemType) {
+                String label = item.toHuman();
+                index.put(label, item);
+            }
+        }
+
+        if (index.size() == 0) {
+            return;
+        }
+
+        out.annotate(0, intro);
+
+        for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) {
+            String label = entry.getKey();
+            OffsettedItem item = entry.getValue();
+            out.annotate(0, item.offsetString() + ' ' + label + '\n');
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void prepare0() {
+        DexFile file = getFile();
+
+        /*
+         * It's okay for new items to be added as a result of an
+         * addContents() call; we just have to deal with the possibility.
+         */
+
+        int i = 0;
+        for (;;) {
+            int sz = items.size();
+            if (i >= sz) {
+                break;
+            }
+
+            for (/*i*/; i < sz; i++) {
+                OffsettedItem one = items.get(i);
+                one.addContents(file);
+            }
+        }
+    }
+
+    /**
+     * Places all the items in this instance at particular offsets. This
+     * will call {@link OffsettedItem#place} on each item. If an item
+     * does not know its write size before the call to {@code place},
+     * it is that call which is responsible for setting the write size.
+     * This method may only be called once per instance; subsequent calls
+     * will throw an exception.
+     */
+    public void placeItems() {
+        throwIfNotPrepared();
+
+        switch (sort) {
+            case INSTANCE: {
+                Collections.sort(items);
+                break;
+            }
+            case TYPE: {
+                Collections.sort(items, TYPE_SORTER);
+                break;
+            }
+        }
+
+        int sz = items.size();
+        int outAt = 0;
+        for (int i = 0; i < sz; i++) {
+            OffsettedItem one = items.get(i);
+            try {
+                int placedAt = one.place(this, outAt);
+
+                if (placedAt < outAt) {
+                    throw new RuntimeException("bogus place() result for " +
+                            one);
+                }
+
+                outAt = placedAt + one.writeSize();
+            } catch (RuntimeException ex) {
+                throw ExceptionWithContext.withContext(ex,
+                        "...while placing " + one);
+            }
+        }
+
+        writeSize = outAt;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(AnnotatedOutput out) {
+        boolean annotates = out.annotates();
+        boolean first = true;
+        DexFile file = getFile();
+        int at = 0;
+
+        for (OffsettedItem one : items) {
+            if (annotates) {
+                if (first) {
+                    first = false;
+                } else {
+                    out.annotate(0, "\n");
+                }
+            }
+
+            int alignMask = one.getAlignment() - 1;
+            int writeAt = (at + alignMask) & ~alignMask;
+
+            if (at != writeAt) {
+                out.writeZeroes(writeAt - at);
+                at = writeAt;
+            }
+
+            one.writeTo(file, out);
+            at += one.writeSize();
+        }
+
+        if (at != writeSize) {
+            throw new RuntimeException("output size mismatch");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/OffsettedItem.java b/dx/src/com/android/dx/dex/file/OffsettedItem.java
new file mode 100644
index 0000000..7721470
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/OffsettedItem.java
@@ -0,0 +1,314 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * An item in a Dalvik file which is referenced by absolute offset.
+ */
+public abstract class OffsettedItem extends Item
+        implements Comparable<OffsettedItem> {
+    /** {@code > 0;} alignment requirement */
+    private final int alignment;
+
+    /** {@code >= -1;} the size of this instance when written, in bytes, or
+     * {@code -1} if not yet known */
+    private int writeSize;
+
+    /**
+     * {@code null-ok;} section the item was added to, or {@code null} if
+     * not yet added
+     */
+    private Section addedTo;
+
+    /**
+     * {@code >= -1;} assigned offset of the item from the start of its section,
+     * or {@code -1} if not yet assigned
+     */
+    private int offset;
+
+    /**
+     * Gets the absolute offset of the given item, returning {@code 0}
+     * if handed {@code null}.
+     *
+     * @param item {@code null-ok;} the item in question
+     * @return {@code >= 0;} the item's absolute offset, or {@code 0}
+     * if {@code item == null}
+     */
+    public static int getAbsoluteOffsetOr0(OffsettedItem item) {
+        if (item == null) {
+            return 0;
+        }
+
+        return item.getAbsoluteOffset();
+    }
+
+    /**
+     * Constructs an instance. The offset is initially unassigned.
+     *
+     * @param alignment {@code > 0;} output alignment requirement; must be a
+     * power of 2
+     * @param writeSize {@code >= -1;} the size of this instance when written,
+     * in bytes, or {@code -1} if not immediately known
+     */
+    public OffsettedItem(int alignment, int writeSize) {
+        Section.validateAlignment(alignment);
+
+        if (writeSize < -1) {
+            throw new IllegalArgumentException("writeSize < -1");
+        }
+
+        this.alignment = alignment;
+        this.writeSize = writeSize;
+        this.addedTo = null;
+        this.offset = -1;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Comparisons for this class are defined to be type-major (if the
+     * types don't match then the objects are not equal), with
+     * {@link #compareTo0} deciding same-type comparisons.
+     */
+    @Override
+    public final boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        OffsettedItem otherItem = (OffsettedItem) other;
+        ItemType thisType = itemType();
+        ItemType otherType = otherItem.itemType();
+
+        if (thisType != otherType) {
+            return false;
+        }
+
+        return (compareTo0(otherItem) == 0);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Comparisons for this class are defined to be class-major (if the
+     * classes don't match then the objects are not equal), with
+     * {@link #compareTo0} deciding same-class comparisons.
+     */
+    public final int compareTo(OffsettedItem other) {
+        if (this == other) {
+            return 0;
+        }
+
+        ItemType thisType = itemType();
+        ItemType otherType = other.itemType();
+
+        if (thisType != otherType) {
+            return thisType.compareTo(otherType);
+        }
+
+        return compareTo0(other);
+    }
+
+    /**
+     * Sets the write size of this item. This may only be called once
+     * per instance, and only if the size was unknown upon instance
+     * creation.
+     *
+     * @param writeSize {@code > 0;} the write size, in bytes
+     */
+    public final void setWriteSize(int writeSize) {
+        if (writeSize < 0) {
+            throw new IllegalArgumentException("writeSize < 0");
+        }
+
+        if (this.writeSize >= 0) {
+            throw new UnsupportedOperationException("writeSize already set");
+        }
+
+        this.writeSize = writeSize;
+    }
+
+    /** {@inheritDoc}
+     *
+     * @throws UnsupportedOperationException thrown if the write size
+     * is not yet known
+     */
+    @Override
+    public final int writeSize() {
+        if (writeSize < 0) {
+            throw new UnsupportedOperationException("writeSize is unknown");
+        }
+
+        return writeSize;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void writeTo(DexFile file, AnnotatedOutput out) {
+        out.alignTo(alignment);
+
+        try {
+            if (writeSize < 0) {
+                throw new UnsupportedOperationException(
+                        "writeSize is unknown");
+            }
+            out.assertCursor(getAbsoluteOffset());
+        } catch (RuntimeException ex) {
+            throw ExceptionWithContext.withContext(ex,
+                    "...while writing " + this);
+        }
+
+        writeTo0(file, out);
+    }
+
+    /**
+     * Gets the relative item offset. The offset is from the start of
+     * the section which the instance was written to.
+     *
+     * @return {@code >= 0;} the offset
+     * @throws RuntimeException thrown if the offset is not yet known
+     */
+    public final int getRelativeOffset() {
+        if (offset < 0) {
+            throw new RuntimeException("offset not yet known");
+        }
+
+        return offset;
+    }
+
+    /**
+     * Gets the absolute item offset. The offset is from the start of
+     * the file which the instance was written to.
+     *
+     * @return {@code >= 0;} the offset
+     * @throws RuntimeException thrown if the offset is not yet known
+     */
+    public final int getAbsoluteOffset() {
+        if (offset < 0) {
+            throw new RuntimeException("offset not yet known");
+        }
+
+        return addedTo.getAbsoluteOffset(offset);
+    }
+
+    /**
+     * Indicates that this item has been added to the given section at
+     * the given offset. It is only valid to call this method once per
+     * instance.
+     *
+     * @param addedTo {@code non-null;} the section this instance has
+     * been added to
+     * @param offset {@code >= 0;} the desired offset from the start of the
+     * section where this instance was placed
+     * @return {@code >= 0;} the offset that this instance should be placed at
+     * in order to meet its alignment constraint
+     */
+    public final int place(Section addedTo, int offset) {
+        if (addedTo == null) {
+            throw new NullPointerException("addedTo == null");
+        }
+
+        if (offset < 0) {
+            throw new IllegalArgumentException("offset < 0");
+        }
+
+        if (this.addedTo != null) {
+            throw new RuntimeException("already written");
+        }
+
+        int mask = alignment - 1;
+        offset = (offset + mask) & ~mask;
+
+        this.addedTo = addedTo;
+        this.offset = offset;
+
+        place0(addedTo, offset);
+
+        return offset;
+    }
+
+    /**
+     * Gets the alignment requirement of this instance. An instance should
+     * only be written when so aligned.
+     *
+     * @return {@code > 0;} the alignment requirement; must be a power of 2
+     */
+    public final int getAlignment() {
+        return alignment;
+    }
+
+    /**
+     * Gets the absolute offset of this item as a string, suitable for
+     * including in annotations.
+     *
+     * @return {@code non-null;} the offset string
+     */
+    public final String offsetString() {
+        return '[' + Integer.toHexString(getAbsoluteOffset()) + ']';
+    }
+
+    /**
+     * Gets a short human-readable string representing this instance.
+     *
+     * @return {@code non-null;} the human form
+     */
+    public abstract String toHuman();
+
+    /**
+     * Compares this instance to another which is guaranteed to be of
+     * the same class. The default implementation of this method is to
+     * throw an exception (unsupported operation). If a particular
+     * class needs to actually sort, then it should override this
+     * method.
+     *
+     * @param other {@code non-null;} instance to compare to
+     * @return {@code -1}, {@code 0}, or {@code 1}, depending
+     * on the sort order of this instance and the other
+     */
+    protected int compareTo0(OffsettedItem other) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /**
+     * Does additional work required when placing an instance. The
+     * default implementation of this method is a no-op. If a
+     * particular class needs to do something special, then it should
+     * override this method. In particular, if this instance did not
+     * know its write size up-front, then this method is responsible
+     * for setting it.
+     *
+     * @param addedTo {@code non-null;} the section this instance has been added to
+     * @param offset {@code >= 0;} the offset from the start of the
+     * section where this instance was placed
+     */
+    protected void place0(Section addedTo, int offset) {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Performs the actual write of the contents of this instance to
+     * the given data section. This is called by {@link #writeTo},
+     * which will have taken care of ensuring alignment.
+     *
+     * @param file {@code non-null;} the file to use for reference
+     * @param out {@code non-null;} where to write to
+     */
+    protected abstract void writeTo0(DexFile file, AnnotatedOutput out);
+}
diff --git a/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java b/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java
new file mode 100644
index 0000000..078c219
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+import java.util.ArrayList;
+
+/**
+ * Association of a method and its parameter annotations.
+ */
+public final class ParameterAnnotationStruct
+        implements ToHuman, Comparable<ParameterAnnotationStruct> {
+    /** {@code non-null;} the method in question */
+    private final CstMethodRef method;
+
+    /** {@code non-null;} the associated annotations list */
+    private final AnnotationsList annotationsList;
+
+    /** {@code non-null;} the associated annotations list, as an item */
+    private final UniformListItem<AnnotationSetRefItem> annotationsItem;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method {@code non-null;} the method in question
+     * @param annotationsList {@code non-null;} the associated annotations list
+     */
+    public ParameterAnnotationStruct(CstMethodRef method,
+            AnnotationsList annotationsList) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        if (annotationsList == null) {
+            throw new NullPointerException("annotationsList == null");
+        }
+
+        this.method = method;
+        this.annotationsList = annotationsList;
+
+        /*
+         * Construct an item for the annotations list. TODO: This
+         * requires way too much copying; fix it.
+         */
+
+        int size = annotationsList.size();
+        ArrayList<AnnotationSetRefItem> arrayList = new
+            ArrayList<AnnotationSetRefItem>(size);
+
+        for (int i = 0; i < size; i++) {
+            Annotations annotations = annotationsList.get(i);
+            AnnotationSetItem item = new AnnotationSetItem(annotations);
+            arrayList.add(new AnnotationSetRefItem(item));
+        }
+
+        this.annotationsItem = new UniformListItem<AnnotationSetRefItem>(
+                ItemType.TYPE_ANNOTATION_SET_REF_LIST, arrayList);
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return method.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof ParameterAnnotationStruct)) {
+            return false;
+        }
+
+        return method.equals(((ParameterAnnotationStruct) other).method);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(ParameterAnnotationStruct other) {
+        return method.compareTo(other.method);
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        MethodIdsSection methodIds = file.getMethodIds();
+        MixedItemSection wordData = file.getWordData();
+
+        methodIds.intern(method);
+        wordData.add(annotationsItem);
+    }
+
+    /** {@inheritDoc} */
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int methodIdx = file.getMethodIds().indexOf(method);
+        int annotationsOff = annotationsItem.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(0, "    " + method.toHuman());
+            out.annotate(4, "      method_idx:      " + Hex.u4(methodIdx));
+            out.annotate(4, "      annotations_off: " +
+                    Hex.u4(annotationsOff));
+        }
+
+        out.writeInt(methodIdx);
+        out.writeInt(annotationsOff);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(method.toHuman());
+        sb.append(": ");
+
+        boolean first = true;
+        for (AnnotationSetRefItem item : annotationsItem.getItems()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(item.toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the method this item is for.
+     *
+     * @return {@code non-null;} the method
+     */
+    public CstMethodRef getMethod() {
+        return method;
+    }
+
+    /**
+     * Gets the associated annotations list.
+     *
+     * @return {@code non-null;} the annotations list
+     */
+    public AnnotationsList getAnnotationsList() {
+        return annotationsList;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ProtoIdItem.java b/dx/src/com/android/dx/dex/file/ProtoIdItem.java
new file mode 100644
index 0000000..31cf8fb
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ProtoIdItem.java
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a method prototype reference inside a Dalvik file.
+ */
+public final class ProtoIdItem extends IndexedItem {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 12;
+
+    /** {@code non-null;} the wrapped prototype */
+    private final Prototype prototype;
+
+    /** {@code non-null;} the short-form of the prototype */
+    private final CstUtf8 shortForm;
+
+    /**
+     * {@code null-ok;} the list of parameter types or {@code null} if this
+     * prototype has no parameters
+     */
+    private TypeListItem parameterTypes;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param prototype {@code non-null;} the constant for the prototype
+     */
+    public ProtoIdItem(Prototype prototype) {
+        if (prototype == null) {
+            throw new NullPointerException("prototype == null");
+        }
+
+        this.prototype = prototype;
+        this.shortForm = makeShortForm(prototype);
+
+        StdTypeList parameters = prototype.getParameterTypes();
+        this.parameterTypes = (parameters.size() == 0) ? null
+            : new TypeListItem(parameters);
+    }
+
+    /**
+     * Creates the short-form of the given prototype.
+     *
+     * @param prototype {@code non-null;} the prototype
+     * @return {@code non-null;} the short form
+     */
+    private static CstUtf8 makeShortForm(Prototype prototype) {
+        StdTypeList parameters = prototype.getParameterTypes();
+        int size = parameters.size();
+        StringBuilder sb = new StringBuilder(size + 1);
+
+        sb.append(shortFormCharFor(prototype.getReturnType()));
+
+        for (int i = 0; i < size; i++) {
+            sb.append(shortFormCharFor(parameters.getType(i)));
+        }
+
+        return new CstUtf8(sb.toString());
+    }
+
+    /**
+     * Gets the short-form character for the given type.
+     *
+     * @param type {@code non-null;} the type
+     * @return the corresponding short-form character
+     */
+    private static char shortFormCharFor(Type type) {
+        char descriptorChar = type.getDescriptor().charAt(0);
+
+        if (descriptorChar == '[') {
+            return 'L';
+        }
+
+        return descriptorChar;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_PROTO_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        StringIdsSection stringIds = file.getStringIds();
+        TypeIdsSection typeIds = file.getTypeIds();
+        MixedItemSection typeLists = file.getTypeLists();
+
+        typeIds.intern(prototype.getReturnType());
+        stringIds.intern(shortForm);
+
+        if (parameterTypes != null) {
+            parameterTypes = typeLists.intern(parameterTypes);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int shortyIdx = file.getStringIds().indexOf(shortForm);
+        int returnIdx = file.getTypeIds().indexOf(prototype.getReturnType());
+        int paramsOff = OffsettedItem.getAbsoluteOffsetOr0(parameterTypes);
+
+        if (out.annotates()) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(prototype.getReturnType().toHuman());
+            sb.append(" proto(");
+
+            StdTypeList params = prototype.getParameterTypes();
+            int size = params.size();
+
+            for (int i = 0; i < size; i++) {
+                if (i != 0) {
+                    sb.append(", ");
+                }
+                sb.append(params.getType(i).toHuman());
+            }
+
+            sb.append(")");
+            out.annotate(0, indexString() + ' ' + sb.toString());
+            out.annotate(4, "  shorty_idx:      " + Hex.u4(shortyIdx) +
+                    " // " + shortForm.toQuoted());
+            out.annotate(4, "  return_type_idx: " + Hex.u4(returnIdx) +
+                    " // " + prototype.getReturnType().toHuman());
+            out.annotate(4, "  parameters_off:  " + Hex.u4(paramsOff));
+        }
+
+        out.writeInt(shortyIdx);
+        out.writeInt(returnIdx);
+        out.writeInt(paramsOff);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ProtoIdsSection.java b/dx/src/com/android/dx/dex/file/ProtoIdsSection.java
new file mode 100644
index 0000000..dc6e8ad
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ProtoIdsSection.java
@@ -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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Proto (method prototype) identifiers list section of a
+ * {@code .dex} file.
+ */
+public final class ProtoIdsSection extends UniformItemSection {
+    /**
+     * {@code non-null;} map from method prototypes to {@link ProtoIdItem} instances
+     */
+    private final TreeMap<Prototype, ProtoIdItem> protoIds;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param file {@code non-null;} file that this instance is part of
+     */
+    public ProtoIdsSection(DexFile file) {
+        super("proto_ids", file, 4);
+
+        protoIds = new TreeMap<Prototype, ProtoIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return protoIds.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     *
+     * @param out {@code non-null;} where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = protoIds.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (sz > 65536) {
+            throw new UnsupportedOperationException("too many proto ids");
+        }
+
+        if (out.annotates()) {
+            out.annotate(4, "proto_ids_size:  " + Hex.u4(sz));
+            out.annotate(4, "proto_ids_off:   " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param prototype {@code non-null;} the prototype to intern
+     * @return {@code non-null;} the interned reference
+     */
+    public ProtoIdItem intern(Prototype prototype) {
+        if (prototype == null) {
+            throw new NullPointerException("prototype == null");
+        }
+
+        throwIfPrepared();
+
+        ProtoIdItem result = protoIds.get(prototype);
+
+        if (result == null) {
+            result = new ProtoIdItem(prototype);
+            protoIds.put(prototype, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the index of the given prototype, which must have
+     * been added to this instance.
+     *
+     * @param prototype {@code non-null;} the prototype to look up
+     * @return {@code >= 0;} the reference's index
+     */
+    public int indexOf(Prototype prototype) {
+        if (prototype == null) {
+            throw new NullPointerException("prototype == null");
+        }
+
+        throwIfNotPrepared();
+
+        ProtoIdItem item = protoIds.get(prototype);
+
+        if (item == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return item.getIndex();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int idx = 0;
+
+        for (Object i : items()) {
+            ((ProtoIdItem) i).setIndex(idx);
+            idx++;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/Section.java b/dx/src/com/android/dx/dex/file/Section.java
new file mode 100644
index 0000000..3f04216
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Section.java
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+
+/**
+ * A section of a {@code .dex} file. Each section consists of a list
+ * of items of some sort or other.
+ */
+public abstract class Section {
+    /** {@code null-ok;} name of this part, for annotation purposes */
+    private final String name;
+
+    /** {@code non-null;} file that this instance is part of */
+    private final DexFile file;
+
+    /** {@code > 0;} alignment requirement for the final output;
+     * must be a power of 2 */
+    private final int alignment;
+
+    /** {@code >= -1;} offset from the start of the file to this part, or
+     * {@code -1} if not yet known */
+    private int fileOffset;
+
+    /** whether {@link #prepare} has been called successfully on this
+     * instance */
+    private boolean prepared;
+
+    /**
+     * Validates an alignment.
+     *
+     * @param alignment the alignment
+     * @throws IllegalArgumentException thrown if {@code alignment}
+     * isn't a positive power of 2
+     */
+    public static void validateAlignment(int alignment) {
+        if ((alignment <= 0) ||
+            (alignment & (alignment - 1)) != 0) {
+            throw new IllegalArgumentException("invalid alignment");
+        }
+    }
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param name {@code null-ok;} the name of this instance, for annotation
+     * purposes
+     * @param file {@code non-null;} file that this instance is part of
+     * @param alignment {@code > 0;} alignment requirement for the final output;
+     * must be a power of 2
+     */
+    public Section(String name, DexFile file, int alignment) {
+        if (file == null) {
+            throw new NullPointerException("file == null");
+        }
+
+        validateAlignment(alignment);
+
+        this.name = name;
+        this.file = file;
+        this.alignment = alignment;
+        this.fileOffset = -1;
+        this.prepared = false;
+    }
+
+    /**
+     * Gets the file that this instance is part of.
+     *
+     * @return {@code non-null;} the file
+     */
+    public final DexFile getFile() {
+        return file;
+    }
+
+    /**
+     * Gets the alignment for this instance's final output.
+     *
+     * @return {@code > 0;} the alignment
+     */
+    public final int getAlignment() {
+        return alignment;
+    }
+
+    /**
+     * Gets the offset from the start of the file to this part. This
+     * throws an exception if the offset has not yet been set.
+     *
+     * @return {@code >= 0;} the file offset
+     */
+    public final int getFileOffset() {
+        if (fileOffset < 0) {
+            throw new RuntimeException("fileOffset not set");
+        }
+
+        return fileOffset;
+    }
+
+    /**
+     * Sets the file offset. It is only valid to call this method once
+     * once per instance.
+     *
+     * @param fileOffset {@code >= 0;} the desired offset from the start of the
+     * file where this for this instance
+     * @return {@code >= 0;} the offset that this instance should be placed at
+     * in order to meet its alignment constraint
+     */
+    public final int setFileOffset(int fileOffset) {
+        if (fileOffset < 0) {
+            throw new IllegalArgumentException("fileOffset < 0");
+        }
+
+        if (this.fileOffset >= 0) {
+            throw new RuntimeException("fileOffset already set");
+        }
+
+        int mask = alignment - 1;
+        fileOffset = (fileOffset + mask) & ~mask;
+
+        this.fileOffset = fileOffset;
+
+        return fileOffset;
+    }
+
+    /**
+     * Writes this instance to the given raw data object.
+     *
+     * @param out {@code non-null;} where to write to
+     */
+    public final void writeTo(AnnotatedOutput out) {
+        throwIfNotPrepared();
+        align(out);
+
+        int cursor = out.getCursor();
+
+        if (fileOffset < 0) {
+            fileOffset = cursor;
+        } else if (fileOffset != cursor) {
+            throw new RuntimeException("alignment mismatch: for " + this +
+                                       ", at " + cursor +
+                                       ", but expected " + fileOffset);
+        }
+
+        if (out.annotates()) {
+            if (name != null) {
+                out.annotate(0, "\n" + name + ":");
+            } else if (cursor != 0) {
+                out.annotate(0, "\n");
+            }
+        }
+
+        writeTo0(out);
+    }
+
+    /**
+     * Returns the absolute file offset, given an offset from the
+     * start of this instance's output. This is only valid to call
+     * once this instance has been assigned a file offset (via {@link
+     * #setFileOffset}).
+     *
+     * @param relative {@code >= 0;} the relative offset
+     * @return {@code >= 0;} the corresponding absolute file offset
+     */
+    public final int getAbsoluteOffset(int relative) {
+        if (relative < 0) {
+            throw new IllegalArgumentException("relative < 0");
+        }
+
+        if (fileOffset < 0) {
+            throw new RuntimeException("fileOffset not yet set");
+        }
+
+        return fileOffset + relative;
+    }
+
+    /**
+     * Returns the absolute file offset of the given item which must
+     * be contained in this section. This is only valid to call
+     * once this instance has been assigned a file offset (via {@link
+     * #setFileOffset}).
+     *
+     * <p><b>Note:</b> Subclasses must implement this as appropriate for
+     * their contents.</p>
+     *
+     * @param item {@code non-null;} the item in question
+     * @return {@code >= 0;} the item's absolute file offset
+     */
+    public abstract int getAbsoluteItemOffset(Item item);
+
+    /**
+     * Prepares this instance for writing. This performs any necessary
+     * prerequisites, including particularly adding stuff to other
+     * sections. This method may only be called once per instance;
+     * subsequent calls will throw an exception.
+     */
+    public final void prepare() {
+        throwIfPrepared();
+        prepare0();
+        prepared = true;
+    }
+
+    /**
+     * Gets the collection of all the items in this section.
+     * It is not valid to attempt to change the returned list.
+     *
+     * @return {@code non-null;} the items
+     */
+    public abstract Collection<? extends Item> items();
+
+    /**
+     * Does the main work of {@link #prepare}.
+     */
+    protected abstract void prepare0();
+
+    /**
+     * Gets the size of this instance when output, in bytes.
+     *
+     * @return {@code >= 0;} the size of this instance, in bytes
+     */
+    public abstract int writeSize();
+
+    /**
+     * Throws an exception if {@link #prepare} has not been
+     * called on this instance.
+     */
+    protected final void throwIfNotPrepared() {
+        if (!prepared) {
+            throw new RuntimeException("not prepared");
+        }
+    }
+
+    /**
+     * Throws an exception if {@link #prepare} has already been called
+     * on this instance.
+     */
+    protected final void throwIfPrepared() {
+        if (prepared) {
+            throw new RuntimeException("already prepared");
+        }
+    }
+
+    /**
+     * Aligns the output of the given data to the alignment of this instance.
+     *
+     * @param out {@code non-null;} the output to align
+     */
+    protected final void align(AnnotatedOutput out) {
+        out.alignTo(alignment);
+    }
+
+    /**
+     * Writes this instance to the given raw data object. This gets
+     * called by {@link #writeTo} after aligning the cursor of
+     * {@code out} and verifying that either the assigned file
+     * offset matches the actual cursor {@code out} or that the
+     * file offset was not previously assigned, in which case it gets
+     * assigned to {@code out}'s cursor.
+     *
+     * @param out {@code non-null;} where to write to
+     */
+    protected abstract void writeTo0(AnnotatedOutput out);
+
+    /**
+     * Returns the name of this section, for annotation purposes.
+     *
+     * @return {@code null-ok;} name of this part, for annotation purposes
+     */
+    protected final String getName() {
+        return name;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/Statistics.java b/dx/src/com/android/dx/dex/file/Statistics.java
new file mode 100644
index 0000000..62e1832
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Statistics.java
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Statistics about the contents of a file.
+ */
+public final class Statistics {
+    /** {@code non-null;} data about each type of item */
+    private final HashMap<String, Data> dataMap;
+
+    /**
+     * Constructs an instance.
+     */
+    public Statistics() {
+        dataMap = new HashMap<String, Data>(50);
+    }
+
+    /**
+     * Adds the given item to the statistics.
+     *
+     * @param item {@code non-null;} the item to add
+     */
+    public void add(Item item) {
+        String typeName = item.typeName();
+        Data data = dataMap.get(typeName);
+
+        if (data == null) {
+            dataMap.put(typeName, new Data(item, typeName));
+        } else {
+            data.add(item);
+        }
+    }
+
+    /**
+     * Adds the given list of items to the statistics.
+     *
+     * @param list {@code non-null;} the list of items to add
+     */
+    public void addAll(Section list) {
+        Collection<? extends Item> items = list.items();
+        for (Item item : items) {
+            add(item);
+        }
+    }
+
+    /**
+     * Writes the statistics as an annotation.
+     *
+     * @param out {@code non-null;} where to write to
+     */
+    public final void writeAnnotation(AnnotatedOutput out) {
+        if (dataMap.size() == 0) {
+            return;
+        }
+
+        out.annotate(0, "\nstatistics:\n");
+
+        TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
+
+        for (Data data : dataMap.values()) {
+            sortedData.put(data.name, data);
+        }
+
+        for (Data data : sortedData.values()) {
+            data.writeAnnotation(out);
+        }
+    }
+
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("Statistics:\n");
+
+        TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
+
+        for (Data data : dataMap.values()) {
+            sortedData.put(data.name, data);
+        }
+
+        for (Data data : sortedData.values()) {
+            sb.append(data.toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Statistical data about a particular class.
+     */
+    private static class Data {
+        /** {@code non-null;} name to use as a label */
+        private final String name;
+
+        /** {@code >= 0;} number of instances */
+        private int count;
+
+        /** {@code >= 0;} total size of instances in bytes */
+        private int totalSize;
+
+        /** {@code >= 0;} largest size of any individual item */
+        private int largestSize;
+
+        /** {@code >= 0;} smallest size of any individual item */
+        private int smallestSize;
+
+        /**
+         * Constructs an instance for the given item.
+         *
+         * @param item {@code non-null;} item in question
+         * @param name {@code non-null;} type name to use
+         */
+        public Data(Item item, String name) {
+            int size = item.writeSize();
+
+            this.name = name;
+            this.count = 1;
+            this.totalSize = size;
+            this.largestSize = size;
+            this.smallestSize = size;
+        }
+
+        /**
+         * Incorporates a new item. This assumes the type name matches.
+         *
+         * @param item {@code non-null;} item to incorporate
+         */
+        public void add(Item item) {
+            int size = item.writeSize();
+
+            count++;
+            totalSize += size;
+
+            if (size > largestSize) {
+                largestSize = size;
+            }
+
+            if (size < smallestSize) {
+                smallestSize = size;
+            }
+        }
+
+        /**
+         * Writes this instance as an annotation.
+         *
+         * @param out {@code non-null;} where to write to
+         */
+        public void writeAnnotation(AnnotatedOutput out) {
+            out.annotate(toHuman());
+        }
+
+        /**
+         * Generates a human-readable string for this data item.
+         *
+         * @return string for human consumption.
+         */
+        public String toHuman() {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append("  " + name + ": " +
+                         count + " item" + (count == 1 ? "" : "s") + "; " +
+                         totalSize + " bytes total\n");
+
+            if (smallestSize == largestSize) {
+                sb.append("    " + smallestSize + " bytes/item\n");
+            } else {
+                int average = totalSize / count;
+                sb.append("    " + smallestSize + ".." + largestSize +
+                             " bytes/item; average " + average + "\n");
+            }
+
+            return sb.toString();
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringDataItem.java b/dx/src/com/android/dx/dex/file/StringDataItem.java
new file mode 100644
index 0000000..80dbced
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringDataItem.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+/**
+ * Representation of string data for a particular string, in a Dalvik file.
+ */
+public final class StringDataItem extends OffsettedItem {
+    /** {@code non-null;} the string value */
+    private final CstUtf8 value;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param value {@code non-null;} the string value
+     */
+    public StringDataItem(CstUtf8 value) {
+        super(1, writeSize(value));
+
+        this.value = value;
+    }
+
+    /**
+     * Gets the write size for a given value.
+     *
+     * @param value {@code non-null;} the string value
+     * @return {@code >= 2}; the write size, in bytes
+     */
+    private static int writeSize(CstUtf8 value) {
+        int utf16Size = value.getUtf16Size();
+
+        // The +1 is for the '\0' termination byte.
+        return Leb128Utils.unsignedLeb128Size(utf16Size)
+            + value.getUtf8Size() + 1;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_STRING_DATA_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        // Nothing to do here.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo0(DexFile file, AnnotatedOutput out) {
+        ByteArray bytes = value.getBytes();
+        int utf16Size = value.getUtf16Size();
+
+        if (out.annotates()) {
+            out.annotate(Leb128Utils.unsignedLeb128Size(utf16Size),
+                    "utf16_size: " + Hex.u4(utf16Size));
+            out.annotate(bytes.size() + 1, value.toQuoted());
+        }
+
+        out.writeUnsignedLeb128(utf16Size);
+        out.write(bytes);
+        out.writeByte(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        return value.toQuoted();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        StringDataItem otherData = (StringDataItem) other;
+
+        return value.compareTo(otherData.value);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringIdItem.java b/dx/src/com/android/dx/dex/file/StringIdItem.java
new file mode 100644
index 0000000..cd0d57b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringIdItem.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a string inside a Dalvik file.
+ */
+public final class StringIdItem
+        extends IndexedItem implements Comparable {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 4;
+
+    /** {@code non-null;} the string value */
+    private final CstUtf8 value;
+
+    /** {@code null-ok;} associated string data object, if known */
+    private StringDataItem data;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param value {@code non-null;} the string value
+     */
+    public StringIdItem(CstUtf8 value) {
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        this.value = value;
+        this.data = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof StringIdItem)) {
+            return false;
+        }
+
+        StringIdItem otherString = (StringIdItem) other;
+        return value.equals(otherString.value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return value.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Object other) {
+        StringIdItem otherString = (StringIdItem) other;
+        return value.compareTo(otherString.value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_STRING_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        if (data == null) {
+            // The string data hasn't yet been added, so add it.
+            MixedItemSection stringData = file.getStringData();
+            data = new StringDataItem(value);
+            stringData.add(data);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        int dataOff = data.getAbsoluteOffset();
+
+        if (out.annotates()) {
+            out.annotate(0, indexString() + ' ' + value.toQuoted(100));
+            out.annotate(4, "  string_data_off: " + Hex.u4(dataOff));
+        }
+
+        out.writeInt(dataOff);
+    }
+
+    /**
+     * Gets the string value.
+     *
+     * @return {@code non-null;} the value
+     */
+    public CstUtf8 getValue() {
+        return value;
+    }
+
+    /**
+     * Gets the associated data object for this instance, if known.
+     *
+     * @return {@code null-ok;} the associated data object or {@code null}
+     * if not yet known
+     */
+    public StringDataItem getData() {
+        return data;
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringIdsSection.java b/dx/src/com/android/dx/dex/file/StringIdsSection.java
new file mode 100644
index 0000000..9039f43
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringIdsSection.java
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Strings list section of a {@code .dex} file.
+ */
+public final class StringIdsSection
+        extends UniformItemSection {
+    /**
+     * {@code non-null;} map from string constants to {@link
+     * StringIdItem} instances
+     */
+    private final TreeMap<CstUtf8, StringIdItem> strings;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param file {@code non-null;} file that this instance is part of
+     */
+    public StringIdsSection(DexFile file) {
+        super("string_ids", file, 4);
+
+        strings = new TreeMap<CstUtf8, StringIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return strings.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        if (cst instanceof CstString) {
+            cst = ((CstString) cst).getString();
+        }
+
+        IndexedItem result = strings.get((CstUtf8) cst);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     *
+     * @param out {@code non-null;} where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = strings.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (out.annotates()) {
+            out.annotate(4, "string_ids_size: " + Hex.u4(sz));
+            out.annotate(4, "string_ids_off:  " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param string {@code non-null;} the string to intern, as a regular Java
+     * {@code String}
+     * @return {@code non-null;} the interned string
+     */
+    public StringIdItem intern(String string) {
+        CstUtf8 utf8 = new CstUtf8(string);
+        return intern(new StringIdItem(utf8));
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param string {@code non-null;} the string to intern, as a {@link CstString}
+     * @return {@code non-null;} the interned string
+     */
+    public StringIdItem intern(CstString string) {
+        CstUtf8 utf8 = string.getString();
+        return intern(new StringIdItem(utf8));
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param string {@code non-null;} the string to intern, as a constant
+     * @return {@code non-null;} the interned string
+     */
+    public StringIdItem intern(CstUtf8 string) {
+        return intern(new StringIdItem(string));
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param string {@code non-null;} the string to intern
+     * @return {@code non-null;} the interned string
+     */
+    public StringIdItem intern(StringIdItem string) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+
+        throwIfPrepared();
+
+        CstUtf8 value = string.getValue();
+        StringIdItem already = strings.get(value);
+
+        if (already != null) {
+            return already;
+        }
+
+        strings.put(value, string);
+        return string;
+    }
+
+    /**
+     * Interns the components of a name-and-type into this instance.
+     *
+     * @param nat {@code non-null;} the name-and-type
+     */
+    public void intern(CstNat nat) {
+        intern(nat.getName());
+        intern(nat.getDescriptor());
+    }
+
+    /**
+     * Gets the index of the given string, which must have been added
+     * to this instance.
+     *
+     * @param string {@code non-null;} the string to look up
+     * @return {@code >= 0;} the string's index
+     */
+    public int indexOf(CstUtf8 string) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+
+        throwIfNotPrepared();
+
+        StringIdItem s = strings.get(string);
+
+        if (s == null) {
+            throw new IllegalArgumentException("not found");
+        }
+
+        return s.getIndex();
+    }
+
+    /**
+     * Gets the index of the given string, which must have been added
+     * to this instance.
+     *
+     * @param string {@code non-null;} the string to look up
+     * @return {@code >= 0;} the string's index
+     */
+    public int indexOf(CstString string) {
+        return indexOf(string.getString());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int idx = 0;
+
+        for (StringIdItem s : strings.values()) {
+            s.setIndex(idx);
+            idx++;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeIdItem.java b/dx/src/com/android/dx/dex/file/TypeIdItem.java
new file mode 100644
index 0000000..c257e00
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeIdItem.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a type reference inside a Dalvik file.
+ */
+public final class TypeIdItem extends IdItem {
+    /** size of instances when written out to a file, in bytes */
+    public static final int WRITE_SIZE = 4;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param type {@code non-null;} the constant for the type
+     */
+    public TypeIdItem(CstType type) {
+        super(type);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_TYPE_ID_ITEM;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int writeSize() {
+        return WRITE_SIZE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        file.getStringIds().intern(getDefiningClass().getDescriptor());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeTo(DexFile file, AnnotatedOutput out) {
+        CstType type = getDefiningClass();
+        CstUtf8 descriptor = type.getDescriptor();
+        int idx = file.getStringIds().indexOf(descriptor);
+
+        if (out.annotates()) {
+            out.annotate(0, indexString() + ' ' + descriptor.toHuman());
+            out.annotate(4, "  descriptor_idx: " + Hex.u4(idx));
+        }
+
+        out.writeInt(idx);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeIdsSection.java b/dx/src/com/android/dx/dex/file/TypeIdsSection.java
new file mode 100644
index 0000000..bcc8250
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeIdsSection.java
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Type identifiers list section of a {@code .dex} file.
+ */
+public final class TypeIdsSection extends UniformItemSection {
+    /**
+     * {@code non-null;} map from types to {@link TypeIdItem} instances
+     */
+    private final TreeMap<Type, TypeIdItem> typeIds;
+
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param file {@code non-null;} file that this instance is part of
+     */
+    public TypeIdsSection(DexFile file) {
+        super("type_ids", file, 4);
+
+        typeIds = new TreeMap<Type, TypeIdItem>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends Item> items() {
+        return typeIds.values();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public IndexedItem get(Constant cst) {
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        throwIfNotPrepared();
+
+        Type type = ((CstType) cst).getClassType();
+        IndexedItem result = typeIds.get(type);
+
+        if (result == null) {
+            throw new IllegalArgumentException("not found: " + cst);
+        }
+
+        return result;
+    }
+
+    /**
+     * Writes the portion of the file header that refers to this instance.
+     *
+     * @param out {@code non-null;} where to write
+     */
+    public void writeHeaderPart(AnnotatedOutput out) {
+        throwIfNotPrepared();
+
+        int sz = typeIds.size();
+        int offset = (sz == 0) ? 0 : getFileOffset();
+
+        if (sz > 65536) {
+            throw new UnsupportedOperationException("too many type ids");
+        }
+
+        if (out.annotates()) {
+            out.annotate(4, "type_ids_size:   " + Hex.u4(sz));
+            out.annotate(4, "type_ids_off:    " + Hex.u4(offset));
+        }
+
+        out.writeInt(sz);
+        out.writeInt(offset);
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param type {@code non-null;} the type to intern
+     * @return {@code non-null;} the interned reference
+     */
+    public TypeIdItem intern(Type type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        throwIfPrepared();
+
+        TypeIdItem result = typeIds.get(type);
+
+        if (result == null) {
+            result = new TypeIdItem(new CstType(type));
+            typeIds.put(type, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Interns an element into this instance.
+     *
+     * @param type {@code non-null;} the type to intern
+     * @return {@code non-null;} the interned reference
+     */
+    public TypeIdItem intern(CstType type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        throwIfPrepared();
+
+        Type typePerSe = type.getClassType();
+        TypeIdItem result = typeIds.get(typePerSe);
+
+        if (result == null) {
+            result = new TypeIdItem(type);
+            typeIds.put(typePerSe, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the index of the given type, which must have
+     * been added to this instance.
+     *
+     * @param type {@code non-null;} the type to look up
+     * @return {@code >= 0;} the reference's index
+     */
+    public int indexOf(Type type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        throwIfNotPrepared();
+
+        TypeIdItem item = typeIds.get(type);
+
+        if (item == null) {
+            throw new IllegalArgumentException("not found: " + type);
+        }
+
+        return item.getIndex();
+    }
+
+    /**
+     * Gets the index of the given type, which must have
+     * been added to this instance.
+     *
+     * @param type {@code non-null;} the type to look up
+     * @return {@code >= 0;} the reference's index
+     */
+    public int indexOf(CstType type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        return indexOf(type.getClassType());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void orderItems() {
+        int idx = 0;
+
+        for (Object i : items()) {
+            ((TypeIdItem) i).setIndex(idx);
+            idx++;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeListItem.java b/dx/src/com/android/dx/dex/file/TypeListItem.java
new file mode 100644
index 0000000..b815dd3
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeListItem.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a list of class references.
+ */
+public final class TypeListItem extends OffsettedItem {
+    /** alignment requirement */
+    private static final int ALIGNMENT = 4;
+
+    /** element size in bytes */
+    private static final int ELEMENT_SIZE = 2;
+
+    /** header size in bytes */
+    private static final int HEADER_SIZE = 4;
+
+    /** {@code non-null;} the actual list */
+    private final TypeList list;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param list {@code non-null;} the actual list
+     */
+    public TypeListItem(TypeList list) {
+        super(ALIGNMENT, (list.size() * ELEMENT_SIZE) + HEADER_SIZE);
+
+        this.list = list;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return StdTypeList.hashContents(list);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return ItemType.TYPE_TYPE_LIST;
+    }
+
+    /** {@inheritDoc} */
+    public void addContents(DexFile file) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        int sz = list.size();
+
+        for (int i = 0; i < sz; i++) {
+            typeIds.intern(list.getType(i));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toHuman() {
+        throw new RuntimeException("unsupported");
+    }
+
+    /**
+     * Gets the underlying list.
+     *
+     * @return {@code non-null;} the list
+     */
+    public TypeList getList() {
+        return list;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        int sz = list.size();
+
+        if (out.annotates()) {
+            out.annotate(0, offsetString() + " type_list");
+            out.annotate(HEADER_SIZE, "  size: " + Hex.u4(sz));
+            for (int i = 0; i < sz; i++) {
+                Type one = list.getType(i);
+                int idx = typeIds.indexOf(one);
+                out.annotate(ELEMENT_SIZE,
+                             "  " + Hex.u2(idx) + " // " + one.toHuman());
+            }
+        }
+
+        out.writeInt(sz);
+
+        for (int i = 0; i < sz; i++) {
+            out.writeShort(typeIds.indexOf(list.getType(i)));
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(OffsettedItem other) {
+        TypeList thisList = this.list;
+        TypeList otherList = ((TypeListItem) other).list;
+
+        return StdTypeList.compareContents(thisList, otherList);
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/UniformItemSection.java b/dx/src/com/android/dx/dex/file/UniformItemSection.java
new file mode 100644
index 0000000..d8c09ab
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/UniformItemSection.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+
+/**
+ * A section of a {@code .dex} file which consists of a sequence of
+ * {@link Item} objects. Each of the items must have the same size in
+ * the output.
+ */
+public abstract class UniformItemSection extends Section {
+    /**
+     * Constructs an instance. The file offset is initially unknown.
+     *
+     * @param name {@code null-ok;} the name of this instance, for annotation
+     * purposes
+     * @param file {@code non-null;} file that this instance is part of
+     * @param alignment {@code > 0;} alignment requirement for the final output;
+     * must be a power of 2
+     */
+    public UniformItemSection(String name, DexFile file, int alignment) {
+        super(name, file, alignment);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int writeSize() {
+        Collection<? extends Item> items = items();
+        int sz = items.size();
+
+        if (sz == 0) {
+            return 0;
+        }
+
+        // Since each item has to be the same size, we can pick any.
+        return sz * items.iterator().next().writeSize();
+    }
+
+    /**
+     * Gets the item corresponding to the given {@link Constant}. This
+     * will throw an exception if the constant is not found, including
+     * if this instance isn't the sort that maps constants to {@link
+     * IndexedItem} instances.
+     *
+     * @param cst {@code non-null;} constant to look for
+     * @return {@code non-null;} the corresponding item found in this instance
+     */
+    public abstract IndexedItem get(Constant cst);
+
+    /** {@inheritDoc} */
+    @Override
+    protected final void prepare0() {
+        DexFile file = getFile();
+
+        orderItems();
+
+        for (Item one : items()) {
+            one.addContents(file);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected final void writeTo0(AnnotatedOutput out) {
+        DexFile file = getFile();
+        int alignment = getAlignment();
+
+        for (Item one : items()) {
+            one.writeTo(file, out);
+            out.alignTo(alignment);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getAbsoluteItemOffset(Item item) {
+        /*
+         * Since all items must be the same size, we can use the size
+         * of the one we're given to calculate its offset.
+         */
+        IndexedItem ii = (IndexedItem) item;
+        int relativeOffset = ii.getIndex() * ii.writeSize();
+
+        return getAbsoluteOffset(relativeOffset);
+    }
+
+    /**
+     * Alters or picks the order for items in this instance if desired,
+     * so that subsequent calls to {@link #items} will yield a
+     * so-ordered collection. If the items in this instance are indexed,
+     * then this method should also assign indices.
+     */
+    protected abstract void orderItems();
+}
diff --git a/dx/src/com/android/dx/dex/file/UniformListItem.java b/dx/src/com/android/dx/dex/file/UniformListItem.java
new file mode 100644
index 0000000..88919c7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/UniformListItem.java
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Class that represents a contiguous list of uniform items. Each
+ * item in the list, in particular, must have the same write size and
+ * alignment.
+ *
+ * <p>This class inherits its alignment from its items, bumped up to
+ * {@code 4} if the items have a looser alignment requirement. If
+ * it is more than {@code 4}, then there will be a gap after the
+ * output list size (which is four bytes) and before the first item.</p>
+ *
+ * @param <T> type of element contained in an instance
+ */
+public final class UniformListItem<T extends OffsettedItem>
+        extends OffsettedItem {
+    /** the size of the list header */
+    private static final int HEADER_SIZE = 4;
+
+    /** {@code non-null;} the item type */
+    private final ItemType itemType;
+
+    /** {@code non-null;} the contents */
+    private final List<T> items;
+
+    /**
+     * Constructs an instance. It is illegal to modify the given list once
+     * it is used to construct an instance of this class.
+     *
+     * @param itemType {@code non-null;} the type of the item
+     * @param items {@code non-null and non-empty;} list of items to represent
+     */
+    public UniformListItem(ItemType itemType, List<T> items) {
+        super(getAlignment(items), writeSize(items));
+
+        if (itemType == null) {
+            throw new NullPointerException("itemType == null");
+        }
+
+        this.items = items;
+        this.itemType = itemType;
+    }
+
+    /**
+     * Helper for {@link #UniformListItem}, which returns the alignment
+     * requirement implied by the given list. See the header comment for
+     * more details.
+     *
+     * @param items {@code non-null;} list of items being represented
+     * @return {@code >= 4;} the alignment requirement
+     */
+    private static int getAlignment(List<? extends OffsettedItem> items) {
+        try {
+            // Since they all must have the same alignment, any one will do.
+            return Math.max(HEADER_SIZE, items.get(0).getAlignment());
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("items.size() == 0");
+        } catch (NullPointerException ex) {
+            // Translate the exception.
+            throw new NullPointerException("items == null");
+        }
+    }
+
+    /**
+     * Calculates the write size for the given list.
+     *
+     * @param items {@code non-null;} the list in question
+     * @return {@code >= 0;} the write size
+     */
+    private static int writeSize(List<? extends OffsettedItem> items) {
+        /*
+         * This class assumes all included items are the same size,
+         * an assumption which is verified in place0().
+         */
+        OffsettedItem first = items.get(0);
+        return (items.size() * first.writeSize()) + getAlignment(items);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ItemType itemType() {
+        return itemType;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append(getClass().getName());
+        sb.append(items);
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addContents(DexFile file) {
+        for (OffsettedItem i : items) {
+            i.addContents(file);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String toHuman() {
+        StringBuffer sb = new StringBuffer(100);
+        boolean first = true;
+
+        sb.append("{");
+
+        for (OffsettedItem i : items) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(i.toHuman());
+        }
+
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Gets the underlying list of items.
+     *
+     * @return {@code non-null;} the list
+     */
+    public final List<T> getItems() {
+        return items;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void place0(Section addedTo, int offset) {
+        offset += headerSize();
+
+        boolean first = true;
+        int theSize = -1;
+        int theAlignment = -1;
+
+        for (OffsettedItem i : items) {
+            int size = i.writeSize();
+            if (first) {
+                theSize = size;
+                theAlignment = i.getAlignment();
+                first = false;
+            } else {
+                if (size != theSize) {
+                    throw new UnsupportedOperationException(
+                            "item size mismatch");
+                }
+                if (i.getAlignment() != theAlignment) {
+                    throw new UnsupportedOperationException(
+                            "item alignment mismatch");
+                }
+            }
+
+            offset = i.place(addedTo, offset) + size;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void writeTo0(DexFile file, AnnotatedOutput out) {
+        int size = items.size();
+
+        if (out.annotates()) {
+            out.annotate(0, offsetString() + " " + typeName());
+            out.annotate(4, "  size: " + Hex.u4(size));
+        }
+
+        out.writeInt(size);
+
+        for (OffsettedItem i : items) {
+            i.writeTo(file, out);
+        }
+    }
+
+    /**
+     * Get the size of the header of this list.
+     *
+     * @return {@code >= 0;} the header size
+     */
+    private int headerSize() {
+        /*
+         * Because of how this instance was set up, this is the same
+         * as the alignment.
+         */
+        return getAlignment();
+    }
+}
diff --git a/dx/src/com/android/dx/dex/file/ValueEncoder.java b/dx/src/com/android/dx/dex/file/ValueEncoder.java
new file mode 100644
index 0000000..fba64a7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ValueEncoder.java
@@ -0,0 +1,529 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+
+/**
+ * Handler for writing out {@code encoded_values} and parts
+ * thereof.
+ */
+public final class ValueEncoder {
+    /** annotation value type constant: {@code byte} */
+    private static final int VALUE_BYTE = 0x00;
+
+    /** annotation value type constant: {@code short} */
+    private static final int VALUE_SHORT = 0x02;
+
+    /** annotation value type constant: {@code char} */
+    private static final int VALUE_CHAR = 0x03;
+
+    /** annotation value type constant: {@code int} */
+    private static final int VALUE_INT = 0x04;
+
+    /** annotation value type constant: {@code long} */
+    private static final int VALUE_LONG = 0x06;
+
+    /** annotation value type constant: {@code float} */
+    private static final int VALUE_FLOAT = 0x10;
+
+    /** annotation value type constant: {@code double} */
+    private static final int VALUE_DOUBLE = 0x11;
+
+    /** annotation value type constant: {@code string} */
+    private static final int VALUE_STRING = 0x17;
+
+    /** annotation value type constant: {@code type} */
+    private static final int VALUE_TYPE = 0x18;
+
+    /** annotation value type constant: {@code field} */
+    private static final int VALUE_FIELD = 0x19;
+
+    /** annotation value type constant: {@code method} */
+    private static final int VALUE_METHOD = 0x1a;
+
+    /** annotation value type constant: {@code enum} */
+    private static final int VALUE_ENUM = 0x1b;
+
+    /** annotation value type constant: {@code array} */
+    private static final int VALUE_ARRAY = 0x1c;
+
+    /** annotation value type constant: {@code annotation} */
+    private static final int VALUE_ANNOTATION = 0x1d;
+
+    /** annotation value type constant: {@code null} */
+    private static final int VALUE_NULL = 0x1e;
+
+    /** annotation value type constant: {@code boolean} */
+    private static final int VALUE_BOOLEAN = 0x1f;
+
+    /** {@code non-null;} file being written */
+    private final DexFile file;
+
+    /** {@code non-null;} output stream to write to */
+    private final AnnotatedOutput out;
+
+    /**
+     * Construct an instance.
+     *
+     * @param file {@code non-null;} file being written
+     * @param out {@code non-null;} output stream to write to
+     */
+    public ValueEncoder(DexFile file, AnnotatedOutput out) {
+        if (file == null) {
+            throw new NullPointerException("file == null");
+        }
+
+        if (out == null) {
+            throw new NullPointerException("out == null");
+        }
+
+        this.file = file;
+        this.out = out;
+    }
+
+    /**
+     * Writes out the encoded form of the given constant.
+     *
+     * @param cst {@code non-null;} the constant to write
+     */
+    public void writeConstant(Constant cst) {
+        int type = constantToValueType(cst);
+        int arg;
+
+        switch (type) {
+            case VALUE_BYTE:
+            case VALUE_SHORT:
+            case VALUE_INT:
+            case VALUE_LONG: {
+                long value = ((CstLiteralBits) cst).getLongBits();
+                writeSignedIntegralValue(type, value);
+                break;
+            }
+            case VALUE_CHAR: {
+                long value = ((CstLiteralBits) cst).getLongBits();
+                writeUnsignedIntegralValue(type, value);
+                break;
+            }
+            case VALUE_FLOAT: {
+                // Shift value left 32 so that right-zero-extension works.
+                long value = ((CstFloat) cst).getLongBits() << 32;
+                writeRightZeroExtendedValue(type, value);
+                break;
+            }
+            case VALUE_DOUBLE: {
+                long value = ((CstDouble) cst).getLongBits();
+                writeRightZeroExtendedValue(type, value);
+                break;
+            }
+            case VALUE_STRING: {
+                int index = file.getStringIds().indexOf((CstString) cst);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_TYPE: {
+                int index = file.getTypeIds().indexOf((CstType) cst);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_FIELD: {
+                int index = file.getFieldIds().indexOf((CstFieldRef) cst);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_METHOD: {
+                int index = file.getMethodIds().indexOf((CstMethodRef) cst);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_ENUM: {
+                CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef();
+                int index = file.getFieldIds().indexOf(fieldRef);
+                writeUnsignedIntegralValue(type, (long) index);
+                break;
+            }
+            case VALUE_ARRAY: {
+                out.writeByte(type);
+                writeArray((CstArray) cst, false);
+                break;
+            }
+            case VALUE_ANNOTATION: {
+                out.writeByte(type);
+                writeAnnotation(((CstAnnotation) cst).getAnnotation(),
+                        false);
+                break;
+            }
+            case VALUE_NULL: {
+                out.writeByte(type);
+                break;
+            }
+            case VALUE_BOOLEAN: {
+                int value = ((CstBoolean) cst).getIntBits();
+                out.writeByte(type | (value << 5));
+                break;
+            }
+            default: {
+                throw new RuntimeException("Shouldn't happen");
+            }
+        }
+    }
+
+    /**
+     * Gets the value type for the given constant.
+     *
+     * @param cst {@code non-null;} the constant
+     * @return the value type; one of the {@code VALUE_*} constants
+     * defined by this class
+     */
+    private static int constantToValueType(Constant cst) {
+        /*
+         * TODO: Constant should probable have an associated enum, so this
+         * can be a switch().
+         */
+        if (cst instanceof CstByte) {
+            return VALUE_BYTE;
+        } else if (cst instanceof CstShort) {
+            return VALUE_SHORT;
+        } else if (cst instanceof CstChar) {
+            return VALUE_CHAR;
+        } else if (cst instanceof CstInteger) {
+            return VALUE_INT;
+        } else if (cst instanceof CstLong) {
+            return VALUE_LONG;
+        } else if (cst instanceof CstFloat) {
+            return VALUE_FLOAT;
+        } else if (cst instanceof CstDouble) {
+            return VALUE_DOUBLE;
+        } else if (cst instanceof CstString) {
+            return VALUE_STRING;
+        } else if (cst instanceof CstType) {
+            return VALUE_TYPE;
+        } else if (cst instanceof CstFieldRef) {
+            return VALUE_FIELD;
+        } else if (cst instanceof CstMethodRef) {
+            return VALUE_METHOD;
+        } else if (cst instanceof CstEnumRef) {
+            return VALUE_ENUM;
+        } else if (cst instanceof CstArray) {
+            return VALUE_ARRAY;
+        } else if (cst instanceof CstAnnotation) {
+            return VALUE_ANNOTATION;
+        } else if (cst instanceof CstKnownNull) {
+            return VALUE_NULL;
+        } else if (cst instanceof CstBoolean) {
+            return VALUE_BOOLEAN;
+        } else {
+            throw new RuntimeException("Shouldn't happen");
+        }
+    }
+
+    /**
+     * Writes out the encoded form of the given array, that is, as
+     * an {@code encoded_array} and not including a
+     * {@code value_type} prefix. If the output stream keeps
+     * (debugging) annotations and {@code topLevel} is
+     * {@code true}, then this method will write (debugging)
+     * annotations.
+     *
+     * @param array {@code non-null;} array instance to write
+     * @param topLevel {@code true} iff the given annotation is the
+     * top-level annotation or {@code false} if it is a sub-annotation
+     * of some other annotation
+     */
+    public void writeArray(CstArray array, boolean topLevel) {
+        boolean annotates = topLevel && out.annotates();
+        CstArray.List list = ((CstArray) array).getList();
+        int size = list.size();
+
+        if (annotates) {
+            out.annotate("  size: " + Hex.u4(size));
+        }
+
+        out.writeUnsignedLeb128(size);
+
+        for (int i = 0; i < size; i++) {
+            Constant cst = list.get(i);
+            if (annotates) {
+                out.annotate("  [" + Integer.toHexString(i) + "] " +
+                        constantToHuman(cst));
+            }
+            writeConstant(cst);
+        }
+
+        if (annotates) {
+            out.endAnnotation();
+        }
+    }
+
+    /**
+     * Writes out the encoded form of the given annotation, that is,
+     * as an {@code encoded_annotation} and not including a
+     * {@code value_type} prefix. If the output stream keeps
+     * (debugging) annotations and {@code topLevel} is
+     * {@code true}, then this method will write (debugging)
+     * annotations.
+     *
+     * @param annotation {@code non-null;} annotation instance to write
+     * @param topLevel {@code true} iff the given annotation is the
+     * top-level annotation or {@code false} if it is a sub-annotation
+     * of some other annotation
+     */
+    public void writeAnnotation(Annotation annotation, boolean topLevel) {
+        boolean annotates = topLevel && out.annotates();
+        StringIdsSection stringIds = file.getStringIds();
+        TypeIdsSection typeIds = file.getTypeIds();
+
+        CstType type = annotation.getType();
+        int typeIdx = typeIds.indexOf(type);
+
+        if (annotates) {
+            out.annotate("  type_idx: " + Hex.u4(typeIdx) + " // " +
+                    type.toHuman());
+        }
+
+        out.writeUnsignedLeb128(typeIds.indexOf(annotation.getType()));
+
+        Collection<NameValuePair> pairs = annotation.getNameValuePairs();
+        int size = pairs.size();
+
+        if (annotates) {
+            out.annotate("  size: " + Hex.u4(size));
+        }
+
+        out.writeUnsignedLeb128(size);
+
+        int at = 0;
+        for (NameValuePair pair : pairs) {
+            CstUtf8 name = pair.getName();
+            int nameIdx = stringIds.indexOf(name);
+            Constant value = pair.getValue();
+
+            if (annotates) {
+                out.annotate(0, "  elements[" + at + "]:");
+                at++;
+                out.annotate("    name_idx: " + Hex.u4(nameIdx) + " // " +
+                        name.toHuman());
+            }
+
+            out.writeUnsignedLeb128(nameIdx);
+
+            if (annotates) {
+                out.annotate("    value: " + constantToHuman(value));
+            }
+
+            writeConstant(value);
+        }
+
+        if (annotates) {
+            out.endAnnotation();
+        }
+    }
+
+    /**
+     * Gets the colloquial type name and human form of the type of the
+     * given constant, when used as an encoded value.
+     *
+     * @param cst {@code non-null;} the constant
+     * @return {@code non-null;} its type name and human form
+     */
+    public static String constantToHuman(Constant cst) {
+        int type = constantToValueType(cst);
+
+        if (type == VALUE_NULL) {
+            return "null";
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(cst.typeName());
+        sb.append(' ');
+        sb.append(cst.toHuman());
+
+        return sb.toString();
+    }
+
+    /**
+     * Helper for {@link #writeConstant}, which writes out the value
+     * for any signed integral type.
+     *
+     * @param type the type constant
+     * @param value {@code long} bits of the value
+     */
+    private void writeSignedIntegralValue(int type, long value) {
+        /*
+         * Figure out how many bits are needed to represent the value,
+         * including a sign bit: The bit count is subtracted from 65
+         * and not 64 to account for the sign bit. The xor operation
+         * has the effect of leaving non-negative values alone and
+         * unary complementing negative values (so that a leading zero
+         * count always returns a useful number for our present
+         * purpose).
+         */
+        int requiredBits =
+            65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
+
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+    /**
+     * Helper for {@link #writeConstant}, which writes out the value
+     * for any unsigned integral type.
+     *
+     * @param type the type constant
+     * @param value {@code long} bits of the value
+     */
+    private void writeUnsignedIntegralValue(int type, long value) {
+        // Figure out how many bits are needed to represent the value.
+        int requiredBits = 64 - Long.numberOfLeadingZeros(value);
+        if (requiredBits == 0) {
+            requiredBits = 1;
+        }
+
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+    /**
+     * Helper for {@link #writeConstant}, which writes out a
+     * right-zero-extended value.
+     *
+     * @param type the type constant
+     * @param value {@code long} bits of the value
+     */
+    private void writeRightZeroExtendedValue(int type, long value) {
+        // Figure out how many bits are needed to represent the value.
+        int requiredBits = 64 - Long.numberOfTrailingZeros(value);
+        if (requiredBits == 0) {
+            requiredBits = 1;
+        }
+
+        // Round up the requiredBits to a number of bytes.
+        int requiredBytes = (requiredBits + 0x07) >> 3;
+
+        // Scootch the first bits to be written down to the low-order bits.
+        value >>= 64 - (requiredBytes * 8);
+
+        /*
+         * Write the header byte, which includes the type and
+         * requiredBytes - 1.
+         */
+        out.writeByte(type | ((requiredBytes - 1) << 5));
+
+        // Write the value, per se.
+        while (requiredBytes > 0) {
+            out.writeByte((byte) value);
+            value >>= 8;
+            requiredBytes--;
+        }
+    }
+
+
+    /**
+     * Helper for {@code addContents()} methods, which adds
+     * contents for a particular {@link Annotation}, calling itself
+     * recursively should it encounter a nested annotation.
+     *
+     * @param file {@code non-null;} the file to add to
+     * @param annotation {@code non-null;} the annotation to add contents for
+     */
+    public static void addContents(DexFile file, Annotation annotation) {
+        TypeIdsSection typeIds = file.getTypeIds();
+        StringIdsSection stringIds = file.getStringIds();
+
+        typeIds.intern(annotation.getType());
+
+        for (NameValuePair pair : annotation.getNameValuePairs()) {
+            stringIds.intern(pair.getName());
+            addContents(file, pair.getValue());
+        }
+    }
+
+    /**
+     * Helper for {@code addContents()} methods, which adds
+     * contents for a particular constant, calling itself recursively
+     * should it encounter a {@link CstArray} and calling {@link
+     * #addContents(DexFile,Annotation)} recursively should it
+     * encounter a {@link CstAnnotation}.
+     *
+     * @param file {@code non-null;} the file to add to
+     * @param cst {@code non-null;} the constant to add contents for
+     */
+    public static void addContents(DexFile file, Constant cst) {
+        if (cst instanceof CstAnnotation) {
+            addContents(file, ((CstAnnotation) cst).getAnnotation());
+        } else if (cst instanceof CstArray) {
+            CstArray.List list = ((CstArray) cst).getList();
+            int size = list.size();
+            for (int i = 0; i < size; i++) {
+                addContents(file, list.get(i));
+            }
+        } else {
+            file.internIfAppropriate(cst);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/Annotation.java b/dx/src/com/android/dx/rop/annotation/Annotation.java
new file mode 100644
index 0000000..ad6d67e
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/Annotation.java
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.util.ToHuman;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+/**
+ * An annotation on an element of a class. Annotations have an
+ * associated type and additionally consist of a set of (name, value)
+ * pairs, where the names are unique.
+ */
+public final class Annotation extends MutabilityControl
+        implements Comparable<Annotation>, ToHuman {
+    /** {@code non-null;} type of the annotation */
+    private final CstType type;
+
+    /** {@code non-null;} the visibility of the annotation */
+    private final AnnotationVisibility visibility;
+
+    /** {@code non-null;} map from names to {@link NameValuePair} instances */
+    private final TreeMap<CstUtf8, NameValuePair> elements;
+
+    /**
+     * Construct an instance. It initially contains no elements.
+     *
+     * @param type {@code non-null;} type of the annotation
+     * @param visibility {@code non-null;} the visibility of the annotation
+     */
+    public Annotation(CstType type, AnnotationVisibility visibility) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        if (visibility == null) {
+            throw new NullPointerException("visibility == null");
+        }
+
+        this.type = type;
+        this.visibility = visibility;
+        this.elements = new TreeMap<CstUtf8, NameValuePair>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (! (other instanceof Annotation)) {
+            return false;
+        }
+
+        Annotation otherAnnotation = (Annotation) other;
+
+        if (! (type.equals(otherAnnotation.type)
+                        && (visibility == otherAnnotation.visibility))) {
+            return false;
+        }
+
+        return elements.equals(otherAnnotation.elements);
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        int hash = type.hashCode();
+        hash = (hash * 31) + elements.hashCode();
+        hash = (hash * 31) + visibility.hashCode();
+        return hash;
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Annotation other) {
+        int result = type.compareTo(other.type);
+
+        if (result != 0) {
+            return result;
+        }
+
+        result = visibility.compareTo(other.visibility);
+
+        if (result != 0) {
+            return result;
+        }
+
+        Iterator<NameValuePair> thisIter = elements.values().iterator();
+        Iterator<NameValuePair> otherIter = other.elements.values().iterator();
+
+        while (thisIter.hasNext() && otherIter.hasNext()) {
+            NameValuePair thisOne = thisIter.next();
+            NameValuePair otherOne = otherIter.next();
+
+            result = thisOne.compareTo(otherOne);
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        if (thisIter.hasNext()) {
+            return 1;
+        } else if (otherIter.hasNext()) {
+            return -1;
+        }
+
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return toHuman();
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(visibility.toHuman());
+        sb.append("-annotation ");
+        sb.append(type.toHuman());
+        sb.append(" {");
+
+        boolean first = true;
+        for (NameValuePair pair : elements.values()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(pair.getName().toHuman());
+            sb.append(": ");
+            sb.append(pair.getValue().toHuman());
+        }
+
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Gets the type of this instance.
+     *
+     * @return {@code non-null;} the type
+     */
+    public CstType getType() {
+        return type;
+    }
+
+    /**
+     * Gets the visibility of this instance.
+     *
+     * @return {@code non-null;} the visibility
+     */
+    public AnnotationVisibility getVisibility() {
+        return visibility;
+    }
+
+    /**
+     * Put an element into the set of (name, value) pairs for this instance.
+     * If there is a preexisting element with the same name, it will be
+     * replaced by this method.
+     *
+     * @param pair {@code non-null;} the (name, value) pair to place into this instance
+     */
+    public void put(NameValuePair pair) {
+        throwIfImmutable();
+
+        if (pair == null) {
+            throw new NullPointerException("pair == null");
+        }
+
+        elements.put(pair.getName(), pair);
+    }
+
+    /**
+     * Add an element to the set of (name, value) pairs for this instance.
+     * It is an error to call this method if there is a preexisting element
+     * with the same name.
+     *
+     * @param pair {@code non-null;} the (name, value) pair to add to this instance
+     */
+    public void add(NameValuePair pair) {
+        throwIfImmutable();
+
+        if (pair == null) {
+            throw new NullPointerException("pair == null");
+        }
+
+        CstUtf8 name = pair.getName();
+
+        if (elements.get(name) != null) {
+            throw new IllegalArgumentException("name already added: " + name);
+        }
+
+        elements.put(name, pair);
+    }
+
+    /**
+     * Gets the set of name-value pairs contained in this instance. The
+     * result is always unmodifiable.
+     *
+     * @return {@code non-null;} the set of name-value pairs
+     */
+    public Collection<NameValuePair> getNameValuePairs() {
+        return Collections.unmodifiableCollection(elements.values());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java b/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java
new file mode 100644
index 0000000..c717b8c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Visibility scope of an annotation.
+ */
+public enum AnnotationVisibility implements ToHuman {
+    RUNTIME("runtime"),
+    BUILD("build"),
+    SYSTEM("system"),
+    EMBEDDED("embedded");
+
+    /** {@code non-null;} the human-oriented string representation */
+    private final String human;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param human {@code non-null;} the human-oriented string representation
+     */
+    private AnnotationVisibility(String human) {
+        this.human = human;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return human;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/Annotations.java b/dx/src/com/android/dx/rop/annotation/Annotations.java
new file mode 100644
index 0000000..807d5d4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/Annotations.java
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+/**
+ * List of {@link Annotation} instances.
+ */
+public final class Annotations extends MutabilityControl
+        implements Comparable<Annotations> {
+    /** {@code non-null;} immutable empty instance */
+    public static final Annotations EMPTY = new Annotations();
+
+    static {
+        EMPTY.setImmutable();
+    }
+
+    /** {@code non-null;} map from types to annotations */
+    private final TreeMap<CstType, Annotation> annotations;
+
+    /**
+     * Constructs an immutable instance which is the combination of the
+     * two given instances. The two instances must contain disjoint sets
+     * of types.
+     *
+     * @param a1 {@code non-null;} an instance
+     * @param a2 {@code non-null;} the other instance
+     * @return {@code non-null;} the combination
+     * @throws IllegalArgumentException thrown if there is a duplicate type
+     */
+    public static Annotations combine(Annotations a1, Annotations a2) {
+        Annotations result = new Annotations();
+
+        result.addAll(a1);
+        result.addAll(a2);
+        result.setImmutable();
+
+        return result;
+    }
+
+    /**
+     * Constructs an immutable instance which is the combination of the
+     * given instance with the given additional annotation. The latter's
+     * type must not already appear in the former.
+     *
+     * @param annotations {@code non-null;} the instance to augment
+     * @param annotation {@code non-null;} the additional annotation
+     * @return {@code non-null;} the combination
+     * @throws IllegalArgumentException thrown if there is a duplicate type
+     */
+    public static Annotations combine(Annotations annotations,
+            Annotation annotation) {
+        Annotations result = new Annotations();
+
+        result.addAll(annotations);
+        result.add(annotation);
+        result.setImmutable();
+
+        return result;
+    }
+
+    /**
+     * Constructs an empty instance.
+     */
+    public Annotations() {
+        annotations = new TreeMap<CstType, Annotation>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return annotations.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (! (other instanceof Annotations)) {
+            return false;
+        }
+
+        Annotations otherAnnotations = (Annotations) other;
+
+        return annotations.equals(otherAnnotations.annotations);
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Annotations other) {
+        Iterator<Annotation> thisIter = annotations.values().iterator();
+        Iterator<Annotation> otherIter = other.annotations.values().iterator();
+
+        while (thisIter.hasNext() && otherIter.hasNext()) {
+            Annotation thisOne = thisIter.next();
+            Annotation otherOne = otherIter.next();
+
+            int result = thisOne.compareTo(otherOne);
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        if (thisIter.hasNext()) {
+            return 1;
+        } else if (otherIter.hasNext()) {
+            return -1;
+        }
+
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+
+        sb.append("annotations{");
+
+        for (Annotation a : annotations.values()) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(a.toHuman());
+        }
+
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Gets the number of elements in this instance.
+     *
+     * @return {@code >= 0;} the size
+     */
+    public int size() {
+        return annotations.size();
+    }
+
+    /**
+     * Adds an element to this instance. There must not already be an
+     * element of the same type.
+     *
+     * @param annotation {@code non-null;} the element to add
+     * @throws IllegalArgumentException thrown if there is a duplicate type
+     */
+    public void add(Annotation annotation) {
+        throwIfImmutable();
+
+        if (annotation == null) {
+            throw new NullPointerException("annotation == null");
+        }
+
+        CstType type = annotation.getType();
+
+        if (annotations.containsKey(type)) {
+            throw new IllegalArgumentException("duplicate type: " +
+                    type.toHuman());
+        }
+
+        annotations.put(type, annotation);
+    }
+
+    /**
+     * Adds all of the elements of the given instance to this one. The
+     * instances must not have any duplicate types.
+     *
+     * @param toAdd {@code non-null;} the annotations to add
+     * @throws IllegalArgumentException thrown if there is a duplicate type
+     */
+    public void addAll(Annotations toAdd) {
+        throwIfImmutable();
+
+        if (toAdd == null) {
+            throw new NullPointerException("toAdd == null");
+        }
+
+        for (Annotation a : toAdd.annotations.values()) {
+            add(a);
+        }
+    }
+
+    /**
+     * Gets the set of annotations contained in this instance. The
+     * result is always unmodifiable.
+     *
+     * @return {@code non-null;} the set of annotations
+     */
+    public Collection<Annotation> getAnnotations() {
+        return Collections.unmodifiableCollection(annotations.values());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/AnnotationsList.java b/dx/src/com/android/dx/rop/annotation/AnnotationsList.java
new file mode 100644
index 0000000..b97b385
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/AnnotationsList.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link Annotations} instances.
+ */
+public final class AnnotationsList
+        extends FixedSizeList {
+    /** {@code non-null;} immutable empty instance */
+    public static final AnnotationsList EMPTY = new AnnotationsList(0);
+
+    /**
+     * Constructs an immutable instance which is the combination of
+     * the two given instances. The two instances must each have the
+     * same number of elements, and each pair of elements must contain
+     * disjoint sets of types.
+     *
+     * @param list1 {@code non-null;} an instance
+     * @param list2 {@code non-null;} the other instance
+     * @return {@code non-null;} the combination
+     */
+    public static AnnotationsList combine(AnnotationsList list1,
+            AnnotationsList list2) {
+        int size = list1.size();
+
+        if (size != list2.size()) {
+            throw new IllegalArgumentException("list1.size() != list2.size()");
+        }
+
+        AnnotationsList result = new AnnotationsList(size);
+
+        for (int i = 0; i < size; i++) {
+            Annotations a1 = list1.get(i);
+            Annotations a2 = list2.get(i);
+            result.set(i, Annotations.combine(a1, a2));
+        }
+
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public AnnotationsList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @return {@code non-null;} element at that index
+     */
+    public Annotations get(int n) {
+        return (Annotations) get0(n);
+    }
+
+    /**
+     * Sets the element at the given index. The given element must be
+     * immutable.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param a {@code null-ok;} the element to set at {@code n}
+     */
+    public void set(int n, Annotations a) {
+        a.throwIfMutable();
+        set0(n, a);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/NameValuePair.java b/dx/src/com/android/dx/rop/annotation/NameValuePair.java
new file mode 100644
index 0000000..39a9dfd
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/NameValuePair.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * A (name, value) pair. These are used as the contents of an annotation.
+ */
+public final class NameValuePair implements Comparable<NameValuePair> {
+    /** {@code non-null;} the name */
+    private final CstUtf8 name;
+
+    /** {@code non-null;} the value */
+    private final Constant value;
+
+    /**
+     * Construct an instance.
+     *
+     * @param name {@code non-null;} the name
+     * @param value {@code non-null;} the value
+     */
+    public NameValuePair(CstUtf8 name, Constant value) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        if (value == null) {
+            throw new NullPointerException("value == null");
+        }
+
+        // Reject CstUtf8 values. (They should be CstStrings.)
+        if (value instanceof CstUtf8) {
+            throw new IllegalArgumentException("bad value: " + value);
+        }
+
+        this.name = name;
+        this.value = value;
+    }
+
+    /** {@inheritDoc} */
+    public String toString() {
+        return name.toHuman() + ":" + value;
+    }
+
+    /** {@inheritDoc} */
+    public int hashCode() {
+        return name.hashCode() * 31 + value.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public boolean equals(Object other) {
+        if (! (other instanceof NameValuePair)) {
+            return false;
+        }
+
+        NameValuePair otherPair = (NameValuePair) other;
+
+        return name.equals(otherPair.name)
+            && value.equals(otherPair.value);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Instances of this class compare in name-major and value-minor
+     * order.</p>
+     */
+    public int compareTo(NameValuePair other) {
+        int result = name.compareTo(other.name);
+
+        if (result != 0) {
+            return result;
+        }
+
+        return value.compareTo(other.value);
+    }
+
+    /**
+     * Gets the name.
+     *
+     * @return {@code non-null;} the name
+     */
+    public CstUtf8 getName() {
+        return name;
+    }
+
+    /**
+     * Gets the value.
+     *
+     * @return {@code non-null;} the value
+     */
+    public Constant getValue() {
+        return value;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/AccessFlags.java b/dx/src/com/android/dx/rop/code/AccessFlags.java
new file mode 100644
index 0000000..2d84fe8
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/AccessFlags.java
@@ -0,0 +1,374 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * Constants used as "access flags" in various places in classes, and
+ * related utilities. Although, at the rop layer, flags are generally
+ * ignored, this is the layer of communication, and as such, this
+ * package is where these definitions belong. The flag definitions are
+ * identical to Java access flags, but {@code ACC_SUPER} isn't
+ * used at all in translated code, and {@code ACC_SYNCHRONIZED}
+ * is only used in a very limited way.
+ */
+public final class AccessFlags {
+    /** public member / class */
+    public static final int ACC_PUBLIC = 0x0001;
+
+    /** private member */
+    public static final int ACC_PRIVATE = 0x0002;
+
+    /** protected member */
+    public static final int ACC_PROTECTED = 0x0004;
+
+    /** static member */
+    public static final int ACC_STATIC = 0x0008;
+
+    /** final member / class */
+    public static final int ACC_FINAL = 0x0010;
+
+    /**
+     * synchronized method; only valid in dex files for {@code native}
+     * methods
+     */
+    public static final int ACC_SYNCHRONIZED = 0x0020;
+
+    /**
+     * class with new-style {@code invokespecial} for superclass
+     * method access
+     */
+    public static final int ACC_SUPER = 0x0020;
+
+    /** volatile field */
+    public static final int ACC_VOLATILE = 0x0040;
+
+    /** bridge method (generated) */
+    public static final int ACC_BRIDGE = 0x0040;
+
+    /** transient field */
+    public static final int ACC_TRANSIENT = 0x0080;
+
+    /** varargs method */
+    public static final int ACC_VARARGS = 0x0080;
+
+    /** native method */
+    public static final int ACC_NATIVE = 0x0100;
+
+    /** "class" is in fact an public static final interface */
+    public static final int ACC_INTERFACE = 0x0200;
+
+    /** abstract method / class */
+    public static final int ACC_ABSTRACT = 0x0400;
+
+    /**
+     * method with strict floating point ({@code strictfp})
+     * behavior
+     */
+    public static final int ACC_STRICT = 0x0800;
+
+    /** synthetic member */
+    public static final int ACC_SYNTHETIC = 0x1000;
+
+    /** class is an annotation type */
+    public static final int ACC_ANNOTATION = 0x2000;
+
+    /**
+     * class is an enumerated type; field is an element of an enumerated
+     * type
+     */
+    public static final int ACC_ENUM = 0x4000;
+
+    /** method is a constructor */
+    public static final int ACC_CONSTRUCTOR = 0x10000;
+
+    /**
+     * method was declared {@code synchronized}; has no effect on
+     * execution (other than inspecting this flag, per se)
+     */
+    public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000;
+
+    /** flags defined on classes */
+    public static final int CLASS_FLAGS =
+        ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT |
+        ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM;
+
+    /** flags defined on inner classes */
+    public static final int INNER_CLASS_FLAGS =
+        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+        ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION |
+        ACC_ENUM;
+
+    /** flags defined on fields */
+    public static final int FIELD_FLAGS =
+        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+        ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM;
+
+    /** flags defined on methods */
+    public static final int METHOD_FLAGS =
+        ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+        ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE |
+        ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR |
+        ACC_DECLARED_SYNCHRONIZED;
+
+    /** indicates conversion of class flags */
+    private static final int CONV_CLASS = 1;
+
+    /** indicates conversion of field flags */
+    private static final int CONV_FIELD = 2;
+
+    /** indicates conversion of method flags */
+    private static final int CONV_METHOD = 3;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private AccessFlags() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Returns a human-oriented string representing the given access flags,
+     * as defined on classes (not fields or methods).
+     *
+     * @param flags the flags
+     * @return {@code non-null;} human-oriented string
+     */
+    public static String classString(int flags) {
+        return humanHelper(flags, CLASS_FLAGS, CONV_CLASS);
+    }
+
+    /**
+     * Returns a human-oriented string representing the given access flags,
+     * as defined on inner classes.
+     *
+     * @param flags the flags
+     * @return {@code non-null;} human-oriented string
+     */
+    public static String innerClassString(int flags) {
+        return humanHelper(flags, INNER_CLASS_FLAGS, CONV_CLASS);
+    }
+
+    /**
+     * Returns a human-oriented string representing the given access flags,
+     * as defined on fields (not classes or methods).
+     *
+     * @param flags the flags
+     * @return {@code non-null;} human-oriented string
+     */
+    public static String fieldString(int flags) {
+        return humanHelper(flags, FIELD_FLAGS, CONV_FIELD);
+    }
+
+    /**
+     * Returns a human-oriented string representing the given access flags,
+     * as defined on methods (not classes or fields).
+     *
+     * @param flags the flags
+     * @return {@code non-null;} human-oriented string
+     */
+    public static String methodString(int flags) {
+        return humanHelper(flags, METHOD_FLAGS, CONV_METHOD);
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_PUBLIC} is on in the given
+     * flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_PUBLIC} flag
+     */
+    public static boolean isPublic(int flags) {
+        return (flags & ACC_PUBLIC) != 0;
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_PROTECTED} is on in the given
+     * flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_PROTECTED} flag
+     */
+    public static boolean isProtected(int flags) {
+        return (flags & ACC_PROTECTED) != 0;
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_PRIVATE} is on in the given
+     * flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_PRIVATE} flag
+     */
+    public static boolean isPrivate(int flags) {
+        return (flags & ACC_PRIVATE) != 0;
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_STATIC} is on in the given
+     * flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_STATIC} flag
+     */
+    public static boolean isStatic(int flags) {
+        return (flags & ACC_STATIC) != 0;
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_SYNCHRONIZED} is on in
+     * the given flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_SYNCHRONIZED} flag
+     */
+    public static boolean isSynchronized(int flags) {
+        return (flags & ACC_SYNCHRONIZED) != 0;
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_ABSTRACT} is on in the given
+     * flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_ABSTRACT} flag
+     */
+    public static boolean isAbstract(int flags) {
+        return (flags & ACC_ABSTRACT) != 0;
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_NATIVE} is on in the given
+     * flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_NATIVE} flag
+     */
+    public static boolean isNative(int flags) {
+        return (flags & ACC_NATIVE) != 0;
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_ANNOTATION} is on in the given
+     * flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_ANNOTATION} flag
+     */
+    public static boolean isAnnotation(int flags) {
+        return (flags & ACC_ANNOTATION) != 0;
+    }
+
+    /**
+     * Returns whether the flag {@code ACC_DECLARED_SYNCHRONIZED} is
+     * on in the given flags.
+     *
+     * @param flags the flags to check
+     * @return the value of the {@code ACC_DECLARED_SYNCHRONIZED} flag
+     */
+    public static boolean isDeclaredSynchronized(int flags) {
+        return (flags & ACC_DECLARED_SYNCHRONIZED) != 0;
+    }
+
+    /**
+     * Helper to return a human-oriented string representing the given
+     * access flags.
+     *
+     * @param flags the defined flags
+     * @param mask mask for the "defined" bits
+     * @param what what the flags represent (one of {@code CONV_*})
+     * @return {@code non-null;} human-oriented string
+     */
+    private static String humanHelper(int flags, int mask, int what) {
+        StringBuffer sb = new StringBuffer(80);
+        int extra = flags & ~mask;
+
+        flags &= mask;
+
+        if ((flags & ACC_PUBLIC) != 0) {
+            sb.append("|public");
+        }
+        if ((flags & ACC_PRIVATE) != 0) {
+            sb.append("|private");
+        }
+        if ((flags & ACC_PROTECTED) != 0) {
+            sb.append("|protected");
+        }
+        if ((flags & ACC_STATIC) != 0) {
+            sb.append("|static");
+        }
+        if ((flags & ACC_FINAL) != 0) {
+            sb.append("|final");
+        }
+        if ((flags & ACC_SYNCHRONIZED) != 0) {
+            if (what == CONV_CLASS) {
+                sb.append("|super");
+            } else {
+                sb.append("|synchronized");
+            }
+        }
+        if ((flags & ACC_VOLATILE) != 0) {
+            if (what == CONV_METHOD) {
+                sb.append("|bridge");
+            } else {
+                sb.append("|volatile");
+            }
+        }
+        if ((flags & ACC_TRANSIENT) != 0) {
+            if (what == CONV_METHOD) {
+                sb.append("|varargs");
+            } else {
+                sb.append("|transient");
+            }
+        }
+        if ((flags & ACC_NATIVE) != 0) {
+            sb.append("|native");
+        }
+        if ((flags & ACC_INTERFACE) != 0) {
+            sb.append("|interface");
+        }
+        if ((flags & ACC_ABSTRACT) != 0) {
+            sb.append("|abstract");
+        }
+        if ((flags & ACC_STRICT) != 0) {
+            sb.append("|strictfp");
+        }
+        if ((flags & ACC_SYNTHETIC) != 0) {
+            sb.append("|synthetic");
+        }
+        if ((flags & ACC_ANNOTATION) != 0) {
+            sb.append("|annotation");
+        }
+        if ((flags & ACC_ENUM) != 0) {
+            sb.append("|enum");
+        }
+        if ((flags & ACC_CONSTRUCTOR) != 0) {
+            sb.append("|constructor");
+        }
+        if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
+            sb.append("|declared_synchronized");
+        }
+
+        if ((extra != 0) || (sb.length() == 0)) {
+            sb.append('|');
+            sb.append(Hex.u2(extra));
+        }
+
+        return sb.substring(1);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/BasicBlock.java b/dx/src/com/android/dx/rop/code/BasicBlock.java
new file mode 100644
index 0000000..d6ee886
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/BasicBlock.java
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledItem;
+
+/**
+ * Basic block of register-based instructions.
+ */
+public final class BasicBlock implements LabeledItem {
+    /** {@code >= 0;} target label for this block */
+    private final int label;
+
+    /** {@code non-null;} list of instructions in this block */
+    private final InsnList insns;
+
+    /**
+     * {@code non-null;} full list of successors that this block may
+     * branch to
+     */
+    private final IntList successors;
+
+    /**
+     * {@code >= -1;} the primary / standard-flow / "default" successor, or
+     * {@code -1} if this block has no successors (that is, it
+     * exits the function/method)
+     */
+    private final int primarySuccessor;
+
+    /**
+     * Constructs an instance. The predecessor set is set to {@code null}.
+     *
+     * @param label {@code >= 0;} target label for this block
+     * @param insns {@code non-null;} list of instructions in this block
+     * @param successors {@code non-null;} full list of successors that this
+     * block may branch to
+     * @param primarySuccessor {@code >= -1;} the primary / standard-flow /
+     * "default" successor, or {@code -1} if this block has no
+     * successors (that is, it exits the function/method or is an
+     * unconditional throw)
+     */
+    public BasicBlock(int label, InsnList insns, IntList successors,
+                      int primarySuccessor) {
+        if (label < 0) {
+            throw new IllegalArgumentException("label < 0");
+        }
+
+        try {
+            insns.throwIfMutable();
+        } catch (NullPointerException ex) {
+            // Elucidate exception.
+            throw new NullPointerException("insns == null");
+        }
+
+        int sz = insns.size();
+
+        if (sz == 0) {
+            throw new IllegalArgumentException("insns.size() == 0");
+        }
+
+        for (int i = sz - 2; i >= 0; i--) {
+            Rop one = insns.get(i).getOpcode();
+            if (one.getBranchingness() != Rop.BRANCH_NONE) {
+                throw new IllegalArgumentException("insns[" + i + "] is a " +
+                                                   "branch or can throw");
+            }
+        }
+
+        Insn lastInsn = insns.get(sz - 1);
+        if (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) {
+            throw new IllegalArgumentException("insns does not end with " +
+                                               "a branch or throwing " +
+                                               "instruction");
+        }
+
+        try {
+            successors.throwIfMutable();
+        } catch (NullPointerException ex) {
+            // Elucidate exception.
+            throw new NullPointerException("successors == null");
+        }
+
+        if (primarySuccessor < -1) {
+            throw new IllegalArgumentException("primarySuccessor < -1");
+        }
+
+        if (primarySuccessor >= 0 && !successors.contains(primarySuccessor)) {
+            throw new IllegalArgumentException(
+                    "primarySuccessor not in successors");
+        }
+
+        this.label = label;
+        this.insns = insns;
+        this.successors = successors;
+        this.primarySuccessor = primarySuccessor;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Instances of this class compare by identity. That is,
+     * {@code x.equals(y)} is only true if {@code x == y}.
+     */
+    @Override
+    public boolean equals(Object other) {
+        return (this == other);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Return the identity hashcode of this instance. This is proper,
+     * since instances of this class compare by identity (see {@link #equals}).
+     */
+    @Override
+    public int hashCode() {
+        return System.identityHashCode(this);
+    }
+
+    /**
+     * Gets the target label of this block.
+     *
+     * @return {@code >= 0;} the label
+     */
+    public int getLabel() {
+        return label;
+    }
+
+    /**
+     * Gets the list of instructions inside this block.
+     *
+     * @return {@code non-null;} the instruction list
+     */
+    public InsnList getInsns() {
+        return insns;
+    }
+
+    /**
+     * Gets the list of successors that this block may branch to.
+     *
+     * @return {@code non-null;} the successors list
+     */
+    public IntList getSuccessors() {
+        return successors;
+    }
+
+    /**
+     * Gets the primary successor of this block.
+     *
+     * @return {@code >= -1;} the primary successor, or {@code -1} if this
+     * block has no successors at all
+     */
+    public int getPrimarySuccessor() {
+        return primarySuccessor;
+    }
+
+    /**
+     * Gets the secondary successor of this block. It is only valid to call
+     * this method on blocks that have exactly two successors.
+     *
+     * @return {@code >= 0;} the secondary successor
+     */
+    public int getSecondarySuccessor() {
+        if (successors.size() != 2) {
+            throw new UnsupportedOperationException(
+                    "block doesn't have exactly two successors");
+        }
+
+        int succ = successors.get(0);
+        if (succ == primarySuccessor) {
+            succ = successors.get(1);
+        }
+
+        return succ;
+    }
+
+    /**
+     * Gets the first instruction of this block. This is just a
+     * convenient shorthand for {@code getInsns().get(0)}.
+     *
+     * @return {@code non-null;} the first instruction
+     */
+    public Insn getFirstInsn() {
+        return insns.get(0);
+    }
+
+    /**
+     * Gets the last instruction of this block. This is just a
+     * convenient shorthand for {@code getInsns().getLast()}.
+     *
+     * @return {@code non-null;} the last instruction
+     */
+    public Insn getLastInsn() {
+        return insns.getLast();
+    }
+
+    /**
+     * Returns whether this block might throw an exception. This is
+     * just a convenient shorthand for {@code getLastInsn().canThrow()}.
+     *
+     * @return {@code true} iff this block might throw an
+     * exception
+     */
+    public boolean canThrow() {
+        return insns.getLast().canThrow();
+    }
+
+    /**
+     * Returns whether this block has any associated exception handlers.
+     * This is just a shorthand for inspecting the last instruction in
+     * the block to see if it could throw, and if so, whether it in fact
+     * has any associated handlers.
+     *
+     * @return {@code true} iff this block has any associated
+     * exception handlers
+     */
+    public boolean hasExceptionHandlers() {
+        Insn lastInsn = insns.getLast();
+        return lastInsn.getCatches().size() != 0;
+    }
+
+    /**
+     * Returns the exception handler types associated with this block,
+     * if any. This is just a shorthand for inspecting the last
+     * instruction in the block to see if it could throw, and if so,
+     * grabbing the catch list out of it. If not, this returns an
+     * empty list (not {@code null}).
+     *
+     * @return {@code non-null;} the exception handler types associated with
+     * this block
+     */
+    public TypeList getExceptionHandlerTypes() {
+        Insn lastInsn = insns.getLast();
+        return lastInsn.getCatches();
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the registers in each instruction are offset by the given
+     * amount.
+     *
+     * @param delta the amount to offset register numbers by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public BasicBlock withRegisterOffset(int delta) {
+        return new BasicBlock(label, insns.withRegisterOffset(delta),
+                              successors, primarySuccessor);
+    }
+
+    public String toString() {
+        return '{' + Hex.u2(label) + '}';
+    }
+
+    /**
+     * BasicBlock visitor interface
+     */
+    public interface Visitor {
+        /**
+         * Visits a basic block
+         * @param b block visited
+         */
+        public void visitBlock (BasicBlock b);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/BasicBlockList.java b/dx/src/com/android/dx/rop/code/BasicBlockList.java
new file mode 100644
index 0000000..ea7b497
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/BasicBlockList.java
@@ -0,0 +1,398 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledList;
+
+/**
+ * List of {@link BasicBlock} instances.
+ */
+public final class BasicBlockList extends LabeledList {
+    /**
+     * {@code >= -1;} the count of registers required by this method or
+     * {@code -1} if not yet calculated
+     */
+    private int regCount;
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null},
+     * and the first-block label is initially {@code -1}.
+     *
+     * @param size the size of the list
+     */
+    public BasicBlockList(int size) {
+        super(size);
+
+        regCount = -1;
+    }
+
+    /**
+     * Constructs a mutable copy for {@code getMutableCopy()}.
+     *
+     * @param old block to copy
+     */
+    private BasicBlockList (BasicBlockList old) {
+        super(old);
+        regCount = old.regCount;
+    }
+
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @return {@code non-null;} element at that index
+     */
+    public BasicBlock get(int n) {
+        return (BasicBlock) get0(n);
+    }
+
+    /**
+     * Sets the basic block at the given index.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param bb {@code null-ok;} the element to set at {@code n}
+     */
+    public void set(int n, BasicBlock bb) {
+        super.set(n, bb);
+
+        // Reset regCount, since it will need to be recalculated.
+        regCount = -1;
+    }
+
+    /**
+     * Returns how many registers this method requires. This is simply
+     * the maximum of register-number-plus-category referred to by this
+     * instance's instructions (indirectly through {@link BasicBlock}
+     * instances).
+     *
+     * @return {@code >= 0;} the register count
+     */
+    public int getRegCount() {
+        if (regCount == -1) {
+            RegCountVisitor visitor = new RegCountVisitor();
+            forEachInsn(visitor);
+            regCount = visitor.getRegCount();
+        }
+
+        return regCount;
+    }
+
+    /**
+     * Gets the total instruction count for this instance. This is the
+     * sum of the instruction counts of each block.
+     *
+     * @return {@code >= 0;} the total instruction count
+     */
+    public int getInstructionCount() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = (BasicBlock) getOrNull0(i);
+            if (one != null) {
+                result += one.getInsns().size();
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the total instruction count for this instance, ignoring
+     * mark-local instructions which are not actually emitted.
+     *
+     * @return {@code >= 0;} the total instruction count
+     */
+    public int getEffectiveInstructionCount() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = (BasicBlock) getOrNull0(i);
+            if (one != null) {
+                InsnList insns = one.getInsns();
+                int insnsSz = insns.size();
+
+                for (int j = 0; j < insnsSz; j++) {
+                    Insn insn = insns.get(j);
+
+                    if (insn.getOpcode().getOpcode() != RegOps.MARK_LOCAL) {
+                        result++;
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Gets the first block in the list with the given label, if any.
+     *
+     * @param label {@code >= 0;} the label to look for
+     * @return {@code non-null;} the so-labelled block
+     * @throws IllegalArgumentException thrown if the label isn't found
+     */
+    public BasicBlock labelToBlock(int label) {
+        int idx = indexOfLabel(label);
+
+        if (idx < 0) {
+            throw new IllegalArgumentException("no such label: "
+                    + Hex.u2(label));
+        }
+
+        return get(idx);
+    }
+
+    /**
+     * Visits each instruction of each block in the list, in order.
+     *
+     * @param visitor {@code non-null;} visitor to use
+     */
+    public void forEachInsn(Insn.Visitor visitor) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = get(i);
+            InsnList insns = one.getInsns();
+            insns.forEach(visitor);
+        }
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the registers in each instruction are offset by the given
+     * amount. Mutability of the result is inherited from the
+     * original.
+     *
+     * @param delta the amount to offset register numbers by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public BasicBlockList withRegisterOffset(int delta) {
+        int sz = size();
+        BasicBlockList result = new BasicBlockList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = (BasicBlock) get0(i);
+            if (one != null) {
+                result.set(i, one.withRegisterOffset(delta));
+            }
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a mutable copy of this list.
+     *
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public BasicBlockList getMutableCopy() {
+        return new BasicBlockList(this);
+    }
+
+    /**
+     * Gets the preferred successor for the given block. If the block
+     * only has one successor, then that is the preferred successor.
+     * Otherwise, if the block has a primay successor, then that is
+     * the preferred successor. If the block has no successors, then
+     * this returns {@code null}.
+     *
+     * @param block {@code non-null;} the block in question
+     * @return {@code null-ok;} the preferred successor, if any
+     */
+    public BasicBlock preferredSuccessorOf(BasicBlock block) {
+        int primarySuccessor = block.getPrimarySuccessor();
+        IntList successors = block.getSuccessors();
+        int succSize = successors.size();
+
+        switch (succSize) {
+            case 0: {
+                return null;
+            }
+            case 1: {
+                return labelToBlock(successors.get(0));
+            }
+        }
+
+        if (primarySuccessor != -1) {
+            return labelToBlock(primarySuccessor);
+        } else {
+            return labelToBlock(successors.get(0));
+        }
+    }
+
+    /**
+     * Compares the catches of two blocks for equality. This includes
+     * both the catch types and target labels.
+     *
+     * @param block1 {@code non-null;} one block to compare
+     * @param block2 {@code non-null;} the other block to compare
+     * @return {@code true} if the two blocks' non-primary successors
+     * are identical
+     */
+    public boolean catchesEqual(BasicBlock block1,
+            BasicBlock block2) {
+        TypeList catches1 = block1.getExceptionHandlerTypes();
+        TypeList catches2 = block2.getExceptionHandlerTypes();
+
+        if (!StdTypeList.equalContents(catches1, catches2)) {
+            return false;
+        }
+
+        IntList succ1 = block1.getSuccessors();
+        IntList succ2 = block2.getSuccessors();
+        int size = succ1.size(); // Both are guaranteed to be the same size.
+
+        int primary1 = block1.getPrimarySuccessor();
+        int primary2 = block2.getPrimarySuccessor();
+
+        if (((primary1 == -1) || (primary2 == -1))
+                && (primary1 != primary2)) {
+            /*
+             * For the current purpose, both blocks in question must
+             * either both have a primary or both not have a primary to
+             * be considered equal, and it turns out here that that's not
+             * the case.
+             */
+            return false;
+        }
+
+        for (int i = 0; i < size; i++) {
+            int label1 = succ1.get(i);
+            int label2 = succ2.get(i);
+
+            if (label1 == primary1) {
+                /*
+                 * It should be the case that block2's primary is at the
+                 * same index. If not, we consider the blocks unequal for
+                 * the current purpose.
+                 */
+                if (label2 != primary2) {
+                    return false;
+                }
+                continue;
+            }
+
+            if (label1 != label2) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Instruction visitor class for counting registers used.
+     */
+    private static class RegCountVisitor
+            implements Insn.Visitor {
+        /** {@code >= 0;} register count in-progress */
+        private int regCount;
+
+        /**
+         * Constructs an instance.
+         */
+        public RegCountVisitor() {
+            regCount = 0;
+        }
+
+        /**
+         * Gets the register count.
+         *
+         * @return {@code >= 0;} the count
+         */
+        public int getRegCount() {
+            return regCount;
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainInsn(PlainInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainCstInsn(PlainCstInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitchInsn(SwitchInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingInsn(ThrowingInsn insn) {
+            visit(insn);
+        }
+
+        /** {@inheritDoc} */
+        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+            visit(insn);
+        }
+
+        /**
+         * Helper for all the {@code visit*} methods.
+         *
+         * @param insn {@code non-null;} instruction being visited
+         */
+        private void visit(Insn insn) {
+            RegisterSpec result = insn.getResult();
+
+            if (result != null) {
+                processReg(result);
+            }
+
+            RegisterSpecList sources = insn.getSources();
+            int sz = sources.size();
+
+            for (int i = 0; i < sz; i++) {
+                processReg(sources.get(i));
+            }
+        }
+
+        /**
+         * Processes the given register spec.
+         *
+         * @param spec {@code non-null;} the register spec
+         */
+        private void processReg(RegisterSpec spec) {
+            int reg = spec.getNextReg();
+
+            if (reg > regCount) {
+                regCount = reg;
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java b/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java
new file mode 100644
index 0000000..6c48acf
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+/**
+ * Implementation of {@link TranslationAdvice} which conservatively answers
+ * {@code false} to all methods.
+ */
+public final class ConservativeTranslationAdvice
+        implements TranslationAdvice {
+    /** {@code non-null;} standard instance of this class */
+    public static final ConservativeTranslationAdvice THE_ONE =
+        new ConservativeTranslationAdvice();
+
+    /**
+     * This class is not publicly instantiable. Use {@link #THE_ONE}.
+     */
+    private ConservativeTranslationAdvice() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    public boolean hasConstantOperation(Rop opcode,
+            RegisterSpec sourceA, RegisterSpec sourceB) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public boolean requiresSourcesInOrder(Rop opcode,
+            RegisterSpecList sources) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public int getMaxOptimalRegisterCount() {
+        return Integer.MAX_VALUE;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/CstInsn.java b/dx/src/com/android/dx/rop/code/CstInsn.java
new file mode 100644
index 0000000..d7de2f4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/CstInsn.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Instruction which contains an explicit reference to a constant.
+ */
+public abstract class CstInsn
+        extends Insn {
+    /** {@code non-null;} the constant */
+    private final Constant cst;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param result {@code null-ok;} spec for the result, if any
+     * @param sources {@code non-null;} specs for all the sources
+     * @param cst {@code non-null;} constant
+     */
+    public CstInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+                   RegisterSpecList sources, Constant cst) {
+        super(opcode, position, result, sources);
+
+        if (cst == null) {
+            throw new NullPointerException("cst == null");
+        }
+
+        this.cst = cst;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getInlineString() {
+        return cst.toHuman();
+    }
+
+    /**
+     * Gets the constant.
+     *
+     * @return {@code non-null;} the constant
+     */
+    public Constant getConstant() {
+        return cst;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean contentEquals(Insn b) {
+        /*
+         * The cast (CstInsn)b below should always succeed since
+         * Insn.contentEquals compares classes of this and b.
+         */
+        return super.contentEquals(b)
+                && cst.equals(((CstInsn)b).getConstant());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java
new file mode 100644
index 0000000..8dbc00b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.type.Type;
+
+/**
+ * Implementation of {@link TranslationAdvice} which represents what
+ * the dex format will be able to represent.
+ */
+public final class DexTranslationAdvice
+        implements TranslationAdvice {
+    /** {@code non-null;} standard instance of this class */
+    public static final DexTranslationAdvice THE_ONE =
+        new DexTranslationAdvice();
+
+    /** debug advice for disabling invoke-range optimization */
+    public static final DexTranslationAdvice NO_SOURCES_IN_ORDER =
+        new DexTranslationAdvice(true);
+
+    /**
+     * The minimum source width, in register units, for an invoke
+     * instruction that requires its sources to be in order and contiguous.
+     */
+    private static final int MIN_INVOKE_IN_ORDER = 6;
+
+    /** when true: always returns false for requiresSourcesInOrder */
+    private final boolean disableSourcesInOrder;
+
+    /**
+     * This class is not publicly instantiable. Use {@link #THE_ONE}.
+     */
+    private DexTranslationAdvice() {
+        disableSourcesInOrder = false;
+    }
+
+    private DexTranslationAdvice(boolean disableInvokeRange) {
+        this.disableSourcesInOrder = disableInvokeRange;
+    }
+
+    /** {@inheritDoc} */
+    public boolean hasConstantOperation(Rop opcode,
+            RegisterSpec sourceA, RegisterSpec sourceB) {
+        if (sourceA.getType() != Type.INT) {
+            return false;
+        }
+
+        if (! (sourceB.getTypeBearer() instanceof CstInteger)) {
+            return false;
+        }
+
+        CstInteger cst = (CstInteger) sourceB.getTypeBearer();
+
+        // TODO handle rsub
+        switch (opcode.getOpcode()) {
+            // These have 8 and 16 bit cst representations
+            case RegOps.REM:
+            case RegOps.ADD:
+            case RegOps.MUL:
+            case RegOps.DIV:
+            case RegOps.AND:
+            case RegOps.OR:
+            case RegOps.XOR:
+                return cst.fitsIn16Bits();
+            // These only have 8 bit cst reps
+            case RegOps.SHL:
+            case RegOps.SHR:
+            case RegOps.USHR:
+                return cst.fitsIn8Bits();
+            default:
+                return false;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public boolean requiresSourcesInOrder(Rop opcode,
+            RegisterSpecList sources) {
+
+        return !disableSourcesInOrder && opcode.isCallLike()
+                && totalRopWidth(sources) >= MIN_INVOKE_IN_ORDER;
+    }
+
+    /**
+     * Calculates the total rop width of the list of SSA registers
+     *
+     * @param sources {@code non-null;} list of SSA registers
+     * @return {@code >= 0;} rop-form width in register units
+     */
+    private int totalRopWidth(RegisterSpecList sources) {
+        int sz = sources.size();
+        int total = 0;
+
+        for (int i = 0; i < sz; i++) {
+            total += sources.get(i).getCategory();
+        }
+
+        return total;
+    }
+
+    /** {@inheritDoc} */
+    public int getMaxOptimalRegisterCount() {
+        return 16;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/Exceptions.java b/dx/src/com/android/dx/rop/code/Exceptions.java
new file mode 100644
index 0000000..1e27a8c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Exceptions.java
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+
+/**
+ * Common exception types.
+ */
+public final class Exceptions {
+    /** {@code non-null;} the type {@code java.lang.ArithmeticException} */
+    public static final Type TYPE_ArithmeticException =
+        Type.intern("Ljava/lang/ArithmeticException;");
+
+    /**
+     * {@code non-null;} the type
+     * {@code java.lang.ArrayIndexOutOfBoundsException}
+     */
+    public static final Type TYPE_ArrayIndexOutOfBoundsException =
+        Type.intern("Ljava/lang/ArrayIndexOutOfBoundsException;");
+
+    /** {@code non-null;} the type {@code java.lang.ArrayStoreException} */
+    public static final Type TYPE_ArrayStoreException =
+        Type.intern("Ljava/lang/ArrayStoreException;");
+
+    /** {@code non-null;} the type {@code java.lang.ClassCastException} */
+    public static final Type TYPE_ClassCastException =
+        Type.intern("Ljava/lang/ClassCastException;");
+
+    /** {@code non-null;} the type {@code java.lang.Error} */
+    public static final Type TYPE_Error = Type.intern("Ljava/lang/Error;");
+
+    /**
+     * {@code non-null;} the type
+     * {@code java.lang.IllegalMonitorStateException}
+     */
+    public static final Type TYPE_IllegalMonitorStateException =
+        Type.intern("Ljava/lang/IllegalMonitorStateException;");
+
+    /** {@code non-null;} the type {@code java.lang.NegativeArraySizeException} */
+    public static final Type TYPE_NegativeArraySizeException =
+        Type.intern("Ljava/lang/NegativeArraySizeException;");
+
+    /** {@code non-null;} the type {@code java.lang.NullPointerException} */
+    public static final Type TYPE_NullPointerException =
+        Type.intern("Ljava/lang/NullPointerException;");
+
+    /** {@code non-null;} the list {@code [java.lang.Error]} */
+    public static final StdTypeList LIST_Error = StdTypeList.make(TYPE_Error);
+
+    /**
+     * {@code non-null;} the list {@code[java.lang.Error,
+     * java.lang.ArithmeticException]}
+     */
+    public static final StdTypeList LIST_Error_ArithmeticException =
+        StdTypeList.make(TYPE_Error, TYPE_ArithmeticException);
+
+    /**
+     * {@code non-null;} the list {@code[java.lang.Error,
+     * java.lang.ClassCastException]}
+     */
+    public static final StdTypeList LIST_Error_ClassCastException =
+        StdTypeList.make(TYPE_Error, TYPE_ClassCastException);
+
+    /**
+     * {@code non-null;} the list {@code [java.lang.Error,
+     * java.lang.NegativeArraySizeException]}
+     */
+    public static final StdTypeList LIST_Error_NegativeArraySizeException =
+        StdTypeList.make(TYPE_Error, TYPE_NegativeArraySizeException);
+
+    /**
+     * {@code non-null;} the list {@code [java.lang.Error,
+     * java.lang.NullPointerException]}
+     */
+    public static final StdTypeList LIST_Error_NullPointerException =
+        StdTypeList.make(TYPE_Error, TYPE_NullPointerException);
+
+    /**
+     * {@code non-null;} the list {@code [java.lang.Error,
+     * java.lang.NullPointerException,
+     * java.lang.ArrayIndexOutOfBoundsException]}
+     */
+    public static final StdTypeList LIST_Error_Null_ArrayIndexOutOfBounds =
+        StdTypeList.make(TYPE_Error,
+                      TYPE_NullPointerException,
+                      TYPE_ArrayIndexOutOfBoundsException);
+
+    /**
+     * {@code non-null;} the list {@code [java.lang.Error,
+     * java.lang.NullPointerException,
+     * java.lang.ArrayIndexOutOfBoundsException,
+     * java.lang.ArrayStoreException]}
+     */
+    public static final StdTypeList LIST_Error_Null_ArrayIndex_ArrayStore =
+        StdTypeList.make(TYPE_Error,
+                      TYPE_NullPointerException,
+                      TYPE_ArrayIndexOutOfBoundsException,
+                      TYPE_ArrayStoreException);
+
+    /**
+     * {@code non-null;} the list {@code [java.lang.Error,
+     * java.lang.NullPointerException,
+     * java.lang.IllegalMonitorStateException]}
+     */
+    public static final StdTypeList
+        LIST_Error_Null_IllegalMonitorStateException =
+        StdTypeList.make(TYPE_Error,
+                      TYPE_NullPointerException,
+                      TYPE_IllegalMonitorStateException);
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Exceptions() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java b/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java
new file mode 100644
index 0000000..ed9345d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java
@@ -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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.rop.type.StdTypeList;
+
+import java.util.ArrayList;
+
+/**
+ * Instruction which fills a newly created array with a predefined list of
+ * constant values.
+ */
+public final class FillArrayDataInsn
+        extends Insn {
+
+    /** non-null: initial values to fill the newly created array */
+    private final ArrayList<Constant> initValues;
+
+    /**
+     * non-null: type of the array. Will be used to determine the width of
+     * elements in the array-data table.
+     */
+    private final Constant arrayType;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param sources {@code non-null;} specs for all the sources
+     * @param initValues {@code non-null;} list of initial values to fill the array
+     * @param cst {@code non-null;} type of the new array
+     */
+    public FillArrayDataInsn(Rop opcode, SourcePosition position,
+                             RegisterSpecList sources,
+                             ArrayList<Constant> initValues,
+                             Constant cst) {
+        super(opcode, position, null, sources);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        this.initValues = initValues;
+        this.arrayType = cst;
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return StdTypeList.EMPTY;
+    }
+
+    /**
+     * Return the list of init values
+     * @return {@code non-null;} list of init values
+     */
+    public ArrayList<Constant> getInitValues() {
+        return initValues;
+    }
+
+    /**
+     * Return the type of the newly created array
+     * @return {@code non-null;} array type
+     */
+    public Constant getConstant() {
+        return arrayType;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitFillArrayDataInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        throw new  UnsupportedOperationException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new FillArrayDataInsn(getOpcode(), getPosition(),
+                                     getSources().withOffset(delta),
+                                     initValues, arrayType);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new FillArrayDataInsn(getOpcode(), getPosition(),
+                                     sources, initValues, arrayType);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/Insn.java b/dx/src/com/android/dx/rop/code/Insn.java
new file mode 100644
index 0000000..dad2852
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Insn.java
@@ -0,0 +1,458 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ToHuman;
+
+/**
+ * A register-based instruction. An instruction is the combination of
+ * an opcode (which specifies operation and source/result types), a
+ * list of actual sources and result registers/values, and additional
+ * information.
+ */
+public abstract class Insn implements ToHuman {
+    /** {@code non-null;} opcode */
+    private final Rop opcode;
+
+    /** {@code non-null;} source position */
+    private final SourcePosition position;
+
+    /** {@code null-ok;} spec for the result of this instruction, if any */
+    private final RegisterSpec result;
+
+    /** {@code non-null;} specs for all the sources of this instruction */
+    private final RegisterSpecList sources;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param result {@code null-ok;} spec for the result, if any
+     * @param sources {@code non-null;} specs for all the sources
+     */
+    public Insn(Rop opcode, SourcePosition position, RegisterSpec result,
+                RegisterSpecList sources) {
+        if (opcode == null) {
+            throw new NullPointerException("opcode == null");
+        }
+
+        if (position == null) {
+            throw new NullPointerException("position == null");
+        }
+
+        if (sources == null) {
+            throw new NullPointerException("sources == null");
+        }
+
+        this.opcode = opcode;
+        this.position = position;
+        this.result = result;
+        this.sources = sources;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Instances of this class compare by identity. That is,
+     * {@code x.equals(y)} is only true if {@code x == y}.
+     */
+    @Override
+    public final boolean equals(Object other) {
+        return (this == other);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This implementation returns the identity hashcode of this
+     * instance. This is proper, since instances of this class compare
+     * by identity (see {@link #equals}).
+     */
+    @Override
+    public final int hashCode() {
+        return System.identityHashCode(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return toStringWithInline(getInlineString());
+    }
+
+    /**
+     * Gets a human-oriented (and slightly lossy) string for this instance.
+     *
+     * @return {@code non-null;} the human string form
+     */
+    public String toHuman() {
+        return toHumanWithInline(getInlineString());
+    }
+
+    /**
+     * Gets an "inline" string portion for toHuman(), if available. This
+     * is the portion that appears after the Rop opcode
+     *
+     * @return {@code null-ok;} if non-null, the inline text for toHuman()
+     */
+    public String getInlineString() {
+        return null;
+    }
+
+    /**
+     * Gets the opcode.
+     *
+     * @return {@code non-null;} the opcode
+     */
+    public final Rop getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the source position.
+     *
+     * @return {@code non-null;} the source position
+     */
+    public final SourcePosition getPosition() {
+        return position;
+    }
+
+    /**
+     * Gets the result spec, if any. A return value of {@code null}
+     * means this instruction returns nothing.
+     *
+     * @return {@code null-ok;} the result spec, if any
+     */
+    public final RegisterSpec getResult() {
+        return result;
+    }
+
+    /**
+     * Gets the spec of a local variable assignment that occurs at this
+     * instruction, or null if no local variable assignment occurs. This
+     * may be the result register, or for {@code mark-local} insns
+     * it may be the source.
+     *
+     * @return {@code null-ok;} a named register spec or null
+     */
+    public final RegisterSpec getLocalAssignment() {
+        RegisterSpec assignment;
+        if (opcode.getOpcode() == RegOps.MARK_LOCAL) {
+            assignment = sources.get(0);
+        } else {
+            assignment = result;
+        }
+
+        if (assignment == null) {
+            return null;
+        }
+
+        LocalItem localItem = assignment.getLocalItem();
+
+        if (localItem == null) {
+            return null;
+        }
+
+        return assignment;
+    }
+
+    /**
+     * Gets the source specs.
+     *
+     * @return {@code non-null;} the source specs
+     */
+    public final RegisterSpecList getSources() {
+        return sources;
+    }
+
+    /**
+     * Gets whether this instruction can possibly throw an exception. This
+     * is just a convenient wrapper for {@code getOpcode().canThrow()}.
+     *
+     * @return {@code true} iff this instruction can possibly throw
+     */
+    public final boolean canThrow() {
+        return opcode.canThrow();
+    }
+
+    /**
+     * Gets the list of possibly-caught exceptions. This returns {@link
+     * StdTypeList#EMPTY} if this instruction has no handlers,
+     * which can be <i>either</i> if this instruction can't possibly
+     * throw or if it merely doesn't handle any of its possible
+     * exceptions. To determine whether this instruction can throw,
+     * use {@link #canThrow}.
+     *
+     * @return {@code non-null;} the catches list
+     */
+    public abstract TypeList getCatches();
+
+    /**
+     * Calls the appropriate method on the given visitor, depending on the
+     * class of this instance. Subclasses must override this.
+     *
+     * @param visitor {@code non-null;} the visitor to call on
+     */
+    public abstract void accept(Visitor visitor);
+
+    /**
+     * Returns an instance that is just like this one, except that it
+     * has a catch list with the given item appended to the end. This
+     * method throws an exception if this instance can't possibly
+     * throw. To determine whether this instruction can throw, use
+     * {@link #canThrow}.
+     *
+     * @param type {@code non-null;} type to append to the catch list
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public abstract Insn withAddedCatch(Type type);
+
+    /**
+     * Returns an instance that is just like this one, except that all
+     * register references have been offset by the given delta.
+     *
+     * @param delta the amount to offset register references by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public abstract Insn withRegisterOffset(int delta);
+
+    /**
+     * Returns an instance that is just like this one, except that, if
+     * possible, the insn is converted into a version in which the last
+     * source (if it is a constant) is represented directly rather than
+     * as a register reference. {@code this} is returned in cases where
+     * the translation is not possible.
+     *
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public Insn withLastSourceLiteral() {
+        return this;
+    }
+
+    /**
+     * Returns an exact copy of this Insn
+     *
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public Insn copy() {
+        return withRegisterOffset(0);
+    }
+
+
+    /**
+     * Compares, handling nulls safely
+     *
+     * @param a first object
+     * @param b second object
+     * @return true if they're equal or both null.
+     */
+    private static boolean equalsHandleNulls (Object a, Object b) {
+        return (a == b) || ((a != null) && a.equals(b));
+    }
+
+    /**
+     * Compares Insn contents, since {@code Insn.equals()} is defined
+     * to be an identity compare. Insn's are {@code contentEquals()}
+     * if they have the same opcode, registers, source position, and other
+     * metadata.
+     *
+     * @return true in the case described above
+     */
+    public boolean contentEquals(Insn b) {
+        return opcode == b.getOpcode()
+                && position.equals(b.getPosition())
+                && (getClass() == b.getClass())
+                && equalsHandleNulls(result, b.getResult())
+                && equalsHandleNulls(sources, b.getSources())
+                && StdTypeList.equalContents(getCatches(), b.getCatches());
+    }
+
+    /**
+     * Returns an instance that is just like this one, except
+     * with new result and source registers.
+     *
+     * @param result {@code null-ok;} new result register
+     * @param sources {@code non-null;} new sources registers
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public abstract Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources);
+
+    /**
+     * Returns the string form of this instance, with the given bit added in
+     * the standard location for an inline argument.
+     *
+     * @param extra {@code null-ok;} the inline argument string
+     * @return {@code non-null;} the string form
+     */
+    protected final String toStringWithInline(String extra) {
+        StringBuffer sb = new StringBuffer(80);
+
+        sb.append("Insn{");
+        sb.append(position);
+        sb.append(' ');
+        sb.append(opcode);
+
+        if (extra != null) {
+            sb.append(' ');
+            sb.append(extra);
+        }
+
+        sb.append(" :: ");
+
+        if (result != null) {
+            sb.append(result);
+            sb.append(" <- ");
+        }
+
+        sb.append(sources);
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns the human string form of this instance, with the given
+     * bit added in the standard location for an inline argument.
+     *
+     * @param extra {@code null-ok;} the inline argument string
+     * @return {@code non-null;} the human string form
+     */
+    protected final String toHumanWithInline(String extra) {
+        StringBuffer sb = new StringBuffer(80);
+
+        sb.append(position);
+        sb.append(": ");
+        sb.append(opcode.getNickname());
+
+        if (extra != null) {
+            sb.append("(");
+            sb.append(extra);
+            sb.append(")");
+        }
+
+        if (result == null) {
+            sb.append(" .");
+        } else {
+            sb.append(" ");
+            sb.append(result.toHuman());
+        }
+
+        sb.append(" <-");
+
+        int sz = sources.size();
+        if (sz == 0) {
+            sb.append(" .");
+        } else {
+            for (int i = 0; i < sz; i++) {
+                sb.append(" ");
+                sb.append(sources.get(i).toHuman());
+            }
+        }
+
+        return sb.toString();
+    }
+
+
+    /**
+     * Visitor interface for this (outer) class.
+     */
+    public static interface Visitor {
+        /**
+         * Visits a {@link PlainInsn}.
+         *
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitPlainInsn(PlainInsn insn);
+
+        /**
+         * Visits a {@link PlainCstInsn}.
+         *
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitPlainCstInsn(PlainCstInsn insn);
+
+        /**
+         * Visits a {@link SwitchInsn}.
+         *
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitSwitchInsn(SwitchInsn insn);
+
+        /**
+         * Visits a {@link ThrowingCstInsn}.
+         *
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitThrowingCstInsn(ThrowingCstInsn insn);
+
+        /**
+         * Visits a {@link ThrowingInsn}.
+         *
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitThrowingInsn(ThrowingInsn insn);
+
+        /**
+         * Visits a {@link FillArrayDataInsn}.
+         *
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitFillArrayDataInsn(FillArrayDataInsn insn);
+    }
+
+    /**
+     * Base implementation of {@link Visitor}, which has empty method
+     * bodies for all methods.
+     */
+    public static class BaseVisitor implements Visitor {
+        /** {@inheritDoc} */
+        public void visitPlainInsn(PlainInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitPlainCstInsn(PlainCstInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitSwitchInsn(SwitchInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitThrowingInsn(ThrowingInsn insn) {
+            // This space intentionally left blank.
+        }
+
+        /** {@inheritDoc} */
+        public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+            // This space intentionally left blank.
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/InsnList.java b/dx/src/com/android/dx/rop/code/InsnList.java
new file mode 100644
index 0000000..88abd72
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/InsnList.java
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link Insn} instances.
+ */
+public final class InsnList
+        extends FixedSizeList {
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public InsnList(int size) {
+        super(size);
+    }
+
+    /**
+     * Gets the element at the given index. It is an error to call
+     * this with the index for an element which was never set; if you
+     * do that, this will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @return {@code non-null;} element at that index
+     */
+    public Insn get(int n) {
+        return (Insn) get0(n);
+    }
+
+    /**
+     * Sets the instruction at the given index.
+     *
+     * @param n {@code >= 0, < size();} which index
+     * @param insn {@code non-null;} the instruction to set at {@code n}
+     */
+    public void set(int n, Insn insn) {
+        set0(n, insn);
+    }
+
+    /**
+     * Gets the last instruction. This is just a convenient shorthand for
+     * {@code get(size() - 1)}.
+     *
+     * @return {@code non-null;} the last instruction
+     */
+    public Insn getLast() {
+        return get(size() - 1);
+    }
+
+    /**
+     * Visits each instruction in the list, in order.
+     *
+     * @param visitor {@code non-null;} visitor to use
+     */
+    public void forEach(Insn.Visitor visitor) {
+        int sz = size();
+
+        for (int i = 0; i < sz; i++) {
+            get(i).accept(visitor);
+        }
+    }
+
+    /**
+     * Compares the contents of this {@code InsnList} with another.
+     * The blocks must have the same number of insns, and each Insn must
+     * also return true to {@code Insn.contentEquals()}.
+     *
+     * @param b to compare
+     * @return true in the case described above.
+     */
+    public boolean contentEquals(InsnList b) {
+        if (b == null) return false;
+
+        int sz = size();
+
+        if (sz != b.size()) return false;
+
+        for (int i = 0; i < sz; i++) {
+            if (!get(i).contentEquals(b.get(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the registers in each instruction are offset by the given
+     * amount. Mutability of the result is inherited from the
+     * original.
+     *
+     * @param delta the amount to offset register numbers by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public InsnList withRegisterOffset(int delta) {
+        int sz = size();
+        InsnList result = new InsnList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            Insn one = (Insn) get0(i);
+            if (one != null) {
+                result.set0(i, one.withRegisterOffset(delta));
+            }
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalItem.java b/dx/src/com/android/dx/rop/code/LocalItem.java
new file mode 100644
index 0000000..82b227c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalItem.java
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * A local variable item: either a name or a signature or both.
+ */
+public class LocalItem implements Comparable<LocalItem> {
+    /** {@code null-ok;} local variable name */
+    private final CstUtf8 name;
+
+    /** {@code null-ok;} local variable signature */
+    private final CstUtf8 signature;
+
+    /**
+     * Make a new item. If both name and signature are null, null is returned.
+     *
+     * TODO: intern these
+     *
+     * @param name {@code null-ok;} local variable name
+     * @param signature {@code null-ok;} local variable signature
+     * @return {@code non-null;} appropriate instance.
+     */
+    public static LocalItem make(CstUtf8 name, CstUtf8 signature) {
+        if (name == null && signature == null) {
+            return null;
+        }
+
+        return new LocalItem (name, signature);
+    }
+
+    /**
+     * Constructs instance.
+     *
+     * @param name {@code null-ok;} local variable name
+     * @param signature {@code null-ok;} local variable signature
+     */
+    private LocalItem(CstUtf8 name, CstUtf8 signature) {
+        this.name = name;
+        this.signature = signature;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof LocalItem)) {
+            return false;
+        }
+
+        LocalItem local = (LocalItem) other;
+
+        return 0 == compareTo(local);
+    }
+
+    /**
+     * Compares two strings like String.compareTo(), excepts treats a null
+     * as the least-possible string value.
+     *
+     * @return negative integer, zero, or positive integer in accordance
+     * with Comparable.compareTo()
+     */
+    private static int compareHandlesNulls(CstUtf8 a, CstUtf8 b) {
+        if (a == b) {
+            return 0;
+        } else if (a == null) {
+            return -1;
+        } else if (b == null) {
+            return 1;
+        } else {
+            return a.compareTo(b);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(LocalItem local) {
+        int ret;
+
+        ret = compareHandlesNulls(name, local.name);
+
+        if (ret != 0) {
+            return ret;
+        }
+
+        ret = compareHandlesNulls(signature, local.signature);
+
+        return ret;
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return (name == null ? 0 : name.hashCode()) * 31
+                + (signature == null ? 0 : signature.hashCode());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        if (name != null && signature == null) {
+            return name.toQuoted();
+        } else if (name == null && signature == null) {
+            return "";
+        }
+
+        return "[" + (name == null ? "" : name.toQuoted())
+                + "|" + (signature == null ? "" : signature.toQuoted());
+    }
+
+    /**
+     * Gets name.
+     *
+     * @return {@code null-ok;} name
+     */
+    public CstUtf8 getName() {
+        return name;
+    }
+
+    /**
+     * Gets signature.
+     *
+     * @return {@code null-ok;} signature
+     */
+    public CstUtf8 getSignature() {
+        return signature;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java b/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java
new file mode 100644
index 0000000..c2c4021
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+
+/**
+ * Code to figure out which local variables are active at which points in
+ * a method.
+ */
+public final class LocalVariableExtractor {
+    /** {@code non-null;} method being extracted from */
+    private final RopMethod method;
+
+    /** {@code non-null;} block list for the method */
+    private final BasicBlockList blocks;
+
+    /** {@code non-null;} result in-progress */
+    private final LocalVariableInfo resultInfo;
+
+    /** {@code non-null;} work set indicating blocks needing to be processed */
+    private final int[] workSet;
+
+    /**
+     * Extracts out all the local variable information from the given method.
+     *
+     * @param method {@code non-null;} the method to extract from
+     * @return {@code non-null;} the extracted information
+     */
+    public static LocalVariableInfo extract(RopMethod method) {
+        LocalVariableExtractor lve = new LocalVariableExtractor(method);
+        return lve.doit();
+    }
+
+    /**
+     * Constructs an instance. This method is private. Use {@link #extract}.
+     *
+     * @param method {@code non-null;} the method to extract from
+     */
+    private LocalVariableExtractor(RopMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        BasicBlockList blocks = method.getBlocks();
+        int maxLabel = blocks.getMaxLabel();
+
+        this.method = method;
+        this.blocks = blocks;
+        this.resultInfo = new LocalVariableInfo(method);
+        this.workSet = Bits.makeBitSet(maxLabel);
+    }
+
+    /**
+     * Does the extraction.
+     *
+     * @return {@code non-null;} the extracted information
+     */
+    private LocalVariableInfo doit() {
+        for (int label = method.getFirstLabel();
+             label >= 0;
+             label = Bits.findFirst(workSet, 0)) {
+            Bits.clear(workSet, label);
+            processBlock(label);
+        }
+
+        resultInfo.setImmutable();
+        return resultInfo;
+    }
+
+    /**
+     * Processes a single block.
+     *
+     * @param label {@code >= 0;} label of the block to process
+     */
+    private void processBlock(int label) {
+        RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(label);
+        BasicBlock block = blocks.labelToBlock(label);
+        InsnList insns = block.getInsns();
+        int insnSz = insns.size();
+
+        /*
+         * We may have to treat the last instruction specially: If it
+         * can (but doesn't always) throw, and the exception can be
+         * caught within the same method, then we need to use the
+         * state *before* executing it to be what is merged into
+         * exception targets.
+         */
+        boolean canThrowDuringLastInsn = block.hasExceptionHandlers() &&
+            (insns.getLast().getResult() != null);
+        int freezeSecondaryStateAt = insnSz - 1;
+        RegisterSpecSet secondaryState = primaryState;
+
+        /*
+         * Iterate over the instructions, adding information for each place
+         * that the active variable set changes.
+         */
+
+        for (int i = 0; i < insnSz; i++) {
+            if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) {
+                // Until this point, primaryState == secondaryState.
+                primaryState.setImmutable();
+                primaryState = primaryState.mutableCopy();
+            }
+
+            Insn insn = insns.get(i);
+            RegisterSpec result;
+
+            result = insn.getLocalAssignment();
+
+            if (result == null) {
+                /*
+                 * If an assignment assigns over an existing local, make
+                 * sure to mark the local as going out of scope.
+                 */
+
+                result = insn.getResult();
+
+                if (result != null
+                        && primaryState.get(result.getReg()) != null) {
+                    primaryState.remove(primaryState.get(result.getReg()));
+                }
+                continue;
+            }
+
+            result = result.withSimpleType();
+
+            RegisterSpec already = primaryState.get(result);
+            /*
+             * The equals() check ensures we only add new info if
+             * the instruction causes a change to the set of
+             * active variables.
+             */
+            if (!result.equals(already)) {
+                /*
+                 * If this insn represents a local moving from one register
+                 * to another, remove the association between the old register
+                 * and the local.
+                 */
+                RegisterSpec previous
+                        = primaryState.localItemToSpec(result.getLocalItem());
+
+                if (previous != null
+                        && (previous.getReg() != result.getReg())) {
+
+                    primaryState.remove(previous);
+                }
+
+                resultInfo.addAssignment(insn, result);
+                primaryState.put(result);
+            }
+        }
+
+        primaryState.setImmutable();
+
+        /*
+         * Merge this state into the start state for each successor,
+         * and update the work set where required (that is, in cases
+         * where the start state for a block changes).
+         */
+
+        IntList successors = block.getSuccessors();
+        int succSz = successors.size();
+        int primarySuccessor = block.getPrimarySuccessor();
+
+        for (int i = 0; i < succSz; i++) {
+            int succ = successors.get(i);
+            RegisterSpecSet state = (succ == primarySuccessor) ?
+                primaryState : secondaryState;
+
+            if (resultInfo.mergeStarts(succ, state)) {
+                Bits.set(workSet, succ);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalVariableInfo.java b/dx/src/com/android/dx/rop/code/LocalVariableInfo.java
new file mode 100644
index 0000000..99a10ee
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalVariableInfo.java
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.HashMap;
+
+/**
+ * Container for local variable information for a particular {@link
+ * RopMethod}.
+ */
+public final class LocalVariableInfo
+        extends MutabilityControl {
+    /** {@code >= 0;} the register count for the method */
+    private final int regCount;
+
+    /**
+     * {@code non-null;} {@link RegisterSpecSet} to use when indicating a block
+     * that has no locals; it is empty and immutable but has an appropriate
+     * max size for the method
+     */
+    private final RegisterSpecSet emptySet;
+
+    /**
+     * {@code non-null;} array consisting of register sets representing the
+     * sets of variables already assigned upon entry to each block,
+     * where array indices correspond to block labels
+     */
+    private final RegisterSpecSet[] blockStarts;
+
+    /** {@code non-null;} map from instructions to the variable each assigns */
+    private final HashMap<Insn, RegisterSpec> insnAssignments;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method {@code non-null;} the method being represented by this instance
+     */
+    public LocalVariableInfo(RopMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        BasicBlockList blocks = method.getBlocks();
+        int maxLabel = blocks.getMaxLabel();
+
+        this.regCount = blocks.getRegCount();
+        this.emptySet = new RegisterSpecSet(regCount);
+        this.blockStarts = new RegisterSpecSet[maxLabel];
+        this.insnAssignments =
+            new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount());
+
+        emptySet.setImmutable();
+    }
+
+    /**
+     * Sets the register set associated with the start of the block with
+     * the given label.
+     *
+     * @param label {@code >= 0;} the block label
+     * @param specs {@code non-null;} the register set to associate with the block
+     */
+    public void setStarts(int label, RegisterSpecSet specs) {
+        throwIfImmutable();
+
+        if (specs == null) {
+            throw new NullPointerException("specs == null");
+        }
+
+        try {
+            blockStarts[label] = specs;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus label");
+        }
+    }
+
+    /**
+     * Merges the given register set into the set for the block with the
+     * given label. If there was not already an associated set, then this
+     * is the same as calling {@link #setStarts}. Otherwise, this will
+     * merge the two sets and call {@link #setStarts} on the result of the
+     * merge.
+     *
+     * @param label {@code >= 0;} the block label
+     * @param specs {@code non-null;} the register set to merge into the start set
+     * for the block
+     * @return {@code true} if the merge resulted in an actual change
+     * to the associated set (including storing one for the first time) or
+     * {@code false} if there was no change
+     */
+    public boolean mergeStarts(int label, RegisterSpecSet specs) {
+        RegisterSpecSet start = getStarts0(label);
+        boolean changed = false;
+
+        if (start == null) {
+            setStarts(label, specs);
+            return true;
+        }
+
+        RegisterSpecSet newStart = start.mutableCopy();
+        newStart.intersect(specs, true);
+
+        if (start.equals(newStart)) {
+            return false;
+        }
+
+        newStart.setImmutable();
+        setStarts(label, newStart);
+
+        return true;
+    }
+
+    /**
+     * Gets the register set associated with the start of the block
+     * with the given label. This returns an empty set with the appropriate
+     * max size if no set was associated with the block in question.
+     *
+     * @param label {@code >= 0;} the block label
+     * @return {@code non-null;} the associated register set
+     */
+    public RegisterSpecSet getStarts(int label) {
+        RegisterSpecSet result = getStarts0(label);
+
+        return (result != null) ? result : emptySet;
+    }
+
+    /**
+     * Gets the register set associated with the start of the given
+     * block. This is just convenient shorthand for
+     * {@code getStarts(block.getLabel())}.
+     *
+     * @param block {@code non-null;} the block in question
+     * @return {@code non-null;} the associated register set
+     */
+    public RegisterSpecSet getStarts(BasicBlock block) {
+        return getStarts(block.getLabel());
+    }
+
+    /**
+     * Gets a mutable copy of the register set associated with the
+     * start of the block with the given label. This returns a
+     * newly-allocated empty {@link RegisterSpecSet} of appropriate
+     * max size if there is not yet any set associated with the block.
+     *
+     * @param label {@code >= 0;} the block label
+     * @return {@code non-null;} the associated register set
+     */
+    public RegisterSpecSet mutableCopyOfStarts(int label) {
+        RegisterSpecSet result = getStarts0(label);
+
+        return (result != null) ?
+            result.mutableCopy() : new RegisterSpecSet(regCount);
+    }
+
+    /**
+     * Adds an assignment association for the given instruction and
+     * register spec. This throws an exception if the instruction
+     * doesn't actually perform a named variable assignment.
+     *
+     * <b>Note:</b> Although the instruction contains its own spec for
+     * the result, it still needs to be passed in explicitly to this
+     * method, since the spec that is stored here should always have a
+     * simple type and the one in the instruction can be an arbitrary
+     * {@link TypeBearer} (such as a constant value).
+     *
+     * @param insn {@code non-null;} the instruction in question
+     * @param spec {@code non-null;} the associated register spec
+     */
+    public void addAssignment(Insn insn, RegisterSpec spec) {
+        throwIfImmutable();
+
+        if (insn == null) {
+            throw new NullPointerException("insn == null");
+        }
+
+        if (spec == null) {
+            throw new NullPointerException("spec == null");
+        }
+
+        insnAssignments.put(insn, spec);
+    }
+
+    /**
+     * Gets the named register being assigned by the given instruction, if
+     * previously stored in this instance.
+     *
+     * @param insn {@code non-null;} instruction in question
+     * @return {@code null-ok;} the named register being assigned, if any
+     */
+    public RegisterSpec getAssignment(Insn insn) {
+        return insnAssignments.get(insn);
+    }
+
+    /**
+     * Gets the number of assignments recorded by this instance.
+     *
+     * @return {@code >= 0;} the number of assignments
+     */
+    public int getAssignmentCount() {
+        return insnAssignments.size();
+    }
+
+    public void debugDump() {
+        for (int label = 0 ; label < blockStarts.length; label++) {
+            if (blockStarts[label] == null) {
+                continue;
+            }
+
+            if (blockStarts[label] == emptySet) {
+                System.out.printf("%04x: empty set\n", label);
+            } else {
+                System.out.printf("%04x: %s\n", label, blockStarts[label]);
+            }
+        }
+    }
+
+    /**
+     * Helper method, to get the starts for a label, throwing the
+     * right exception for range problems.
+     *
+     * @param label {@code >= 0;} the block label
+     * @return {@code null-ok;} associated register set or {@code null} if there
+     * is none
+     */
+    private RegisterSpecSet getStarts0(int label) {
+        try {
+            return blockStarts[label];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus label");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/PlainCstInsn.java b/dx/src/com/android/dx/rop/code/PlainCstInsn.java
new file mode 100644
index 0000000..fffa76b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/PlainCstInsn.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which contains an explicit reference to a constant
+ * but which cannot throw an exception.
+ */
+public final class PlainCstInsn
+        extends CstInsn {
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param result {@code null-ok;} spec for the result, if any
+     * @param sources {@code non-null;} specs for all the sources
+     * @param cst {@code non-null;} the constant
+     */
+    public PlainCstInsn(Rop opcode, SourcePosition position,
+                        RegisterSpec result, RegisterSpecList sources,
+                        Constant cst) {
+        super(opcode, position, result, sources, cst);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return StdTypeList.EMPTY;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitPlainCstInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new PlainCstInsn(getOpcode(), getPosition(),
+                                getResult().withOffset(delta),
+                                getSources().withOffset(delta),
+                                getConstant());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new PlainCstInsn(getOpcode(), getPosition(),
+                                result,
+                                sources,
+                                getConstant());
+
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/PlainInsn.java b/dx/src/com/android/dx/rop/code/PlainInsn.java
new file mode 100644
index 0000000..3fd2ba5
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/PlainInsn.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Plain instruction, which has no embedded data and which cannot possibly
+ * throw an exception.
+ */
+public final class PlainInsn
+        extends Insn {
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param result {@code null-ok;} spec for the result, if any
+     * @param sources {@code non-null;} specs for all the sources
+     */
+    public PlainInsn(Rop opcode, SourcePosition position,
+                     RegisterSpec result, RegisterSpecList sources) {
+        super(opcode, position, result, sources);
+
+        switch (opcode.getBranchingness()) {
+            case Rop.BRANCH_SWITCH:
+            case Rop.BRANCH_THROW: {
+                throw new IllegalArgumentException("bogus branchingness");
+            }
+        }
+
+        if (result != null && opcode.getBranchingness() != Rop.BRANCH_NONE) {
+            // move-result-pseudo is required here
+            throw new IllegalArgumentException
+                    ("can't mix branchingness with result");
+        }
+    }
+
+    /**
+     * Constructs a single-source instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param result {@code null-ok;} spec for the result, if any
+     * @param source {@code non-null;} spec for the source
+     */
+    public PlainInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+                     RegisterSpec source) {
+        this(opcode, position, result, RegisterSpecList.make(source));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return StdTypeList.EMPTY;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitPlainInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new PlainInsn(getOpcode(), getPosition(),
+                             getResult().withOffset(delta),
+                             getSources().withOffset(delta));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withLastSourceLiteral() {
+        RegisterSpecList sources = getSources();
+        int szSources = sources.size();
+
+        if (szSources == 0) {
+            return this;
+        }
+
+        TypeBearer lastType = sources.get(szSources - 1).getTypeBearer();
+
+        if (!lastType.isConstant()) {
+            return this;
+        }
+
+        Constant cst = (Constant) lastType;
+
+        RegisterSpecList newSources = sources.withoutLast();
+
+        Rop newRop;
+        try {
+            newRop = Rops.ropFor(getOpcode().getOpcode(),
+                    getResult(), newSources, (Constant)lastType);
+        } catch (IllegalArgumentException ex) {
+            // There's no rop for this case
+            return this;
+        }
+
+        return new PlainCstInsn(newRop, getPosition(),
+                getResult(), newSources, cst);
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new PlainInsn(getOpcode(), getPosition(),
+                             result,
+                             sources);
+
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegOps.java b/dx/src/com/android/dx/rop/code/RegOps.java
new file mode 100644
index 0000000..bdf9342
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegOps.java
@@ -0,0 +1,399 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * All the register-based opcodes, and related utilities.
+ *
+ * <p><b>Note:</b> Opcode descriptions use a rough pseudocode. {@code r}
+ * is the result register, {@code x} is the first argument,
+ * {@code y} is the second argument, and {@code z} is the
+ * third argument. The expression which describes
+ * the operation uses Java-ish syntax but is preceded by type indicators for
+ * each of the values.
+ */
+public final class RegOps {
+    /** {@code nop()} */
+    public static final int NOP = 1;
+
+    /** {@code T: any type; r,x: T :: r = x;} */
+    public static final int MOVE = 2;
+
+    /** {@code T: any type; r,param(x): T :: r = param(x)} */
+    public static final int MOVE_PARAM = 3;
+
+    /**
+     * {@code T: Throwable; r: T :: r = caught_exception}.
+     * <b>Note:</b> This opcode should only ever be used in the
+     * first instruction of a block, and such blocks must be
+     * the start of an exception handler.
+     */
+    public static final int MOVE_EXCEPTION = 4;
+
+    /** {@code T: any type; r, literal: T :: r = literal;} */
+    public static final int CONST = 5;
+
+    /** {@code goto label} */
+    public static final int GOTO = 6;
+
+    /**
+     * {@code T: int or Object; x,y: T :: if (x == y) goto
+     * label}
+     */
+    public static final int IF_EQ = 7;
+
+    /**
+     * {@code T: int or Object; x,y: T :: if (x != y) goto
+     * label}
+     */
+    public static final int IF_NE = 8;
+
+    /** {@code x,y: int :: if (x < y) goto label} */
+    public static final int IF_LT = 9;
+
+    /** {@code x,y: int :: if (x >= y) goto label} */
+    public static final int IF_GE = 10;
+
+    /** {@code x,y: int :: if (x <= y) goto label} */
+    public static final int IF_LE = 11;
+
+    /** {@code x,y: int :: if (x > y) goto label} */
+    public static final int IF_GT = 12;
+
+    /** {@code x: int :: goto table[x]} */
+    public static final int SWITCH = 13;
+
+    /** {@code T: any numeric type; r,x,y: T :: r = x + y} */
+    public static final int ADD = 14;
+
+    /** {@code T: any numeric type; r,x,y: T :: r = x - y} */
+    public static final int SUB = 15;
+
+    /** {@code T: any numeric type; r,x,y: T :: r = x * y} */
+    public static final int MUL = 16;
+
+    /** {@code T: any numeric type; r,x,y: T :: r = x / y} */
+    public static final int DIV = 17;
+
+    /**
+     * {@code T: any numeric type; r,x,y: T :: r = x % y}
+     * (Java-style remainder)
+     */
+    public static final int REM = 18;
+
+    /** {@code T: any numeric type; r,x: T :: r = -x} */
+    public static final int NEG = 19;
+
+    /** {@code T: any integral type; r,x,y: T :: r = x & y} */
+    public static final int AND = 20;
+
+    /** {@code T: any integral type; r,x,y: T :: r = x | y} */
+    public static final int OR = 21;
+
+    /** {@code T: any integral type; r,x,y: T :: r = x ^ y} */
+    public static final int XOR = 22;
+
+    /**
+     * {@code T: any integral type; r,x: T; y: int :: r = x << y}
+     */
+    public static final int SHL = 23;
+
+    /**
+     * {@code T: any integral type; r,x: T; y: int :: r = x >> y}
+     * (signed right-shift)
+     */
+    public static final int SHR = 24;
+
+    /**
+     * {@code T: any integral type; r,x: T; y: int :: r = x >>> y}
+     * (unsigned right-shift)
+     */
+    public static final int USHR = 25;
+
+    /** {@code T: any integral type; r,x: T :: r = ~x} */
+    public static final int NOT = 26;
+
+    /**
+     * {@code T: any numeric type; r: int; x,y: T :: r = (x == y) ? 0
+     * : (x > y) ? 1 : -1} (Java-style "cmpl" where a NaN is
+     * considered "less than" all other values; also used for integral
+     * comparisons)
+     */
+    public static final int CMPL = 27;
+
+    /**
+     * {@code T: any floating point type; r: int; x,y: T :: r = (x == y) ? 0
+     * : (x < y) ? -1 : 1} (Java-style "cmpg" where a NaN is
+     * considered "greater than" all other values)
+     */
+    public static final int CMPG = 28;
+
+    /**
+     * {@code T: any numeric type; U: any numeric type; r: T; x: U ::
+     * r = (T) x} (numeric type conversion between the four
+     * "real" numeric types)
+     */
+    public static final int CONV = 29;
+
+    /**
+     * {@code r,x: int :: r = (x << 24) >> 24} (Java-style
+     * convert int to byte)
+     */
+    public static final int TO_BYTE = 30;
+
+    /**
+     * {@code r,x: int :: r = x & 0xffff} (Java-style convert int to char)
+     */
+    public static final int TO_CHAR = 31;
+
+    /**
+     * {@code r,x: int :: r = (x << 16) >> 16} (Java-style
+     * convert int to short)
+     */
+    public static final int TO_SHORT = 32;
+
+    /** {@code T: return type for the method; x: T; return x} */
+    public static final int RETURN = 33;
+
+    /** {@code T: any type; r: int; x: T[]; :: r = x.length} */
+    public static final int ARRAY_LENGTH = 34;
+
+    /** {@code x: Throwable :: throw(x)} */
+    public static final int THROW = 35;
+
+    /** {@code x: Object :: monitorenter(x)} */
+    public static final int MONITOR_ENTER = 36;
+
+    /** {@code x: Object :: monitorexit(x)} */
+    public static final int MONITOR_EXIT = 37;
+
+    /** {@code T: any type; r: T; x: T[]; y: int :: r = x[y]} */
+    public static final int AGET = 38;
+
+    /** {@code T: any type; x: T; y: T[]; z: int :: x[y] = z} */
+    public static final int APUT = 39;
+
+    /**
+     * {@code T: any non-array object type :: r =
+     * alloc(T)} (allocate heap space for an object)
+     */
+    public static final int NEW_INSTANCE = 40;
+
+    /** {@code T: any array type; r: T; x: int :: r = new T[x]} */
+    public static final int NEW_ARRAY = 41;
+
+    /**
+     * {@code T: any array type; r: T; x: int; v0..vx: T :: r = new T[x]
+     * {v0, ..., vx}}
+     */
+    public static final int FILLED_NEW_ARRAY = 42;
+
+    /**
+     * {@code T: any object type; x: Object :: (T) x} (can
+     * throw {@code ClassCastException})
+     */
+    public static final int CHECK_CAST = 43;
+
+    /**
+     * {@code T: any object type; x: Object :: x instanceof T}
+     */
+    public static final int INSTANCE_OF = 44;
+
+    /**
+     * {@code T: any type; r: T; x: Object; f: instance field spec of
+     * type T :: r = x.f}
+     */
+    public static final int GET_FIELD = 45;
+
+    /**
+     * {@code T: any type; r: T; f: static field spec of type T :: r =
+     * f}
+     */
+    public static final int GET_STATIC = 46;
+
+    /**
+     * {@code T: any type; x: T; y: Object; f: instance field spec of type
+     * T :: y.f = x}
+     */
+    public static final int PUT_FIELD = 47;
+
+    /**
+     * {@code T: any type; f: static field spec of type T; x: T :: f = x}
+     */
+    public static final int PUT_STATIC = 48;
+
+    /**
+     * {@code Tr, T0, T1...: any types; r: Tr; m: static method spec;
+     * y0: T0; y1: T1 ... :: r = m(y0, y1, ...)} (call static
+     * method)
+     */
+    public static final int INVOKE_STATIC = 49;
+
+    /**
+     * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+     * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call normal
+     * virtual method)
+     */
+    public static final int INVOKE_VIRTUAL = 50;
+
+    /**
+     * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+     * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call
+     * superclass virtual method)
+     */
+    public static final int INVOKE_SUPER = 51;
+
+    /**
+     * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+     * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call
+     * direct/special method)
+     */
+    public static final int INVOKE_DIRECT = 52;
+
+    /**
+     * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: interface
+     * (instance) method spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1,
+     * ...)} (call interface method)
+     */
+    public static final int INVOKE_INTERFACE = 53;
+
+    /**
+     * {@code T0: any type; name: local variable name  :: mark(name,T0)}
+     * (mark beginning or end of local variable name)
+     */
+    public static final int MARK_LOCAL = 54;
+
+    /**
+     * {@code T: Any type; r: T :: r = return_type}.
+     * <b>Note:</b> This opcode should only ever be used in the
+     * first instruction of a block following an invoke-*.
+     */
+    public static final int MOVE_RESULT = 55;
+
+    /**
+     * {@code T: Any type; r: T :: r = return_type}.
+     * <b>Note:</b> This opcode should only ever be used in the
+     * first instruction of a block following a non-invoke throwing insn
+     */
+    public static final int MOVE_RESULT_PSEUDO = 56;
+
+    /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */
+    public static final int FILL_ARRAY_DATA = 57;
+
+    /**
+     * This class is uninstantiable.
+     */
+    private RegOps() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the name of the given opcode.
+     *
+     * @param opcode {@code >= 0, <= 255;} the opcode
+     * @return {@code non-null;} its name
+     */
+    public static String opName(int opcode) {
+        switch (opcode) {
+            case NOP: return "nop";
+            case MOVE: return "move";
+            case MOVE_PARAM: return "move-param";
+            case MOVE_EXCEPTION: return "move-exception";
+            case CONST: return "const";
+            case GOTO: return "goto";
+            case IF_EQ: return "if-eq";
+            case IF_NE: return "if-ne";
+            case IF_LT: return "if-lt";
+            case IF_GE: return "if-ge";
+            case IF_LE: return "if-le";
+            case IF_GT: return "if-gt";
+            case SWITCH: return "switch";
+            case ADD: return "add";
+            case SUB: return "sub";
+            case MUL: return "mul";
+            case DIV: return "div";
+            case REM: return "rem";
+            case NEG: return "neg";
+            case AND: return "and";
+            case OR: return "or";
+            case XOR: return "xor";
+            case SHL: return "shl";
+            case SHR: return "shr";
+            case USHR: return "ushr";
+            case NOT: return "not";
+            case CMPL: return "cmpl";
+            case CMPG: return "cmpg";
+            case CONV: return "conv";
+            case TO_BYTE: return "to-byte";
+            case TO_CHAR: return "to-char";
+            case TO_SHORT: return "to-short";
+            case RETURN: return "return";
+            case ARRAY_LENGTH: return "array-length";
+            case THROW: return "throw";
+            case MONITOR_ENTER: return "monitor-enter";
+            case MONITOR_EXIT: return "monitor-exit";
+            case AGET: return "aget";
+            case APUT: return "aput";
+            case NEW_INSTANCE: return "new-instance";
+            case NEW_ARRAY: return "new-array";
+            case FILLED_NEW_ARRAY: return "filled-new-array";
+            case CHECK_CAST: return "check-cast";
+            case INSTANCE_OF: return "instance-of";
+            case GET_FIELD: return "get-field";
+            case GET_STATIC: return "get-static";
+            case PUT_FIELD: return "put-field";
+            case PUT_STATIC: return "put-static";
+            case INVOKE_STATIC: return "invoke-static";
+            case INVOKE_VIRTUAL: return "invoke-virtual";
+            case INVOKE_SUPER: return "invoke-super";
+            case INVOKE_DIRECT: return "invoke-direct";
+            case INVOKE_INTERFACE: return "invoke-interface";
+            case MOVE_RESULT: return "move-result";
+            case MOVE_RESULT_PSEUDO: return "move-result-pseudo";
+            case FILL_ARRAY_DATA: return "fill-array-data";
+        }
+
+        return "unknown-" + Hex.u1(opcode);
+    }
+
+    /**
+     * Given an IF_* RegOp, returns the right-to-left flipped version. For
+     * example, IF_GT becomes IF_LT.
+     *
+     * @param opcode An IF_* RegOp
+     * @return flipped IF Regop
+     */
+    public static int flippedIfOpcode(final int opcode) {
+        switch (opcode) {
+            case RegOps.IF_EQ:
+            case RegOps.IF_NE:
+                return opcode;
+            case RegOps.IF_LT:
+                return RegOps.IF_GT;
+            case RegOps.IF_GE:
+                return RegOps.IF_LE;
+            case RegOps.IF_LE:
+                return RegOps.IF_GE;
+            case RegOps.IF_GT:
+                return RegOps.IF_LT;
+            default:
+                throw new RuntimeException("Unrecognized IF regop: " + opcode);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpec.java b/dx/src/com/android/dx/rop/code/RegisterSpec.java
new file mode 100644
index 0000000..f1ac563
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpec.java
@@ -0,0 +1,650 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ToHuman;
+
+import java.util.HashMap;
+
+/**
+ * Combination of a register number and a type, used as the sources and
+ * destinations of register-based operations.
+ */
+public final class RegisterSpec
+        implements TypeBearer, ToHuman, Comparable<RegisterSpec> {
+    /** {@code non-null;} string to prefix register numbers with */
+    public static final String PREFIX = "v";
+
+    /** {@code non-null;} intern table for instances */
+    private static final HashMap<Object, RegisterSpec> theInterns =
+        new HashMap<Object, RegisterSpec>(1000);
+
+    /** {@code non-null;} common comparison instance used while interning */
+    private static final ForComparison theInterningItem = new ForComparison();
+
+    /** {@code >= 0;} register number */
+    private final int reg;
+
+    /** {@code non-null;} type loaded or stored */
+    private final TypeBearer type;
+
+    /** {@code null-ok;} local variable info associated with this register, if any */
+    private final LocalItem local;
+
+    /**
+     * Intern the given triple as an instance of this class.
+     *
+     * @param reg {@code >= 0;} the register number
+     * @param type {@code non-null;} the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @param local {@code null-ok;} the associated local variable, if any
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    private static RegisterSpec intern(int reg, TypeBearer type,
+            LocalItem local) {
+        theInterningItem.set(reg, type, local);
+        RegisterSpec found = theInterns.get(theInterningItem);
+
+        if (found != null) {
+            return found;
+        }
+
+        found = theInterningItem.toRegisterSpec();
+        theInterns.put(found, found);
+        return found;
+    }
+
+    /**
+     * Returns an instance for the given register number and type, with
+     * no variable info. This method is allowed to return shared
+     * instances (but doesn't necessarily do so).
+     *
+     * @param reg {@code >= 0;} the register number
+     * @param type {@code non-null;} the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static RegisterSpec make(int reg, TypeBearer type) {
+        return intern(reg, type, null);
+    }
+
+    /**
+     * Returns an instance for the given register number, type, and
+     * variable info. This method is allowed to return shared
+     * instances (but doesn't necessarily do so).
+     *
+     * @param reg {@code >= 0;} the register number
+     * @param type {@code non-null;} the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @param local {@code non-null;} the associated local variable
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static RegisterSpec make(int reg, TypeBearer type,
+            LocalItem local) {
+        if (local == null) {
+            throw new NullPointerException("local  == null");
+        }
+
+        return intern(reg, type, local);
+    }
+
+    /**
+     * Returns an instance for the given register number, type, and
+     * variable info. This method is allowed to return shared
+     * instances (but doesn't necessarily do so).
+     *
+     * @param reg {@code >= 0;} the register number
+     * @param type {@code non-null;} the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @param local {@code null-ok;} the associated variable info or null for
+     * none
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static RegisterSpec makeLocalOptional(
+            int reg, TypeBearer type, LocalItem local) {
+
+        return intern(reg, type, local);
+    }
+
+    /**
+     * Gets the string form for the given register number.
+     *
+     * @param reg {@code >= 0;} the register number
+     * @return {@code non-null;} the string form
+     */
+    public static String regString(int reg) {
+        return PREFIX + reg;
+    }
+
+    /**
+     * Constructs an instance. This constructor is private. Use
+     * {@link #make}.
+     *
+     * @param reg {@code >= 0;} the register number
+     * @param type {@code non-null;} the type (or possibly actual value) which
+     * is loaded from or stored to the indicated register
+     * @param local {@code null-ok;} the associated local variable, if any
+     */
+    private RegisterSpec(int reg, TypeBearer type, LocalItem local) {
+        if (reg < 0) {
+            throw new IllegalArgumentException("reg < 0");
+        }
+
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        this.reg = reg;
+        this.type = type;
+        this.local = local;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof RegisterSpec)) {
+            if (other instanceof ForComparison) {
+                ForComparison fc = (ForComparison) other;
+                return equals(fc.reg, fc.type, fc.local);
+            }
+            return false;
+        }
+
+        RegisterSpec spec = (RegisterSpec) other;
+        return equals(spec.reg, spec.type, spec.local);
+    }
+
+    /**
+     * Like {@code equals}, but only consider the simple types of the
+     * registers. That is, this compares {@code getType()} on the types
+     * to ignore whatever arbitrary extra stuff might be carried around
+     * by an outer {@link TypeBearer}.
+     *
+     * @param other {@code null-ok;} spec to compare to
+     * @return {@code true} iff {@code this} and {@code other} are equal
+     * in the stated way
+     */
+    public boolean equalsUsingSimpleType(RegisterSpec other) {
+        if (!matchesVariable(other)) {
+            return false;
+        }
+
+        return (reg == other.reg);
+    }
+
+    /**
+     * Like {@link #equalsUsingSimpleType} but ignoring the register number.
+     * This is useful to determine if two instances refer to the "same"
+     * local variable.
+     *
+     * @param other {@code null-ok;} spec to compare to
+     * @return {@code true} iff {@code this} and {@code other} are equal
+     * in the stated way
+     */
+    public boolean matchesVariable(RegisterSpec other) {
+        if (other == null) {
+            return false;
+        }
+
+        return type.getType().equals(other.type.getType())
+            && ((local == other.local)
+                    || ((local != null) && local.equals(other.local)));
+    }
+
+    /**
+     * Helper for {@link #equals} and {@link #ForComparison.equals},
+     * which actually does the test.
+     *
+     * @param reg value of the instance variable, for another instance
+     * @param type value of the instance variable, for another instance
+     * @param local value of the instance variable, for another instance
+     * @return whether this instance is equal to one with the given
+     * values
+     */
+    private boolean equals(int reg, TypeBearer type, LocalItem local) {
+        return (this.reg == reg)
+            && this.type.equals(type)
+            && ((this.local == local)
+                    || ((this.local != null) && this.local.equals(local)));
+    }
+
+    /**
+     * Compares by (in priority order) register number, unwrapped type
+     * (that is types not {@link TypeBearer}s, and local info.
+     *
+     * @param other {@code non-null;} spec to compare to
+     * @return {@code -1..1;} standard result of comparison
+     */
+    public int compareTo(RegisterSpec other) {
+        if (this.reg < other.reg) {
+            return -1;
+        } else if (this.reg > other.reg) {
+            return 1;
+        }
+
+        int compare = type.getType().compareTo(other.type.getType());
+
+        if (compare != 0) {
+            return compare;
+        }
+
+        if (this.local == null) {
+            return (other.local == null) ? 0 : -1;
+        } else if (other.local == null) {
+            return 1;
+        }
+
+        return this.local.compareTo(other.local);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return hashCodeOf(reg, type, local);
+    }
+
+    /**
+     * Helper for {@link #hashCode} and {@link #ForComparison.hashCode},
+     * which actually does the calculation.
+     *
+     * @param reg value of the instance variable
+     * @param type value of the instance variable
+     * @param local value of the instance variable
+     * @return the hash code
+     */
+    private static int hashCodeOf(int reg, TypeBearer type, LocalItem local) {
+        int hash = (local != null) ? local.hashCode() : 0;
+
+        hash = (hash * 31 + type.hashCode()) * 31 + reg;
+        return hash;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return toString0(false);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toString0(true);
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return type.getType();
+    }
+
+    /** {@inheritDoc} */
+    public TypeBearer getFrameType() {
+        return type.getFrameType();
+    }
+
+    /** {@inheritDoc} */
+    public final int getBasicType() {
+        return type.getBasicType();
+    }
+
+    /** {@inheritDoc} */
+    public final int getBasicFrameType() {
+        return type.getBasicFrameType();
+    }
+
+    /** {@inheritDoc} */
+    public final boolean isConstant() {
+        return false;
+    }
+
+    /**
+     * Gets the register number.
+     *
+     * @return {@code >= 0;} the register number
+     */
+    public int getReg() {
+        return reg;
+    }
+
+    /**
+     * Gets the type (or actual value) which is loaded from or stored
+     * to the register associated with this instance.
+     *
+     * @return {@code non-null;} the type
+     */
+    public TypeBearer getTypeBearer() {
+        return type;
+    }
+
+    /**
+     * Gets the variable info associated with this instance, if any.
+     *
+     * @return {@code null-ok;} the variable info, or {@code null} if this
+     * instance has none
+     */
+    public LocalItem getLocalItem() {
+        return local;
+    }
+
+    /**
+     * Gets the next available register number after the one in this
+     * instance. This is equal to the register number plus the width
+     * (category) of the type used. Among other things, this may also
+     * be used to determine the minimum required register count
+     * implied by this instance.
+     *
+     * @return {@code >= 0;} the required registers size
+     */
+    public int getNextReg() {
+        return reg + getCategory();
+    }
+
+    /**
+     * Gets the category of this instance's type. This is just a convenient
+     * shorthand for {@code getType().getCategory()}.
+     *
+     * @see #isCategory1
+     * @see #isCategory2
+     * @return {@code 1..2;} the category of this instance's type
+     */
+    public int getCategory() {
+        return type.getType().getCategory();
+    }
+
+    /**
+     * Gets whether this instance's type is category 1. This is just a
+     * convenient shorthand for {@code getType().isCategory1()}.
+     *
+     * @see #getCategory
+     * @see #isCategory2
+     * @return whether or not this instance's type is of category 1
+     */
+    public boolean isCategory1() {
+        return type.getType().isCategory1();
+    }
+
+    /**
+     * Gets whether this instance's type is category 2. This is just a
+     * convenient shorthand for {@code getType().isCategory2()}.
+     *
+     * @see #getCategory
+     * @see #isCategory1
+     * @return whether or not this instance's type is of category 2
+     */
+    public boolean isCategory2() {
+        return type.getType().isCategory2();
+    }
+
+    /**
+     * Gets the string form for just the register number of this instance.
+     *
+     * @return {@code non-null;} the register string form
+     */
+    public String regString() {
+        return regString(reg);
+    }
+
+    /**
+     * Returns an instance that is the intersection between this instance
+     * and the given one, if any. The intersection is defined as follows:
+     *
+     * <ul>
+     *   <li>If {@code other} is {@code null}, then the result
+     *     is {@code null}.
+     *   <li>If the register numbers don't match, then the intersection
+     *     is {@code null}. Otherwise, the register number of the
+     *     intersection is the same as the one in the two instances.</li>
+     *   <li>If the types returned by {@code getType()} are not
+     *     {@code equals()}, then the intersection is null.</li>
+     *   <li>If the type bearers returned by {@code getTypeBearer()}
+     *     are {@code equals()}, then the intersection's type bearer
+     *     is the one from this instance. Otherwise, the intersection's
+     *     type bearer is the {@code getType()} of this instance.</li>
+     *   <li>If the locals are {@code equals()}, then the local info
+     *     of the intersection is the local info of this instance. Otherwise,
+     *     the local info of the intersection is {@code null}.</li>
+     * </ul>
+     *
+     * @param other {@code null-ok;} instance to intersect with (or {@code null})
+     * @param localPrimary whether local variables are primary to the
+     * intersection; if {@code true}, then the only non-null
+     * results occur when registers being intersected have equal local
+     * infos (or both have {@code null} local infos)
+     * @return {@code null-ok;} the intersection
+     */
+    public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) {
+        if (this == other) {
+            // Easy out.
+            return this;
+        }
+
+        if ((other == null) || (reg != other.getReg())) {
+            return null;
+        }
+
+        LocalItem resultLocal =
+            ((local == null) || !local.equals(other.getLocalItem()))
+            ? null : local;
+        boolean sameName = (resultLocal == local);
+
+        if (localPrimary && !sameName) {
+            return null;
+        }
+
+        Type thisType = getType();
+        Type otherType = other.getType();
+
+        // Note: Types are always interned.
+        if (thisType != otherType) {
+            return null;
+        }
+
+        TypeBearer resultTypeBearer =
+            type.equals(other.getTypeBearer()) ? type : thisType;
+
+        if ((resultTypeBearer == type) && sameName) {
+            // It turns out that the intersection is "this" after all.
+            return this;
+        }
+
+        return (resultLocal == null) ? make(reg, resultTypeBearer) :
+            make(reg, resultTypeBearer, resultLocal);
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that the
+     * register number is replaced by the given one.
+     *
+     * @param newReg {@code >= 0;} the new register number
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpec withReg(int newReg) {
+        if (reg == newReg) {
+            return this;
+        }
+
+        return makeLocalOptional(newReg, type, local);
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the type is replaced by the given one.
+     *
+     * @param newType {@code non-null;} the new type
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpec withType(TypeBearer newType) {
+        return makeLocalOptional(reg, newType, local);
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that the
+     * register number is offset by the given amount.
+     *
+     * @param delta the amount to offset the register number by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpec withOffset(int delta) {
+        if (delta == 0) {
+            return this;
+        }
+
+        return withReg(reg + delta);
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the type bearer is replaced by the actual underlying type
+     * (thereby stripping off non-type information) with any
+     * initialization information stripped away as well.
+     *
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpec withSimpleType() {
+        TypeBearer orig = type;
+        Type newType;
+
+        if (orig instanceof Type) {
+            newType = (Type) orig;
+        } else {
+            newType = orig.getType();
+        }
+
+        if (newType.isUninitialized()) {
+            newType = newType.getInitializedType();
+        }
+
+        if (newType == orig) {
+            return this;
+        }
+
+        return makeLocalOptional(reg, newType, local);
+    }
+
+    /**
+     * Returns an instance that is identical to this one except that the
+     * local variable is as specified in the parameter.
+     *
+     * @param local {@code null-ok;} the local item or null for none
+     * @return an appropriate instance
+     */
+    public RegisterSpec withLocalItem(LocalItem local) {
+        if ((this.local== local)
+                    || ((this.local != null) && this.local.equals(local))) {
+
+            return this;
+        }
+
+        return makeLocalOptional(reg, type, local);
+    }
+
+
+    /**
+     * Helper for {@link #toString} and {@link #toHuman}.
+     *
+     * @param human whether to be human-oriented
+     * @return {@code non-null;} the string form
+     */
+    private String toString0(boolean human) {
+        StringBuffer sb = new StringBuffer(40);
+
+        sb.append(regString());
+        sb.append(":");
+
+        if (local != null) {
+            sb.append(local.toString());
+        }
+
+        Type justType = type.getType();
+        sb.append(justType);
+
+        if (justType != type) {
+            sb.append("=");
+            if (human && (type instanceof Constant)) {
+                sb.append(((Constant) type).toHuman());
+            } else {
+                sb.append(type);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Holder of register spec data for the purposes of comparison (so that
+     * {@code RegisterSpec} itself can still keep {@code final}
+     * instance variables.
+     */
+    private static class ForComparison {
+        /** {@code >= 0;} register number */
+        private int reg;
+
+        /** {@code non-null;} type loaded or stored */
+        private TypeBearer type;
+
+        /**
+         * {@code null-ok;} local variable associated with this
+         * register, if any
+         */
+        private LocalItem local;
+
+        /**
+         * Set all the instance variables.
+         *
+         * @param reg {@code >= 0;} the register number
+         * @param type {@code non-null;} the type (or possibly actual
+         * value) which is loaded from or stored to the indicated
+         * register
+         * @param local {@code null-ok;} the associated local variable, if any
+         * @return {@code non-null;} an appropriately-constructed instance
+         */
+        public void set(int reg, TypeBearer type, LocalItem local) {
+            this.reg = reg;
+            this.type = type;
+            this.local = local;
+        }
+
+        /**
+         * Construct a {@code RegisterSpec} of this instance's
+         * contents.
+         *
+         * @return {@code non-null;} an appropriately-constructed instance
+         */
+        public RegisterSpec toRegisterSpec() {
+            return new RegisterSpec(reg, type, local);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof RegisterSpec)) {
+                return false;
+            }
+
+            RegisterSpec spec = (RegisterSpec) other;
+            return spec.equals(reg, type, local);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public int hashCode() {
+            return hashCodeOf(reg, type, local);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecList.java b/dx/src/com/android/dx/rop/code/RegisterSpecList.java
new file mode 100644
index 0000000..e900787
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecList.java
@@ -0,0 +1,362 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link RegisterSpec} instances.
+ */
+public final class RegisterSpecList
+        extends FixedSizeList implements TypeList {
+    /** {@code non-null;} no-element instance */
+    public static final RegisterSpecList EMPTY = new RegisterSpecList(0);
+
+    /**
+     * Makes a single-element instance.
+     *
+     * @param spec {@code non-null;} the element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static RegisterSpecList make(RegisterSpec spec) {
+        RegisterSpecList result = new RegisterSpecList(1);
+        result.set(0, spec);
+        return result;
+    }
+
+    /**
+     * Makes a two-element instance.
+     *
+     * @param spec0 {@code non-null;} the first element
+     * @param spec1 {@code non-null;} the second element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static RegisterSpecList make(RegisterSpec spec0,
+                                        RegisterSpec spec1) {
+        RegisterSpecList result = new RegisterSpecList(2);
+        result.set(0, spec0);
+        result.set(1, spec1);
+        return result;
+    }
+
+    /**
+     * Makes a three-element instance.
+     *
+     * @param spec0 {@code non-null;} the first element
+     * @param spec1 {@code non-null;} the second element
+     * @param spec2 {@code non-null;} the third element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
+                                        RegisterSpec spec2) {
+        RegisterSpecList result = new RegisterSpecList(3);
+        result.set(0, spec0);
+        result.set(1, spec1);
+        result.set(2, spec2);
+        return result;
+    }
+
+    /**
+     * Makes a four-element instance.
+     *
+     * @param spec0 {@code non-null;} the first element
+     * @param spec1 {@code non-null;} the second element
+     * @param spec2 {@code non-null;} the third element
+     * @param spec3 {@code non-null;} the fourth element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
+                                        RegisterSpec spec2,
+                                        RegisterSpec spec3) {
+        RegisterSpecList result = new RegisterSpecList(4);
+        result.set(0, spec0);
+        result.set(1, spec1);
+        result.set(2, spec2);
+        result.set(3, spec3);
+        return result;
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public RegisterSpecList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Type getType(int n) {
+        return get(n).getType().getType();
+    }
+
+    /** {@inheritDoc} */
+    public int getWordCount() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            result += getType(i).getCategory();
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public TypeList withAddedType(Type type) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @return {@code non-null;} the indicated element
+     */
+    public RegisterSpec get(int n) {
+        return (RegisterSpec) get0(n);
+    }
+
+    /**
+     * Returns a RegisterSpec in this list that uses the specified register,
+     * or null if there is none in this list.
+     * @param reg Register to find
+     * @return RegisterSpec that uses argument or null.
+     */
+    public RegisterSpec specForRegister(int reg) {
+        int sz = size();
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec rs;
+
+            rs = get(i);
+
+            if (rs.getReg() == reg) {
+                return rs;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the index of a RegisterSpec in this list that uses the specified
+     * register, or -1 if none in this list uses the register.
+     * @param reg Register to find
+     * @return index of RegisterSpec or -1
+     */
+    public int indexOfRegister(int reg) {
+        int sz = size();
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec rs;
+
+            rs = get(i);
+
+            if (rs.getReg() == reg) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Sets the element at the given index.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param spec {@code non-null;} the value to store
+     */
+    public void set(int n, RegisterSpec spec) {
+        set0(n, spec);
+    }
+
+    /**
+     * Gets the minimum required register count implied by this
+     * instance. This is equal to the highest register number referred
+     * to plus the widest width (largest category) of the type used in
+     * that register.
+     *
+     * @return {@code >= 0;} the required registers size
+     */
+    public int getRegistersSize() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec spec = (RegisterSpec) get0(i);
+            if (spec != null) {
+                int min = spec.getNextReg();
+                if (min > result) {
+                    result = min;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a new instance, which is the same as this instance,
+     * except that it has an additional element prepended to the original.
+     * Mutability of the result is inherited from the original.
+     *
+     * @param spec {@code non-null;} the new first spec (to prepend)
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpecList withFirst(RegisterSpec spec) {
+        int sz = size();
+        RegisterSpecList result = new RegisterSpecList(sz + 1);
+
+        for (int i = 0; i < sz; i++) {
+            result.set0(i + 1, get0(i));
+        }
+
+        result.set0(0, spec);
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a new instance, which is the same as this instance,
+     * except that its first element is removed. Mutability of the
+     * result is inherited from the original.
+     *
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpecList withoutFirst() {
+        int newSize = size() - 1;
+
+        if (newSize == 0) {
+            return EMPTY;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(newSize);
+
+        for (int i = 0; i < newSize; i++) {
+            result.set0(i, get0(i + 1));
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a new instance, which is the same as this instance,
+     * except that its last element is removed. Mutability of the
+     * result is inherited from the original.
+     *
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpecList withoutLast() {
+        int newSize = size() - 1;
+
+        if (newSize == 0) {
+            return EMPTY;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(newSize);
+
+        for (int i = 0; i < newSize; i++) {
+            result.set0(i, get0(i));
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * all register numbers are offset by the given amount. Mutability
+     * of the result is inherited from the original.
+     *
+     * @param delta the amount to offset the register numbers by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpecList withOffset(int delta) {
+        int sz = size();
+
+        if (sz == 0) {
+            // Don't bother making a new zero-element instance.
+            return this;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = (RegisterSpec) get0(i);
+            if (one != null) {
+                result.set0(i, one.withOffset(delta));
+            }
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * all register numbers are renumbered sequentially from the given
+     * base, with the first number duplicated if indicated.
+     *
+     * @param base the base register number
+     * @param duplicateFirst whether to duplicate the first number
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpecList withSequentialRegisters(int base,
+                                                    boolean duplicateFirst) {
+        int sz = size();
+
+        if (sz == 0) {
+            // Don't bother making a new zero-element instance.
+            return this;
+        }
+
+        RegisterSpecList result = new RegisterSpecList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec one = (RegisterSpec) get0(i);
+            result.set0(i, one.withReg(base));
+            if (duplicateFirst) {
+                duplicateFirst = false;
+            } else {
+                base += one.getCategory();
+            }
+        }
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
new file mode 100644
index 0000000..d16a82a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
@@ -0,0 +1,397 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Set of {@link RegisterSpec} instances, where a given register number
+ * may appear only once in the set.
+ */
+public final class RegisterSpecSet
+        extends MutabilityControl {
+    /** {@code non-null;} no-element instance */
+    public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0);
+
+    /**
+     * {@code non-null;} array of register specs, where each element is
+     * {@code null} or is an instance whose {@code reg}
+     * matches the array index
+     */
+    private final RegisterSpec[] specs;
+
+    /** {@code >= -1;} size of the set or {@code -1} if not yet calculated */
+    private int size;
+
+    /**
+     * Constructs an instance. The instance is initially empty.
+     *
+     * @param maxSize {@code >= 0;} the maximum register number (exclusive) that
+     * may be represented in this instance
+     */
+    public RegisterSpecSet(int maxSize) {
+        super(maxSize != 0);
+
+        this.specs = new RegisterSpec[maxSize];
+        this.size = 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof RegisterSpecSet)) {
+            return false;
+        }
+
+        RegisterSpecSet otherSet = (RegisterSpecSet) other;
+        RegisterSpec[] otherSpecs = otherSet.specs;
+        int len = specs.length;
+
+        if ((len != otherSpecs.length) || (size() != otherSet.size())) {
+            return false;
+        }
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec s1 = specs[i];
+            RegisterSpec s2 = otherSpecs[i];
+
+            if (s1 == s2) {
+                continue;
+            }
+
+            if ((s1 == null) || !s1.equals(s2)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        int len = specs.length;
+        int hash = 0;
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+            int oneHash = (spec == null) ? 0 : spec.hashCode();
+            hash = (hash * 31) + oneHash;
+        }
+
+        return hash;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int len = specs.length;
+        StringBuffer sb = new StringBuffer(len * 25);
+
+        sb.append('{');
+
+        boolean any = false;
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+            if (spec != null) {
+                if (any) {
+                    sb.append(", ");
+                } else {
+                    any = true;
+                }
+                sb.append(spec);
+            }
+        }
+
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /**
+     * Gets the maximum number of registers that may be in this instance, which
+     * is also the maximum-plus-one of register numbers that may be
+     * represented.
+     *
+     * @return {@code >= 0;} the maximum size
+     */
+    public int getMaxSize() {
+        return specs.length;
+    }
+
+    /**
+     * Gets the current size of this instance.
+     *
+     * @return {@code >= 0;} the size
+     */
+    public int size() {
+        int result = size;
+
+        if (result < 0) {
+            int len = specs.length;
+
+            result = 0;
+            for (int i = 0; i < len; i++) {
+                if (specs[i] != null) {
+                    result++;
+                }
+            }
+
+            size = result;
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the element with the given register number, if any.
+     *
+     * @param reg {@code >= 0;} the desired register number
+     * @return {@code null-ok;} the element with the given register number or
+     * {@code null} if there is none
+     */
+    public RegisterSpec get(int reg) {
+        try {
+            return specs[reg];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus reg");
+        }
+    }
+
+    /**
+     * Gets the element with the same register number as the given
+     * spec, if any. This is just a convenient shorthand for
+     * {@code get(spec.getReg())}.
+     *
+     * @param spec {@code non-null;} spec with the desired register number
+     * @return {@code null-ok;} the element with the matching register number or
+     * {@code null} if there is none
+     */
+    public RegisterSpec get(RegisterSpec spec) {
+        return get(spec.getReg());
+    }
+
+    /**
+     * Returns the spec in this set that's currently associated with a
+     * given local (type, name, and signature), or {@code null} if there is
+     * none. This ignores the register number of the given spec but
+     * matches on everything else.
+     *
+     * @param spec {@code non-null;} local to look for
+     * @return {@code null-ok;} first register found that matches, if any
+     */
+    public RegisterSpec findMatchingLocal(RegisterSpec spec) {
+        int length = specs.length;
+
+        for (int reg = 0; reg < length; reg++) {
+            RegisterSpec s = specs[reg];
+
+            if (s == null) {
+                continue;
+            }
+
+            if (spec.matchesVariable(s)) {
+                return s;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the spec in this set that's currently associated with a given
+     * local (name and signature), or {@code null} if there is none.
+     *
+     * @param local {@code non-null;} local item to search for
+     * @return {@code null-ok;} first register found with matching name and signature
+     */
+    public RegisterSpec localItemToSpec(LocalItem local) {
+        int length = specs.length;
+
+        for (int reg = 0; reg < length; reg++) {
+            RegisterSpec spec = specs[reg];
+
+            if ((spec != null) && local.equals(spec.getLocalItem())) {
+                return spec;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Removes a spec from the set. Only the register number
+     * of the parameter is significant.
+     *
+     * @param toRemove {@code non-null;} register to remove.
+     */
+    public void remove(RegisterSpec toRemove) {
+        try {
+            specs[toRemove.getReg()] = null;
+            size = -1;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus reg");
+        }
+    }
+
+    /**
+     * Puts the given spec into the set. If there is already an element in
+     * the set with the same register number, it is replaced. Additionally,
+     * if the previous element is for a category-2 register, then that
+     * previous element is nullified. Finally, if the given spec is for
+     * a category-2 register, then the immediately subsequent element
+     * is nullified.
+     *
+     * @param spec {@code non-null;} the register spec to put in the instance
+     */
+    public void put(RegisterSpec spec) {
+        throwIfImmutable();
+
+        if (spec == null) {
+            throw new NullPointerException("spec == null");
+        }
+
+        size = -1;
+
+        try {
+            int reg = spec.getReg();
+            specs[reg] = spec;
+
+            if (reg > 0) {
+                int prevReg = reg - 1;
+                RegisterSpec prevSpec = specs[prevReg];
+                if ((prevSpec != null) && (prevSpec.getCategory() == 2)) {
+                    specs[prevReg] = null;
+                }
+            }
+
+            if (spec.getCategory() == 2) {
+                specs[reg + 1] = null;
+            }
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("spec.getReg() out of range");
+        }
+    }
+
+    /**
+     * Put the entire contents of the given set into this one.
+     *
+     * @param set {@code non-null;} the set to put into this instance
+     */
+    public void putAll(RegisterSpecSet set) {
+        int max = set.getMaxSize();
+
+        for (int i = 0; i < max; i++) {
+            RegisterSpec spec = set.get(i);
+            if (spec != null) {
+                put(spec);
+            }
+        }
+    }
+
+    /**
+     * Intersects this instance with the given one, modifying this
+     * instance. The intersection consists of the pairwise
+     * {@link RegisterSpec#intersect} of corresponding elements from
+     * this instance and the given one where both are non-null.
+     *
+     * @param other {@code non-null;} set to intersect with
+     * @param localPrimary whether local variables are primary to
+     * the intersection; if {@code true}, then the only non-null
+     * result elements occur when registers being intersected have
+     * equal names (or both have {@code null} names)
+     */
+    public void intersect(RegisterSpecSet other, boolean localPrimary) {
+        throwIfImmutable();
+
+        RegisterSpec[] otherSpecs = other.specs;
+        int thisLen = specs.length;
+        int len = Math.min(thisLen, otherSpecs.length);
+
+        size = -1;
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+
+            if (spec == null) {
+                continue;
+            }
+
+            RegisterSpec intersection =
+                spec.intersect(otherSpecs[i], localPrimary);
+            if (intersection != spec) {
+                specs[i] = intersection;
+            }
+        }
+
+        for (int i = len; i < thisLen; i++) {
+            specs[i] = null;
+        }
+    }
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * all register numbers are offset by the given amount. Mutability
+     * of the result is inherited from the original.
+     *
+     * @param delta the amount to offset the register numbers by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RegisterSpecSet withOffset(int delta) {
+        int len = specs.length;
+        RegisterSpecSet result = new RegisterSpecSet(len + delta);
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+            if (spec != null) {
+                result.put(spec.withOffset(delta));
+            }
+        }
+
+        result.size = size;
+
+        if (isImmutable()) {
+            result.setImmutable();
+        }
+
+        return result;
+    }
+
+    /**
+     * Makes and return a mutable copy of this instance.
+     *
+     * @return {@code non-null;} the mutable copy
+     */
+    public RegisterSpecSet mutableCopy() {
+        int len = specs.length;
+        RegisterSpecSet copy = new RegisterSpecSet(len);
+
+        for (int i = 0; i < len; i++) {
+            RegisterSpec spec = specs[i];
+            if (spec != null) {
+                copy.put(spec);
+            }
+        }
+
+        copy.size = size;
+
+        return copy;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/Rop.java b/dx/src/com/android/dx/rop/code/Rop.java
new file mode 100644
index 0000000..8224584
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Rop.java
@@ -0,0 +1,407 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+
+/**
+ * Class that describes all the immutable parts of register-based operations.
+ */
+public final class Rop {
+    /** minimum {@code BRANCH_*} value */
+    public static final int BRANCH_MIN = 1;
+
+    /** indicates a non-branching op */
+    public static final int BRANCH_NONE = 1;
+
+    /** indicates a function/method return */
+    public static final int BRANCH_RETURN = 2;
+
+    /** indicates an unconditional goto */
+    public static final int BRANCH_GOTO = 3;
+
+    /** indicates a two-way branch */
+    public static final int BRANCH_IF = 4;
+
+    /** indicates a switch-style branch */
+    public static final int BRANCH_SWITCH = 5;
+
+    /** indicates a throw-style branch (both always-throws and may-throw) */
+    public static final int BRANCH_THROW = 6;
+
+    /** maximum {@code BRANCH_*} value */
+    public static final int BRANCH_MAX = 6;
+
+    /** the opcode; one of the constants in {@link RegOps} */
+    private final int opcode;
+
+    /**
+     * {@code non-null;} result type of this operation; {@link Type#VOID} for
+     * no-result operations
+     */
+    private final Type result;
+
+    /** {@code non-null;} types of all the sources of this operation */
+    private final TypeList sources;
+
+    /** {@code non-null;} list of possible types thrown by this operation */
+    private final TypeList exceptions;
+
+    /**
+     * the branchingness of this op; one of the {@code BRANCH_*}
+     * constants in this class
+     */
+    private final int branchingness;
+
+    /** whether this is a function/method call op or similar */
+    private final boolean isCallLike;
+
+    /** {@code null-ok;} nickname, if specified (used for debugging) */
+    private final String nickname;
+
+    /**
+     * Constructs an instance. This method is private. Use one of the
+     * public constructors.
+     *
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result {@code non-null;} result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources {@code non-null;} types of all the sources of this operation
+     * @param exceptions {@code non-null;} list of possible types thrown by this
+     * operation
+     * @param branchingness the branchingness of this op; one of the
+     * {@code BRANCH_*} constants
+     * @param isCallLike whether the op is a function/method call or similar
+     * @param nickname {@code null-ok;} optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources,
+               TypeList exceptions, int branchingness, boolean isCallLike,
+               String nickname) {
+        if (result == null) {
+            throw new NullPointerException("result == null");
+        }
+
+        if (sources == null) {
+            throw new NullPointerException("sources == null");
+        }
+
+        if (exceptions == null) {
+            throw new NullPointerException("exceptions == null");
+        }
+
+        if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) {
+            throw new IllegalArgumentException("exceptions / branchingness " +
+                                               "mismatch");
+        }
+
+        this.opcode = opcode;
+        this.result = result;
+        this.sources = sources;
+        this.exceptions = exceptions;
+        this.branchingness = branchingness;
+        this.isCallLike = isCallLike;
+        this.nickname = nickname;
+    }
+
+    /**
+     * Constructs an instance. The constructed instance is never a
+     * call-like op (see {@link #isCallLike}).
+     *
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result {@code non-null;} result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources {@code non-null;} types of all the sources of this operation
+     * @param exceptions {@code non-null;} list of possible types thrown by this
+     * operation
+     * @param branchingness the branchingness of this op; one of the
+     * {@code BRANCH_*} constants
+     * @param nickname {@code null-ok;} optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources,
+               TypeList exceptions, int branchingness, String nickname) {
+        this(opcode, result, sources, exceptions, branchingness, false,
+             nickname);
+    }
+
+    /**
+     * Constructs a no-exception instance. The constructed instance is never a
+     * call-like op (see {@link #isCallLike}).
+     *
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result {@code non-null;} result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources {@code non-null;} types of all the sources of this operation
+     * @param branchingness the branchingness of this op; one of the
+     * {@code BRANCH_*} constants
+     * @param nickname {@code null-ok;} optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources, int branchingness,
+               String nickname) {
+        this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false,
+             nickname);
+    }
+
+    /**
+     * Constructs a non-branching no-exception instance. The
+     * {@code branchingness} is always {@code BRANCH_NONE},
+     * and it is never a call-like op (see {@link #isCallLike}).
+     *
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result {@code non-null;} result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources {@code non-null;} types of all the sources of this operation
+     * @param nickname {@code null-ok;} optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources, String nickname) {
+        this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE,
+             false, nickname);
+    }
+
+    /**
+     * Constructs a non-empty exceptions instance. Its
+     * {@code branchingness} is always {@code BRANCH_THROW},
+     * but it is never a call-like op (see {@link #isCallLike}).
+     *
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param result {@code non-null;} result type of this operation; {@link
+     * Type#VOID} for no-result operations
+     * @param sources {@code non-null;} types of all the sources of this operation
+     * @param exceptions {@code non-null;} list of possible types thrown by this
+     * operation
+     * @param nickname {@code null-ok;} optional nickname (used for debugging)
+     */
+    public Rop(int opcode, Type result, TypeList sources, TypeList exceptions,
+               String nickname) {
+        this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false,
+             nickname);
+    }
+
+    /**
+     * Constructs a non-nicknamed instance with non-empty exceptions, which
+     * is always a call-like op (see {@link #isCallLike}). Its
+     * {@code branchingness} is always {@code BRANCH_THROW}.
+     *
+     * @param opcode the opcode; one of the constants in {@link RegOps}
+     * @param sources {@code non-null;} types of all the sources of this operation
+     * @param exceptions {@code non-null;} list of possible types thrown by this
+     * operation
+     */
+    public Rop(int opcode, TypeList sources, TypeList exceptions) {
+        this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true,
+             null);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            // Easy out.
+            return true;
+        }
+
+        if (!(other instanceof Rop)) {
+            return false;
+        }
+
+        Rop rop = (Rop) other;
+
+        return (opcode == rop.opcode) &&
+            (branchingness == rop.branchingness) &&
+            (result == rop.result) &&
+            sources.equals(rop.sources) &&
+            exceptions.equals(rop.exceptions);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        int h = (opcode * 31) + branchingness;
+        h = (h * 31) + result.hashCode();
+        h = (h * 31) + sources.hashCode();
+        h = (h * 31) + exceptions.hashCode();
+
+        return h;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(40);
+
+        sb.append("Rop{");
+
+        sb.append(RegOps.opName(opcode));
+
+        if (result != Type.VOID) {
+            sb.append(" ");
+            sb.append(result);
+        } else {
+            sb.append(" .");
+        }
+
+        sb.append(" <-");
+
+        int sz = sources.size();
+        if (sz == 0) {
+            sb.append(" .");
+        } else {
+            for (int i = 0; i < sz; i++) {
+                sb.append(' ');
+                sb.append(sources.getType(i));
+            }
+        }
+
+        if (isCallLike) {
+            sb.append(" call");
+        }
+
+        sz = exceptions.size();
+        if (sz != 0) {
+            sb.append(" throws");
+            for (int i = 0; i < sz; i++) {
+                sb.append(' ');
+                Type one = exceptions.getType(i);
+                if (one == Type.THROWABLE) {
+                    sb.append("<any>");
+                } else {
+                    sb.append(exceptions.getType(i));
+                }
+            }
+        } else {
+            switch (branchingness) {
+                case BRANCH_NONE:   sb.append(" flows"); break;
+                case BRANCH_RETURN: sb.append(" returns"); break;
+                case BRANCH_GOTO:   sb.append(" gotos"); break;
+                case BRANCH_IF:     sb.append(" ifs"); break;
+                case BRANCH_SWITCH: sb.append(" switches"); break;
+                default: sb.append(" " + Hex.u1(branchingness)); break;
+            }
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the opcode.
+     *
+     * @return the opcode
+     */
+    public int getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Gets the result type. A return value of {@link Type#VOID}
+     * means this operation returns nothing.
+     *
+     * @return {@code null-ok;} the result spec
+     */
+    public Type getResult() {
+        return result;
+    }
+
+    /**
+     * Gets the source types.
+     *
+     * @return {@code non-null;} the source types
+     */
+    public TypeList getSources() {
+        return sources;
+    }
+
+    /**
+     * Gets the list of exception types that might be thrown.
+     *
+     * @return {@code non-null;} the list of exception types
+     */
+    public TypeList getExceptions() {
+        return exceptions;
+    }
+
+    /**
+     * Gets the branchingness of this instance.
+     *
+     * @return the branchingness
+     */
+    public int getBranchingness() {
+        return branchingness;
+    }
+
+    /**
+     * Gets whether this opcode is a function/method call or similar.
+     *
+     * @return {@code true} iff this opcode is call-like
+     */
+    public boolean isCallLike() {
+        return isCallLike;
+    }
+
+
+    /**
+     * Gets whether this opcode is commutative (the order of its sources are
+     * unimportant) or not. All commutative Rops have exactly two sources and
+     * have no branchiness.
+     *
+     * @return true if rop is commutative
+     */
+    public boolean isCommutative() {
+        switch (opcode) {
+            case RegOps.AND:
+            case RegOps.OR:
+            case RegOps.XOR:
+            case RegOps.ADD:
+            case RegOps.MUL:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Gets the nickname. If this instance has no nickname, this returns
+     * the result of calling {@link #toString}.
+     *
+     * @return {@code non-null;} the nickname
+     */
+    public String getNickname() {
+        if (nickname != null) {
+            return nickname;
+        }
+
+        return toString();
+    }
+
+    /**
+     * Gets whether this operation can possibly throw an exception. This
+     * is just a convenient wrapper for
+     * {@code getExceptions().size() != 0}.
+     *
+     * @return {@code true} iff this operation can possibly throw
+     */
+    public final boolean canThrow() {
+        return (exceptions.size() != 0);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/RopMethod.java b/dx/src/com/android/dx/rop/code/RopMethod.java
new file mode 100644
index 0000000..591d325
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RopMethod.java
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Bits;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * All of the parts that make up a method at the rop layer.
+ */
+public final class RopMethod {
+    /** {@code non-null;} basic block list of the method */
+    private final BasicBlockList blocks;
+
+    /** {@code >= 0;} label for the block which starts the method */
+    private final int firstLabel;
+
+    /**
+     * {@code null-ok;} array of predecessors for each block, indexed by block
+     * label
+     */
+    private IntList[] predecessors;
+
+    /**
+     * {@code null-ok;} the predecessors for the implicit "exit" block, that is
+     * the labels for the blocks that return, if calculated
+     */
+    private IntList exitPredecessors;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param blocks {@code non-null;} basic block list of the method
+     * @param firstLabel {@code >= 0;} the label of the first block to execute
+     */
+    public RopMethod(BasicBlockList blocks, int firstLabel) {
+        if (blocks == null) {
+            throw new NullPointerException("blocks == null");
+        }
+
+        if (firstLabel < 0) {
+            throw new IllegalArgumentException("firstLabel < 0");
+        }
+
+        this.blocks = blocks;
+        this.firstLabel = firstLabel;
+
+        this.predecessors = null;
+        this.exitPredecessors = null;
+    }
+
+    /**
+     * Gets the basic block list for this method.
+     *
+     * @return {@code non-null;} the list
+     */
+    public BasicBlockList getBlocks() {
+        return blocks;
+    }
+
+    /**
+     * Gets the label for the first block in the method that this list
+     * represents.
+     *
+     * @return {@code >= 0;} the first-block label
+     */
+    public int getFirstLabel() {
+        return firstLabel;
+    }
+
+    /**
+     * Gets the predecessors associated with the given block. This throws
+     * an exception if there is no block with the given label.
+     *
+     * @param label {@code >= 0;} the label of the block in question
+     * @return {@code non-null;} the predecessors of that block
+     */
+    public IntList labelToPredecessors(int label) {
+        if (exitPredecessors == null) {
+            calcPredecessors();
+        }
+
+        IntList result = predecessors[label];
+
+        if (result == null) {
+            throw new RuntimeException("no such block: " + Hex.u2(label));
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets the exit predecessors for this instance.
+     *
+     * @return {@code non-null;} the exit predecessors
+     */
+    public IntList getExitPredecessors() {
+        if (exitPredecessors == null) {
+            calcPredecessors();
+        }
+
+        return exitPredecessors;
+    }
+
+
+    /**
+     * Returns an instance that is identical to this one, except that
+     * the registers in each instruction are offset by the given
+     * amount.
+     *
+     * @param delta the amount to offset register numbers by
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public RopMethod withRegisterOffset(int delta) {
+        RopMethod result = new RopMethod(blocks.withRegisterOffset(delta),
+                                         firstLabel);
+
+        if (exitPredecessors != null) {
+            /*
+             * The predecessors have been calculated. It's safe to
+             * inject these into the new instance, since the
+             * transformation being applied doesn't affect the
+             * predecessors.
+             */
+            result.exitPredecessors = exitPredecessors;
+            result.predecessors = predecessors;
+        }
+
+        return result;
+    }
+
+    /**
+     * Calculates the predecessor sets for each block as well as for the
+     * exit.
+     */
+    private void calcPredecessors() {
+        int maxLabel = blocks.getMaxLabel();
+        IntList[] predecessors = new IntList[maxLabel];
+        IntList exitPredecessors = new IntList(10);
+        int sz = blocks.size();
+
+        /*
+         * For each block, find its successors, and add the block's label to
+         * the successor's predecessors.
+         */
+        for (int i = 0; i < sz; i++) {
+            BasicBlock one = blocks.get(i);
+            int label = one.getLabel();
+            IntList successors = one.getSuccessors();
+            int ssz = successors.size();
+            if (ssz == 0) {
+                // This block exits.
+                exitPredecessors.add(label);
+            } else {
+                for (int j = 0; j < ssz; j++) {
+                    int succLabel = successors.get(j);
+                    IntList succPreds = predecessors[succLabel];
+                    if (succPreds == null) {
+                        succPreds = new IntList(10);
+                        predecessors[succLabel] = succPreds;
+                    }
+                    succPreds.add(label);
+                }
+            }
+        }
+
+        // Sort and immutablize all the predecessor lists.
+        for (int i = 0; i < maxLabel; i++) {
+            IntList preds = predecessors[i];
+            if (preds != null) {
+                preds.sort();
+                preds.setImmutable();
+            }
+        }
+
+        exitPredecessors.sort();
+        exitPredecessors.setImmutable();
+
+        /*
+         * The start label might not ever have had any predecessors
+         * added to it (probably doesn't, because of how Java gets
+         * translated into rop form). So, check for this and rectify
+         * the situation if required.
+         */
+        if (predecessors[firstLabel] == null) {
+            predecessors[firstLabel] = IntList.EMPTY;
+        }
+
+        this.predecessors = predecessors;
+        this.exitPredecessors = exitPredecessors;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/Rops.java b/dx/src/com/android/dx/rop/code/Rops.java
new file mode 100644
index 0000000..9085ff4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Rops.java
@@ -0,0 +1,2086 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Standard instances of {@link Rop}.
+ */
+public final class Rops {
+    /** {@code nop()} */
+    public static final Rop NOP =
+        new Rop(RegOps.NOP, Type.VOID, StdTypeList.EMPTY, "nop");
+
+    /** {@code r,x: int :: r = x;} */
+    public static final Rop MOVE_INT =
+        new Rop(RegOps.MOVE, Type.INT, StdTypeList.INT, "move-int");
+
+    /** {@code r,x: long :: r = x;} */
+    public static final Rop MOVE_LONG =
+        new Rop(RegOps.MOVE, Type.LONG, StdTypeList.LONG, "move-long");
+
+    /** {@code r,x: float :: r = x;} */
+    public static final Rop MOVE_FLOAT =
+        new Rop(RegOps.MOVE, Type.FLOAT, StdTypeList.FLOAT, "move-float");
+
+    /** {@code r,x: double :: r = x;} */
+    public static final Rop MOVE_DOUBLE =
+        new Rop(RegOps.MOVE, Type.DOUBLE, StdTypeList.DOUBLE, "move-double");
+
+    /** {@code r,x: Object :: r = x;} */
+    public static final Rop MOVE_OBJECT =
+        new Rop(RegOps.MOVE, Type.OBJECT, StdTypeList.OBJECT, "move-object");
+
+    /**
+     * {@code r,x: ReturnAddress :: r = x;}
+     *
+     * Note that this rop-form instruction has no dex-form equivilent and
+     * must be removed before the dex conversion.
+     */
+    public static final Rop MOVE_RETURN_ADDRESS =
+        new Rop(RegOps.MOVE, Type.RETURN_ADDRESS,
+                StdTypeList.RETURN_ADDRESS, "move-return-address");
+
+    /** {@code r,param(x): int :: r = param(x);} */
+    public static final Rop MOVE_PARAM_INT =
+        new Rop(RegOps.MOVE_PARAM, Type.INT, StdTypeList.EMPTY,
+                "move-param-int");
+
+    /** {@code r,param(x): long :: r = param(x);} */
+    public static final Rop MOVE_PARAM_LONG =
+        new Rop(RegOps.MOVE_PARAM, Type.LONG, StdTypeList.EMPTY,
+                "move-param-long");
+
+    /** {@code r,param(x): float :: r = param(x);} */
+    public static final Rop MOVE_PARAM_FLOAT =
+        new Rop(RegOps.MOVE_PARAM, Type.FLOAT, StdTypeList.EMPTY,
+                "move-param-float");
+
+    /** {@code r,param(x): double :: r = param(x);} */
+    public static final Rop MOVE_PARAM_DOUBLE =
+        new Rop(RegOps.MOVE_PARAM, Type.DOUBLE, StdTypeList.EMPTY,
+                "move-param-double");
+
+    /** {@code r,param(x): Object :: r = param(x);} */
+    public static final Rop MOVE_PARAM_OBJECT =
+        new Rop(RegOps.MOVE_PARAM, Type.OBJECT, StdTypeList.EMPTY,
+                "move-param-object");
+
+    /** {@code r, literal: int :: r = literal;} */
+    public static final Rop CONST_INT =
+        new Rop(RegOps.CONST, Type.INT, StdTypeList.EMPTY, "const-int");
+
+    /** {@code r, literal: long :: r = literal;} */
+    public static final Rop CONST_LONG =
+        new Rop(RegOps.CONST, Type.LONG, StdTypeList.EMPTY, "const-long");
+
+    /** {@code r, literal: float :: r = literal;} */
+    public static final Rop CONST_FLOAT =
+        new Rop(RegOps.CONST, Type.FLOAT, StdTypeList.EMPTY, "const-float");
+
+    /** {@code r, literal: double :: r = literal;} */
+    public static final Rop CONST_DOUBLE =
+        new Rop(RegOps.CONST, Type.DOUBLE, StdTypeList.EMPTY, "const-double");
+
+    /** {@code r, literal: Object :: r = literal;} */
+    public static final Rop CONST_OBJECT =
+        new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "const-object");
+
+    /** {@code r, literal: Object :: r = literal;} */
+    public static final Rop CONST_OBJECT_NOTHROW =
+        new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY,
+                "const-object-nothrow");
+
+    /** {@code goto label} */
+    public static final Rop GOTO =
+        new Rop(RegOps.GOTO, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_GOTO,
+                "goto");
+
+    /** {@code x: int :: if (x == 0) goto label} */
+    public static final Rop IF_EQZ_INT =
+        new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-eqz-int");
+
+    /** {@code x: int :: if (x != 0) goto label} */
+    public static final Rop IF_NEZ_INT =
+        new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-nez-int");
+
+    /** {@code x: int :: if (x < 0) goto label} */
+    public static final Rop IF_LTZ_INT =
+        new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-ltz-int");
+
+    /** {@code x: int :: if (x >= 0) goto label} */
+    public static final Rop IF_GEZ_INT =
+        new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-gez-int");
+
+    /** {@code x: int :: if (x <= 0) goto label} */
+    public static final Rop IF_LEZ_INT =
+        new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-lez-int");
+
+    /** {@code x: int :: if (x > 0) goto label} */
+    public static final Rop IF_GTZ_INT =
+        new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+                "if-gtz-int");
+
+    /** {@code x: Object :: if (x == null) goto label} */
+    public static final Rop IF_EQZ_OBJECT =
+        new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF,
+                "if-eqz-object");
+
+    /** {@code x: Object :: if (x != null) goto label} */
+    public static final Rop IF_NEZ_OBJECT =
+        new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF,
+                "if-nez-object");
+
+    /** {@code x,y: int :: if (x == y) goto label} */
+    public static final Rop IF_EQ_INT =
+        new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-eq-int");
+
+    /** {@code x,y: int :: if (x != y) goto label} */
+    public static final Rop IF_NE_INT =
+        new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-ne-int");
+
+    /** {@code x,y: int :: if (x < y) goto label} */
+    public static final Rop IF_LT_INT =
+        new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-lt-int");
+
+    /** {@code x,y: int :: if (x >= y) goto label} */
+    public static final Rop IF_GE_INT =
+        new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-ge-int");
+
+    /** {@code x,y: int :: if (x <= y) goto label} */
+    public static final Rop IF_LE_INT =
+        new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-le-int");
+
+    /** {@code x,y: int :: if (x > y) goto label} */
+    public static final Rop IF_GT_INT =
+        new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+                "if-gt-int");
+
+    /** {@code x,y: Object :: if (x == y) goto label} */
+    public static final Rop IF_EQ_OBJECT =
+        new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT_OBJECT,
+                Rop.BRANCH_IF, "if-eq-object");
+
+    /** {@code x,y: Object :: if (x != y) goto label} */
+    public static final Rop IF_NE_OBJECT =
+        new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT_OBJECT,
+                Rop.BRANCH_IF, "if-ne-object");
+
+    /** {@code x: int :: goto switchtable[x]} */
+    public static final Rop SWITCH =
+        new Rop(RegOps.SWITCH, Type.VOID, StdTypeList.INT, Rop.BRANCH_SWITCH,
+                "switch");
+
+    /** {@code r,x,y: int :: r = x + y;} */
+    public static final Rop ADD_INT =
+        new Rop(RegOps.ADD, Type.INT, StdTypeList.INT_INT, "add-int");
+
+    /** {@code r,x,y: long :: r = x + y;} */
+    public static final Rop ADD_LONG =
+        new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG_LONG, "add-long");
+
+    /** {@code r,x,y: float :: r = x + y;} */
+    public static final Rop ADD_FLOAT =
+        new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "add-float");
+
+    /** {@code r,x,y: double :: r = x + y;} */
+    public static final Rop ADD_DOUBLE =
+        new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                Rop.BRANCH_NONE, "add-double");
+
+    /** {@code r,x,y: int :: r = x - y;} */
+    public static final Rop SUB_INT =
+        new Rop(RegOps.SUB, Type.INT, StdTypeList.INT_INT, "sub-int");
+
+    /** {@code r,x,y: long :: r = x - y;} */
+    public static final Rop SUB_LONG =
+        new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG_LONG, "sub-long");
+
+    /** {@code r,x,y: float :: r = x - y;} */
+    public static final Rop SUB_FLOAT =
+        new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "sub-float");
+
+    /** {@code r,x,y: double :: r = x - y;} */
+    public static final Rop SUB_DOUBLE =
+        new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                Rop.BRANCH_NONE, "sub-double");
+
+    /** {@code r,x,y: int :: r = x * y;} */
+    public static final Rop MUL_INT =
+        new Rop(RegOps.MUL, Type.INT, StdTypeList.INT_INT, "mul-int");
+
+    /** {@code r,x,y: long :: r = x * y;} */
+    public static final Rop MUL_LONG =
+        new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG_LONG, "mul-long");
+
+    /** {@code r,x,y: float :: r = x * y;} */
+    public static final Rop MUL_FLOAT =
+        new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "mul-float");
+
+    /** {@code r,x,y: double :: r = x * y;} */
+    public static final Rop MUL_DOUBLE =
+        new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                Rop.BRANCH_NONE, "mul-double");
+
+    /** {@code r,x,y: int :: r = x / y;} */
+    public static final Rop DIV_INT =
+        new Rop(RegOps.DIV, Type.INT, StdTypeList.INT_INT,
+                Exceptions.LIST_Error_ArithmeticException, "div-int");
+
+    /** {@code r,x,y: long :: r = x / y;} */
+    public static final Rop DIV_LONG =
+        new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG_LONG,
+                Exceptions.LIST_Error_ArithmeticException, "div-long");
+
+    /** {@code r,x,y: float :: r = x / y;} */
+    public static final Rop DIV_FLOAT =
+        new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "div-float");
+
+    /** {@code r,x,y: double :: r = x / y;} */
+    public static final Rop DIV_DOUBLE =
+        new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                "div-double");
+
+    /** {@code r,x,y: int :: r = x % y;} */
+    public static final Rop REM_INT =
+        new Rop(RegOps.REM, Type.INT, StdTypeList.INT_INT,
+                Exceptions.LIST_Error_ArithmeticException, "rem-int");
+
+    /** {@code r,x,y: long :: r = x % y;} */
+    public static final Rop REM_LONG =
+        new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG_LONG,
+                Exceptions.LIST_Error_ArithmeticException, "rem-long");
+
+    /** {@code r,x,y: float :: r = x % y;} */
+    public static final Rop REM_FLOAT =
+        new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "rem-float");
+
+    /** {@code r,x,y: double :: r = x % y;} */
+    public static final Rop REM_DOUBLE =
+        new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+                "rem-double");
+
+    /** {@code r,x: int :: r = -x;} */
+    public static final Rop NEG_INT =
+        new Rop(RegOps.NEG, Type.INT, StdTypeList.INT, "neg-int");
+
+    /** {@code r,x: long :: r = -x;} */
+    public static final Rop NEG_LONG =
+        new Rop(RegOps.NEG, Type.LONG, StdTypeList.LONG, "neg-long");
+
+    /** {@code r,x: float :: r = -x;} */
+    public static final Rop NEG_FLOAT =
+        new Rop(RegOps.NEG, Type.FLOAT, StdTypeList.FLOAT, "neg-float");
+
+    /** {@code r,x: double :: r = -x;} */
+    public static final Rop NEG_DOUBLE =
+        new Rop(RegOps.NEG, Type.DOUBLE, StdTypeList.DOUBLE, "neg-double");
+
+    /** {@code r,x,y: int :: r = x & y;} */
+    public static final Rop AND_INT =
+        new Rop(RegOps.AND, Type.INT, StdTypeList.INT_INT, "and-int");
+
+    /** {@code r,x,y: long :: r = x & y;} */
+    public static final Rop AND_LONG =
+        new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG_LONG, "and-long");
+
+    /** {@code r,x,y: int :: r = x | y;} */
+    public static final Rop OR_INT =
+        new Rop(RegOps.OR, Type.INT, StdTypeList.INT_INT, "or-int");
+
+    /** {@code r,x,y: long :: r = x | y;} */
+    public static final Rop OR_LONG =
+        new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG_LONG, "or-long");
+
+    /** {@code r,x,y: int :: r = x ^ y;} */
+    public static final Rop XOR_INT =
+        new Rop(RegOps.XOR, Type.INT, StdTypeList.INT_INT, "xor-int");
+
+    /** {@code r,x,y: long :: r = x ^ y;} */
+    public static final Rop XOR_LONG =
+        new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG_LONG, "xor-long");
+
+    /** {@code r,x,y: int :: r = x << y;} */
+    public static final Rop SHL_INT =
+        new Rop(RegOps.SHL, Type.INT, StdTypeList.INT_INT, "shl-int");
+
+    /** {@code r,x: long; y: int :: r = x << y;} */
+    public static final Rop SHL_LONG =
+        new Rop(RegOps.SHL, Type.LONG, StdTypeList.LONG_INT, "shl-long");
+
+    /** {@code r,x,y: int :: r = x >> y;} */
+    public static final Rop SHR_INT =
+        new Rop(RegOps.SHR, Type.INT, StdTypeList.INT_INT, "shr-int");
+
+    /** {@code r,x: long; y: int :: r = x >> y;} */
+    public static final Rop SHR_LONG =
+        new Rop(RegOps.SHR, Type.LONG, StdTypeList.LONG_INT, "shr-long");
+
+    /** {@code r,x,y: int :: r = x >>> y;} */
+    public static final Rop USHR_INT =
+        new Rop(RegOps.USHR, Type.INT, StdTypeList.INT_INT, "ushr-int");
+
+    /** {@code r,x: long; y: int :: r = x >>> y;} */
+    public static final Rop USHR_LONG =
+        new Rop(RegOps.USHR, Type.LONG, StdTypeList.LONG_INT, "ushr-long");
+
+    /** {@code r,x: int :: r = ~x;} */
+    public static final Rop NOT_INT =
+        new Rop(RegOps.NOT, Type.INT, StdTypeList.INT, "not-int");
+
+    /** {@code r,x: long :: r = ~x;} */
+    public static final Rop NOT_LONG =
+        new Rop(RegOps.NOT, Type.LONG, StdTypeList.LONG, "not-long");
+
+    /** {@code r,x,c: int :: r = x + c;} */
+    public static final Rop ADD_CONST_INT =
+        new Rop(RegOps.ADD, Type.INT, StdTypeList.INT, "add-const-int");
+
+    /** {@code r,x,c: long :: r = x + c;} */
+    public static final Rop ADD_CONST_LONG =
+        new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG, "add-const-long");
+
+    /** {@code r,x,c: float :: r = x + c;} */
+    public static final Rop ADD_CONST_FLOAT =
+        new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT, "add-const-float");
+
+    /** {@code r,x,c: double :: r = x + c;} */
+    public static final Rop ADD_CONST_DOUBLE =
+        new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE,
+                "add-const-double");
+
+    /** {@code r,x,c: int :: r = x - c;} */
+    public static final Rop SUB_CONST_INT =
+        new Rop(RegOps.SUB, Type.INT, StdTypeList.INT, "sub-const-int");
+
+    /** {@code r,x,c: long :: r = x - c;} */
+    public static final Rop SUB_CONST_LONG =
+        new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG, "sub-const-long");
+
+    /** {@code r,x,c: float :: r = x - c;} */
+    public static final Rop SUB_CONST_FLOAT =
+        new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT, "sub-const-float");
+
+    /** {@code r,x,c: double :: r = x - c;} */
+    public static final Rop SUB_CONST_DOUBLE =
+        new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE,
+                "sub-const-double");
+
+    /** {@code r,x,c: int :: r = x * c;} */
+    public static final Rop MUL_CONST_INT =
+        new Rop(RegOps.MUL, Type.INT, StdTypeList.INT, "mul-const-int");
+
+    /** {@code r,x,c: long :: r = x * c;} */
+    public static final Rop MUL_CONST_LONG =
+        new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG, "mul-const-long");
+
+    /** {@code r,x,c: float :: r = x * c;} */
+    public static final Rop MUL_CONST_FLOAT =
+        new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT, "mul-const-float");
+
+    /** {@code r,x,c: double :: r = x * c;} */
+    public static final Rop MUL_CONST_DOUBLE =
+        new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE,
+                "mul-const-double");
+
+    /** {@code r,x,c: int :: r = x / c;} */
+    public static final Rop DIV_CONST_INT =
+        new Rop(RegOps.DIV, Type.INT, StdTypeList.INT,
+                Exceptions.LIST_Error_ArithmeticException, "div-const-int");
+
+    /** {@code r,x,c: long :: r = x / c;} */
+    public static final Rop DIV_CONST_LONG =
+        new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG,
+                Exceptions.LIST_Error_ArithmeticException, "div-const-long");
+
+    /** {@code r,x,c: float :: r = x / c;} */
+    public static final Rop DIV_CONST_FLOAT =
+        new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT, "div-const-float");
+
+    /** {@code r,x,c: double :: r = x / c;} */
+    public static final Rop DIV_CONST_DOUBLE =
+        new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE,
+                "div-const-double");
+
+    /** {@code r,x,c: int :: r = x % c;} */
+    public static final Rop REM_CONST_INT =
+        new Rop(RegOps.REM, Type.INT, StdTypeList.INT,
+                Exceptions.LIST_Error_ArithmeticException, "rem-const-int");
+
+    /** {@code r,x,c: long :: r = x % c;} */
+    public static final Rop REM_CONST_LONG =
+        new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG,
+                Exceptions.LIST_Error_ArithmeticException, "rem-const-long");
+
+    /** {@code r,x,c: float :: r = x % c;} */
+    public static final Rop REM_CONST_FLOAT =
+        new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT, "rem-const-float");
+
+    /** {@code r,x,c: double :: r = x % c;} */
+    public static final Rop REM_CONST_DOUBLE =
+        new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE,
+                "rem-const-double");
+
+    /** {@code r,x,c: int :: r = x & c;} */
+    public static final Rop AND_CONST_INT =
+        new Rop(RegOps.AND, Type.INT, StdTypeList.INT, "and-const-int");
+
+    /** {@code r,x,c: long :: r = x & c;} */
+    public static final Rop AND_CONST_LONG =
+        new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG, "and-const-long");
+
+    /** {@code r,x,c: int :: r = x | c;} */
+    public static final Rop OR_CONST_INT =
+        new Rop(RegOps.OR, Type.INT, StdTypeList.INT, "or-const-int");
+
+    /** {@code r,x,c: long :: r = x | c;} */
+    public static final Rop OR_CONST_LONG =
+        new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG, "or-const-long");
+
+    /** {@code r,x,c: int :: r = x ^ c;} */
+    public static final Rop XOR_CONST_INT =
+        new Rop(RegOps.XOR, Type.INT, StdTypeList.INT, "xor-const-int");
+
+    /** {@code r,x,c: long :: r = x ^ c;} */
+    public static final Rop XOR_CONST_LONG =
+        new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG, "xor-const-long");
+
+    /** {@code r,x,c: int :: r = x << c;} */
+    public static final Rop SHL_CONST_INT =
+        new Rop(RegOps.SHL, Type.INT, StdTypeList.INT, "shl-const-int");
+
+    /** {@code r,x: long; c: int :: r = x << c;} */
+    public static final Rop SHL_CONST_LONG =
+        new Rop(RegOps.SHL, Type.LONG, StdTypeList.INT, "shl-const-long");
+
+    /** {@code r,x,c: int :: r = x >> c;} */
+    public static final Rop SHR_CONST_INT =
+        new Rop(RegOps.SHR, Type.INT, StdTypeList.INT, "shr-const-int");
+
+    /** {@code r,x: long; c: int :: r = x >> c;} */
+    public static final Rop SHR_CONST_LONG =
+        new Rop(RegOps.SHR, Type.LONG, StdTypeList.INT, "shr-const-long");
+
+    /** {@code r,x,c: int :: r = x >>> c;} */
+    public static final Rop USHR_CONST_INT =
+        new Rop(RegOps.USHR, Type.INT, StdTypeList.INT, "ushr-const-int");
+
+    /** {@code r,x: long; c: int :: r = x >>> c;} */
+    public static final Rop USHR_CONST_LONG =
+        new Rop(RegOps.USHR, Type.LONG, StdTypeList.INT, "ushr-const-long");
+
+    /** {@code r: int; x,y: long :: r = cmp(x, y);} */
+    public static final Rop CMPL_LONG =
+        new Rop(RegOps.CMPL, Type.INT, StdTypeList.LONG_LONG, "cmpl-long");
+
+    /** {@code r: int; x,y: float :: r = cmpl(x, y);} */
+    public static final Rop CMPL_FLOAT =
+        new Rop(RegOps.CMPL, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpl-float");
+
+    /** {@code r: int; x,y: double :: r = cmpl(x, y);} */
+    public static final Rop CMPL_DOUBLE =
+        new Rop(RegOps.CMPL, Type.INT, StdTypeList.DOUBLE_DOUBLE,
+                "cmpl-double");
+
+    /** {@code r: int; x,y: float :: r = cmpg(x, y);} */
+    public static final Rop CMPG_FLOAT =
+        new Rop(RegOps.CMPG, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpg-float");
+
+    /** {@code r: int; x,y: double :: r = cmpg(x, y);} */
+    public static final Rop CMPG_DOUBLE =
+        new Rop(RegOps.CMPG, Type.INT, StdTypeList.DOUBLE_DOUBLE,
+                "cmpg-double");
+
+    /** {@code r: int; x: long :: r = (int) x} */
+    public static final Rop CONV_L2I =
+        new Rop(RegOps.CONV, Type.INT, StdTypeList.LONG, "conv-l2i");
+
+    /** {@code r: int; x: float :: r = (int) x} */
+    public static final Rop CONV_F2I =
+        new Rop(RegOps.CONV, Type.INT, StdTypeList.FLOAT, "conv-f2i");
+
+    /** {@code r: int; x: double :: r = (int) x} */
+    public static final Rop CONV_D2I =
+        new Rop(RegOps.CONV, Type.INT, StdTypeList.DOUBLE, "conv-d2i");
+
+    /** {@code r: long; x: int :: r = (long) x} */
+    public static final Rop CONV_I2L =
+        new Rop(RegOps.CONV, Type.LONG, StdTypeList.INT, "conv-i2l");
+
+    /** {@code r: long; x: float :: r = (long) x} */
+    public static final Rop CONV_F2L =
+        new Rop(RegOps.CONV, Type.LONG, StdTypeList.FLOAT, "conv-f2l");
+
+    /** {@code r: long; x: double :: r = (long) x} */
+    public static final Rop CONV_D2L =
+        new Rop(RegOps.CONV, Type.LONG, StdTypeList.DOUBLE, "conv-d2l");
+
+    /** {@code r: float; x: int :: r = (float) x} */
+    public static final Rop CONV_I2F =
+        new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.INT, "conv-i2f");
+
+    /** {@code r: float; x: long :: r = (float) x} */
+    public static final Rop CONV_L2F =
+        new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.LONG, "conv-l2f");
+
+    /** {@code r: float; x: double :: r = (float) x} */
+    public static final Rop CONV_D2F =
+        new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.DOUBLE, "conv-d2f");
+
+    /** {@code r: double; x: int :: r = (double) x} */
+    public static final Rop CONV_I2D =
+        new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.INT, "conv-i2d");
+
+    /** {@code r: double; x: long :: r = (double) x} */
+    public static final Rop CONV_L2D =
+        new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.LONG, "conv-l2d");
+
+    /** {@code r: double; x: float :: r = (double) x} */
+    public static final Rop CONV_F2D =
+        new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.FLOAT, "conv-f2d");
+
+    /**
+     * {@code r,x: int :: r = (x << 24) >> 24} (Java-style
+     * convert int to byte)
+     */
+    public static final Rop TO_BYTE =
+        new Rop(RegOps.TO_BYTE, Type.INT, StdTypeList.INT, "to-byte");
+
+    /**
+     * {@code r,x: int :: r = x & 0xffff} (Java-style
+     * convert int to char)
+     */
+    public static final Rop TO_CHAR =
+        new Rop(RegOps.TO_CHAR, Type.INT, StdTypeList.INT, "to-char");
+
+    /**
+     * {@code r,x: int :: r = (x << 16) >> 16} (Java-style
+     * convert int to short)
+     */
+    public static final Rop TO_SHORT =
+        new Rop(RegOps.TO_SHORT, Type.INT, StdTypeList.INT, "to-short");
+
+    /** {@code return void} */
+    public static final Rop RETURN_VOID =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_RETURN,
+                "return-void");
+
+    /** {@code x: int; return x} */
+    public static final Rop RETURN_INT =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.INT, Rop.BRANCH_RETURN,
+                "return-int");
+
+    /** {@code x: long; return x} */
+    public static final Rop RETURN_LONG =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.LONG, Rop.BRANCH_RETURN,
+                "return-long");
+
+    /** {@code x: float; return x} */
+    public static final Rop RETURN_FLOAT =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.FLOAT, Rop.BRANCH_RETURN,
+                "return-float");
+
+    /** {@code x: double; return x} */
+    public static final Rop RETURN_DOUBLE =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.DOUBLE,
+                Rop.BRANCH_RETURN, "return-double");
+
+    /** {@code x: Object; return x} */
+    public static final Rop RETURN_OBJECT =
+        new Rop(RegOps.RETURN, Type.VOID, StdTypeList.OBJECT,
+                Rop.BRANCH_RETURN, "return-object");
+
+    /** {@code T: any type; r: int; x: T[]; :: r = x.length} */
+    public static final Rop ARRAY_LENGTH =
+        new Rop(RegOps.ARRAY_LENGTH, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "array-length");
+
+    /** {@code x: Throwable :: throw(x)} */
+    public static final Rop THROW =
+        new Rop(RegOps.THROW, Type.VOID, StdTypeList.THROWABLE,
+                StdTypeList.THROWABLE, "throw");
+
+    /** {@code x: Object :: monitorenter(x)} */
+    public static final Rop MONITOR_ENTER =
+        new Rop(RegOps.MONITOR_ENTER, Type.VOID, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "monitor-enter");
+
+    /** {@code x: Object :: monitorexit(x)} */
+    public static final Rop MONITOR_EXIT =
+        new Rop(RegOps.MONITOR_EXIT, Type.VOID, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_Null_IllegalMonitorStateException,
+                "monitor-exit");
+
+    /** {@code r,y: int; x: int[] :: r = x[y]} */
+    public static final Rop AGET_INT =
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.INTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-int");
+
+    /** {@code r: long; x: long[]; y: int :: r = x[y]} */
+    public static final Rop AGET_LONG =
+        new Rop(RegOps.AGET, Type.LONG, StdTypeList.LONGARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-long");
+
+    /** {@code r: float; x: float[]; y: int :: r = x[y]} */
+    public static final Rop AGET_FLOAT =
+        new Rop(RegOps.AGET, Type.FLOAT, StdTypeList.FLOATARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-float");
+
+    /** {@code r: double; x: double[]; y: int :: r = x[y]} */
+    public static final Rop AGET_DOUBLE =
+        new Rop(RegOps.AGET, Type.DOUBLE, StdTypeList.DOUBLEARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-double");
+
+    /** {@code r: Object; x: Object[]; y: int :: r = x[y]} */
+    public static final Rop AGET_OBJECT =
+        new Rop(RegOps.AGET, Type.OBJECT, StdTypeList.OBJECTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-object");
+
+    /** {@code r: boolean; x: boolean[]; y: int :: r = x[y]} */
+    public static final Rop AGET_BOOLEAN =
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.BOOLEANARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-boolean");
+
+    /** {@code r: byte; x: byte[]; y: int :: r = x[y]} */
+    public static final Rop AGET_BYTE =
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.BYTEARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-byte");
+
+    /** {@code r: char; x: char[]; y: int :: r = x[y]} */
+    public static final Rop AGET_CHAR =
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.CHARARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-char");
+
+    /** {@code r: short; x: short[]; y: int :: r = x[y]} */
+    public static final Rop AGET_SHORT =
+        new Rop(RegOps.AGET, Type.INT, StdTypeList.SHORTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aget-short");
+
+    /** {@code x,z: int; y: int[] :: y[z] = x} */
+    public static final Rop APUT_INT =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_INTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-int");
+
+    /** {@code x: long; y: long[]; z: int :: y[z] = x} */
+    public static final Rop APUT_LONG =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.LONG_LONGARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-long");
+
+    /** {@code x: float; y: float[]; z: int :: y[z] = x} */
+    public static final Rop APUT_FLOAT =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.FLOAT_FLOATARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aput-float");
+
+    /** {@code x: double; y: double[]; z: int :: y[z] = x} */
+    public static final Rop APUT_DOUBLE =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.DOUBLE_DOUBLEARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+                "aput-double");
+
+    /** {@code x: Object; y: Object[]; z: int :: y[z] = x} */
+    public static final Rop APUT_OBJECT =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.OBJECT_OBJECTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+                "aput-object");
+
+    /** {@code x: boolean; y: boolean[]; z: int :: y[z] = x} */
+    public static final Rop APUT_BOOLEAN =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BOOLEANARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+                "aput-boolean");
+
+    /** {@code x: byte; y: byte[]; z: int :: y[z] = x} */
+    public static final Rop APUT_BYTE =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BYTEARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-byte");
+
+    /** {@code x: char; y: char[]; z: int :: y[z] = x} */
+    public static final Rop APUT_CHAR =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_CHARARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-char");
+
+    /** {@code x: short; y: short[]; z: int :: y[z] = x} */
+    public static final Rop APUT_SHORT =
+        new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_SHORTARR_INT,
+                Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+                "aput-short");
+
+    /**
+     * {@code T: any non-array object type :: r =
+     * alloc(T)} (allocate heap space for an object)
+     */
+    public static final Rop NEW_INSTANCE =
+        new Rop(RegOps.NEW_INSTANCE, Type.OBJECT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "new-instance");
+
+    /** {@code r: int[]; x: int :: r = new int[x]} */
+    public static final Rop NEW_ARRAY_INT =
+        new Rop(RegOps.NEW_ARRAY, Type.INT_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-int");
+
+    /** {@code r: long[]; x: int :: r = new long[x]} */
+    public static final Rop NEW_ARRAY_LONG =
+        new Rop(RegOps.NEW_ARRAY, Type.LONG_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-long");
+
+    /** {@code r: float[]; x: int :: r = new float[x]} */
+    public static final Rop NEW_ARRAY_FLOAT =
+        new Rop(RegOps.NEW_ARRAY, Type.FLOAT_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-float");
+
+    /** {@code r: double[]; x: int :: r = new double[x]} */
+    public static final Rop NEW_ARRAY_DOUBLE =
+        new Rop(RegOps.NEW_ARRAY, Type.DOUBLE_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-double");
+
+    /** {@code r: boolean[]; x: int :: r = new boolean[x]} */
+    public static final Rop NEW_ARRAY_BOOLEAN =
+        new Rop(RegOps.NEW_ARRAY, Type.BOOLEAN_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-boolean");
+
+    /** {@code r: byte[]; x: int :: r = new byte[x]} */
+    public static final Rop NEW_ARRAY_BYTE =
+        new Rop(RegOps.NEW_ARRAY, Type.BYTE_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-byte");
+
+    /** {@code r: char[]; x: int :: r = new char[x]} */
+    public static final Rop NEW_ARRAY_CHAR =
+        new Rop(RegOps.NEW_ARRAY, Type.CHAR_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-char");
+
+    /** {@code r: short[]; x: int :: r = new short[x]} */
+    public static final Rop NEW_ARRAY_SHORT =
+        new Rop(RegOps.NEW_ARRAY, Type.SHORT_ARRAY, StdTypeList.INT,
+                Exceptions.LIST_Error_NegativeArraySizeException,
+                "new-array-short");
+
+    /**
+     * {@code T: any non-array object type; x: Object :: (T) x} (can
+     * throw {@code ClassCastException})
+     */
+    public static final Rop CHECK_CAST =
+        new Rop(RegOps.CHECK_CAST, Type.VOID, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_ClassCastException, "check-cast");
+
+    /**
+     * {@code T: any non-array object type; x: Object :: x instanceof
+     * T}. Note: This is listed as throwing {@code Error}
+     * explicitly because the op <i>can</i> throw, but there are no
+     * other predefined exceptions for it.
+     */
+    public static final Rop INSTANCE_OF =
+        new Rop(RegOps.INSTANCE_OF, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error, "instance-of");
+
+    /**
+     * {@code r: int; x: Object; f: instance field spec of
+     * type int :: r = x.f}
+     */
+    public static final Rop GET_FIELD_INT =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "get-field-int");
+
+    /**
+     * {@code r: long; x: Object; f: instance field spec of
+     * type long :: r = x.f}
+     */
+    public static final Rop GET_FIELD_LONG =
+        new Rop(RegOps.GET_FIELD, Type.LONG, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "get-field-long");
+
+    /**
+     * {@code r: float; x: Object; f: instance field spec of
+     * type float :: r = x.f}
+     */
+    public static final Rop GET_FIELD_FLOAT =
+        new Rop(RegOps.GET_FIELD, Type.FLOAT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-float");
+
+    /**
+     * {@code r: double; x: Object; f: instance field spec of
+     * type double :: r = x.f}
+     */
+    public static final Rop GET_FIELD_DOUBLE =
+        new Rop(RegOps.GET_FIELD, Type.DOUBLE, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-double");
+
+    /**
+     * {@code r: Object; x: Object; f: instance field spec of
+     * type Object :: r = x.f}
+     */
+    public static final Rop GET_FIELD_OBJECT =
+        new Rop(RegOps.GET_FIELD, Type.OBJECT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-object");
+
+    /**
+     * {@code r: boolean; x: Object; f: instance field spec of
+     * type boolean :: r = x.f}
+     */
+    public static final Rop GET_FIELD_BOOLEAN =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-boolean");
+
+    /**
+     * {@code r: byte; x: Object; f: instance field spec of
+     * type byte :: r = x.f}
+     */
+    public static final Rop GET_FIELD_BYTE =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-byte");
+
+    /**
+     * {@code r: char; x: Object; f: instance field spec of
+     * type char :: r = x.f}
+     */
+    public static final Rop GET_FIELD_CHAR =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-char");
+
+    /**
+     * {@code r: short; x: Object; f: instance field spec of
+     * type short :: r = x.f}
+     */
+    public static final Rop GET_FIELD_SHORT =
+        new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "get-field-short");
+
+    /** {@code r: int; f: static field spec of type int :: r = f} */
+    public static final Rop GET_STATIC_INT =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-int");
+
+    /** {@code r: long; f: static field spec of type long :: r = f} */
+    public static final Rop GET_STATIC_LONG =
+        new Rop(RegOps.GET_STATIC, Type.LONG, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-long");
+
+    /** {@code r: float; f: static field spec of type float :: r = f} */
+    public static final Rop GET_STATIC_FLOAT =
+        new Rop(RegOps.GET_STATIC, Type.FLOAT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-float");
+
+    /** {@code r: double; f: static field spec of type double :: r = f} */
+    public static final Rop GET_STATIC_DOUBLE =
+        new Rop(RegOps.GET_STATIC, Type.DOUBLE, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-double");
+
+    /** {@code r: Object; f: static field spec of type Object :: r = f} */
+    public static final Rop GET_STATIC_OBJECT =
+        new Rop(RegOps.GET_STATIC, Type.OBJECT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-static-object");
+
+    /** {@code r: boolean; f: static field spec of type boolean :: r = f} */
+    public static final Rop GET_STATIC_BOOLEAN =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-field-boolean");
+
+    /** {@code r: byte; f: static field spec of type byte :: r = f} */
+    public static final Rop GET_STATIC_BYTE =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-field-byte");
+
+    /** {@code r: char; f: static field spec of type char :: r = f} */
+    public static final Rop GET_STATIC_CHAR =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-field-char");
+
+    /** {@code r: short; f: static field spec of type short :: r = f} */
+    public static final Rop GET_STATIC_SHORT =
+        new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+                Exceptions.LIST_Error, "get-field-short");
+
+    /**
+     * {@code x: int; y: Object; f: instance field spec of type
+     * int :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_INT =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "put-field-int");
+
+    /**
+     * {@code x: long; y: Object; f: instance field spec of type
+     * long :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_LONG =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.LONG_OBJECT,
+                Exceptions.LIST_Error_NullPointerException, "put-field-long");
+
+    /**
+     * {@code x: float; y: Object; f: instance field spec of type
+     * float :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_FLOAT =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.FLOAT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-float");
+
+    /**
+     * {@code x: double; y: Object; f: instance field spec of type
+     * double :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_DOUBLE =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.DOUBLE_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-double");
+
+    /**
+     * {@code x: Object; y: Object; f: instance field spec of type
+     * Object :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_OBJECT =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.OBJECT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-object");
+
+    /**
+     * {@code x: int; y: Object; f: instance field spec of type
+     * boolean :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_BOOLEAN =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-boolean");
+
+    /**
+     * {@code x: int; y: Object; f: instance field spec of type
+     * byte :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_BYTE =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-byte");
+
+    /**
+     * {@code x: int; y: Object; f: instance field spec of type
+     * char :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_CHAR =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-char");
+
+    /**
+     * {@code x: int; y: Object; f: instance field spec of type
+     * short :: y.f = x}
+     */
+    public static final Rop PUT_FIELD_SHORT =
+        new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+                Exceptions.LIST_Error_NullPointerException,
+                "put-field-short");
+
+    /** {@code f: static field spec of type int; x: int :: f = x} */
+    public static final Rop PUT_STATIC_INT =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-int");
+
+    /** {@code f: static field spec of type long; x: long :: f = x} */
+    public static final Rop PUT_STATIC_LONG =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.LONG,
+                Exceptions.LIST_Error, "put-static-long");
+
+    /** {@code f: static field spec of type float; x: float :: f = x} */
+    public static final Rop PUT_STATIC_FLOAT =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.FLOAT,
+                Exceptions.LIST_Error, "put-static-float");
+
+    /** {@code f: static field spec of type double; x: double :: f = x} */
+    public static final Rop PUT_STATIC_DOUBLE =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.DOUBLE,
+                Exceptions.LIST_Error, "put-static-double");
+
+    /** {@code f: static field spec of type Object; x: Object :: f = x} */
+    public static final Rop PUT_STATIC_OBJECT =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.OBJECT,
+                Exceptions.LIST_Error, "put-static-object");
+
+    /**
+     * {@code f: static field spec of type boolean; x: boolean :: f =
+     * x}
+     */
+    public static final Rop PUT_STATIC_BOOLEAN =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-boolean");
+
+    /** {@code f: static field spec of type byte; x: byte :: f = x} */
+    public static final Rop PUT_STATIC_BYTE =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-byte");
+
+    /** {@code f: static field spec of type char; x: char :: f = x} */
+    public static final Rop PUT_STATIC_CHAR =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-char");
+
+    /** {@code f: static field spec of type short; x: short :: f = x} */
+    public static final Rop PUT_STATIC_SHORT =
+        new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+                Exceptions.LIST_Error, "put-static-short");
+
+    /** {@code x: Int :: local variable begins in x} */
+    public static final Rop MARK_LOCAL_INT =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.INT, "mark-local-int");
+
+    /** {@code x: Long :: local variable begins in x} */
+    public static final Rop MARK_LOCAL_LONG =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.LONG, "mark-local-long");
+
+    /** {@code x: Float :: local variable begins in x} */
+    public static final Rop MARK_LOCAL_FLOAT =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.FLOAT, "mark-local-float");
+
+    /** {@code x: Double :: local variable begins in x} */
+    public static final Rop MARK_LOCAL_DOUBLE =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.DOUBLE, "mark-local-double");
+
+    /** {@code x: Object :: local variable begins in x} */
+    public static final Rop MARK_LOCAL_OBJECT =
+            new Rop (RegOps.MARK_LOCAL, Type.VOID,
+                    StdTypeList.OBJECT, "mark-local-object");
+
+    /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */
+    public static final Rop FILL_ARRAY_DATA =
+        new Rop(RegOps.FILL_ARRAY_DATA, Type.VOID, StdTypeList.EMPTY,
+                "fill-array-data");
+
+    /**
+     * Returns the appropriate rop for the given opcode, destination,
+     * and sources. The result is typically, but not necessarily, a
+     * shared instance.
+     *
+     * <p><b>Note:</b> This method does not do complete error checking on
+     * its arguments, and so it may return an instance which seemed "right
+     * enough" even though in actuality the passed arguments don't quite
+     * match what is returned. TODO: Revisit this issue.</p>
+     *
+     * @param opcode the opcode
+     * @param dest {@code non-null;} destination (result) type, or
+     * {@link Type#VOID} if none
+     * @param sources {@code non-null;} list of source types
+     * @param cst {@code null-ok;} associated constant, if any
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop ropFor(int opcode, TypeBearer dest, TypeList sources,
+            Constant cst) {
+        switch (opcode) {
+            case RegOps.NOP: return NOP;
+            case RegOps.MOVE: return opMove(dest);
+            case RegOps.MOVE_PARAM: return opMoveParam(dest);
+            case RegOps.MOVE_EXCEPTION: return opMoveException(dest);
+            case RegOps.CONST: return opConst(dest);
+            case RegOps.GOTO: return GOTO;
+            case RegOps.IF_EQ: return opIfEq(sources);
+            case RegOps.IF_NE: return opIfNe(sources);
+            case RegOps.IF_LT: return opIfLt(sources);
+            case RegOps.IF_GE: return opIfGe(sources);
+            case RegOps.IF_LE: return opIfLe(sources);
+            case RegOps.IF_GT: return opIfGt(sources);
+            case RegOps.SWITCH: return SWITCH;
+            case RegOps.ADD: return opAdd(sources);
+            case RegOps.SUB: return opSub(sources);
+            case RegOps.MUL: return opMul(sources);
+            case RegOps.DIV: return opDiv(sources);
+            case RegOps.REM: return opRem(sources);
+            case RegOps.NEG: return opNeg(dest);
+            case RegOps.AND: return opAnd(sources);
+            case RegOps.OR: return opOr(sources);
+            case RegOps.XOR: return opXor(sources);
+            case RegOps.SHL: return opShl(sources);
+            case RegOps.SHR: return opShr(sources);
+            case RegOps.USHR: return opUshr(sources);
+            case RegOps.NOT: return opNot(dest);
+            case RegOps.CMPL: return opCmpl(sources.getType(0));
+            case RegOps.CMPG: return opCmpg(sources.getType(0));
+            case RegOps.CONV: return opConv(dest, sources.getType(0));
+            case RegOps.TO_BYTE: return TO_BYTE;
+            case RegOps.TO_CHAR: return TO_CHAR;
+            case RegOps.TO_SHORT: return TO_SHORT;
+            case RegOps.RETURN: {
+                if (sources.size() == 0) {
+                    return RETURN_VOID;
+                }
+                return opReturn(sources.getType(0));
+            }
+            case RegOps.ARRAY_LENGTH: return ARRAY_LENGTH;
+            case RegOps.THROW: return THROW;
+            case RegOps.MONITOR_ENTER: return MONITOR_ENTER;
+            case RegOps.MONITOR_EXIT: return MONITOR_EXIT;
+            case RegOps.AGET: {
+                Type source = sources.getType(0);
+                Type componentType;
+                if (source == Type.KNOWN_NULL) {
+                    /*
+                     * Treat a known-null as an array of the expected
+                     * result type.
+                     */
+                    componentType = dest.getType();
+                } else {
+                    componentType = source.getComponentType();
+                }
+                return opAget(componentType);
+            }
+            case RegOps.APUT: {
+                Type source = sources.getType(1);
+                Type componentType;
+                if (source == Type.KNOWN_NULL) {
+                    /*
+                     * Treat a known-null as an array of the type being
+                     * stored.
+                     */
+                    componentType = sources.getType(0);
+                } else {
+                    componentType = source.getComponentType();
+                }
+                return opAput(componentType);
+            }
+            case RegOps.NEW_INSTANCE: return NEW_INSTANCE;
+            case RegOps.NEW_ARRAY: return opNewArray(dest.getType());
+            case RegOps.CHECK_CAST: return CHECK_CAST;
+            case RegOps.INSTANCE_OF: return INSTANCE_OF;
+            case RegOps.GET_FIELD: return opGetField(dest);
+            case RegOps.GET_STATIC: return opGetStatic(dest);
+            case RegOps.PUT_FIELD: return opPutField(sources.getType(0));
+            case RegOps.PUT_STATIC: return opPutStatic(sources.getType(0));
+            case RegOps.INVOKE_STATIC: {
+                return opInvokeStatic(((CstMethodRef) cst).getPrototype());
+            }
+            case RegOps.INVOKE_VIRTUAL: {
+                CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+                Prototype meth = cstMeth.getPrototype();
+                CstType definer = cstMeth.getDefiningClass();
+                meth = meth.withFirstParameter(definer.getClassType());
+                return opInvokeVirtual(meth);
+            }
+            case RegOps.INVOKE_SUPER: {
+                CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+                Prototype meth = cstMeth.getPrototype();
+                CstType definer = cstMeth.getDefiningClass();
+                meth = meth.withFirstParameter(definer.getClassType());
+                return opInvokeSuper(meth);
+            }
+            case RegOps.INVOKE_DIRECT: {
+                CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+                Prototype meth = cstMeth.getPrototype();
+                CstType definer = cstMeth.getDefiningClass();
+                meth = meth.withFirstParameter(definer.getClassType());
+                return opInvokeDirect(meth);
+            }
+            case RegOps.INVOKE_INTERFACE: {
+                CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+                Prototype meth = cstMeth.getPrototype();
+                CstType definer = cstMeth.getDefiningClass();
+                meth = meth.withFirstParameter(definer.getClassType());
+                return opInvokeInterface(meth);
+            }
+        }
+
+        throw new RuntimeException("unknown opcode " + RegOps.opName(opcode));
+    }
+
+    /**
+     * Returns the appropriate {@code move} rop for the given type. The
+     * result is a shared instance.
+     *
+     * @param type {@code non-null;} type of value being moved
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opMove(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return MOVE_INT;
+            case Type.BT_LONG:   return MOVE_LONG;
+            case Type.BT_FLOAT:  return MOVE_FLOAT;
+            case Type.BT_DOUBLE: return MOVE_DOUBLE;
+            case Type.BT_OBJECT: return MOVE_OBJECT;
+            case Type.BT_ADDR:   return MOVE_RETURN_ADDRESS;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code move-param} rop for the
+     * given type. The result is a shared instance.
+     *
+     * @param type {@code non-null;} type of value being moved
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opMoveParam(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return MOVE_PARAM_INT;
+            case Type.BT_LONG:   return MOVE_PARAM_LONG;
+            case Type.BT_FLOAT:  return MOVE_PARAM_FLOAT;
+            case Type.BT_DOUBLE: return MOVE_PARAM_DOUBLE;
+            case Type.BT_OBJECT: return MOVE_PARAM_OBJECT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code move-exception} rop for the
+     * given type. The result may be a shared instance.
+     *
+     * @param type {@code non-null;} type of the exception
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opMoveException(TypeBearer type) {
+        return new Rop(RegOps.MOVE_EXCEPTION, type.getType(),
+                       StdTypeList.EMPTY, (String) null);
+    }
+
+    /**
+     * Returns the appropriate {@code move-result} rop for the
+     * given type. The result may be a shared instance.
+     *
+     * @param type {@code non-null;} type of the parameter
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opMoveResult(TypeBearer type) {
+        return new Rop(RegOps.MOVE_RESULT, type.getType(),
+                       StdTypeList.EMPTY, (String) null);
+    }
+
+    /**
+     * Returns the appropriate {@code move-result-pseudo} rop for the
+     * given type. The result may be a shared instance.
+     *
+     * @param type {@code non-null;} type of the parameter
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opMoveResultPseudo(TypeBearer type) {
+        return new Rop(RegOps.MOVE_RESULT_PSEUDO, type.getType(),
+                       StdTypeList.EMPTY, (String) null);
+    }
+
+    /**
+     * Returns the appropriate {@code const} rop for the given
+     * type. The result is a shared instance.
+     *
+     * @param type {@code non-null;} type of the constant
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opConst(TypeBearer type) {
+        if (type.getType() == Type.KNOWN_NULL) {
+            return CONST_OBJECT_NOTHROW;
+        }
+
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return CONST_INT;
+            case Type.BT_LONG:   return CONST_LONG;
+            case Type.BT_FLOAT:  return CONST_FLOAT;
+            case Type.BT_DOUBLE: return CONST_DOUBLE;
+            case Type.BT_OBJECT: return CONST_OBJECT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code if-eq} rop for the given
+     * sources. The result is a shared instance.
+     *
+     * @param types {@code non-null;} source types
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opIfEq(TypeList types) {
+        return pickIf(types, IF_EQZ_INT, IF_EQZ_OBJECT,
+                      IF_EQ_INT, IF_EQ_OBJECT);
+    }
+
+    /**
+     * Returns the appropriate {@code if-ne} rop for the given
+     * sources. The result is a shared instance.
+     *
+     * @param types {@code non-null;} source types
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opIfNe(TypeList types) {
+        return pickIf(types, IF_NEZ_INT, IF_NEZ_OBJECT,
+                      IF_NE_INT, IF_NE_OBJECT);
+    }
+
+    /**
+     * Returns the appropriate {@code if-lt} rop for the given
+     * sources. The result is a shared instance.
+     *
+     * @param types {@code non-null;} source types
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opIfLt(TypeList types) {
+        return pickIf(types, IF_LTZ_INT, null, IF_LT_INT, null);
+    }
+
+    /**
+     * Returns the appropriate {@code if-ge} rop for the given
+     * sources. The result is a shared instance.
+     *
+     * @param types {@code non-null;} source types
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opIfGe(TypeList types) {
+        return pickIf(types, IF_GEZ_INT, null, IF_GE_INT, null);
+    }
+
+    /**
+     * Returns the appropriate {@code if-gt} rop for the given
+     * sources. The result is a shared instance.
+     *
+     * @param types {@code non-null;} source types
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opIfGt(TypeList types) {
+        return pickIf(types, IF_GTZ_INT, null, IF_GT_INT, null);
+    }
+
+    /**
+     * Returns the appropriate {@code if-le} rop for the given
+     * sources. The result is a shared instance.
+     *
+     * @param types {@code non-null;} source types
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opIfLe(TypeList types) {
+        return pickIf(types, IF_LEZ_INT, null, IF_LE_INT, null);
+    }
+
+    /**
+     * Helper for all the {@code if*}-related methods, which
+     * checks types and picks one of the four variants, throwing if
+     * there's a problem.
+     *
+     * @param types {@code non-null;} the types
+     * @param intZ {@code non-null;} the int-to-0 comparison
+     * @param objZ {@code null-ok;} the object-to-null comparison
+     * @param intInt {@code non-null;} the int-to-int comparison
+     * @param objObj {@code non-null;} the object-to-object comparison
+     * @return {@code non-null;} the appropriate instance
+     */
+    private static Rop pickIf(TypeList types, Rop intZ, Rop objZ, Rop intInt,
+                              Rop objObj) {
+        switch(types.size()) {
+            case 1: {
+                switch (types.getType(0).getBasicFrameType()) {
+                    case Type.BT_INT: {
+                        return intZ;
+                    }
+                    case Type.BT_OBJECT: {
+                        if (objZ != null) {
+                            return objZ;
+                        }
+                    }
+                }
+                break;
+            }
+            case 2: {
+                int bt = types.getType(0).getBasicFrameType();
+                if (bt == types.getType(1).getBasicFrameType()) {
+                    switch (bt) {
+                        case Type.BT_INT: {
+                            return intInt;
+                        }
+                        case Type.BT_OBJECT: {
+                            if (objObj != null) {
+                                return objObj;
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+
+        return throwBadTypes(types);
+    }
+
+    /**
+     * Returns the appropriate {@code add} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opAdd(TypeList types) {
+        return pickBinaryOp(types, ADD_CONST_INT, ADD_CONST_LONG,
+                            ADD_CONST_FLOAT, ADD_CONST_DOUBLE, ADD_INT,
+                            ADD_LONG, ADD_FLOAT, ADD_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate {@code sub} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opSub(TypeList types) {
+        return pickBinaryOp(types, SUB_CONST_INT, SUB_CONST_LONG,
+                            SUB_CONST_FLOAT, SUB_CONST_DOUBLE, SUB_INT,
+                            SUB_LONG, SUB_FLOAT, SUB_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate {@code mul} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opMul(TypeList types) {
+        return pickBinaryOp(types, MUL_CONST_INT, MUL_CONST_LONG,
+                            MUL_CONST_FLOAT, MUL_CONST_DOUBLE, MUL_INT,
+                            MUL_LONG, MUL_FLOAT, MUL_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate {@code div} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opDiv(TypeList types) {
+        return pickBinaryOp(types, DIV_CONST_INT, DIV_CONST_LONG,
+                            DIV_CONST_FLOAT, DIV_CONST_DOUBLE, DIV_INT,
+                            DIV_LONG, DIV_FLOAT, DIV_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate {@code rem} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opRem(TypeList types) {
+        return pickBinaryOp(types, REM_CONST_INT, REM_CONST_LONG,
+                            REM_CONST_FLOAT, REM_CONST_DOUBLE, REM_INT,
+                            REM_LONG, REM_FLOAT, REM_DOUBLE);
+    }
+
+    /**
+     * Returns the appropriate {@code and} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opAnd(TypeList types) {
+        return pickBinaryOp(types, AND_CONST_INT, AND_CONST_LONG, null, null,
+                            AND_INT, AND_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate {@code or} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opOr(TypeList types) {
+        return pickBinaryOp(types, OR_CONST_INT, OR_CONST_LONG, null, null,
+                            OR_INT, OR_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate {@code xor} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opXor(TypeList types) {
+        return pickBinaryOp(types, XOR_CONST_INT, XOR_CONST_LONG, null, null,
+                            XOR_INT, XOR_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate {@code shl} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opShl(TypeList types) {
+        return pickBinaryOp(types, SHL_CONST_INT, SHL_CONST_LONG, null, null,
+                            SHL_INT, SHL_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate {@code shr} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opShr(TypeList types) {
+        return pickBinaryOp(types, SHR_CONST_INT, SHR_CONST_LONG, null, null,
+                            SHR_INT, SHR_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate {@code ushr} rop for the given
+     * types. The result is a shared instance.
+     *
+     * @param types {@code non-null;} types of the sources
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opUshr(TypeList types) {
+        return pickBinaryOp(types, USHR_CONST_INT, USHR_CONST_LONG, null, null,
+                            USHR_INT, USHR_LONG, null, null);
+    }
+
+    /**
+     * Returns the appropriate binary arithmetic rop for the given type
+     * and arguments. The result is a shared instance.
+     *
+     * @param types {@code non-null;} sources of the operation
+     * @param int1 {@code non-null;} the int-to-constant rop
+     * @param long1 {@code non-null;} the long-to-constant rop
+     * @param float1 {@code null-ok;} the float-to-constant rop, if any
+     * @param double1 {@code null-ok;} the double-to-constant rop, if any
+     * @param int2 {@code non-null;} the int-to-int rop
+     * @param long2 {@code non-null;} the long-to-long or long-to-int rop
+     * @param float2 {@code null-ok;} the float-to-float rop, if any
+     * @param double2 {@code null-ok;} the double-to-double rop, if any
+     * @return {@code non-null;} an appropriate instance
+     */
+    private static Rop pickBinaryOp(TypeList types, Rop int1, Rop long1,
+                                    Rop float1, Rop double1, Rop int2,
+                                    Rop long2, Rop float2, Rop double2) {
+        int bt1 = types.getType(0).getBasicFrameType();
+        Rop result = null;
+
+        switch (types.size()) {
+            case 1: {
+                switch(bt1) {
+                    case Type.BT_INT:    return int1;
+                    case Type.BT_LONG:   return long1;
+                    case Type.BT_FLOAT:  result = float1; break;
+                    case Type.BT_DOUBLE: result = double1; break;
+                }
+                break;
+            }
+            case 2: {
+                switch(bt1) {
+                    case Type.BT_INT:    return int2;
+                    case Type.BT_LONG:   return long2;
+                    case Type.BT_FLOAT:  result = float2; break;
+                    case Type.BT_DOUBLE: result = double2; break;
+                }
+                break;
+            }
+        }
+
+        if (result == null) {
+            return throwBadTypes(types);
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns the appropriate {@code neg} rop for the given type. The
+     * result is a shared instance.
+     *
+     * @param type {@code non-null;} type of value being operated on
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opNeg(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return NEG_INT;
+            case Type.BT_LONG:   return NEG_LONG;
+            case Type.BT_FLOAT:  return NEG_FLOAT;
+            case Type.BT_DOUBLE: return NEG_DOUBLE;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code not} rop for the given type. The
+     * result is a shared instance.
+     *
+     * @param type {@code non-null;} type of value being operated on
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opNot(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:  return NOT_INT;
+            case Type.BT_LONG: return NOT_LONG;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code cmpl} rop for the given type. The
+     * result is a shared instance.
+     *
+     * @param type {@code non-null;} type of value being compared
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opCmpl(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_LONG:   return CMPL_LONG;
+            case Type.BT_FLOAT:  return CMPL_FLOAT;
+            case Type.BT_DOUBLE: return CMPL_DOUBLE;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code cmpg} rop for the given type. The
+     * result is a shared instance.
+     *
+     * @param type {@code non-null;} type of value being compared
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opCmpg(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_FLOAT:  return CMPG_FLOAT;
+            case Type.BT_DOUBLE: return CMPG_DOUBLE;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code conv} rop for the given types. The
+     * result is a shared instance.
+     *
+     * @param dest {@code non-null;} target value type
+     * @param source {@code non-null;} source value type
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opConv(TypeBearer dest, TypeBearer source) {
+        int dbt = dest.getBasicFrameType();
+        switch (source.getBasicFrameType()) {
+            case Type.BT_INT: {
+                switch (dbt) {
+                    case Type.BT_LONG:   return CONV_I2L;
+                    case Type.BT_FLOAT:  return CONV_I2F;
+                    case Type.BT_DOUBLE: return CONV_I2D;
+                }
+            }
+            case Type.BT_LONG: {
+                switch (dbt) {
+                    case Type.BT_INT:    return CONV_L2I;
+                    case Type.BT_FLOAT:  return CONV_L2F;
+                    case Type.BT_DOUBLE: return CONV_L2D;
+                }
+            }
+            case Type.BT_FLOAT: {
+                switch (dbt) {
+                    case Type.BT_INT:    return CONV_F2I;
+                    case Type.BT_LONG:   return CONV_F2L;
+                    case Type.BT_DOUBLE: return CONV_F2D;
+                }
+            }
+            case Type.BT_DOUBLE: {
+                switch (dbt) {
+                    case Type.BT_INT:   return CONV_D2I;
+                    case Type.BT_LONG:  return CONV_D2L;
+                    case Type.BT_FLOAT: return CONV_D2F;
+                }
+            }
+        }
+
+        return throwBadTypes(StdTypeList.make(dest.getType(),
+                                              source.getType()));
+    }
+
+    /**
+     * Returns the appropriate {@code return} rop for the given type. The
+     * result is a shared instance.
+     *
+     * @param type {@code non-null;} type of value being returned
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opReturn(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return RETURN_INT;
+            case Type.BT_LONG:   return RETURN_LONG;
+            case Type.BT_FLOAT:  return RETURN_FLOAT;
+            case Type.BT_DOUBLE: return RETURN_DOUBLE;
+            case Type.BT_OBJECT: return RETURN_OBJECT;
+            case Type.BT_VOID:   return RETURN_VOID;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code aget} rop for the given type. The
+     * result is a shared instance.
+     *
+     * @param type {@code non-null;} element type of array being accessed
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opAget(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return AGET_INT;
+            case Type.BT_LONG:    return AGET_LONG;
+            case Type.BT_FLOAT:   return AGET_FLOAT;
+            case Type.BT_DOUBLE:  return AGET_DOUBLE;
+            case Type.BT_OBJECT:  return AGET_OBJECT;
+            case Type.BT_BOOLEAN: return AGET_BOOLEAN;
+            case Type.BT_BYTE:    return AGET_BYTE;
+            case Type.BT_CHAR:    return AGET_CHAR;
+            case Type.BT_SHORT:   return AGET_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code aput} rop for the given type. The
+     * result is a shared instance.
+     *
+     * @param type {@code non-null;} element type of array being accessed
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opAput(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return APUT_INT;
+            case Type.BT_LONG:    return APUT_LONG;
+            case Type.BT_FLOAT:   return APUT_FLOAT;
+            case Type.BT_DOUBLE:  return APUT_DOUBLE;
+            case Type.BT_OBJECT:  return APUT_OBJECT;
+            case Type.BT_BOOLEAN: return APUT_BOOLEAN;
+            case Type.BT_BYTE:    return APUT_BYTE;
+            case Type.BT_CHAR:    return APUT_CHAR;
+            case Type.BT_SHORT:   return APUT_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code new-array} rop for the given
+     * type. The result is a shared instance.
+     *
+     * @param arrayType {@code non-null;} array type of array being created
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opNewArray(TypeBearer arrayType) {
+        Type type = arrayType.getType();
+        Type elementType = type.getComponentType();
+
+        switch (elementType.getBasicType()) {
+            case Type.BT_INT:     return NEW_ARRAY_INT;
+            case Type.BT_LONG:    return NEW_ARRAY_LONG;
+            case Type.BT_FLOAT:   return NEW_ARRAY_FLOAT;
+            case Type.BT_DOUBLE:  return NEW_ARRAY_DOUBLE;
+            case Type.BT_BOOLEAN: return NEW_ARRAY_BOOLEAN;
+            case Type.BT_BYTE:    return NEW_ARRAY_BYTE;
+            case Type.BT_CHAR:    return NEW_ARRAY_CHAR;
+            case Type.BT_SHORT:   return NEW_ARRAY_SHORT;
+            case Type.BT_OBJECT: {
+                return new Rop(RegOps.NEW_ARRAY, type, StdTypeList.INT,
+                        Exceptions.LIST_Error_NegativeArraySizeException,
+                        "new-array-object");
+            }
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code filled-new-array} rop for the given
+     * type. The result may be a shared instance.
+     *
+     * @param arrayType {@code non-null;} type of array being created
+     * @param count {@code >= 0;} number of elements that the array should have
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opFilledNewArray(TypeBearer arrayType, int count) {
+        Type type = arrayType.getType();
+        Type elementType = type.getComponentType();
+
+        if (elementType.isCategory2()) {
+            return throwBadType(arrayType);
+        }
+
+        if (count < 0) {
+            throw new IllegalArgumentException("count < 0");
+        }
+
+        StdTypeList sourceTypes = new StdTypeList(count);
+
+        for (int i = 0; i < count; i++) {
+            sourceTypes.set(i, elementType);
+        }
+
+        // Note: The resulting rop is considered call-like.
+        return new Rop(RegOps.FILLED_NEW_ARRAY,
+                       sourceTypes,
+                       Exceptions.LIST_Error);
+    }
+
+    /**
+     * Returns the appropriate {@code get-field} rop for the given
+     * type. The result is a shared instance.
+     *
+     * @param type {@code non-null;} type of the field in question
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opGetField(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return GET_FIELD_INT;
+            case Type.BT_LONG:    return GET_FIELD_LONG;
+            case Type.BT_FLOAT:   return GET_FIELD_FLOAT;
+            case Type.BT_DOUBLE:  return GET_FIELD_DOUBLE;
+            case Type.BT_OBJECT:  return GET_FIELD_OBJECT;
+            case Type.BT_BOOLEAN: return GET_FIELD_BOOLEAN;
+            case Type.BT_BYTE:    return GET_FIELD_BYTE;
+            case Type.BT_CHAR:    return GET_FIELD_CHAR;
+            case Type.BT_SHORT:   return GET_FIELD_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code put-field} rop for the given
+     * type. The result is a shared instance.
+     *
+     * @param type {@code non-null;} type of the field in question
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opPutField(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return PUT_FIELD_INT;
+            case Type.BT_LONG:    return PUT_FIELD_LONG;
+            case Type.BT_FLOAT:   return PUT_FIELD_FLOAT;
+            case Type.BT_DOUBLE:  return PUT_FIELD_DOUBLE;
+            case Type.BT_OBJECT:  return PUT_FIELD_OBJECT;
+            case Type.BT_BOOLEAN: return PUT_FIELD_BOOLEAN;
+            case Type.BT_BYTE:    return PUT_FIELD_BYTE;
+            case Type.BT_CHAR:    return PUT_FIELD_CHAR;
+            case Type.BT_SHORT:   return PUT_FIELD_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code get-static} rop for the given
+     * type. The result is a shared instance.
+     *
+     * @param type {@code non-null;} type of the field in question
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opGetStatic(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return GET_STATIC_INT;
+            case Type.BT_LONG:    return GET_STATIC_LONG;
+            case Type.BT_FLOAT:   return GET_STATIC_FLOAT;
+            case Type.BT_DOUBLE:  return GET_STATIC_DOUBLE;
+            case Type.BT_OBJECT:  return GET_STATIC_OBJECT;
+            case Type.BT_BOOLEAN: return GET_STATIC_BOOLEAN;
+            case Type.BT_BYTE:    return GET_STATIC_BYTE;
+            case Type.BT_CHAR:    return GET_STATIC_CHAR;
+            case Type.BT_SHORT:   return GET_STATIC_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code put-static} rop for the given
+     * type. The result is a shared instance.
+     *
+     * @param type {@code non-null;} type of the field in question
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opPutStatic(TypeBearer type) {
+        switch (type.getBasicType()) {
+            case Type.BT_INT:     return PUT_STATIC_INT;
+            case Type.BT_LONG:    return PUT_STATIC_LONG;
+            case Type.BT_FLOAT:   return PUT_STATIC_FLOAT;
+            case Type.BT_DOUBLE:  return PUT_STATIC_DOUBLE;
+            case Type.BT_OBJECT:  return PUT_STATIC_OBJECT;
+            case Type.BT_BOOLEAN: return PUT_STATIC_BOOLEAN;
+            case Type.BT_BYTE:    return PUT_STATIC_BYTE;
+            case Type.BT_CHAR:    return PUT_STATIC_CHAR;
+            case Type.BT_SHORT:   return PUT_STATIC_SHORT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * Returns the appropriate {@code invoke-static} rop for the
+     * given type. The result is typically a newly-allocated instance.
+     *
+     * @param meth {@code non-null;} descriptor of the method
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opInvokeStatic(Prototype meth) {
+        return new Rop(RegOps.INVOKE_STATIC,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate {@code invoke-virtual} rop for the
+     * given type. The result is typically a newly-allocated instance.
+     *
+     * @param meth {@code non-null;} descriptor of the method, including the
+     * {@code this} parameter
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opInvokeVirtual(Prototype meth) {
+        return new Rop(RegOps.INVOKE_VIRTUAL,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate {@code invoke-super} rop for the
+     * given type. The result is typically a newly-allocated instance.
+     *
+     * @param meth {@code non-null;} descriptor of the method, including the
+     * {@code this} parameter
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opInvokeSuper(Prototype meth) {
+        return new Rop(RegOps.INVOKE_SUPER,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate {@code invoke-direct} rop for the
+     * given type. The result is typically a newly-allocated instance.
+     *
+     * @param meth {@code non-null;} descriptor of the method, including the
+     * {@code this} parameter
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opInvokeDirect(Prototype meth) {
+        return new Rop(RegOps.INVOKE_DIRECT,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate {@code invoke-interface} rop for the
+     * given type. The result is typically a newly-allocated instance.
+     *
+     * @param meth {@code non-null;} descriptor of the method, including the
+     * {@code this} parameter
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opInvokeInterface(Prototype meth) {
+        return new Rop(RegOps.INVOKE_INTERFACE,
+                       meth.getParameterFrameTypes(),
+                       StdTypeList.THROWABLE);
+    }
+
+    /**
+     * Returns the appropriate {@code mark-local} rop for the given type.
+     * The result is a shared instance.
+     *
+     * @param type {@code non-null;} type of value being marked
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static Rop opMarkLocal(TypeBearer type) {
+        switch (type.getBasicFrameType()) {
+            case Type.BT_INT:    return MARK_LOCAL_INT;
+            case Type.BT_LONG:   return MARK_LOCAL_LONG;
+            case Type.BT_FLOAT:  return MARK_LOCAL_FLOAT;
+            case Type.BT_DOUBLE: return MARK_LOCAL_DOUBLE;
+            case Type.BT_OBJECT: return MARK_LOCAL_OBJECT;
+        }
+
+        return throwBadType(type);
+    }
+
+    /**
+     * This class is uninstantiable.
+     */
+    private Rops() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Throws the right exception to complain about a bogus type.
+     *
+     * @param type {@code non-null;} the bad type
+     * @return never
+     */
+    private static Rop throwBadType(TypeBearer type) {
+        throw new IllegalArgumentException("bad type: " + type);
+    }
+
+    /**
+     * Throws the right exception to complain about a bogus list of types.
+     *
+     * @param types {@code non-null;} the bad types
+     * @return never
+     */
+    private static Rop throwBadTypes(TypeList types) {
+        throw new IllegalArgumentException("bad types: " + types);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/SourcePosition.java b/dx/src/com/android/dx/rop/code/SourcePosition.java
new file mode 100644
index 0000000..f7a7961
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/SourcePosition.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.Hex;
+
+/**
+ * Information about a source position for code, which includes both a
+ * line number and original bytecode address.
+ */
+public final class SourcePosition {
+    /** {@code non-null;} convenient "no information known" instance */
+    public static final SourcePosition NO_INFO =
+        new SourcePosition(null, -1, -1);
+
+    /** {@code null-ok;} name of the file of origin or {@code null} if unknown */
+    private final CstUtf8 sourceFile;
+
+    /**
+     * {@code >= -1;} the bytecode address, or {@code -1} if that
+     * information is unknown
+     */
+    private final int address;
+
+    /**
+     * {@code >= -1;} the line number, or {@code -1} if that
+     * information is unknown
+     */
+    private final int line;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param sourceFile {@code null-ok;} name of the file of origin or
+     * {@code null} if unknown
+     * @param address {@code >= -1;} original bytecode address or {@code -1}
+     * if unknown
+     * @param line {@code >= -1;} original line number or {@code -1} if
+     * unknown
+     */
+    public SourcePosition(CstUtf8 sourceFile, int address, int line) {
+        if (address < -1) {
+            throw new IllegalArgumentException("address < -1");
+        }
+
+        if (line < -1) {
+            throw new IllegalArgumentException("line < -1");
+        }
+
+        this.sourceFile = sourceFile;
+        this.address = address;
+        this.line = line;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(50);
+
+        if (sourceFile != null) {
+            sb.append(sourceFile.toHuman());
+            sb.append(":");
+        }
+
+        if (line >= 0) {
+            sb.append(line);
+        }
+
+        sb.append('@');
+
+        if (address < 0) {
+            sb.append("????");
+        } else {
+            sb.append(Hex.u2(address));
+        }
+
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof SourcePosition)) {
+            return false;
+        }
+
+        if (this == other) {
+            return true;
+        }
+
+        SourcePosition pos = (SourcePosition) other;
+
+        return (address == pos.address) && sameLineAndFile(pos);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return sourceFile.hashCode() + address + line;
+    }
+
+    /**
+     * Returns whether the lines match between this instance and
+     * the one given.
+     *
+     * @param other {@code non-null;} the instance to compare to
+     * @return {@code true} iff the lines match
+     */
+    public boolean sameLine(SourcePosition other) {
+        return (line == other.line);
+    }
+
+    /**
+     * Returns whether the lines and files match between this instance and
+     * the one given.
+     *
+     * @param other {@code non-null;} the instance to compare to
+     * @return {@code true} iff the lines and files match
+     */
+    public boolean sameLineAndFile(SourcePosition other) {
+        return (line == other.line) &&
+            ((sourceFile == other.sourceFile) ||
+             ((sourceFile != null) && sourceFile.equals(other.sourceFile)));
+    }
+
+    /**
+     * Gets the source file, if known.
+     *
+     * @return {@code null-ok;} the source file or {@code null} if unknown
+     */
+    public CstUtf8 getSourceFile() {
+        return sourceFile;
+    }
+
+    /**
+     * Gets the original bytecode address.
+     *
+     * @return {@code >= -1;} the address or {@code -1} if unknown
+     */
+    public int getAddress() {
+        return address;
+    }
+
+    /**
+     * Gets the original line number.
+     *
+     * @return {@code >= -1;} the original line number or {@code -1} if
+     * unknown
+     */
+    public int getLine() {
+        return line;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/SwitchInsn.java b/dx/src/com/android/dx/rop/code/SwitchInsn.java
new file mode 100644
index 0000000..31bb94d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/SwitchInsn.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+/**
+ * Instruction which contains switch cases.
+ */
+public final class SwitchInsn
+        extends Insn {
+    /** {@code non-null;} list of switch cases */
+    private final IntList cases;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param result {@code null-ok;} spec for the result, if any
+     * @param sources {@code non-null;} specs for all the sources
+     * @param cases {@code non-null;} list of switch cases
+     */
+    public SwitchInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+                      RegisterSpecList sources, IntList cases) {
+        super(opcode, position, result, sources);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_SWITCH) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        if (cases == null) {
+            throw new NullPointerException("cases == null");
+        }
+
+        this.cases = cases;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getInlineString() {
+        return cases.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return StdTypeList.EMPTY;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitSwitchInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        throw new UnsupportedOperationException("unsupported");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new SwitchInsn(getOpcode(), getPosition(),
+                              getResult().withOffset(delta),
+                              getSources().withOffset(delta),
+                              cases);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p> SwitchInsn always compares false. The current use for this method
+     * never encounters {@code SwitchInsn}s
+     */
+    @Override
+    public boolean contentEquals(Insn b) {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new SwitchInsn(getOpcode(), getPosition(),
+                              result,
+                              sources,
+                              cases);
+    }
+
+    /**
+     * Gets the list of switch cases.
+     *
+     * @return {@code non-null;} the case list
+     */
+    public IntList getCases() {
+        return cases;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java
new file mode 100644
index 0000000..cdd21d1
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which contains an explicit reference to a constant
+ * and which might throw an exception.
+ */
+public final class ThrowingCstInsn
+        extends CstInsn {
+    /** {@code non-null;} list of exceptions caught */
+    private final TypeList catches;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param sources {@code non-null;} specs for all the sources
+     * @param catches {@code non-null;} list of exceptions caught
+     * @param cst {@code non-null;} the constant
+     */
+    public ThrowingCstInsn(Rop opcode, SourcePosition position,
+                           RegisterSpecList sources,
+                           TypeList catches, Constant cst) {
+        super(opcode, position, null, sources, cst);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_THROW) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        if (catches == null) {
+            throw new NullPointerException("catches == null");
+        }
+
+        this.catches = catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getInlineString() {
+        return getConstant().toHuman() + " " +
+                                 ThrowingInsn.toCatchString(catches);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitThrowingCstInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        return new ThrowingCstInsn(getOpcode(), getPosition(),
+                                   getSources(), catches.withAddedType(type),
+                                   getConstant());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new ThrowingCstInsn(getOpcode(), getPosition(),
+                                   getSources().withOffset(delta),
+                                   catches,
+                                   getConstant());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new ThrowingCstInsn(getOpcode(), getPosition(),
+                                   sources,
+                                   catches,
+                                   getConstant());
+    }
+
+
+}
diff --git a/dx/src/com/android/dx/rop/code/ThrowingInsn.java b/dx/src/com/android/dx/rop/code/ThrowingInsn.java
new file mode 100644
index 0000000..6561d41
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ThrowingInsn.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which possibly throws. The {@code successors} list in the
+ * basic block an instance of this class is inside corresponds in-order to
+ * the list of exceptions handled by this instruction, with the
+ * no-exception case appended as the final target.
+ */
+public final class ThrowingInsn
+        extends Insn {
+    /** {@code non-null;} list of exceptions caught */
+    private final TypeList catches;
+
+    /**
+     * Gets the string form of a register spec list to be used as a catches
+     * list.
+     *
+     * @param catches {@code non-null;} the catches list
+     * @return {@code non-null;} the string form
+     */
+    public static String toCatchString(TypeList catches) {
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append("catch");
+
+        int sz = catches.size();
+        for (int i = 0; i < sz; i++) {
+            sb.append(" ");
+            sb.append(catches.getType(i).toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param position {@code non-null;} source position
+     * @param sources {@code non-null;} specs for all the sources
+     * @param catches {@code non-null;} list of exceptions caught
+     */
+    public ThrowingInsn(Rop opcode, SourcePosition position,
+                        RegisterSpecList sources,
+                        TypeList catches) {
+        super(opcode, position, null, sources);
+
+        if (opcode.getBranchingness() != Rop.BRANCH_THROW) {
+            throw new IllegalArgumentException("bogus branchingness");
+        }
+
+        if (catches == null) {
+            throw new NullPointerException("catches == null");
+        }
+
+        this.catches = catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getInlineString() {
+        return toCatchString(catches);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TypeList getCatches() {
+        return catches;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor visitor) {
+        visitor.visitThrowingInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withAddedCatch(Type type) {
+        return new ThrowingInsn(getOpcode(), getPosition(),
+                                getSources(), catches.withAddedType(type));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withRegisterOffset(int delta) {
+        return new ThrowingInsn(getOpcode(), getPosition(),
+                                getSources().withOffset(delta),
+                                catches);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn withNewRegisters(RegisterSpec result,
+            RegisterSpecList sources) {
+
+        return new ThrowingInsn(getOpcode(), getPosition(),
+                                sources,
+                                catches);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/code/TranslationAdvice.java b/dx/src/com/android/dx/rop/code/TranslationAdvice.java
new file mode 100644
index 0000000..832d84d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/TranslationAdvice.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.code;
+
+/**
+ * Interface for "advice" passed from the late stage of translation back
+ * to the early stage. This allows for the final target architecture to
+ * exert its influence early in the translation process without having
+ * the early stage code be explicitly tied to the target.
+ */
+public interface TranslationAdvice {
+    /**
+     * Returns an indication of whether the target can directly represent an
+     * instruction with the given opcode operating on the given arguments,
+     * where the last source argument is used as a constant. (That is, the
+     * last argument must have a type which indicates it is a known constant.)
+     * The instruction associated must have exactly two sources.
+     *
+     * @param opcode {@code non-null;} the opcode
+     * @param sourceA {@code non-null;} the first source
+     * @param sourceB {@code non-null;} the second source
+     * @return {@code true} iff the target can represent the operation
+     * using a constant for the last argument
+     */
+    public boolean hasConstantOperation(Rop opcode,
+            RegisterSpec sourceA, RegisterSpec sourceB);
+
+    /**
+     * Returns true if the translation target requires the sources of the
+     * specified opcode to be in order and contiguous (eg, for an invoke-range)
+     *
+     * @param opcode {@code non-null;} opcode
+     * @param sources {@code non-null;} source list
+     * @return {@code true} iff the target requires the sources to be
+     * in order and contiguous.
+     */
+    public boolean requiresSourcesInOrder(Rop opcode, RegisterSpecList sources);
+
+    /**
+     * Gets the maximum register width that can be represented optimally.
+     * For example, Dex bytecode does not have instruction forms that take
+     * register numbers larger than 15 for all instructions so
+     * DexTranslationAdvice returns 15 here.
+     *
+     * @return register count noted above
+     */
+    public int getMaxOptimalRegisterCount();
+}
diff --git a/dx/src/com/android/dx/rop/code/package.html b/dx/src/com/android/dx/rop/code/package.html
new file mode 100644
index 0000000..86566b4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/package.html
@@ -0,0 +1,8 @@
+<body>
+<p>Classes relating to a register-based opcode system.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/rop/cst/Constant.java b/dx/src/com/android/dx/rop/cst/Constant.java
new file mode 100644
index 0000000..3ef035e
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/Constant.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Base class for constants of all sorts.
+ */
+public abstract class Constant
+        implements ToHuman, Comparable<Constant> {
+    /**
+     * Returns {@code true} if this instance is a category-2 constant,
+     * meaning it takes up two slots in the constant pool, or
+     * {@code false} if this instance is category-1.
+     *
+     * @return {@code true} iff this instance is category-2
+     */
+    public abstract boolean isCategory2();
+
+    /**
+     * Returns the human name for the particular type of constant
+     * this instance is.
+     *
+     * @return {@code non-null;} the name
+     */
+    public abstract String typeName();
+
+    /**
+     * {@inheritDoc}
+     *
+     * This compares in class-major and value-minor order.
+     */
+    public final int compareTo(Constant other) {
+        Class clazz = getClass();
+        Class otherClazz = other.getClass();
+
+        if (clazz != otherClazz) {
+            return clazz.getName().compareTo(otherClazz.getName());
+        }
+
+        return compareTo0(other);
+    }
+
+    /**
+     * Compare the values of this and another instance, which are guaranteed
+     * to be of the same class. Subclasses must implement this.
+     *
+     * @param other {@code non-null;} the instance to compare to
+     * @return {@code -1}, {@code 0}, or {@code 1}, as usual
+     * for a comparison
+     */
+    protected abstract int compareTo0(Constant other);
+}
diff --git a/dx/src/com/android/dx/rop/cst/ConstantPool.java b/dx/src/com/android/dx/rop/cst/ConstantPool.java
new file mode 100644
index 0000000..efc394d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/ConstantPool.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Interface for constant pools, which are, more or less, just lists of
+ * {@link Constant} objects.
+ */
+public interface ConstantPool {
+    /**
+     * Get the "size" of the constant pool. This corresponds to the
+     * class file field {@code constant_pool_count}, and is in fact
+     * always at least one more than the actual size of the constant pool,
+     * as element {@code 0} is always invalid.
+     *
+     * @return {@code >= 1;} the size
+     */
+    public int size();
+
+    /**
+     * Get the {@code n}th entry in the constant pool, which must
+     * be valid.
+     *
+     * @param n {@code n >= 0, n < size();} the constant pool index
+     * @return {@code non-null;} the corresponding entry
+     * @throws IllegalArgumentException thrown if {@code n} is
+     * in-range but invalid
+     */
+    public Constant get(int n);
+
+    /**
+     * Get the {@code n}th entry in the constant pool, which must
+     * be valid unless {@code n == 0}, in which case {@code null}
+     * is returned.
+     *
+     * @param n {@code n >= 0, n < size();} the constant pool index
+     * @return {@code null-ok;} the corresponding entry, if {@code n != 0}
+     * @throws IllegalArgumentException thrown if {@code n} is
+     * in-range and non-zero but invalid
+     */
+    public Constant get0Ok(int n);
+
+    /**
+     * Get the {@code n}th entry in the constant pool, or
+     * {@code null} if the index is in-range but invalid. In
+     * particular, {@code null} is returned for index {@code 0}
+     * as well as the index after any entry which is defined to take up
+     * two slots (that is, {@code Long} and {@code Double}
+     * entries).
+     *
+     * @param n {@code n >= 0, n < size();} the constant pool index
+     * @return {@code null-ok;} the corresponding entry, or {@code null} if
+     * the index is in-range but invalid
+     */
+    public Constant getOrNull(int n);
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstAnnotation.java b/dx/src/com/android/dx/rop/cst/CstAnnotation.java
new file mode 100644
index 0000000..8cdf1df
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstAnnotation.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.annotation.Annotation;
+
+/**
+ * Constant type that represents an annotation.
+ */
+public final class CstAnnotation extends Constant {
+    /** {@code non-null;} the actual annotation */
+    private final Annotation annotation;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param annotation {@code non-null;} the annotation to hold
+     */
+    public CstAnnotation(Annotation annotation) {
+        if (annotation == null) {
+            throw new NullPointerException("annotation == null");
+        }
+
+        annotation.throwIfMutable();
+
+        this.annotation = annotation;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (! (other instanceof CstAnnotation)) {
+            return false;
+        }
+
+        return annotation.equals(((CstAnnotation) other).annotation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return annotation.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return annotation.compareTo(((CstAnnotation) other).annotation);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return annotation.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "annotation";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return annotation.toString();
+    }
+
+    /**
+     * Get the underlying annotation.
+     *
+     * @return {@code non-null;} the annotation
+     */
+    public Annotation getAnnotation() {
+        return annotation;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstArray.java b/dx/src/com/android/dx/rop/cst/CstArray.java
new file mode 100644
index 0000000..cb7d54d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstArray.java
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Constant type to represent a fixed array of other constants. The contents
+ * may be of any type <i>other</i> than {@link CstUtf8}.
+ */
+public final class CstArray extends Constant {
+    /** {@code non-null;} the actual list of contents */
+    private final List list;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param list {@code non-null;} the actual list of contents
+     */
+    public CstArray(List list) {
+        if (list == null) {
+            throw new NullPointerException("list == null");
+        }
+
+        list.throwIfMutable();
+
+        this.list = list;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (! (other instanceof CstArray)) {
+            return false;
+        }
+
+        return list.equals(((CstArray) other).list);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return list.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return list.compareTo(((CstArray) other).list);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return list.toString("array{", ", ", "}");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "array";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return list.toHuman("{", ", ", "}");
+    }
+
+    /**
+     * Get the underlying list.
+     *
+     * @return {@code non-null;} the list
+     */
+    public List getList() {
+        return list;
+    }
+
+    /**
+     * List of {@link Constant} instances.
+     */
+    public static final class List
+            extends FixedSizeList implements Comparable<List> {
+        /**
+         * Constructs an instance. All indices initially contain
+         * {@code null}.
+         *
+         * @param size the size of the list
+         */
+        public List(int size) {
+            super(size);
+        }
+
+        /** {@inheritDoc} */
+        public int compareTo(List other) {
+            int thisSize = size();
+            int otherSize = other.size();
+            int compareSize = (thisSize < otherSize) ? thisSize : otherSize;
+
+            for (int i = 0; i < compareSize; i++) {
+                Constant thisItem = (Constant) get0(i);
+                Constant otherItem = (Constant) other.get0(i);
+                int compare = thisItem.compareTo(otherItem);
+                if (compare != 0) {
+                    return compare;
+                }
+            }
+
+            if (thisSize < otherSize) {
+                return -1;
+            } else if (thisSize > otherSize) {
+                return 1;
+            }
+
+            return 0;
+        }
+
+        /**
+         * Gets the element at the given index. It is an error to call
+         * this with the index for an element which was never set; if you
+         * do that, this will throw {@code NullPointerException}.
+         *
+         * @param n {@code >= 0, < size();} which index
+         * @return {@code non-null;} element at that index
+         */
+        public Constant get(int n) {
+            return (Constant) get0(n);
+        }
+
+        /**
+         * Sets the element at the given index.
+         *
+         * @param n {@code >= 0, < size();} which index
+         * @param a {@code null-ok;} the element to set at {@code n}
+         */
+        public void set(int n, Constant a) {
+            if (a instanceof CstUtf8) {
+                throw new IllegalArgumentException("bad value: " + a);
+            }
+
+            set0(n, a);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java b/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java
new file mode 100644
index 0000000..5b0aeb6
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+/**
+ * Base class for constants of "methodish" type.
+ *
+ * <p><b>Note:</b> As a {@link TypeBearer}, this class bears the return type
+ * of the method.</p>
+ */
+public abstract class CstBaseMethodRef
+        extends CstMemberRef {
+    /** {@code non-null;} the raw prototype for this method */
+    private final Prototype prototype;
+
+    /**
+     * {@code null-ok;} the prototype for this method taken to be an instance
+     * method, or {@code null} if not yet calculated
+     */
+    private Prototype instancePrototype;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass {@code non-null;} the type of the defining class
+     * @param nat {@code non-null;} the name-and-type
+     */
+    /*package*/ CstBaseMethodRef(CstType definingClass, CstNat nat) {
+        super(definingClass, nat);
+
+        String descriptor = getNat().getDescriptor().getString();
+        this.prototype = Prototype.intern(descriptor);
+        this.instancePrototype = null;
+    }
+
+    /**
+     * Gets the raw prototype of this method. This doesn't include a
+     * {@code this} argument.
+     *
+     * @return {@code non-null;} the method prototype
+     */
+    public final Prototype getPrototype() {
+        return prototype;
+    }
+
+    /**
+     * Gets the prototype of this method as either a
+     * {@code static} or instance method. In the case of a
+     * {@code static} method, this is the same as the raw
+     * prototype. In the case of an instance method, this has an
+     * appropriately-typed {@code this} argument as the first
+     * one.
+     *
+     * @param isStatic whether the method should be considered static
+     * @return {@code non-null;} the method prototype
+     */
+    public final Prototype getPrototype(boolean isStatic) {
+        if (isStatic) {
+            return prototype;
+        } else {
+            if (instancePrototype == null) {
+                Type thisType = getDefiningClass().getClassType();
+                instancePrototype = prototype.withFirstParameter(thisType);
+            }
+            return instancePrototype;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected final int compareTo0(Constant other) {
+        int cmp = super.compareTo0(other);
+
+        if (cmp != 0) {
+            return cmp;
+        }
+
+        CstBaseMethodRef otherMethod = (CstBaseMethodRef) other;
+        return prototype.compareTo(otherMethod.prototype);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * In this case, this method returns the <i>return type</i> of this method.
+     *
+     * @return {@code non-null;} the method's return type
+     */
+    public final Type getType() {
+        return prototype.getReturnType();
+    }
+
+    /**
+     * Gets the number of words of parameters required by this
+     * method's descriptor. Since instances of this class have no way
+     * to know if they will be used in a {@code static} or
+     * instance context, one has to indicate this explicitly as an
+     * argument. This method is just a convenient shorthand for
+     * {@code getPrototype().getParameterTypes().getWordCount()},
+     * plus {@code 1} if the method is to be treated as an
+     * instance method.
+     *
+     * @param isStatic whether the method should be considered static
+     * @return {@code >= 0;} the argument word count
+     */
+    public final int getParameterWordCount(boolean isStatic) {
+        return getPrototype(isStatic).getParameterTypes().getWordCount();
+    }
+
+    /**
+     * Gets whether this is a reference to an instance initialization
+     * method. This is just a convenient shorthand for
+     * {@code getNat().isInstanceInit()}.
+     *
+     * @return {@code true} iff this is a reference to an
+     * instance initialization method
+     */
+    public final boolean isInstanceInit() {
+        return getNat().isInstanceInit();
+    }
+
+    /**
+     * Gets whether this is a reference to a class initialization
+     * method. This is just a convenient shorthand for
+     * {@code getNat().isClassInit()}.
+     *
+     * @return {@code true} iff this is a reference to an
+     * instance initialization method
+     */
+    public final boolean isClassInit() {
+        return getNat().isClassInit();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstBoolean.java b/dx/src/com/android/dx/rop/cst/CstBoolean.java
new file mode 100644
index 0000000..5ff858a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstBoolean.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type {@code boolean}.
+ */
+public final class CstBoolean
+        extends CstLiteral32 {
+    /** {@code non-null;} instance representing {@code false} */
+    public static final CstBoolean VALUE_FALSE = new CstBoolean(false);
+
+    /** {@code non-null;} instance representing {@code true} */
+    public static final CstBoolean VALUE_TRUE = new CstBoolean(true);
+
+    /**
+     * Makes an instance for the given value. This will return an
+     * already-allocated instance.
+     *
+     * @param value the {@code boolean} value
+     * @return {@code non-null;} the appropriate instance
+     */
+    public static CstBoolean make(boolean value) {
+        return value ? VALUE_TRUE : VALUE_FALSE;
+    }
+
+    /**
+     * Makes an instance for the given {@code int} value. This
+     * will return an already-allocated instance.
+     *
+     * @param value must be either {@code 0} or {@code 1}
+     * @return {@code non-null;} the appropriate instance
+     */
+    public static CstBoolean make(int value) {
+        if (value == 0) {
+            return VALUE_FALSE;
+        } else if (value == 1) {
+            return VALUE_TRUE;
+        } else {
+            throw new IllegalArgumentException("bogus value: " + value);
+        }
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     *
+     * @param value the {@code boolean} value
+     */
+    private CstBoolean(boolean value) {
+        super(value ? 1 : 0);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return getValue() ? "boolean{true}" : "boolean{false}";
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.BOOLEAN;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "boolean";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return getValue() ? "true" : "false";
+    }
+
+    /**
+     * Gets the {@code boolean} value.
+     *
+     * @return the value
+     */
+    public boolean getValue() {
+        return (getIntBits() == 0) ? false : true;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstByte.java b/dx/src/com/android/dx/rop/cst/CstByte.java
new file mode 100644
index 0000000..fc8f58f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstByte.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code byte}.
+ */
+public final class CstByte
+        extends CstLiteral32 {
+    /** {@code non-null;} the value {@code 0} as an instance of this class */
+    public static final CstByte VALUE_0 = make((byte) 0);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     *
+     * @param value the {@code byte} value
+     */
+    public static CstByte make(byte value) {
+        return new CstByte(value);
+    }
+
+    /**
+     * Makes an instance for the given {@code int} value. This
+     * may (but does not necessarily) return an already-allocated
+     * instance.
+     *
+     * @param value the value, which must be in range for a {@code byte}
+     * @return {@code non-null;} the appropriate instance
+     */
+    public static CstByte make(int value) {
+        byte cast = (byte) value;
+
+        if (cast != value) {
+            throw new IllegalArgumentException("bogus byte value: " +
+                    value);
+        }
+
+        return make(cast);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     *
+     * @param value the {@code byte} value
+     */
+    private CstByte(byte value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int value = getIntBits();
+        return "byte{0x" + Hex.u1(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.BYTE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "byte";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Integer.toString(getIntBits());
+    }
+
+    /**
+     * Gets the {@code byte} value.
+     *
+     * @return the value
+     */
+    public byte getValue() {
+        return (byte) getIntBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstChar.java b/dx/src/com/android/dx/rop/cst/CstChar.java
new file mode 100644
index 0000000..21d8b67
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstChar.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code char}.
+ */
+public final class CstChar
+        extends CstLiteral32 {
+    /** {@code non-null;} the value {@code 0} as an instance of this class */
+    public static final CstChar VALUE_0 = make((char) 0);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     *
+     * @param value the {@code char} value
+     */
+    public static CstChar make(char value) {
+        return new CstChar(value);
+    }
+
+    /**
+     * Makes an instance for the given {@code int} value. This
+     * may (but does not necessarily) return an already-allocated
+     * instance.
+     *
+     * @param value the value, which must be in range for a {@code char}
+     * @return {@code non-null;} the appropriate instance
+     */
+    public static CstChar make(int value) {
+        char cast = (char) value;
+
+        if (cast != value) {
+            throw new IllegalArgumentException("bogus char value: " +
+                    value);
+        }
+
+        return make(cast);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     *
+     * @param value the {@code char} value
+     */
+    private CstChar(char value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int value = getIntBits();
+        return "char{0x" + Hex.u2(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.CHAR;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "char";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Integer.toString(getIntBits());
+    }
+
+    /**
+     * Gets the {@code char} value.
+     *
+     * @return the value
+     */
+    public char getValue() {
+        return (char) getIntBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstDouble.java b/dx/src/com/android/dx/rop/cst/CstDouble.java
new file mode 100644
index 0000000..8f1766f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstDouble.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Double_info}.
+ */
+public final class CstDouble
+        extends CstLiteral64 {
+    /** {@code non-null;} instance representing {@code 0} */
+    public static final CstDouble VALUE_0 =
+        new CstDouble(Double.doubleToLongBits(0.0));
+
+    /** {@code non-null;} instance representing {@code 1} */
+    public static final CstDouble VALUE_1 =
+        new CstDouble(Double.doubleToLongBits(1.0));
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     *
+     * @param bits the {@code double} value as {@code long} bits
+     */
+    public static CstDouble make(long bits) {
+        /*
+         * Note: Javadoc notwithstanding, this implementation always
+         * allocates.
+         */
+        return new CstDouble(bits);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     *
+     * @param bits the {@code double} value as {@code long} bits
+     */
+    private CstDouble(long bits) {
+        super(bits);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        long bits = getLongBits();
+        return "double{0x" + Hex.u8(bits) + " / " +
+            Double.longBitsToDouble(bits) + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.DOUBLE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "double";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Double.toString(Double.longBitsToDouble(getLongBits()));
+    }
+
+    /**
+     * Gets the {@code double} value.
+     *
+     * @return the value
+     */
+    public double getValue() {
+        return Double.longBitsToDouble(getLongBits());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstEnumRef.java b/dx/src/com/android/dx/rop/cst/CstEnumRef.java
new file mode 100644
index 0000000..641ab3f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstEnumRef.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constant type to represent a reference to a particular constant
+ * value of an enumerated type.
+ */
+public final class CstEnumRef extends CstMemberRef {
+    /** {@code null-ok;} the corresponding field ref, lazily initialized */
+    private CstFieldRef fieldRef;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param nat {@code non-null;} the name-and-type; the defining class is derived
+     * from this
+     */
+    public CstEnumRef(CstNat nat) {
+        super(new CstType(nat.getFieldType()), nat);
+
+        fieldRef = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "enum";
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <b>Note:</b> This returns the enumerated type.
+     */
+    public Type getType() {
+        return getDefiningClass().getClassType();
+    }
+
+    /**
+     * Get a {@link CstFieldRef} that corresponds with this instance.
+     *
+     * @return {@code non-null;} the corresponding field reference
+     */
+    public CstFieldRef getFieldRef() {
+        if (fieldRef == null) {
+            fieldRef = new CstFieldRef(getDefiningClass(), getNat());
+        }
+
+        return fieldRef;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstFieldRef.java b/dx/src/com/android/dx/rop/cst/CstFieldRef.java
new file mode 100644
index 0000000..06f0b15
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstFieldRef.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type {@code CONSTANT_Fieldref_info}.
+ */
+public final class CstFieldRef extends CstMemberRef {
+    /**
+     * Returns an instance of this class that represents the static
+     * field which should hold the class corresponding to a given
+     * primitive type. For example, if given {@link Type#INT}, this
+     * method returns an instance corresponding to the field
+     * {@code java.lang.Integer.TYPE}.
+     *
+     * @param primitiveType {@code non-null;} the primitive type
+     * @return {@code non-null;} the corresponding static field
+     */
+    public static CstFieldRef forPrimitiveType(Type primitiveType) {
+        return new CstFieldRef(CstType.forBoxedPrimitiveType(primitiveType),
+                CstNat.PRIMITIVE_TYPE_NAT);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass {@code non-null;} the type of the defining class
+     * @param nat {@code non-null;} the name-and-type
+     */
+    public CstFieldRef(CstType definingClass, CstNat nat) {
+        super(definingClass, nat);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "field";
+    }
+
+    /**
+     * Returns the type of this field.
+     *
+     * @return {@code non-null;} the field's type
+     */
+    public Type getType() {
+        return getNat().getFieldType();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        int cmp = super.compareTo0(other);
+
+        if (cmp != 0) {
+            return cmp;
+        }
+
+        CstFieldRef otherField = (CstFieldRef) other;
+        CstUtf8 thisDescriptor = getNat().getDescriptor();
+        CstUtf8 otherDescriptor = otherField.getNat().getDescriptor();
+        return thisDescriptor.compareTo(otherDescriptor);
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstFloat.java b/dx/src/com/android/dx/rop/cst/CstFloat.java
new file mode 100644
index 0000000..0a2354a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstFloat.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Float_info}.
+ */
+public final class CstFloat
+        extends CstLiteral32 {
+    /** {@code non-null;} instance representing {@code 0} */
+    public static final CstFloat VALUE_0 = make(Float.floatToIntBits(0.0f));
+
+    /** {@code non-null;} instance representing {@code 1} */
+    public static final CstFloat VALUE_1 = make(Float.floatToIntBits(1.0f));
+
+    /** {@code non-null;} instance representing {@code 2} */
+    public static final CstFloat VALUE_2 = make(Float.floatToIntBits(2.0f));
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     *
+     * @param bits the {@code float} value as {@code int} bits
+     */
+    public static CstFloat make(int bits) {
+        /*
+         * Note: Javadoc notwithstanding, this implementation always
+         * allocates.
+         */
+        return new CstFloat(bits);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     *
+     * @param bits the {@code float} value as {@code int} bits
+     */
+    private CstFloat(int bits) {
+        super(bits);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int bits = getIntBits();
+        return "float{0x" + Hex.u4(bits) + " / " +
+            Float.intBitsToFloat(bits) + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.FLOAT;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "float";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Float.toString(Float.intBitsToFloat(getIntBits()));
+    }
+
+    /**
+     * Gets the {@code float} value.
+     *
+     * @return the value
+     */
+    public float getValue() {
+        return Float.intBitsToFloat(getIntBits());
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstInteger.java b/dx/src/com/android/dx/rop/cst/CstInteger.java
new file mode 100644
index 0000000..3691fc0
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstInteger.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Integer_info}.
+ */
+public final class CstInteger
+        extends CstLiteral32 {
+    /** {@code non-null;} array of cached instances */
+    private static final CstInteger[] cache = new CstInteger[511];
+
+    /** {@code non-null;} instance representing {@code -1} */
+    public static final CstInteger VALUE_M1 = make(-1);
+
+    /** {@code non-null;} instance representing {@code 0} */
+    public static final CstInteger VALUE_0 = make(0);
+
+    /** {@code non-null;} instance representing {@code 1} */
+    public static final CstInteger VALUE_1 = make(1);
+
+    /** {@code non-null;} instance representing {@code 2} */
+    public static final CstInteger VALUE_2 = make(2);
+
+    /** {@code non-null;} instance representing {@code 3} */
+    public static final CstInteger VALUE_3 = make(3);
+
+    /** {@code non-null;} instance representing {@code 4} */
+    public static final CstInteger VALUE_4 = make(4);
+
+    /** {@code non-null;} instance representing {@code 5} */
+    public static final CstInteger VALUE_5 = make(5);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     *
+     * @param value the {@code int} value
+     * @return {@code non-null;} the appropriate instance
+     */
+    public static CstInteger make(int value) {
+        /*
+         * Note: No need to synchronize, since we don't make any sort
+         * of guarantee about ==, and it's okay to overwrite existing
+         * entries too.
+         */
+        int idx = (value & 0x7fffffff) % cache.length;
+        CstInteger obj = cache[idx];
+
+        if ((obj != null) && (obj.getValue() == value)) {
+            return obj;
+        }
+
+        obj = new CstInteger(value);
+        cache[idx] = obj;
+        return obj;
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     *
+     * @param value the {@code int} value
+     */
+    private CstInteger(int value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int value = getIntBits();
+        return "int{0x" + Hex.u4(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.INT;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "int";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Integer.toString(getIntBits());
+    }
+
+    /**
+     * Gets the {@code int} value.
+     *
+     * @return the value
+     */
+    public int getValue() {
+        return getIntBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java b/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java
new file mode 100644
index 0000000..8b8cb30
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type {@code CONSTANT_InterfaceMethodref_info}.
+ */
+public final class CstInterfaceMethodRef
+        extends CstBaseMethodRef {
+    /**
+     * {@code null-ok;} normal {@link CstMethodRef} that corresponds to this
+     * instance, if calculated
+     */
+    private CstMethodRef methodRef;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass {@code non-null;} the type of the defining class
+     * @param nat {@code non-null;} the name-and-type
+     */
+    public CstInterfaceMethodRef(CstType definingClass, CstNat nat) {
+        super(definingClass, nat);
+        methodRef = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "ifaceMethod";
+    }
+
+    /**
+     * Gets a normal (non-interface) {@link CstMethodRef} that corresponds to
+     * this instance.
+     *
+     * @return {@code non-null;} an appropriate instance
+     */
+    public CstMethodRef toMethodRef() {
+        if (methodRef == null) {
+            methodRef = new CstMethodRef(getDefiningClass(), getNat());
+        }
+
+        return methodRef;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstKnownNull.java b/dx/src/com/android/dx/rop/cst/CstKnownNull.java
new file mode 100644
index 0000000..a80322c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstKnownNull.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constant type to represent a known-{@code null} value.
+ */
+public final class CstKnownNull extends CstLiteralBits {
+    /** {@code non-null;} unique instance of this class */
+    public static final CstKnownNull THE_ONE = new CstKnownNull();
+
+    /**
+     * Constructs an instance. This class is not publicly instantiable. Use
+     * {@link #THE_ONE}.
+     */
+    private CstKnownNull() {
+        // This space intentionally left blank.
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        return (other instanceof CstKnownNull);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return 0x4466757a;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "known-null";
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.KNOWN_NULL;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "known-null";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return "null";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean fitsInInt() {
+        // See comment in getIntBits().
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * As "literal bits," a known-null is always represented as the
+     * number zero.
+     */
+    @Override
+    public int getIntBits() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * As "literal bits," a known-null is always represented as the
+     * number zero.
+     */
+    @Override
+    public long getLongBits() {
+        return 0;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteral32.java b/dx/src/com/android/dx/rop/cst/CstLiteral32.java
new file mode 100644
index 0000000..042cbd9
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteral32.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal 32-bit values of some sort.
+ */
+public abstract class CstLiteral32
+        extends CstLiteralBits {
+    /** the value as {@code int} bits */
+    private final int bits;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bits the value as {@code int} bits
+     */
+    /*package*/ CstLiteral32(int bits) {
+        this.bits = bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean equals(Object other) {
+        return (other != null) &&
+            (getClass() == other.getClass()) &&
+            bits == ((CstLiteral32) other).bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int hashCode() {
+        return bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        int otherBits = ((CstLiteral32) other).bits;
+
+        if (bits < otherBits) {
+            return -1;
+        } else if (bits > otherBits) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean fitsInInt() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getIntBits() {
+        return bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final long getLongBits() {
+        return (long) bits;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteral64.java b/dx/src/com/android/dx/rop/cst/CstLiteral64.java
new file mode 100644
index 0000000..94cfa8c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteral64.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal 64-bit values of some sort.
+ */
+public abstract class CstLiteral64
+        extends CstLiteralBits {
+    /** the value as {@code long} bits */
+    private final long bits;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bits the value as {@code long} bits
+     */
+    /*package*/ CstLiteral64(long bits) {
+        this.bits = bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean equals(Object other) {
+        return (other != null) &&
+            (getClass() == other.getClass()) &&
+            bits == ((CstLiteral64) other).bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int hashCode() {
+        return (int) bits ^ (int) (bits >> 32);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        long otherBits = ((CstLiteral64) other).bits;
+
+        if (bits < otherBits) {
+            return -1;
+        } else if (bits > otherBits) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean isCategory2() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean fitsInInt() {
+        return (int) bits == bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int getIntBits() {
+        return (int) bits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final long getLongBits() {
+        return bits;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteralBits.java b/dx/src/com/android/dx/rop/cst/CstLiteralBits.java
new file mode 100644
index 0000000..8bf13a2
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteralBits.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal bitwise values of some sort.
+ */
+public abstract class CstLiteralBits
+        extends TypedConstant {
+    /**
+     * Returns whether or not this instance's value may be accurately
+     * represented as an {@code int}. The rule is that if there
+     * is an {@code int} which may be sign-extended to yield this
+     * instance's value, then this method returns {@code true}.
+     * Otherwise, it returns {@code false}.
+     *
+     * @return {@code true} iff this instance fits in an {@code int}
+     */
+    public abstract boolean fitsInInt();
+
+    /**
+     * Gets the value as {@code int} bits. If this instance contains
+     * more bits than fit in an {@code int}, then this returns only
+     * the low-order bits.
+     *
+     * @return the bits
+     */
+    public abstract int getIntBits();
+
+    /**
+     * Gets the value as {@code long} bits. If this instance contains
+     * fewer bits than fit in a {@code long}, then the result of this
+     * method is the sign extension of the value.
+     *
+     * @return the bits
+     */
+    public abstract long getLongBits();
+
+    /**
+     * Returns true if this value can fit in 16 bits with sign-extension.
+     *
+     * @return true if the sign-extended lower 16 bits are the same as
+     * the value.
+     */
+    public boolean fitsIn16Bits() {
+        if (! fitsInInt()) {
+            return false;
+        }
+
+        int bits = getIntBits();
+        return (short) bits == bits;
+    }
+
+    /**
+     * Returns true if this value can fit in 8 bits with sign-extension.
+     *
+     * @return true if the sign-extended lower 8 bits are the same as
+     * the value.
+     */
+    public boolean fitsIn8Bits() {
+        if (! fitsInInt()) {
+            return false;
+        }
+
+        int bits = getIntBits();
+        return (byte) bits == bits;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLong.java b/dx/src/com/android/dx/rop/cst/CstLong.java
new file mode 100644
index 0000000..d159529
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLong.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Long_info}.
+ */
+public final class CstLong
+        extends CstLiteral64 {
+    /** {@code non-null;} instance representing {@code 0} */
+    public static final CstLong VALUE_0 = make(0);
+
+    /** {@code non-null;} instance representing {@code 1} */
+    public static final CstLong VALUE_1 = make(1);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     *
+     * @param value the {@code long} value
+     */
+    public static CstLong make(long value) {
+        /*
+         * Note: Javadoc notwithstanding, this implementation always
+         * allocates.
+         */
+        return new CstLong(value);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     *
+     * @param value the {@code long} value
+     */
+    private CstLong(long value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        long value = getLongBits();
+        return "long{0x" + Hex.u8(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.LONG;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "long";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Long.toString(getLongBits());
+    }
+
+    /**
+     * Gets the {@code long} value.
+     *
+     * @return the value
+     */
+    public long getValue() {
+        return getLongBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstMemberRef.java b/dx/src/com/android/dx/rop/cst/CstMemberRef.java
new file mode 100644
index 0000000..0791087
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstMemberRef.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type {@code CONSTANT_*ref_info}.
+ */
+public abstract class CstMemberRef extends TypedConstant {
+    /** {@code non-null;} the type of the defining class */
+    private final CstType definingClass;
+
+    /** {@code non-null;} the name-and-type */
+    private final CstNat nat;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass {@code non-null;} the type of the defining class
+     * @param nat {@code non-null;} the name-and-type
+     */
+    /*package*/ CstMemberRef(CstType definingClass, CstNat nat) {
+        if (definingClass == null) {
+            throw new NullPointerException("definingClass == null");
+        }
+
+        if (nat == null) {
+            throw new NullPointerException("nat == null");
+        }
+
+        this.definingClass = definingClass;
+        this.nat = nat;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean equals(Object other) {
+        if ((other == null) || (getClass() != other.getClass())) {
+            return false;
+        }
+
+        CstMemberRef otherRef = (CstMemberRef) other;
+        return definingClass.equals(otherRef.definingClass) &&
+            nat.equals(otherRef.nat);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final int hashCode() {
+        return (definingClass.hashCode() * 31) ^ nat.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p><b>Note:</b> This implementation just compares the defining
+     * class and name, and it is up to subclasses to compare the rest
+     * after calling {@code super.compareTo0()}.</p>
+     */
+    @Override
+    protected int compareTo0(Constant other) {
+        CstMemberRef otherMember = (CstMemberRef) other;
+        int cmp = definingClass.compareTo(otherMember.definingClass);
+
+        if (cmp != 0) {
+            return cmp;
+        }
+
+        CstUtf8 thisName = nat.getName();
+        CstUtf8 otherName = otherMember.nat.getName();
+
+        return thisName.compareTo(otherName);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String toString() {
+        return typeName() + '{' + toHuman() + '}';
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public final String toHuman() {
+        return definingClass.toHuman() + '.' + nat.toHuman();
+    }
+
+    /**
+     * Gets the type of the defining class.
+     *
+     * @return {@code non-null;} the type of defining class
+     */
+    public final CstType getDefiningClass() {
+        return definingClass;
+    }
+
+    /**
+     * Gets the defining name-and-type.
+     *
+     * @return {@code non-null;} the name-and-type
+     */
+    public final CstNat getNat() {
+        return nat;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstMethodRef.java b/dx/src/com/android/dx/rop/cst/CstMethodRef.java
new file mode 100644
index 0000000..075bc7c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstMethodRef.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type {@code CONSTANT_Methodref_info}.
+ */
+public final class CstMethodRef
+        extends CstBaseMethodRef {
+    /**
+     * Constructs an instance.
+     *
+     * @param definingClass {@code non-null;} the type of the defining class
+     * @param nat {@code non-null;} the name-and-type
+     */
+    public CstMethodRef(CstType definingClass, CstNat nat) {
+        super(definingClass, nat);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "method";
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstNat.java b/dx/src/com/android/dx/rop/cst/CstNat.java
new file mode 100644
index 0000000..8a2c591
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstNat.java
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type {@code CONSTANT_NameAndType_info}.
+ */
+public final class CstNat extends Constant {
+    /**
+     * {@code non-null;} the instance for name {@code TYPE} and descriptor
+     * {@code java.lang.Class}, which is useful when dealing with
+     * wrapped primitives
+     */
+    public static final CstNat PRIMITIVE_TYPE_NAT =
+        new CstNat(new CstUtf8("TYPE"),
+                   new CstUtf8("Ljava/lang/Class;"));
+
+    /** {@code non-null;} the name */
+    private final CstUtf8 name;
+
+    /** {@code non-null;} the descriptor (type) */
+    private final CstUtf8 descriptor;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param name {@code non-null;} the name
+     * @param descriptor {@code non-null;} the descriptor
+     */
+    public CstNat(CstUtf8 name, CstUtf8 descriptor) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        if (descriptor == null) {
+            throw new NullPointerException("descriptor == null");
+        }
+
+        this.name = name;
+        this.descriptor = descriptor;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CstNat)) {
+            return false;
+        }
+
+        CstNat otherNat = (CstNat) other;
+        return name.equals(otherNat.name) &&
+            descriptor.equals(otherNat.descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return (name.hashCode() * 31) ^ descriptor.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        CstNat otherNat = (CstNat) other;
+        int cmp = name.compareTo(otherNat.name);
+
+        if (cmp != 0) {
+            return cmp;
+        }
+
+        return descriptor.compareTo(otherNat.descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "nat{" + toHuman() + '}';
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "nat";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /**
+     * Gets the name.
+     *
+     * @return {@code non-null;} the name
+     */
+    public CstUtf8 getName() {
+        return name;
+    }
+
+    /**
+     * Gets the descriptor.
+     *
+     * @return {@code non-null;} the descriptor
+     */
+    public CstUtf8 getDescriptor() {
+        return descriptor;
+    }
+
+    /**
+     * Returns an unadorned but human-readable version of the name-and-type
+     * value.
+     *
+     * @return {@code non-null;} the human form
+     */
+    public String toHuman() {
+        return name.toHuman() + ':' + descriptor.toHuman();
+    }
+
+    /**
+     * Gets the field type corresponding to this instance's descriptor.
+     * This method is only valid to call if the descriptor in fact describes
+     * a field (and not a method).
+     *
+     * @return {@code non-null;} the field type
+     */
+    public Type getFieldType() {
+        return Type.intern(descriptor.getString());
+    }
+
+    /**
+     * Gets whether this instance has the name of a standard instance
+     * initialization method. This is just a convenient shorthand for
+     * {@code getName().getString().equals("<init>")}.
+     *
+     * @return {@code true} iff this is a reference to an
+     * instance initialization method
+     */
+    public final boolean isInstanceInit() {
+        return name.getString().equals("<init>");
+    }
+
+    /**
+     * Gets whether this instance has the name of a standard class
+     * initialization method. This is just a convenient shorthand for
+     * {@code getName().getString().equals("<clinit>")}.
+     *
+     * @return {@code true} iff this is a reference to an
+     * instance initialization method
+     */
+    public final boolean isClassInit() {
+        return name.getString().equals("<clinit>");
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstShort.java b/dx/src/com/android/dx/rop/cst/CstShort.java
new file mode 100644
index 0000000..5be1022
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstShort.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code short}.
+ */
+public final class CstShort
+        extends CstLiteral32 {
+    /** {@code non-null;} the value {@code 0} as an instance of this class */
+    public static final CstShort VALUE_0 = make((short) 0);
+
+    /**
+     * Makes an instance for the given value. This may (but does not
+     * necessarily) return an already-allocated instance.
+     *
+     * @param value the {@code short} value
+     * @return {@code non-null;} the appropriate instance
+     */
+    public static CstShort make(short value) {
+        return new CstShort(value);
+    }
+
+    /**
+     * Makes an instance for the given {@code int} value. This
+     * may (but does not necessarily) return an already-allocated
+     * instance.
+     *
+     * @param value the value, which must be in range for a {@code short}
+     * @return {@code non-null;} the appropriate instance
+     */
+    public static CstShort make(int value) {
+        short cast = (short) value;
+
+        if (cast != value) {
+            throw new IllegalArgumentException("bogus short value: " +
+                    value);
+        }
+
+        return make(cast);
+    }
+
+    /**
+     * Constructs an instance. This constructor is private; use {@link #make}.
+     *
+     * @param value the {@code short} value
+     */
+    private CstShort(short value) {
+        super(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        int value = getIntBits();
+        return "short{0x" + Hex.u2(value) + " / " + value + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.SHORT;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "short";
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return Integer.toString(getIntBits());
+    }
+
+    /**
+     * Gets the {@code short} value.
+     *
+     * @return the value
+     */
+    public short getValue() {
+        return (short) getIntBits();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstString.java b/dx/src/com/android/dx/rop/cst/CstString.java
new file mode 100644
index 0000000..7dbfa02
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstString.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type {@code CONSTANT_String_info}.
+ */
+public final class CstString
+        extends TypedConstant {
+    /** {@code non-null;} the string value */
+    private final CstUtf8 string;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param string {@code non-null;} the string value
+     */
+    public CstString(CstUtf8 string) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+
+        this.string = string;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param string {@code non-null;} the string value
+     */
+    public CstString(String string) {
+        this(new CstUtf8(string));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CstString)) {
+            return false;
+        }
+
+        return string.equals(((CstString) other).string);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return string.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return string.compareTo(((CstString) other).string);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "string{" + toHuman() + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.STRING;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "string";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return string.toQuoted();
+    }
+
+    /**
+     * Gets the string value.
+     *
+     * @return {@code non-null;} the string value
+     */
+    public CstUtf8 getString() {
+        return string;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstType.java b/dx/src/com/android/dx/rop/cst/CstType.java
new file mode 100644
index 0000000..593adf8
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstType.java
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+import java.util.HashMap;
+
+/**
+ * Constants that represent an arbitrary type (reference or primitive).
+ */
+public final class CstType extends TypedConstant {
+    /** {@code non-null;} map of interned types */
+    private static final HashMap<Type, CstType> interns =
+        new HashMap<Type, CstType>(100);
+
+    /** {@code non-null;} instance corresponding to the class {@code Object} */
+    public static final CstType OBJECT = intern(Type.OBJECT);
+
+    /** {@code non-null;} instance corresponding to the class {@code Boolean} */
+    public static final CstType BOOLEAN = intern(Type.BOOLEAN_CLASS);
+
+    /** {@code non-null;} instance corresponding to the class {@code Byte} */
+    public static final CstType BYTE = intern(Type.BYTE_CLASS);
+
+    /** {@code non-null;} instance corresponding to the class {@code Character} */
+    public static final CstType CHARACTER = intern(Type.CHARACTER_CLASS);
+
+    /** {@code non-null;} instance corresponding to the class {@code Double} */
+    public static final CstType DOUBLE = intern(Type.DOUBLE_CLASS);
+
+    /** {@code non-null;} instance corresponding to the class {@code Float} */
+    public static final CstType FLOAT = intern(Type.FLOAT_CLASS);
+
+    /** {@code non-null;} instance corresponding to the class {@code Long} */
+    public static final CstType LONG = intern(Type.LONG_CLASS);
+
+    /** {@code non-null;} instance corresponding to the class {@code Integer} */
+    public static final CstType INTEGER = intern(Type.INTEGER_CLASS);
+
+    /** {@code non-null;} instance corresponding to the class {@code Short} */
+    public static final CstType SHORT = intern(Type.SHORT_CLASS);
+
+    /** {@code non-null;} instance corresponding to the class {@code Void} */
+    public static final CstType VOID = intern(Type.VOID_CLASS);
+
+    /** {@code non-null;} instance corresponding to the type {@code boolean[]} */
+    public static final CstType BOOLEAN_ARRAY = intern(Type.BOOLEAN_ARRAY);
+
+    /** {@code non-null;} instance corresponding to the type {@code byte[]} */
+    public static final CstType BYTE_ARRAY = intern(Type.BYTE_ARRAY);
+
+    /** {@code non-null;} instance corresponding to the type {@code char[]} */
+    public static final CstType CHAR_ARRAY = intern(Type.CHAR_ARRAY);
+
+    /** {@code non-null;} instance corresponding to the type {@code double[]} */
+    public static final CstType DOUBLE_ARRAY = intern(Type.DOUBLE_ARRAY);
+
+    /** {@code non-null;} instance corresponding to the type {@code float[]} */
+    public static final CstType FLOAT_ARRAY = intern(Type.FLOAT_ARRAY);
+
+    /** {@code non-null;} instance corresponding to the type {@code long[]} */
+    public static final CstType LONG_ARRAY = intern(Type.LONG_ARRAY);
+
+    /** {@code non-null;} instance corresponding to the type {@code int[]} */
+    public static final CstType INT_ARRAY = intern(Type.INT_ARRAY);
+
+    /** {@code non-null;} instance corresponding to the type {@code short[]} */
+    public static final CstType SHORT_ARRAY = intern(Type.SHORT_ARRAY);
+
+    /** {@code non-null;} the underlying type */
+    private final Type type;
+
+    /**
+     * {@code null-ok;} the type descriptor corresponding to this instance, if
+     * calculated
+     */
+    private CstUtf8 descriptor;
+
+    /**
+     * Returns an instance of this class that represents the wrapper
+     * class corresponding to a given primitive type. For example, if
+     * given {@link Type#INT}, this method returns the class reference
+     * {@code java.lang.Integer}.
+     *
+     * @param primitiveType {@code non-null;} the primitive type
+     * @return {@code non-null;} the corresponding wrapper class
+     */
+    public static CstType forBoxedPrimitiveType(Type primitiveType) {
+        switch (primitiveType.getBasicType()) {
+            case Type.BT_BOOLEAN: return BOOLEAN;
+            case Type.BT_BYTE:    return BYTE;
+            case Type.BT_CHAR:    return CHARACTER;
+            case Type.BT_DOUBLE:  return DOUBLE;
+            case Type.BT_FLOAT:   return FLOAT;
+            case Type.BT_INT:     return INTEGER;
+            case Type.BT_LONG:    return LONG;
+            case Type.BT_SHORT:   return SHORT;
+            case Type.BT_VOID:    return VOID;
+        }
+
+        throw new IllegalArgumentException("not primitive: " + primitiveType);
+    }
+
+    /**
+     * Returns an interned instance of this class for the given type.
+     *
+     * @param type {@code non-null;} the underlying type
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static CstType intern(Type type) {
+        CstType cst = interns.get(type);
+
+        if (cst == null) {
+            cst = new CstType(type);
+            interns.put(type, cst);
+        }
+
+        return cst;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param type {@code non-null;} the underlying type
+     */
+    public CstType(Type type) {
+        if (type == null) {
+            throw new NullPointerException("type == null");
+        }
+
+        if (type == type.KNOWN_NULL) {
+            throw new UnsupportedOperationException(
+                    "KNOWN_NULL is not representable");
+        }
+
+        this.type = type;
+        this.descriptor = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CstType)) {
+            return false;
+        }
+
+        return type == ((CstType) other).type;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return type.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        String thisDescriptor = type.getDescriptor();
+        String otherDescriptor = ((CstType) other).type.getDescriptor();
+        return thisDescriptor.compareTo(otherDescriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "type{" + toHuman() + '}';
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return Type.CLASS;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "type";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return type.toHuman();
+    }
+
+    /**
+     * Gets the underlying type (as opposed to the type corresponding
+     * to this instance as a constant, which is always
+     * {@code Class}).
+     *
+     * @return {@code non-null;} the type corresponding to the name
+     */
+    public Type getClassType() {
+        return type;
+    }
+
+    /**
+     * Gets the type descriptor for this instance.
+     *
+     * @return {@code non-null;} the descriptor
+     */
+    public CstUtf8 getDescriptor() {
+        if (descriptor == null) {
+            descriptor = new CstUtf8(type.getDescriptor());
+        }
+
+        return descriptor;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstUtf8.java b/dx/src/com/android/dx/rop/cst/CstUtf8.java
new file mode 100644
index 0000000..5cfc1f3
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstUtf8.java
@@ -0,0 +1,371 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Utf8_info}.
+ */
+public final class CstUtf8 extends Constant {
+    /**
+     * {@code non-null;} instance representing {@code ""}, that is, the
+     * empty string
+     */
+    public static final CstUtf8 EMPTY_STRING = new CstUtf8("");
+
+    /** {@code non-null;} the UTF-8 value as a string */
+    private final String string;
+
+    /** {@code non-null;} the UTF-8 value as bytes */
+    private final ByteArray bytes;
+
+    /**
+     * Converts a string into its Java-style UTF-8 form. Java-style UTF-8
+     * differs from normal UTF-8 in the handling of character '\0' and
+     * surrogate pairs.
+     *
+     * @param string {@code non-null;} the string to convert
+     * @return {@code non-null;} the UTF-8 bytes for it
+     */
+    public static byte[] stringToUtf8Bytes(String string) {
+        int len = string.length();
+        byte[] bytes = new byte[len * 3]; // Avoid having to reallocate.
+        int outAt = 0;
+
+        for (int i = 0; i < len; i++) {
+            char c = string.charAt(i);
+            if ((c != 0) && (c < 0x80)) {
+                bytes[outAt] = (byte) c;
+                outAt++;
+            } else if (c < 0x800) {
+                bytes[outAt] = (byte) (((c >> 6) & 0x1f) | 0xc0);
+                bytes[outAt + 1] = (byte) ((c & 0x3f) | 0x80);
+                outAt += 2;
+            } else {
+                bytes[outAt] = (byte) (((c >> 12) & 0x0f) | 0xe0);
+                bytes[outAt + 1] = (byte) (((c >> 6) & 0x3f) | 0x80);
+                bytes[outAt + 2] = (byte) ((c & 0x3f) | 0x80);
+                outAt += 3;
+            }
+        }
+
+        byte[] result = new byte[outAt];
+        System.arraycopy(bytes, 0, result, 0, outAt);
+        return result;
+    }
+
+    /**
+     * Converts an array of UTF-8 bytes into a string.
+     *
+     * @param bytes {@code non-null;} the bytes to convert
+     * @return {@code non-null;} the converted string
+     */
+    public static String utf8BytesToString(ByteArray bytes) {
+        int length = bytes.size();
+        char[] chars = new char[length]; // This is sized to avoid a realloc.
+        int outAt = 0;
+
+        for (int at = 0; length > 0; /*at*/) {
+            int v0 = bytes.getUnsignedByte(at);
+            char out;
+            switch (v0 >> 4) {
+                case 0x00: case 0x01: case 0x02: case 0x03:
+                case 0x04: case 0x05: case 0x06: case 0x07: {
+                    // 0XXXXXXX -- single-byte encoding
+                    length--;
+                    if (v0 == 0) {
+                        // A single zero byte is illegal.
+                        return throwBadUtf8(v0, at);
+                    }
+                    out = (char) v0;
+                    at++;
+                    break;
+                }
+                case 0x0c: case 0x0d: {
+                    // 110XXXXX -- two-byte encoding
+                    length -= 2;
+                    if (length < 0) {
+                        return throwBadUtf8(v0, at);
+                    }
+                    int v1 = bytes.getUnsignedByte(at + 1);
+                    if ((v1 & 0xc0) != 0x80) {
+                        return throwBadUtf8(v1, at + 1);
+                    }
+                    int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f);
+                    if ((value != 0) && (value < 0x80)) {
+                        /*
+                         * This should have been represented with
+                         * one-byte encoding.
+                         */
+                        return throwBadUtf8(v1, at + 1);
+                    }
+                    out = (char) value;
+                    at += 2;
+                    break;
+                }
+                case 0x0e: {
+                    // 1110XXXX -- three-byte encoding
+                    length -= 3;
+                    if (length < 0) {
+                        return throwBadUtf8(v0, at);
+                    }
+                    int v1 = bytes.getUnsignedByte(at + 1);
+                    if ((v1 & 0xc0) != 0x80) {
+                        return throwBadUtf8(v1, at + 1);
+                    }
+                    int v2 = bytes.getUnsignedByte(at + 2);
+                    if ((v1 & 0xc0) != 0x80) {
+                        return throwBadUtf8(v2, at + 2);
+                    }
+                    int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) |
+                        (v2 & 0x3f);
+                    if (value < 0x800) {
+                        /*
+                         * This should have been represented with one- or
+                         * two-byte encoding.
+                         */
+                        return throwBadUtf8(v2, at + 2);
+                    }
+                    out = (char) value;
+                    at += 3;
+                    break;
+                }
+                default: {
+                    // 10XXXXXX, 1111XXXX -- illegal
+                    return throwBadUtf8(v0, at);
+                }
+            }
+            chars[outAt] = out;
+            outAt++;
+        }
+
+        return new String(chars, 0, outAt);
+    }
+
+    /**
+     * Helper for {@link #utf8BytesToString}, which throws the right
+     * exception for a bogus utf-8 byte.
+     *
+     * @param value the byte value
+     * @param offset the file offset
+     * @return never
+     * @throws IllegalArgumentException always thrown
+     */
+    private static String throwBadUtf8(int value, int offset) {
+        throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) +
+                                           " at offset " + Hex.u4(offset));
+    }
+
+    /**
+     * Constructs an instance from a {@code String}.
+     *
+     * @param string {@code non-null;} the UTF-8 value as a string
+     */
+    public CstUtf8(String string) {
+        if (string == null) {
+            throw new NullPointerException("string == null");
+        }
+
+        this.string = string.intern();
+        this.bytes = new ByteArray(stringToUtf8Bytes(string));
+    }
+
+    /**
+     * Constructs an instance from some UTF-8 bytes.
+     *
+     * @param bytes {@code non-null;} array of the UTF-8 bytes
+     */
+    public CstUtf8(ByteArray bytes) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        this.bytes = bytes;
+        this.string = utf8BytesToString(bytes).intern();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CstUtf8)) {
+            return false;
+        }
+
+        return string.equals(((CstUtf8) other).string);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return string.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected int compareTo0(Constant other) {
+        return string.compareTo(((CstUtf8) other).string);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "utf8{\"" + toHuman() + "\"}";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String typeName() {
+        return "utf8";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isCategory2() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        int len = string.length();
+        StringBuilder sb = new StringBuilder(len * 3 / 2);
+
+        for (int i = 0; i < len; i++) {
+            char c = string.charAt(i);
+            if ((c >= ' ') && (c < 0x7f)) {
+                if ((c == '\'') || (c == '\"') || (c == '\\')) {
+                    sb.append('\\');
+                }
+                sb.append(c);
+            } else if (c <= 0x7f) {
+                switch (c) {
+                    case '\n': sb.append("\\n"); break;
+                    case '\r': sb.append("\\r"); break;
+                    case '\t': sb.append("\\t"); break;
+                    default: {
+                        /*
+                         * Represent the character as an octal escape.
+                         * If the next character is a valid octal
+                         * digit, disambiguate by using the
+                         * three-digit form.
+                         */
+                        char nextChar =
+                            (i < (len - 1)) ? string.charAt(i + 1) : 0;
+                        boolean displayZero =
+                            (nextChar >= '0') && (nextChar <= '7');
+                        sb.append('\\');
+                        for (int shift = 6; shift >= 0; shift -= 3) {
+                            char outChar = (char) (((c >> shift) & 7) + '0');
+                            if ((outChar != '0') || displayZero) {
+                                sb.append(outChar);
+                                displayZero = true;
+                            }
+                        }
+                        if (! displayZero) {
+                            // Ironic edge case: The original value was 0.
+                            sb.append('0');
+                        }
+                        break;
+                    }
+                }
+            } else {
+                sb.append("\\u");
+                sb.append(Character.forDigit(c >> 12, 16));
+                sb.append(Character.forDigit((c >> 8) & 0x0f, 16));
+                sb.append(Character.forDigit((c >> 4) & 0x0f, 16));
+                sb.append(Character.forDigit(c & 0x0f, 16));
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the value as a human-oriented string, surrounded by double
+     * quotes.
+     *
+     * @return {@code non-null;} the quoted string
+     */
+    public String toQuoted() {
+        return '\"' + toHuman() + '\"';
+    }
+
+    /**
+     * Gets the value as a human-oriented string, surrounded by double
+     * quotes, but ellipsizes the result if it is longer than the given
+     * maximum length
+     *
+     * @param maxLength {@code >= 5;} the maximum length of the string to return
+     * @return {@code non-null;} the quoted string
+     */
+    public String toQuoted(int maxLength) {
+        String string = toHuman();
+        int length = string.length();
+        String ellipses;
+
+        if (length <= (maxLength - 2)) {
+            ellipses = "";
+        } else {
+            string = string.substring(0, maxLength - 5);
+            ellipses = "...";
+        }
+
+        return '\"' + string + ellipses + '\"';
+    }
+
+    /**
+     * Gets the UTF-8 value as a string.
+     * The returned string is always already interned.
+     *
+     * @return {@code non-null;} the UTF-8 value as a string
+     */
+    public String getString() {
+        return string;
+    }
+
+    /**
+     * Gets the UTF-8 value as UTF-8 encoded bytes.
+     *
+     * @return {@code non-null;} an array of the UTF-8 bytes
+     */
+    public ByteArray getBytes() {
+        return bytes;
+    }
+
+    /**
+     * Gets the size of this instance as UTF-8 code points. That is,
+     * get the number of bytes in the UTF-8 encoding of this instance.
+     *
+     * @return {@code >= 0;} the UTF-8 size
+     */
+    public int getUtf8Size() {
+        return bytes.size();
+    }
+
+    /**
+     * Gets the size of this instance as UTF-16 code points. That is,
+     * get the number of 16-bit chars in the UTF-16 encoding of this
+     * instance. This is the same as the {@code length} of the
+     * Java {@code String} representation of this instance.
+     *
+     * @return {@code >= 0;} the UTF-16 size
+     */
+    public int getUtf16Size() {
+        return string.length();
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/StdConstantPool.java b/dx/src/com/android/dx/rop/cst/StdConstantPool.java
new file mode 100644
index 0000000..244395d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/StdConstantPool.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Standard implementation of {@link ConstantPool}, which directly stores
+ * an array of {@link Constant} objects and can be made immutable.
+ */
+public final class StdConstantPool
+        extends MutabilityControl implements ConstantPool {
+    /** {@code non-null;} array of entries */
+    private final Constant[] entries;
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the pool; this corresponds to the
+     * class file field {@code constant_pool_count}, and is in fact
+     * always at least one more than the actual size of the constant pool,
+     * as element {@code 0} is always invalid.
+     */
+    public StdConstantPool(int size) {
+        super(size > 1);
+
+        if (size < 1) {
+            throw new IllegalArgumentException("size < 1");
+        }
+
+        entries = new Constant[size];
+    }
+
+    /** {@inheritDoc} */
+    public int size() {
+        return entries.length;
+    }
+
+    /** {@inheritDoc} */
+    public Constant getOrNull(int n) {
+        try {
+            return entries[n];
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            return throwInvalid(n);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Constant get0Ok(int n) {
+        if (n == 0) {
+            return null;
+        }
+
+        return get(n);
+    }
+
+    /** {@inheritDoc} */
+    public Constant get(int n) {
+        try {
+            Constant result = entries[n];
+
+            if (result == null) {
+                throwInvalid(n);
+            }
+
+            return result;
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            return throwInvalid(n);
+        }
+    }
+
+    /**
+     * Sets the entry at the given index.
+     *
+     * @param n {@code >= 1, < size();} which entry
+     * @param cst {@code null-ok;} the constant to store
+     */
+    public void set(int n, Constant cst) {
+        throwIfImmutable();
+
+        boolean cat2 = (cst != null) && cst.isCategory2();
+
+        if (n < 1) {
+            throw new IllegalArgumentException("n < 1");
+        }
+
+        if (cat2) {
+            // Storing a category-2 entry nulls out the next index.
+            if (n == (entries.length - 1)) {
+                throw new IllegalArgumentException("(n == size - 1) && " +
+                                                   "cst.isCategory2()");
+            }
+            entries[n + 1] = null;
+        }
+
+        if ((cst != null) && (entries[n] == null)) {
+            /*
+             * Overwriting the second half of a category-2 entry nulls out
+             * the first half.
+             */
+            Constant prev = entries[n - 1];
+            if ((prev != null) && prev.isCategory2()) {
+                entries[n - 1] = null;
+            }
+        }
+
+        entries[n] = cst;
+    }
+
+    /**
+     * Throws the right exception for an invalid cpi.
+     *
+     * @param idx the bad cpi
+     * @return never
+     * @throws ExceptionWithContext always thrown
+     */
+    private static Constant throwInvalid(int idx) {
+        throw new ExceptionWithContext("invalid constant pool index " +
+                                       Hex.u2(idx));
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/TypedConstant.java b/dx/src/com/android/dx/rop/cst/TypedConstant.java
new file mode 100644
index 0000000..c250c46
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/TypedConstant.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.TypeBearer;
+
+/**
+ * Base class for constants which implement {@link TypeBearer}.
+ */
+public abstract class TypedConstant
+        extends Constant implements TypeBearer {
+    /**
+     * {@inheritDoc}
+     *
+     * This implentation always returns {@code this}.
+     */
+    public final TypeBearer getFrameType() {
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    public final int getBasicType() {
+        return getType().getBasicType();
+    }
+
+    /** {@inheritDoc} */
+    public final int getBasicFrameType() {
+        return getType().getBasicFrameType();
+    }
+
+    /** {@inheritDoc} */
+    public final boolean isConstant() {
+        return true;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/Zeroes.java b/dx/src/com/android/dx/rop/cst/Zeroes.java
new file mode 100644
index 0000000..7250b5a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/Zeroes.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Utility for turning types into zeroes.
+ */
+public final class Zeroes {
+    /**
+     * This class is uninstantiable.
+     */
+    private Zeroes() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the "zero" (or {@code null}) value for the given type.
+     *
+     * @param type {@code non-null;} the type in question
+     * @return {@code non-null;} its "zero" value
+     */
+    public static Constant zeroFor(Type type) {
+        switch (type.getBasicType()) {
+            case Type.BT_BOOLEAN: return CstBoolean.VALUE_FALSE;
+            case Type.BT_BYTE:    return CstByte.VALUE_0;
+            case Type.BT_CHAR:    return CstChar.VALUE_0;
+            case Type.BT_DOUBLE:  return CstDouble.VALUE_0;
+            case Type.BT_FLOAT:   return CstFloat.VALUE_0;
+            case Type.BT_INT:     return CstInteger.VALUE_0;
+            case Type.BT_LONG:    return CstLong.VALUE_0;
+            case Type.BT_SHORT:   return CstShort.VALUE_0;
+            case Type.BT_OBJECT:  return CstKnownNull.THE_ONE;
+            default: {
+                throw new UnsupportedOperationException("no zero for type: " +
+                        type.toHuman());
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/cst/package.html b/dx/src/com/android/dx/rop/cst/package.html
new file mode 100644
index 0000000..c784d16
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/package.html
@@ -0,0 +1,9 @@
+<body>
+<p>Interfaces and implementation of things related to the constant pool.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.type</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/rop/package-info.java b/dx/src/com/android/dx/rop/package-info.java
new file mode 100644
index 0000000..aaf21ee
--- /dev/null
+++ b/dx/src/com/android/dx/rop/package-info.java
@@ -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.
+ */
+
+package com.android.dx.rop;
+
+/**
+ * <h1>An Introduction to Rop Form</h1>
+ *
+ * This package contains classes associated with dx's {@code Rop}
+ * intermediate form.<p>
+ *
+ * The Rop form is intended to represent the instructions and the control-flow
+ * graph in a reasonably programmatically useful form while closely mirroring
+ * the dex instruction set.<p>
+ *
+ * <h2>Key Classes</h2>
+ *
+ * <ul>
+ * <li> {@link RopMethod}, the representation of an individual method
+ * <li> {@link BasicBlock} and its per-method container, {@link BasicBlockList},
+ * the representation of control flow elements.
+ * <li> {@link Insn} and its subclasses along with its per-basic block
+ * container {@link InsnList}. {@code Insn} instances represent
+ * individual instructions in the abstract register machine.
+ * <li> {@link RegisterSpec} and its container {@link RegisterSpecList}. A
+ * register spec encodes register number, register width, type information,
+ * and potentially local variable information as well for instruction sources
+ * and results.
+ * <li> {@link Rop} instances represent opcodes in the abstract machine. Many
+ * {@code Rop} instances are singletons defined in static fields in
+ * {@link Rops}. The rest are constructed dynamically using static methods
+ * in {@code Rops}
+ * <li> {@link RegOps} lists numeric constants for the opcodes
+ * <li> {@link Constant} and its subclasses represent constant data values
+ * that opcodes may refer to.
+ * <li> {@link Type} instances represent the core data types that can be
+ * handled by the abstract machine.
+ * <li> The {@link TypeBearer} interface is implemented by classes that
+ * represent a core data type, but may also have secondary information
+ * (such as constant value) associated with them.
+ * <ul>
+ *
+ * <h2>Control-Flow Graph</h2>
+ *
+ * Each method is separated into a list of basic blocks. For the most part,
+ * basic blocks are referred to by a positive integer
+ * {@link BasicBlock#getLabel label}, which is always unique per method. The
+ * label value is typically derived from a bytecode address from the source
+ * bytecode. Blocks that don't originate directly from source bytecode have
+ * labels generated for them in a mostly arbitrary order.<p>
+ *
+ * Blocks are referred to by their label, for the most part, because
+ * {@code BasicBlock} instances are immutable and thus any modification to
+ * the control flow graph or the instruction list results in replacement
+ * instances (with identical labels) being created.<p>
+ *
+ * A method has a single {@link RopMethod#getFirstLabel entry block} and 0
+ * to N {@link RopMethod#getExitPredecessors exit predecessor blocks} which
+ * will return. All blocks that are not the entry block will have at least
+ * one predecessor (or are unreachable and should be removed from the block
+ * list). All blocks that are not exit predecessors must have at least one
+ * successor.<p>
+ *
+ * Since all blocks must branch, all blocks must have, as their final
+ * instruction, an instruction whose opcode has a {@link Rop#getBranchingness
+ * branchingness} other than {@link Rop.BRANCH_NONE}. Furthermore, branching
+ * instructions may only be the final instruction in any basic block. If
+ * no other terminating opcode is appropriate, use a {@link Rops#GOTO GOTO}.<p>
+ *
+ * Typically a block will have a {@link BasicBlock#getPrimarySuccessor
+ * primary successor} which distinguishes a particular control flow path.
+ * For {Rops#isCallLike}call or call-like} opcodes, this is the path taken
+ * in the non-exceptional case, where all other successors represent
+ * various exception paths. For comparison operators such as
+ * {@link Rops#IF_EQZ_INT}, the primary successor represents the path taken
+ * if the <b>condition evaluates to false</b>. For {@link SwitchInsn switch
+ * instructions}, the primary successor is the default case.<p>
+ *
+ * A basic block's successor list is ordered and may refer to unique labels
+ * multiple times. For example, if a switch statement contains multiple case
+ * statements for the same code path, a single basic block will likely
+ * appear in the successor list multiple times. In general, the
+ * significance of the successor list's order (like the significance of
+ * the primary successor) is a property of the final instruction of the basic
+ * block. A basic block containing a {@link ThrowingInsn}, for example, has
+ * its successor list in an order identical to the
+ * {@link ThrowingInsn#getCatches} instruction's catches list, with the
+ * primary successor (the no-exception case) listed at the end.
+ *
+ * It is legal for a basic block to have no primary successor. An obvious
+ * example of this is a block that terminates in a {@link Rops#THROW throw}
+ * instruction where a catch block exists inside the current method for that
+ * exception class. Since the only possible path is the exception path, only
+ * the exception path (which cannot be a primary successor) is a successor.
+ * An example of this is shown in {@code dx/tests/092-ssa-cfg-edge-cases}.
+ *
+ * <h2>Rop Instructions</h2>
+ *
+ * <h3>move-result and move-result-pseudo</h3>
+ *
+ * An instruction that may throw an exception may not specify a result. This
+ * is necessary because the result register will not be assigned to if an
+ * exception occurs while processing said instruction and a result assignment
+ * may not occur. Since result assignments only occur in the non-exceptional
+ * case,  the result assignments for throwing instructions can be said to occur
+ * at the beginning of the primary successor block rather than at the end of
+ * the current block. The Rop form represents the result assignments this way.
+ * Throwing instructions may not directly specify results. Instead, result
+ * assignments are represented by {@link
+ * Rops#MOVE_RESULT move-result} or {@link Rops#MOVE_RESULT_PSEUDO
+ * move-result-pseudo} instructions at the top of the primary successor block.
+ *
+ * Only a single {@code move-result} or {@code move-result-pseudo}
+ * may exist in any block and it must be exactly the first instruction in the
+ * block.
+ *
+ * A {@code move-result} instruction is used for the results of call-like
+ * instructions. If the value produced by a {@code move-result} is not
+ * used by the method, it may be eliminated as dead code.
+ *
+ * A {@code move-result-pseudo} instruction is used for the results of
+ * non-call-like throwing instructions. It may never be considered dead code
+ * since the final dex instruction will always indicate a result register.
+ * If a required {@code move-result-pseudo} instruction is not found
+ * during conversion to dex bytecode, an exception will be thrown.
+ *
+ * <h3>move-exception</h3>
+ *
+ * A {@link RegOps.MOVE_EXCEPTION move-exception} instruction may appear at
+ * the start of a catch block, and represents the obtaining of the thrown
+ * exception instance. It may only occur as the first instruction in a
+ * basic block, and any basic block in which it occurs must be reachable only
+ * as an exception successor.
+ *
+ * <h3>move-param</h3>
+ *
+ * A {@link RegOps.MOVE_PARAM move-param} instruction represents a method
+ * parameter. Every {@code move-param} instruction is a
+ * {@link PlainCstInsn}. The index of the method parameter they refer to is
+ * carried as the {@link CstInteger integer constant} associated with the
+ * instruction.
+ *
+ * Any number of {@code move-param} instructions referring to the same
+ * parameter index may be included in a method's instruction lists. They
+ * have no restrictions on placement beyond those of any other
+ * {@link Rop.BRANCH_NONE} instruction. Note that the SSA optimizer arranges the
+ * parameter assignments to align with the dex bytecode calling conventions.
+ * With parameter assignments so arranged, the
+ * {@link com.android.dx.dex.code.RopTranslator} sees Rop {@code move-param}
+ * instructions as unnecessary in dex form and eliminates them.
+ *
+ * <h3>mark-local</h3>
+ *
+ * A {@link RegOps.MARK_LOCAL mark-local} instruction indicates that a local
+ * variable becomes live in a specified register specified register for the
+ * purposes of debug information. A {@code mark-local} instruction has
+ * a single source (the register which will now be considered a local variable)
+ * and no results. The instruction has no side effect.<p>
+ *
+ * In a typical case, a local variable's lifetime begins with an
+ * assignment. The local variable whose information is present in a result's
+ * {@link RegisterSpec#getLocalItem LocalItem} is considered to begin (or move
+ * between registers) when the instruction is executed.<p>
+ *
+ * However, sometimes a local variable can begin its life or move without
+ * an assignment occurring. A common example of this is occurs in the Rop
+ * representation of the following code:<p>
+ *
+ * <pre>
+ * try {
+ *     Object foo = null;
+ *     foo = new Object();
+ * } catch (Throwable ex) { }
+ * </pre>
+ *
+ * An object's initialization occurs in two steps. First, a
+ * {@code new-instance} instruction is executed, whose result is stored in a
+ * register. However, that register can not yet be considered to contain
+ * "foo". That's because the instance's constructor method must be called
+ * via an {@code invoke} instruction. The constructor method, however, may
+ * throw an exception. And if an exception occurs, then "foo" should remain
+ * null. So "foo" becomes the value of the result of the {@code new-instance}
+ * instruction after the (void) constructor method is invoked and
+ * returns successfully. In such a case, a {@code mark-local} will
+ * typically occur at the beginning of the primary successor block following
+ * the invocation to the constructor.
+ */
diff --git a/dx/src/com/android/dx/rop/type/Prototype.java b/dx/src/com/android/dx/rop/type/Prototype.java
new file mode 100644
index 0000000..cbd5328
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/Prototype.java
@@ -0,0 +1,397 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.type;
+
+import java.util.HashMap;
+
+/**
+ * Representation of a method decriptor. Instances of this class are
+ * generally interned and may be usefully compared with each other
+ * using {@code ==}.
+ */
+public final class Prototype implements Comparable<Prototype> {
+    /** {@code non-null;} intern table mapping string descriptors to instances */
+    private static final HashMap<String, Prototype> internTable =
+        new HashMap<String, Prototype>(500);
+
+    /** {@code non-null;} method descriptor */
+    private final String descriptor;
+
+    /** {@code non-null;} return type */
+    private final Type returnType;
+
+    /** {@code non-null;} list of parameter types */
+    private final StdTypeList parameterTypes;
+
+    /** {@code null-ok;} list of parameter frame types, if calculated */
+    private StdTypeList parameterFrameTypes;
+
+    /**
+     * Returns the unique instance corresponding to the
+     * given method descriptor. See vmspec-2 sec4.3.3 for details on the
+     * field descriptor syntax.
+     *
+     * @param descriptor {@code non-null;} the descriptor
+     * @return {@code non-null;} the corresponding instance
+     * @throws IllegalArgumentException thrown if the descriptor has
+     * invalid syntax
+     */
+    public static Prototype intern(String descriptor) {
+        if (descriptor == null) {
+            throw new NullPointerException("descriptor == null");
+        }
+
+        Prototype result = internTable.get(descriptor);
+        if (result != null) {
+            return result;
+        }
+
+        Type[] params = makeParameterArray(descriptor);
+        int paramCount = 0;
+        int at = 1;
+
+        for (;;) {
+            int startAt = at;
+            char c = descriptor.charAt(at);
+            if (c == ')') {
+                at++;
+                break;
+            }
+
+            // Skip array markers.
+            while (c == '[') {
+                at++;
+                c = descriptor.charAt(at);
+            }
+
+            if (c == 'L') {
+                // It looks like the start of a class name; find the end.
+                int endAt = descriptor.indexOf(';', at);
+                if (endAt == -1) {
+                    throw new IllegalArgumentException("bad descriptor");
+                }
+                at = endAt + 1;
+            } else {
+                at++;
+            }
+
+            params[paramCount] =
+                Type.intern(descriptor.substring(startAt, at));
+            paramCount++;
+        }
+
+        Type returnType = Type.internReturnType(descriptor.substring(at));
+        StdTypeList parameterTypes = new StdTypeList(paramCount);
+
+        for (int i = 0; i < paramCount; i++) {
+            parameterTypes.set(i, params[i]);
+        }
+
+        result = new Prototype(descriptor, returnType, parameterTypes);
+        return putIntern(result);
+    }
+
+    /**
+     * Helper for {@link #intern} which returns an empty array to
+     * populate with parsed parameter types, and which also ensures
+     * that there is a '(' at the start of the descriptor and a
+     * single ')' somewhere before the end.
+     *
+     * @param descriptor {@code non-null;} the descriptor string
+     * @return {@code non-null;} array large enough to hold all parsed parameter
+     * types, but which is likely actually larger than needed
+     */
+    private static Type[] makeParameterArray(String descriptor) {
+        int length = descriptor.length();
+
+        if (descriptor.charAt(0) != '(') {
+            throw new IllegalArgumentException("bad descriptor");
+        }
+
+        /*
+         * This is a cheesy way to establish an upper bound on the
+         * number of parameters: Just count capital letters.
+         */
+        int closeAt = 0;
+        int maxParams = 0;
+        for (int i = 1; i < length; i++) {
+            char c = descriptor.charAt(i);
+            if (c == ')') {
+                closeAt = i;
+                break;
+            }
+            if ((c >= 'A') && (c <= 'Z')) {
+                maxParams++;
+            }
+        }
+
+        if ((closeAt == 0) || (closeAt == (length - 1))) {
+            throw new IllegalArgumentException("bad descriptor");
+        }
+
+        if (descriptor.indexOf(')', closeAt + 1) != -1) {
+            throw new IllegalArgumentException("bad descriptor");
+        }
+
+        return new Type[maxParams];
+    }
+
+    /**
+     * Interns an instance, adding to the descriptor as necessary based
+     * on the given definer, name, and flags. For example, an init
+     * method has an uninitialized object of type {@code definer}
+     * as its first argument.
+     *
+     * @param descriptor {@code non-null;} the descriptor string
+     * @param definer {@code non-null;} class the method is defined on
+     * @param isStatic whether this is a static method
+     * @param isInit whether this is an init method
+     * @return {@code non-null;} the interned instance
+     */
+    public static Prototype intern(String descriptor, Type definer,
+            boolean isStatic, boolean isInit) {
+        Prototype base = intern(descriptor);
+
+        if (isStatic) {
+            return base;
+        }
+
+        if (isInit) {
+            definer = definer.asUninitialized(Integer.MAX_VALUE);
+        }
+
+        return base.withFirstParameter(definer);
+    }
+
+    /**
+     * Interns an instance which consists of the given number of
+     * {@code int}s along with the given return type
+     *
+     * @param returnType {@code non-null;} the return type
+     * @param count {@code > 0;} the number of elements in the prototype
+     * @return {@code non-null;} the interned instance
+     */
+    public static Prototype internInts(Type returnType, int count) {
+        // Make the descriptor...
+
+        StringBuffer sb = new StringBuffer(100);
+
+        sb.append('(');
+
+        for (int i = 0; i < count; i++) {
+            sb.append('I');
+        }
+
+        sb.append(')');
+        sb.append(returnType.getDescriptor());
+
+        // ...and intern it.
+        return intern(sb.toString());
+    }
+
+    /**
+     * Constructs an instance. This is a private constructor; use one
+     * of the public static methods to get instances.
+     *
+     * @param descriptor {@code non-null;} the descriptor string
+     */
+    private Prototype(String descriptor, Type returnType,
+            StdTypeList parameterTypes) {
+        if (descriptor == null) {
+            throw new NullPointerException("descriptor == null");
+        }
+
+        if (returnType == null) {
+            throw new NullPointerException("returnType == null");
+        }
+
+        if (parameterTypes == null) {
+            throw new NullPointerException("parameterTypes == null");
+        }
+
+        this.descriptor = descriptor;
+        this.returnType = returnType;
+        this.parameterTypes = parameterTypes;
+        this.parameterFrameTypes = null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            /*
+             * Since externally-visible instances are interned, this
+             * check helps weed out some easy cases.
+             */
+            return true;
+        }
+
+        if (!(other instanceof Prototype)) {
+            return false;
+        }
+
+        return descriptor.equals(((Prototype) other).descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return descriptor.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Prototype other) {
+        if (this == other) {
+            return 0;
+        }
+
+        /*
+         * The return type is the major order, and then args in order,
+         * and then the shorter list comes first (similar to string
+         * sorting).
+         */
+
+        int result = returnType.compareTo(other.returnType);
+
+        if (result != 0) {
+            return result;
+        }
+
+        int thisSize = parameterTypes.size();
+        int otherSize = other.parameterTypes.size();
+        int size = Math.min(thisSize, otherSize);
+
+        for (int i = 0; i < size; i++) {
+            Type thisType = parameterTypes.get(i);
+            Type otherType = other.parameterTypes.get(i);
+
+            result = thisType.compareTo(otherType);
+
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        if (thisSize < otherSize) {
+            return -1;
+        } else if (thisSize > otherSize) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return descriptor;
+    }
+
+    /**
+     * Gets the descriptor string.
+     *
+     * @return {@code non-null;} the descriptor
+     */
+    public String getDescriptor() {
+        return descriptor;
+    }
+
+    /**
+     * Gets the return type.
+     *
+     * @return {@code non-null;} the return type
+     */
+    public Type getReturnType() {
+        return returnType;
+    }
+
+    /**
+     * Gets the list of parameter types.
+     *
+     * @return {@code non-null;} the list of parameter types
+     */
+    public StdTypeList getParameterTypes() {
+        return parameterTypes;
+    }
+
+    /**
+     * Gets the list of frame types corresponding to the list of parameter
+     * types. The difference between the two lists (if any) is that all
+     * "intlike" types (see {@link Type#isIntlike}) are replaced by
+     * {@link Type#INT}.
+     *
+     * @return {@code non-null;} the list of parameter frame types
+     */
+    public StdTypeList getParameterFrameTypes() {
+        if (parameterFrameTypes == null) {
+            int sz = parameterTypes.size();
+            StdTypeList list = new StdTypeList(sz);
+            boolean any = false;
+            for (int i = 0; i < sz; i++) {
+                Type one = parameterTypes.get(i);
+                if (one.isIntlike()) {
+                    any = true;
+                    one = Type.INT;
+                }
+                list.set(i, one);
+            }
+            parameterFrameTypes = any ? list : parameterTypes;
+        }
+
+        return parameterFrameTypes;
+    }
+
+    /**
+     * Returns a new interned instance, which is the same as this instance,
+     * except that it has an additional parameter prepended to the original's
+     * argument list.
+     *
+     * @param param {@code non-null;} the new first parameter
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public Prototype withFirstParameter(Type param) {
+        String newDesc = "(" + param.getDescriptor() + descriptor.substring(1);
+        StdTypeList newParams = parameterTypes.withFirst(param);
+
+        newParams.setImmutable();
+
+        Prototype result =
+            new Prototype(newDesc, returnType, newParams);
+
+        return putIntern(result);
+    }
+
+    /**
+     * Puts the given instance in the intern table if it's not already
+     * there. If a conflicting value is already in the table, then leave it.
+     * Return the interned value.
+     *
+     * @param desc {@code non-null;} instance to make interned
+     * @return {@code non-null;} the actual interned object
+     */
+    private static Prototype putIntern(Prototype desc) {
+        synchronized (internTable) {
+            String descriptor = desc.getDescriptor();
+            Prototype already = internTable.get(descriptor);
+            if (already != null) {
+                return already;
+            }
+            internTable.put(descriptor, desc);
+            return desc;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/type/StdTypeList.java b/dx/src/com/android/dx/rop/type/StdTypeList.java
new file mode 100644
index 0000000..fe6647c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/StdTypeList.java
@@ -0,0 +1,407 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link TypeList}.
+ */
+public final class StdTypeList
+        extends FixedSizeList implements TypeList {
+    /** {@code non-null;} no-element instance */
+    public static final StdTypeList EMPTY = new StdTypeList(0);
+
+    /** {@code non-null;} the list {@code [int]} */
+    public static final StdTypeList INT = StdTypeList.make(Type.INT);
+
+    /** {@code non-null;} the list {@code [long]} */
+    public static final StdTypeList LONG = StdTypeList.make(Type.LONG);
+
+    /** {@code non-null;} the list {@code [float]} */
+    public static final StdTypeList FLOAT = StdTypeList.make(Type.FLOAT);
+
+    /** {@code non-null;} the list {@code [double]} */
+    public static final StdTypeList DOUBLE = StdTypeList.make(Type.DOUBLE);
+
+    /** {@code non-null;} the list {@code [Object]} */
+    public static final StdTypeList OBJECT = StdTypeList.make(Type.OBJECT);
+
+    /** {@code non-null;} the list {@code [ReturnAddress]} */
+    public static final StdTypeList RETURN_ADDRESS
+            = StdTypeList.make(Type.RETURN_ADDRESS);
+
+    /** {@code non-null;} the list {@code [Throwable]} */
+    public static final StdTypeList THROWABLE =
+        StdTypeList.make(Type.THROWABLE);
+
+    /** {@code non-null;} the list {@code [int, int]} */
+    public static final StdTypeList INT_INT =
+        StdTypeList.make(Type.INT, Type.INT);
+
+    /** {@code non-null;} the list {@code [long, long]} */
+    public static final StdTypeList LONG_LONG =
+        StdTypeList.make(Type.LONG, Type.LONG);
+
+    /** {@code non-null;} the list {@code [float, float]} */
+    public static final StdTypeList FLOAT_FLOAT =
+        StdTypeList.make(Type.FLOAT, Type.FLOAT);
+
+    /** {@code non-null;} the list {@code [double, double]} */
+    public static final StdTypeList DOUBLE_DOUBLE =
+        StdTypeList.make(Type.DOUBLE, Type.DOUBLE);
+
+    /** {@code non-null;} the list {@code [Object, Object]} */
+    public static final StdTypeList OBJECT_OBJECT =
+        StdTypeList.make(Type.OBJECT, Type.OBJECT);
+
+    /** {@code non-null;} the list {@code [int, Object]} */
+    public static final StdTypeList INT_OBJECT =
+        StdTypeList.make(Type.INT, Type.OBJECT);
+
+    /** {@code non-null;} the list {@code [long, Object]} */
+    public static final StdTypeList LONG_OBJECT =
+        StdTypeList.make(Type.LONG, Type.OBJECT);
+
+    /** {@code non-null;} the list {@code [float, Object]} */
+    public static final StdTypeList FLOAT_OBJECT =
+        StdTypeList.make(Type.FLOAT, Type.OBJECT);
+
+    /** {@code non-null;} the list {@code [double, Object]} */
+    public static final StdTypeList DOUBLE_OBJECT =
+        StdTypeList.make(Type.DOUBLE, Type.OBJECT);
+
+    /** {@code non-null;} the list {@code [long, int]} */
+    public static final StdTypeList LONG_INT =
+        StdTypeList.make(Type.LONG, Type.INT);
+
+    /** {@code non-null;} the list {@code [int[], int]} */
+    public static final StdTypeList INTARR_INT =
+        StdTypeList.make(Type.INT_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [long[], int]} */
+    public static final StdTypeList LONGARR_INT =
+        StdTypeList.make(Type.LONG_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [float[], int]} */
+    public static final StdTypeList FLOATARR_INT =
+        StdTypeList.make(Type.FLOAT_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [double[], int]} */
+    public static final StdTypeList DOUBLEARR_INT =
+        StdTypeList.make(Type.DOUBLE_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [Object[], int]} */
+    public static final StdTypeList OBJECTARR_INT =
+        StdTypeList.make(Type.OBJECT_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [boolean[], int]} */
+    public static final StdTypeList BOOLEANARR_INT =
+        StdTypeList.make(Type.BOOLEAN_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [byte[], int]} */
+    public static final StdTypeList BYTEARR_INT =
+        StdTypeList.make(Type.BYTE_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [char[], int]} */
+    public static final StdTypeList CHARARR_INT =
+        StdTypeList.make(Type.CHAR_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [short[], int]} */
+    public static final StdTypeList SHORTARR_INT =
+        StdTypeList.make(Type.SHORT_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [int, int[], int]} */
+    public static final StdTypeList INT_INTARR_INT =
+        StdTypeList.make(Type.INT, Type.INT_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [long, long[], int]} */
+    public static final StdTypeList LONG_LONGARR_INT =
+        StdTypeList.make(Type.LONG, Type.LONG_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [float, float[], int]} */
+    public static final StdTypeList FLOAT_FLOATARR_INT =
+        StdTypeList.make(Type.FLOAT, Type.FLOAT_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [double, double[], int]} */
+    public static final StdTypeList DOUBLE_DOUBLEARR_INT =
+        StdTypeList.make(Type.DOUBLE, Type.DOUBLE_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [Object, Object[], int]} */
+    public static final StdTypeList OBJECT_OBJECTARR_INT =
+        StdTypeList.make(Type.OBJECT, Type.OBJECT_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [int, boolean[], int]} */
+    public static final StdTypeList INT_BOOLEANARR_INT =
+        StdTypeList.make(Type.INT, Type.BOOLEAN_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [int, byte[], int]} */
+    public static final StdTypeList INT_BYTEARR_INT =
+        StdTypeList.make(Type.INT, Type.BYTE_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [int, char[], int]} */
+    public static final StdTypeList INT_CHARARR_INT =
+        StdTypeList.make(Type.INT, Type.CHAR_ARRAY, Type.INT);
+
+    /** {@code non-null;} the list {@code [int, short[], int]} */
+    public static final StdTypeList INT_SHORTARR_INT =
+        StdTypeList.make(Type.INT, Type.SHORT_ARRAY, Type.INT);
+
+    /**
+     * Makes a single-element instance.
+     *
+     * @param type {@code non-null;} the element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static StdTypeList make(Type type) {
+        StdTypeList result = new StdTypeList(1);
+        result.set(0, type);
+        return result;
+    }
+
+    /**
+     * Makes a two-element instance.
+     *
+     * @param type0 {@code non-null;} the first element
+     * @param type1 {@code non-null;} the second element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static StdTypeList make(Type type0, Type type1) {
+        StdTypeList result = new StdTypeList(2);
+        result.set(0, type0);
+        result.set(1, type1);
+        return result;
+    }
+
+    /**
+     * Makes a three-element instance.
+     *
+     * @param type0 {@code non-null;} the first element
+     * @param type1 {@code non-null;} the second element
+     * @param type2 {@code non-null;} the third element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static StdTypeList make(Type type0, Type type1, Type type2) {
+        StdTypeList result = new StdTypeList(3);
+        result.set(0, type0);
+        result.set(1, type1);
+        result.set(2, type2);
+        return result;
+    }
+
+    /**
+     * Makes a four-element instance.
+     *
+     * @param type0 {@code non-null;} the first element
+     * @param type1 {@code non-null;} the second element
+     * @param type2 {@code non-null;} the third element
+     * @param type3 {@code non-null;} the fourth element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static StdTypeList make(Type type0, Type type1, Type type2,
+                                   Type type3) {
+        StdTypeList result = new StdTypeList(4);
+        result.set(0, type0);
+        result.set(1, type1);
+        result.set(2, type2);
+        result.set(3, type3);
+        return result;
+    }
+
+    /**
+     * Returns the given list as a comma-separated list of human forms. This
+     * is a static method so as to work on arbitrary {@link TypeList}
+     * instances.
+     *
+     * @param list {@code non-null;} the list to convert
+     * @return {@code non-null;} the human form
+     */
+    public static String toHuman(TypeList list) {
+        int size = list.size();
+
+        if (size == 0) {
+            return "<empty>";
+        }
+
+        StringBuffer sb = new StringBuffer(100);
+
+        for (int i = 0; i < size; i++) {
+            if (i != 0) {
+                sb.append(", ");
+            }
+            sb.append(list.getType(i).toHuman());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns a hashcode of the contents of the given list. This
+     * is a static method so as to work on arbitrary {@link TypeList}
+     * instances.
+     *
+     * @param list {@code non-null;} the list to inspect
+     * @return {@code non-null;} the hash code
+     */
+    public static int hashContents(TypeList list) {
+        int size = list.size();
+        int hash = 0;
+
+        for (int i = 0; i < size; i++) {
+            hash = (hash * 31) + list.getType(i).hashCode();
+        }
+
+        return hash;
+    }
+
+    /**
+     * Compares the contents of the given two instances for equality. This
+     * is a static method so as to work on arbitrary {@link TypeList}
+     * instances.
+     *
+     * @param list1 {@code non-null;} one list to compare
+     * @param list2 {@code non-null;} another list to compare
+     * @return whether the two lists contain corresponding equal elements
+     */
+    public static boolean equalContents(TypeList list1, TypeList list2) {
+        int size = list1.size();
+
+        if (list2.size() != size) {
+            return false;
+        }
+
+        for (int i = 0; i < size; i++) {
+            if (! list1.getType(i).equals(list2.getType(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Compares the contents of the given two instances for ordering. This
+     * is a static method so as to work on arbitrary {@link TypeList}
+     * instances.
+     *
+     * @param list1 {@code non-null;} one list to compare
+     * @param list2 {@code non-null;} another list to compare
+     * @return the order of the two lists
+     */
+    public static int compareContents(TypeList list1, TypeList list2) {
+        int size1 = list1.size();
+        int size2 = list2.size();
+        int size = Math.min(size1, size2);
+
+        for (int i = 0; i < size; i++) {
+            int comparison = list1.getType(i).compareTo(list2.getType(i));
+            if (comparison != 0) {
+                return comparison;
+            }
+        }
+
+        if (size1 == size2) {
+            return 0;
+        } else if (size1 < size2) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public StdTypeList(int size) {
+        super(size);
+    }
+
+    /** {@inheritDoc} */
+    public Type getType(int n) {
+        return get(n);
+    }
+
+    /** {@inheritDoc} */
+    public int getWordCount() {
+        int sz = size();
+        int result = 0;
+
+        for (int i = 0; i < sz; i++) {
+            result += get(i).getCategory();
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public TypeList withAddedType(Type type) {
+        int sz = size();
+        StdTypeList result = new StdTypeList(sz + 1);
+
+        for (int i = 0; i < sz; i++) {
+            result.set0(i, get0(i));
+        }
+
+        result.set(sz, type);
+        result.setImmutable();
+        return result;
+    }
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @return {@code non-null;} the indicated element
+     */
+    public Type get(int n) {
+        return (Type) get0(n);
+    }
+
+    /**
+     * Sets the type at the given index.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param type {@code non-null;} the type to store
+     */
+    public void set(int n, Type type) {
+        set0(n, type);
+    }
+
+    /**
+     * Returns a new instance, which is the same as this instance,
+     * except that it has an additional type prepended to the
+     * original.
+     *
+     * @param type {@code non-null;} the new first element
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public StdTypeList withFirst(Type type) {
+        int sz = size();
+        StdTypeList result = new StdTypeList(sz + 1);
+
+        result.set0(0, type);
+        for (int i = 0; i < sz; i++) {
+            result.set0(i + 1, getOrNull0(i));
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/rop/type/Type.java b/dx/src/com/android/dx/rop/type/Type.java
new file mode 100644
index 0000000..16b16f5
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/Type.java
@@ -0,0 +1,855 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.Hex;
+
+import java.util.HashMap;
+
+/**
+ * Representation of a value type, such as may appear in a field, in a
+ * local, on a stack, or in a method descriptor. Instances of this
+ * class are generally interned and may be usefully compared with each
+ * other using {@code ==}.
+ */
+public final class Type implements TypeBearer, Comparable<Type> {
+    /** {@code non-null;} intern table mapping string descriptors to instances */
+    private static final HashMap<String, Type> internTable =
+        new HashMap<String, Type>(500);
+
+    /** basic type constant for {@code void} */
+    public static final int BT_VOID = 0;
+
+    /** basic type constant for {@code boolean} */
+    public static final int BT_BOOLEAN = 1;
+
+    /** basic type constant for {@code byte} */
+    public static final int BT_BYTE = 2;
+
+    /** basic type constant for {@code char} */
+    public static final int BT_CHAR = 3;
+
+    /** basic type constant for {@code double} */
+    public static final int BT_DOUBLE = 4;
+
+    /** basic type constant for {@code float} */
+    public static final int BT_FLOAT = 5;
+
+    /** basic type constant for {@code int} */
+    public static final int BT_INT = 6;
+
+    /** basic type constant for {@code long} */
+    public static final int BT_LONG = 7;
+
+    /** basic type constant for {@code short} */
+    public static final int BT_SHORT = 8;
+
+    /** basic type constant for {@code Object} */
+    public static final int BT_OBJECT = 9;
+
+    /** basic type constant for a return address */
+    public static final int BT_ADDR = 10;
+
+    /** count of basic type constants */
+    public static final int BT_COUNT = 11;
+
+    /** {@code non-null;} instance representing {@code boolean} */
+    public static final Type BOOLEAN = new Type("Z", BT_BOOLEAN);
+
+    /** {@code non-null;} instance representing {@code byte} */
+    public static final Type BYTE = new Type("B", BT_BYTE);
+
+    /** {@code non-null;} instance representing {@code char} */
+    public static final Type CHAR = new Type("C", BT_CHAR);
+
+    /** {@code non-null;} instance representing {@code double} */
+    public static final Type DOUBLE = new Type("D", BT_DOUBLE);
+
+    /** {@code non-null;} instance representing {@code float} */
+    public static final Type FLOAT = new Type("F", BT_FLOAT);
+
+    /** {@code non-null;} instance representing {@code int} */
+    public static final Type INT = new Type("I", BT_INT);
+
+    /** {@code non-null;} instance representing {@code long} */
+    public static final Type LONG = new Type("J", BT_LONG);
+
+    /** {@code non-null;} instance representing {@code short} */
+    public static final Type SHORT = new Type("S", BT_SHORT);
+
+    /** {@code non-null;} instance representing {@code void} */
+    public static final Type VOID = new Type("V", BT_VOID);
+
+    /** {@code non-null;} instance representing a known-{@code null} */
+    public static final Type KNOWN_NULL = new Type("<null>", BT_OBJECT);
+
+    /** {@code non-null;} instance representing a subroutine return address */
+    public static final Type RETURN_ADDRESS = new Type("<addr>", BT_ADDR);
+
+    static {
+        /*
+         * Put all the primitive types into the intern table. This needs
+         * to happen before the array types below get interned.
+         */
+        putIntern(BOOLEAN);
+        putIntern(BYTE);
+        putIntern(CHAR);
+        putIntern(DOUBLE);
+        putIntern(FLOAT);
+        putIntern(INT);
+        putIntern(LONG);
+        putIntern(SHORT);
+        /*
+         * Note: VOID isn't put in the intern table, since it's special and
+         * shouldn't be found by a normal call to intern().
+         */
+    }
+
+    /**
+     * {@code non-null;} instance representing
+     * {@code java.lang.annotation.Annotation}
+     */
+    public static final Type ANNOTATION =
+        intern("Ljava/lang/annotation/Annotation;");
+
+    /** {@code non-null;} instance representing {@code java.lang.Class} */
+    public static final Type CLASS = intern("Ljava/lang/Class;");
+
+    /** {@code non-null;} instance representing {@code java.lang.Cloneable} */
+    public static final Type CLONEABLE = intern("Ljava/lang/Cloneable;");
+
+    /** {@code non-null;} instance representing {@code java.lang.Object} */
+    public static final Type OBJECT = intern("Ljava/lang/Object;");
+
+    /** {@code non-null;} instance representing {@code java.io.Serializable} */
+    public static final Type SERIALIZABLE = intern("Ljava/io/Serializable;");
+
+    /** {@code non-null;} instance representing {@code java.lang.String} */
+    public static final Type STRING = intern("Ljava/lang/String;");
+
+    /** {@code non-null;} instance representing {@code java.lang.Throwable} */
+    public static final Type THROWABLE = intern("Ljava/lang/Throwable;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Boolean}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type BOOLEAN_CLASS = intern("Ljava/lang/Boolean;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Byte}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type BYTE_CLASS = intern("Ljava/lang/Byte;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Character}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type CHARACTER_CLASS = intern("Ljava/lang/Character;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Double}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type DOUBLE_CLASS = intern("Ljava/lang/Double;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Float}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type FLOAT_CLASS = intern("Ljava/lang/Float;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Integer}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type INTEGER_CLASS = intern("Ljava/lang/Integer;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Long}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type LONG_CLASS = intern("Ljava/lang/Long;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Short}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type SHORT_CLASS = intern("Ljava/lang/Short;");
+
+    /**
+     * {@code non-null;} instance representing {@code java.lang.Void}; the
+     * suffix on the name helps disambiguate this from the instance
+     * representing a primitive type
+     */
+    public static final Type VOID_CLASS = intern("Ljava/lang/Void;");
+
+    /** {@code non-null;} instance representing {@code boolean[]} */
+    public static final Type BOOLEAN_ARRAY = BOOLEAN.getArrayType();
+
+    /** {@code non-null;} instance representing {@code byte[]} */
+    public static final Type BYTE_ARRAY = BYTE.getArrayType();
+
+    /** {@code non-null;} instance representing {@code char[]} */
+    public static final Type CHAR_ARRAY = CHAR.getArrayType();
+
+    /** {@code non-null;} instance representing {@code double[]} */
+    public static final Type DOUBLE_ARRAY = DOUBLE.getArrayType();
+
+    /** {@code non-null;} instance representing {@code float[]} */
+    public static final Type FLOAT_ARRAY = FLOAT.getArrayType();
+
+    /** {@code non-null;} instance representing {@code int[]} */
+    public static final Type INT_ARRAY = INT.getArrayType();
+
+    /** {@code non-null;} instance representing {@code long[]} */
+    public static final Type LONG_ARRAY = LONG.getArrayType();
+
+    /** {@code non-null;} instance representing {@code Object[]} */
+    public static final Type OBJECT_ARRAY = OBJECT.getArrayType();
+
+    /** {@code non-null;} instance representing {@code short[]} */
+    public static final Type SHORT_ARRAY = SHORT.getArrayType();
+
+    /** {@code non-null;} field descriptor for the type */
+    private final String descriptor;
+
+    /**
+     * basic type corresponding to this type; one of the
+     * {@code BT_*} constants
+     */
+    private final int basicType;
+
+    /**
+     * {@code >= -1;} for an uninitialized type, bytecode index that this
+     * instance was allocated at; {@code Integer.MAX_VALUE} if it
+     * was an incoming uninitialized instance; {@code -1} if this
+     * is an <i>inititialized</i> instance
+     */
+    private final int newAt;
+
+    /**
+     * {@code null-ok;} the internal-form class name corresponding to this type, if
+     * calculated; only valid if {@code this} is a reference type and
+     * additionally not a return address
+     */
+    private String className;
+
+    /**
+     * {@code null-ok;} the type corresponding to an array of this type, if
+     * calculated
+     */
+    private Type arrayType;
+
+    /**
+     * {@code null-ok;} the type corresponding to elements of this type, if
+     * calculated; only valid if {@code this} is an array type
+     */
+    private Type componentType;
+
+    /**
+     * {@code null-ok;} the type corresponding to the initialized version of
+     * this type, if this instance is in fact an uninitialized type
+     */
+    private Type initializedType;
+
+    /**
+     * Returns the unique instance corresponding to the type with the
+     * given descriptor. See vmspec-2 sec4.3.2 for details on the
+     * field descriptor syntax. This method does <i>not</i> allow
+     * {@code "V"} (that is, type {@code void}) as a valid
+     * descriptor.
+     *
+     * @param descriptor {@code non-null;} the descriptor
+     * @return {@code non-null;} the corresponding instance
+     * @throws IllegalArgumentException thrown if the descriptor has
+     * invalid syntax
+     */
+    public static Type intern(String descriptor) {
+        Type result = internTable.get(descriptor);
+        if (result != null) {
+            return result;
+        }
+
+        char firstChar;
+        try {
+            firstChar = descriptor.charAt(0);
+        } catch (IndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("descriptor is empty");
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("descriptor == null");
+        }
+
+        if (firstChar == '[') {
+            /*
+             * Recursively strip away array markers to get at the underlying
+             * type, and build back on to form the result.
+             */
+            result = intern(descriptor.substring(1));
+            return result.getArrayType();
+        }
+
+        /*
+         * If the first character isn't '[' and it wasn't found in the
+         * intern cache, then it had better be the descriptor for a class.
+         */
+
+        int length = descriptor.length();
+        if ((firstChar != 'L') ||
+            (descriptor.charAt(length - 1) != ';')) {
+            throw new IllegalArgumentException("bad descriptor");
+        }
+
+        /*
+         * Validate the characters of the class name itself. Note that
+         * vmspec-2 does not have a coherent definition for valid
+         * internal-form class names, and the definition here is fairly
+         * liberal: A name is considered valid as long as it doesn't
+         * contain any of '[' ';' '.' '(' ')', and it has no more than one
+         * '/' in a row, and no '/' at either end.
+         */
+
+        int limit = (length - 1); // Skip the final ';'.
+        for (int i = 1; i < limit; i++) {
+            char c = descriptor.charAt(i);
+            switch (c) {
+                case '[':
+                case ';':
+                case '.':
+                case '(':
+                case ')': {
+                    throw new IllegalArgumentException("bad descriptor");
+                }
+                case '/': {
+                    if ((i == 1) ||
+                        (i == (length - 1)) ||
+                        (descriptor.charAt(i - 1) == '/')) {
+                        throw new IllegalArgumentException("bad descriptor");
+                    }
+                    break;
+                }
+            }
+        }
+
+        result = new Type(descriptor, BT_OBJECT);
+        return putIntern(result);
+    }
+
+    /**
+     * Returns the unique instance corresponding to the type with the
+     * given descriptor, allowing {@code "V"} to return the type
+     * for {@code void}. Other than that one caveat, this method
+     * is identical to {@link #intern}.
+     *
+     * @param descriptor {@code non-null;} the descriptor
+     * @return {@code non-null;} the corresponding instance
+     * @throws IllegalArgumentException thrown if the descriptor has
+     * invalid syntax
+     */
+    public static Type internReturnType(String descriptor) {
+        try {
+            if (descriptor.equals("V")) {
+                // This is the one special case where void may be returned.
+                return VOID;
+            }
+        } catch (NullPointerException ex) {
+            // Elucidate the exception.
+            throw new NullPointerException("descriptor == null");
+        }
+
+        return intern(descriptor);
+    }
+
+    /**
+     * Returns the unique instance corresponding to the type of the
+     * class with the given name. Calling this method is equivalent to
+     * calling {@code intern(name)} if {@code name} begins
+     * with {@code "["} and calling {@code intern("L" + name + ";")}
+     * in all other cases.
+     *
+     * @param name {@code non-null;} the name of the class whose type is desired
+     * @return {@code non-null;} the corresponding type
+     * @throws IllegalArgumentException thrown if the name has
+     * invalid syntax
+     */
+    public static Type internClassName(String name) {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+
+        if (name.startsWith("[")) {
+            return intern(name);
+        }
+
+        return intern('L' + name + ';');
+    }
+
+    /**
+     * Constructs an instance corresponding to an "uninitialized type."
+     * This is a private constructor; use one of the public static
+     * methods to get instances.
+     *
+     * @param descriptor {@code non-null;} the field descriptor for the type
+     * @param basicType basic type corresponding to this type; one of the
+     * {@code BT_*} constants
+     * @param newAt {@code >= -1;} allocation bytecode index
+     */
+    private Type(String descriptor, int basicType, int newAt) {
+        if (descriptor == null) {
+            throw new NullPointerException("descriptor == null");
+        }
+
+        if ((basicType < 0) || (basicType >= BT_COUNT)) {
+            throw new IllegalArgumentException("bad basicType");
+        }
+
+        if (newAt < -1) {
+            throw new IllegalArgumentException("newAt < -1");
+        }
+
+        this.descriptor = descriptor;
+        this.basicType = basicType;
+        this.newAt = newAt;
+        this.arrayType = null;
+        this.componentType = null;
+        this.initializedType = null;
+    }
+
+    /**
+     * Constructs an instance corresponding to an "initialized type."
+     * This is a private constructor; use one of the public static
+     * methods to get instances.
+     *
+     * @param descriptor {@code non-null;} the field descriptor for the type
+     * @param basicType basic type corresponding to this type; one of the
+     * {@code BT_*} constants
+     */
+    private Type(String descriptor, int basicType) {
+        this(descriptor, basicType, -1);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            /*
+             * Since externally-visible types are interned, this check
+             * helps weed out some easy cases.
+             */
+            return true;
+        }
+
+        if (!(other instanceof Type)) {
+            return false;
+        }
+
+        return descriptor.equals(((Type) other).descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return descriptor.hashCode();
+    }
+
+    /** {@inheritDoc} */
+    public int compareTo(Type other) {
+        return descriptor.compareTo(other.descriptor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return descriptor;
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        switch (basicType) {
+            case BT_VOID:    return "void";
+            case BT_BOOLEAN: return "boolean";
+            case BT_BYTE:    return "byte";
+            case BT_CHAR:    return "char";
+            case BT_DOUBLE:  return "double";
+            case BT_FLOAT:   return "float";
+            case BT_INT:     return "int";
+            case BT_LONG:    return "long";
+            case BT_SHORT:   return "short";
+            case BT_OBJECT:  break;
+            default:         return descriptor;
+        }
+
+        if (isArray()) {
+            return getComponentType().toHuman() + "[]";
+        }
+
+        // Remove the "L...;" around the type and convert "/" to ".".
+        return getClassName().replace("/", ".");
+    }
+
+    /** {@inheritDoc} */
+    public Type getType() {
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    public Type getFrameType() {
+        switch (basicType) {
+            case BT_BOOLEAN:
+            case BT_BYTE:
+            case BT_CHAR:
+            case BT_INT:
+            case BT_SHORT: {
+                return INT;
+            }
+        }
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    public int getBasicType() {
+        return basicType;
+    }
+
+    /** {@inheritDoc} */
+    public int getBasicFrameType() {
+        switch (basicType) {
+            case BT_BOOLEAN:
+            case BT_BYTE:
+            case BT_CHAR:
+            case BT_INT:
+            case BT_SHORT: {
+                return BT_INT;
+            }
+        }
+
+        return basicType;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isConstant() {
+        return false;
+    }
+
+    /**
+     * Gets the descriptor.
+     *
+     * @return {@code non-null;} the descriptor
+     */
+    public String getDescriptor() {
+        return descriptor;
+    }
+
+    /**
+     * Gets the name of the class this type corresponds to, in internal
+     * form. This method is only valid if this instance is for a
+     * normal reference type (that is, a reference type and
+     * additionally not a return address).
+     *
+     * @return {@code non-null;} the internal-form class name
+     */
+    public String getClassName() {
+        if (className == null) {
+            if (!isReference()) {
+                throw new IllegalArgumentException("not an object type: " +
+                                                   descriptor);
+            }
+
+            if (descriptor.charAt(0) == '[') {
+                className = descriptor;
+            } else {
+                className = descriptor.substring(1, descriptor.length() - 1);
+            }
+        }
+
+        return className;
+    }
+
+    /**
+     * Gets the category. Most instances are category 1. {@code long}
+     * and {@code double} are the only category 2 types.
+     *
+     * @see #isCategory1
+     * @see #isCategory2
+     * @return the category
+     */
+    public int getCategory() {
+        switch (basicType) {
+            case BT_LONG:
+            case BT_DOUBLE: {
+                return 2;
+            }
+        }
+
+        return 1;
+    }
+
+    /**
+     * Returns whether or not this is a category 1 type.
+     *
+     * @see #getCategory
+     * @see #isCategory2
+     * @return whether or not this is a category 1 type
+     */
+    public boolean isCategory1() {
+        switch (basicType) {
+            case BT_LONG:
+            case BT_DOUBLE: {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns whether or not this is a category 2 type.
+     *
+     * @see #getCategory
+     * @see #isCategory1
+     * @return whether or not this is a category 2 type
+     */
+    public boolean isCategory2() {
+        switch (basicType) {
+            case BT_LONG:
+            case BT_DOUBLE: {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets whether this type is "intlike." An intlike type is one which, when
+     * placed on a stack or in a local, is automatically converted to an
+     * {@code int}.
+     *
+     * @return whether this type is "intlike"
+     */
+    public boolean isIntlike() {
+        switch (basicType) {
+            case BT_BOOLEAN:
+            case BT_BYTE:
+            case BT_CHAR:
+            case BT_INT:
+            case BT_SHORT: {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets whether this type is a primitive type. All types are either
+     * primitive or reference types.
+     *
+     * @return whether this type is primitive
+     */
+    public boolean isPrimitive() {
+        switch (basicType) {
+            case BT_BOOLEAN:
+            case BT_BYTE:
+            case BT_CHAR:
+            case BT_DOUBLE:
+            case BT_FLOAT:
+            case BT_INT:
+            case BT_LONG:
+            case BT_SHORT:
+            case BT_VOID: {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets whether this type is a normal reference type. A normal
+     * reference type is a reference type that is not a return
+     * address. This method is just convenient shorthand for
+     * {@code getBasicType() == Type.BT_OBJECT}.
+     *
+     * @return whether this type is a normal reference type
+     */
+    public boolean isReference() {
+        return (basicType == BT_OBJECT);
+    }
+
+    /**
+     * Gets whether this type is an array type. If this method returns
+     * {@code true}, then it is safe to use {@link #getComponentType}
+     * to determine the component type.
+     *
+     * @return whether this type is an array type
+     */
+    public boolean isArray() {
+        return (descriptor.charAt(0) == '[');
+    }
+
+    /**
+     * Gets whether this type is an array type or is a known-null, and
+     * hence is compatible with array types.
+     *
+     * @return whether this type is an array type
+     */
+    public boolean isArrayOrKnownNull() {
+        return isArray() || equals(KNOWN_NULL);
+    }
+
+    /**
+     * Gets whether this type represents an uninitialized instance. An
+     * uninitialized instance is what one gets back from the {@code new}
+     * opcode, and remains uninitialized until a valid constructor is
+     * invoked on it.
+     *
+     * @return whether this type is "uninitialized"
+     */
+    public boolean isUninitialized() {
+        return (newAt >= 0);
+    }
+
+    /**
+     * Gets the bytecode index at which this uninitialized type was
+     * allocated.  This returns {@code Integer.MAX_VALUE} if this
+     * type is an uninitialized incoming parameter (i.e., the
+     * {@code this} of an {@code <init>} method) or
+     * {@code -1} if this type is in fact <i>initialized</i>.
+     *
+     * @return {@code >= -1;} the allocation bytecode index
+     */
+    public int getNewAt() {
+        return newAt;
+    }
+
+    /**
+     * Gets the initialized type corresponding to this instance, but only
+     * if this instance is in fact an uninitialized object type.
+     *
+     * @return {@code non-null;} the initialized type
+     */
+    public Type getInitializedType() {
+        if (initializedType == null) {
+            throw new IllegalArgumentException("initialized type: " +
+                                               descriptor);
+        }
+
+        return initializedType;
+    }
+
+    /**
+     * Gets the type corresponding to an array of this type.
+     *
+     * @return {@code non-null;} the array type
+     */
+    public Type getArrayType() {
+        if (arrayType == null) {
+            arrayType = putIntern(new Type('[' + descriptor, BT_OBJECT));
+        }
+
+        return arrayType;
+    }
+
+    /**
+     * Gets the component type of this type. This method is only valid on
+     * array types.
+     *
+     * @return {@code non-null;} the component type
+     */
+    public Type getComponentType() {
+        if (componentType == null) {
+            if (descriptor.charAt(0) != '[') {
+                throw new IllegalArgumentException("not an array type: " +
+                                                   descriptor);
+            }
+            componentType = intern(descriptor.substring(1));
+        }
+
+        return componentType;
+    }
+
+    /**
+     * Returns a new interned instance which is identical to this one, except
+     * it is indicated as uninitialized and allocated at the given bytecode
+     * index. This instance must be an initialized object type.
+     *
+     * @param newAt {@code >= 0;} the allocation bytecode index
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public Type asUninitialized(int newAt) {
+        if (newAt < 0) {
+            throw new IllegalArgumentException("newAt < 0");
+        }
+
+        if (!isReference()) {
+            throw new IllegalArgumentException("not a reference type: " +
+                                               descriptor);
+        }
+
+        if (isUninitialized()) {
+            /*
+             * Dealing with uninitialized types as a starting point is
+             * a pain, and it's not clear that it'd ever be used, so
+             * just disallow it.
+             */
+            throw new IllegalArgumentException("already uninitialized: " +
+                                               descriptor);
+        }
+
+        /*
+         * Create a new descriptor that is unique and shouldn't conflict
+         * with "normal" type descriptors
+         */
+        String newDesc = 'N' + Hex.u2(newAt) + descriptor;
+        Type result = new Type(newDesc, BT_OBJECT, newAt);
+        result.initializedType = this;
+        return putIntern(result);
+    }
+
+    /**
+     * Puts the given instance in the intern table if it's not already
+     * there. If a conflicting value is already in the table, then leave it.
+     * Return the interned value.
+     *
+     * @param type {@code non-null;} instance to make interned
+     * @return {@code non-null;} the actual interned object
+     */
+    private static Type putIntern(Type type) {
+        synchronized (internTable) {
+            String descriptor = type.getDescriptor();
+            Type already = internTable.get(descriptor);
+            if (already != null) {
+                return already;
+            }
+            internTable.put(descriptor, type);
+            return type;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/rop/type/TypeBearer.java b/dx/src/com/android/dx/rop/type/TypeBearer.java
new file mode 100644
index 0000000..b03dbaf
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/TypeBearer.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Object which has an associated type, possibly itself.
+ */
+public interface TypeBearer
+        extends ToHuman {
+    /**
+     * Gets the type associated with this instance.
+     *
+     * @return {@code non-null;} the type
+     */
+    public Type getType();
+
+    /**
+     * Gets the frame type corresponding to this type. This method returns
+     * {@code this}, except if {@link Type#isIntlike} on the underlying
+     * type returns {@code true} but the underlying type is not in
+     * fact {@link Type#INT}, in which case this method returns an instance
+     * whose underlying type <i>is</i> {@code INT}.
+     *
+     * @return {@code non-null;} the frame type for this instance
+     */
+    public TypeBearer getFrameType();
+
+    /**
+     * Gets the basic type corresponding to this instance.
+     *
+     * @return the basic type; one of the {@code BT_*} constants
+     * defined by {@link Type}
+     */
+    public int getBasicType();
+
+    /**
+     * Gets the basic type corresponding to this instance's frame type. This
+     * is equivalent to {@code getFrameType().getBasicType()}, and
+     * is the same as calling {@code getFrameType()} unless this
+     * instance is an int-like type, in which case this method returns
+     * {@code BT_INT}.
+     *
+     * @see #getBasicType
+     * @see #getFrameType
+     *
+     * @return the basic frame type; one of the {@code BT_*} constants
+     * defined by {@link Type}
+     */
+    public int getBasicFrameType();
+
+    /**
+     * Returns whether this instance represents a constant value.
+     *
+     * @return {@code true} if this instance represents a constant value
+     * and {@code false} if not
+     */
+    public boolean isConstant();
+}
diff --git a/dx/src/com/android/dx/rop/type/TypeList.java b/dx/src/com/android/dx/rop/type/TypeList.java
new file mode 100644
index 0000000..de2d62e
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/TypeList.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.rop.type;
+
+/**
+ * List of {@link Type} instances (or of things that contain types).
+ */
+public interface TypeList {
+    /**
+     * Returns whether this instance is mutable. Note that the
+     * {@code TypeList} interface itself doesn't provide any
+     * means of mutation, but that doesn't mean that there isn't an
+     * extra-interface way of mutating an instance.
+     *
+     * @return {@code true} if this instance is mutable or
+     * {@code false} if it is immutable
+     */
+    public boolean isMutable();
+
+    /**
+     * Gets the size of this list.
+     *
+     * @return {@code >= 0;} the size
+     */
+    public int size();
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw {@code NullPointerException}.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @return {@code non-null;} the indicated element
+     */
+    public Type getType(int n);
+
+    /**
+     * Gets the number of 32-bit words required to hold instances of
+     * all the elements of this list. This is a sum of the widths (categories)
+     * of all the elements.
+     *
+     * @return {@code >= 0;} the required number of words
+     */
+    public int getWordCount();
+
+    /**
+     * Returns a new instance which is identical to this one, except that
+     * the given item is appended to the end and it is guaranteed to be
+     * immutable.
+     *
+     * @param type {@code non-null;} item to append
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public TypeList withAddedType(Type type);
+}
diff --git a/dx/src/com/android/dx/rop/type/package.html b/dx/src/com/android/dx/rop/type/package.html
new file mode 100644
index 0000000..93d9d5f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/package.html
@@ -0,0 +1,8 @@
+<body>
+<p>Implementation of classes that represent types (classes or primitives).</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/ssa/BasicRegisterMapper.java b/dx/src/com/android/dx/ssa/BasicRegisterMapper.java
new file mode 100644
index 0000000..83045b2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/BasicRegisterMapper.java
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.IntList;
+
+/**
+ * This class maps one register space into another, with
+ * each mapping built up individually and added via addMapping()
+ */
+public class BasicRegisterMapper extends RegisterMapper {
+    /** indexed by old register, containing new name */
+    private IntList oldToNew;
+
+    /** running count of used registers in new namespace */
+    private int runningCountNewRegisters;
+
+    /**
+     * Creates a new OneToOneRegisterMapper.
+     *
+     * @param countOldRegisters the number of registers in the old name space
+     */
+    public BasicRegisterMapper(int countOldRegisters) {
+        oldToNew = new IntList(countOldRegisters);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getNewRegisterCount() {
+        return runningCountNewRegisters;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RegisterSpec map(RegisterSpec registerSpec) {
+        if (registerSpec == null) {
+            return null;
+        }
+
+        int newReg;
+        try {
+            newReg = oldToNew.get(registerSpec.getReg());
+        } catch (IndexOutOfBoundsException ex) {
+            newReg = -1;
+        }
+
+        if (newReg < 0) {
+            throw new RuntimeException("no mapping specified for register");
+        }
+
+        return registerSpec.withReg(newReg);
+    }
+
+    /**
+     * Returns the new-namespace mapping for the specified
+     * old-namespace register, or -1 if one exists.
+     *
+     * @param oldReg {@code >= 0;} old-namespace register
+     * @return new-namespace register or -1 if none
+     */
+    public int oldToNew(int oldReg) {
+        if (oldReg >= oldToNew.size()) {
+            return -1;
+        }
+
+        return oldToNew.get(oldReg);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("Old\tNew\n");
+        int sz = oldToNew.size();
+
+        for (int i = 0; i < sz; i++) {
+            sb.append(i);
+            sb.append('\t');
+            sb.append(oldToNew.get(i));
+            sb.append('\n');
+        }
+
+        sb.append("new reg count:");
+
+        sb.append(runningCountNewRegisters);
+        sb.append('\n');
+
+        return sb.toString();
+    }
+
+    /**
+     * Adds a mapping to the mapper. If oldReg has already been mapped,
+     * overwrites previous mapping with new mapping.
+     *
+     * @param oldReg {@code >= 0;} old register
+     * @param newReg {@code >= 0;} new register
+     * @param category {@code 1..2;} width of reg
+     */
+    public void addMapping(int oldReg, int newReg, int category) {
+        if (oldReg >= oldToNew.size()) {
+            // expand the array as necessary
+            for (int i = oldReg - oldToNew.size(); i >= 0; i--) {
+                oldToNew.add(-1);
+            }
+        }
+
+        oldToNew.set(oldReg, newReg);
+
+        if (runningCountNewRegisters < (newReg + category)) {
+            runningCountNewRegisters = newReg + category;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/ConstCollector.java b/dx/src/com/android/dx/ssa/ConstCollector.java
new file mode 100644
index 0000000..7aa512d
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/ConstCollector.java
@@ -0,0 +1,384 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Collects constants that are used more than once at the top of the
+ * method block. This increases register usage by about 5% but decreases
+ * insn size by about 3%.
+ */
+public class ConstCollector {
+    /** Maximum constants to collect per method. Puts cap on reg use */
+    private static final int MAX_COLLECTED_CONSTANTS = 5;
+
+    /**
+     * Also collect string consts, although they can throw exceptions.
+     * This is off now because it just doesn't seem to gain a whole lot.
+     * TODO if you turn this on, you must change SsaInsn.hasSideEffect()
+     * to return false for const-string insns whose exceptions are not
+     * caught in the current method.
+     */
+    private static boolean COLLECT_STRINGS = false;
+
+    /**
+     * If true, allow one local var to be involved with a collected const.
+     * Turned off because it mostly just inserts more moves.
+     */
+    private static boolean COLLECT_ONE_LOCAL = false;
+
+    /** method we're processing */
+    private final SsaMethod ssaMeth;
+
+    /**
+     * Processes a method.
+     *
+     * @param ssaMethod {@code non-null;} method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        ConstCollector cc = new ConstCollector(ssaMethod);
+        cc.run();
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param ssaMethod {@code non-null;} method to process
+     */
+    private ConstCollector(SsaMethod ssaMethod) {
+        this.ssaMeth = ssaMethod;
+    }
+
+    /**
+     * Applies the optimization.
+     */
+    private void run() {
+        int regSz = ssaMeth.getRegCount();
+
+        ArrayList<TypedConstant> constantList
+                = getConstsSortedByCountUse();
+
+        int toCollect = Math.min(constantList.size(), MAX_COLLECTED_CONSTANTS);
+
+        SsaBasicBlock start = ssaMeth.getEntryBlock();
+
+        // Constant to new register containing the constant
+        HashMap<TypedConstant, RegisterSpec> newRegs
+                = new HashMap<TypedConstant, RegisterSpec> (toCollect);
+
+        for (int i = 0; i < toCollect; i++) {
+            TypedConstant cst = constantList.get(i);
+            RegisterSpec result
+                    = RegisterSpec.make(ssaMeth.makeNewSsaReg(), cst);
+
+            Rop constRop = Rops.opConst(cst);
+
+            if (constRop.getBranchingness() == Rop.BRANCH_NONE) {
+                start.addInsnToHead(
+                        new PlainCstInsn(Rops.opConst(cst),
+                                SourcePosition.NO_INFO, result,
+                                RegisterSpecList.EMPTY, cst));
+            } else {
+                // We need two new basic blocks along with the new insn
+                SsaBasicBlock entryBlock = ssaMeth.getEntryBlock();
+                SsaBasicBlock successorBlock
+                        = entryBlock.getPrimarySuccessor();
+
+                // Insert a block containing the const insn.
+                SsaBasicBlock constBlock
+                        = entryBlock.insertNewSuccessor(successorBlock);
+
+                constBlock.replaceLastInsn(
+                        new ThrowingCstInsn(constRop, SourcePosition.NO_INFO,
+                                RegisterSpecList.EMPTY,
+                                StdTypeList.EMPTY, cst));
+
+                // Insert a block containing the move-result-pseudo insn.
+
+                SsaBasicBlock resultBlock
+                        = constBlock.insertNewSuccessor(successorBlock);
+                PlainInsn insn
+                    = new PlainInsn(
+                            Rops.opMoveResultPseudo(result.getTypeBearer()),
+                            SourcePosition.NO_INFO,
+                            result, RegisterSpecList.EMPTY);
+
+                resultBlock.addInsnToHead(insn);
+            }
+
+            newRegs.put(cst, result);
+        }
+
+        updateConstUses(newRegs, regSz);
+    }
+
+    /**
+     * Gets all of the collectable constant values used in this method,
+     * sorted by most used first. Skips non-collectable consts, such as
+     * non-string object constants
+     *
+     * @return {@code non-null;} list of constants in most-to-least used order
+     */
+    private ArrayList<TypedConstant> getConstsSortedByCountUse() {
+        int regSz = ssaMeth.getRegCount();
+
+        final HashMap<TypedConstant, Integer> countUses
+                = new HashMap<TypedConstant, Integer>();
+
+        /*
+         * Each collected constant can be used by just one local
+         * (used only if COLLECT_ONE_LOCAL is true).
+         */
+        final HashSet<TypedConstant> usedByLocal
+                = new HashSet<TypedConstant>();
+
+        // Count how many times each const value is used.
+        for (int i = 0; i < regSz; i++) {
+            SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+            if (insn == null) continue;
+
+            RegisterSpec result = insn.getResult();
+            TypeBearer typeBearer = result.getTypeBearer();
+
+            if (!typeBearer.isConstant()) continue;
+
+            TypedConstant cst = (TypedConstant) typeBearer;
+
+            if (insn.canThrow()) {
+                /*
+                 * Don't move anything other than strings -- the risk
+                 * of changing where an exception is thrown is too high.
+                 */
+                if (!(cst instanceof CstString) || !COLLECT_STRINGS) {
+                    continue;
+                }
+                /*
+                 * We can't move any throwable const whose throw will be
+                 * caught, so don't count them.
+                 */
+                if (insn.getBlock().getSuccessors().cardinality() > 1) {
+                    continue;
+                }
+            }
+
+            /*
+             * TODO: Might be nice to try and figure out which local
+             * wins most when collected.
+             */
+            if (ssaMeth.isRegALocal(result)) {
+                if (!COLLECT_ONE_LOCAL) {
+                    continue;
+                } else {
+                    if (usedByLocal.contains(cst)) {
+                        // Count one local usage only.
+                        continue;
+                    } else {
+                        usedByLocal.add(cst);
+                    }
+                }
+            }
+
+            Integer has = countUses.get(cst);
+            if (has == null) {
+                countUses.put(cst, 1);
+            } else {
+                countUses.put(cst, has + 1);
+            }
+        }
+
+        // Collect constants that have been reused.
+        ArrayList<TypedConstant> constantList = new ArrayList<TypedConstant>();
+        for (Map.Entry<TypedConstant, Integer> entry : countUses.entrySet()) {
+            if (entry.getValue() > 1) {
+                constantList.add(entry.getKey());
+            }
+        }
+
+        // Sort by use, with most used at the beginning of the list.
+        Collections.sort(constantList, new Comparator<Constant>() {
+            public int compare(Constant a, Constant b) {
+                int ret;
+                ret = countUses.get(b) - countUses.get(a);
+
+                if (ret == 0) {
+                    /*
+                     * Provide sorting determinisim for constants with same
+                     * usage count.
+                     */
+                    ret = a.compareTo(b);
+                }
+
+                return ret;
+            }
+
+            public boolean equals (Object obj) {
+                return obj == this;
+            }
+        });
+
+        return constantList;
+    }
+
+    /**
+     * Inserts mark-locals if necessary when changing a register. If
+     * the definition of {@code origReg} is associated with a local
+     * variable, then insert a mark-local for {@code newReg} just below
+     * it. We expect the definition of  {@code origReg} to ultimately
+     * be removed by the dead code eliminator
+     *
+     * @param origReg {@code non-null;} original register
+     * @param newReg {@code non-null;} new register that will replace
+     * {@code origReg}
+     */
+    private void fixLocalAssignment(RegisterSpec origReg,
+            RegisterSpec newReg) {
+        for (SsaInsn use : ssaMeth.getUseListForRegister(origReg.getReg())) {
+            RegisterSpec localAssignment = use.getLocalAssignment();
+            if (localAssignment == null) {
+                continue;
+            }
+
+            if (use.getResult() == null) {
+                /*
+                 * This is a mark-local. it will be updated when all uses
+                 * are updated.
+                 */
+                continue;
+            }
+
+            LocalItem local = localAssignment.getLocalItem();
+
+            // Un-associate original use.
+            use.setResultLocal(null);
+
+            // Now add a mark-local to the new reg immediately after.
+            newReg = newReg.withLocalItem(local);
+
+            SsaInsn newInsn
+                    = SsaInsn.makeFromRop(
+                        new PlainInsn(Rops.opMarkLocal(newReg),
+                        SourcePosition.NO_INFO, null,
+                                RegisterSpecList.make(newReg)),
+                    use.getBlock());
+
+            ArrayList<SsaInsn> insns = use.getBlock().getInsns();
+
+            insns.add(insns.indexOf(use) + 1, newInsn);
+        }
+    }
+
+    /**
+     * Updates all uses of various consts to use the values in the newly
+     * assigned registers.
+     *
+     * @param newRegs {@code non-null;} mapping between constant and new reg
+     * @param origRegCount {@code >=0;} original SSA reg count, not including
+     * newly added constant regs
+     */
+    private void updateConstUses(HashMap<TypedConstant, RegisterSpec> newRegs,
+            int origRegCount) {
+
+        /*
+         * set of constants associated with a local variable; used
+         * only if COLLECT_ONE_LOCAL is true.
+         */
+        final HashSet<TypedConstant> usedByLocal
+                = new HashSet<TypedConstant>();
+
+        final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy();
+
+        for (int i = 0; i < origRegCount; i++) {
+            SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+            if (insn == null) {
+                continue;
+            }
+
+            final RegisterSpec origReg = insn.getResult();
+            TypeBearer typeBearer = insn.getResult().getTypeBearer();
+
+            if (!typeBearer.isConstant()) continue;
+
+            TypedConstant cst = (TypedConstant) typeBearer;
+            final RegisterSpec newReg = newRegs.get(cst);
+
+            if (newReg == null) {
+                continue;
+            }
+
+            if (ssaMeth.isRegALocal(origReg)) {
+                if (!COLLECT_ONE_LOCAL) {
+                    continue;
+                } else {
+                    /*
+                     * TODO: If the same local gets the same cst
+                     * multiple times, it would be nice to reuse the
+                     * register.
+                     */
+                    if (usedByLocal.contains(cst)) {
+                        continue;
+                    } else {
+                        usedByLocal.add(cst);
+                        fixLocalAssignment(origReg, newRegs.get(cst));
+                    }
+                }
+            }
+
+            // maps an original const register to the new collected register
+            RegisterMapper mapper = new RegisterMapper() {
+                @Override
+                public int getNewRegisterCount() {
+                    return ssaMeth.getRegCount();
+                }
+
+                @Override
+                public RegisterSpec map(RegisterSpec registerSpec) {
+                    if (registerSpec.getReg() == origReg.getReg()) {
+                        return newReg.withLocalItem(
+                                registerSpec.getLocalItem());
+                    }
+
+                    return registerSpec;
+                }
+            };
+
+            for (SsaInsn use : useList[origReg.getReg()]) {
+                if (use.canThrow()
+                        && use.getBlock().getSuccessors().cardinality() > 1) {
+                    continue;
+                }
+                use.mapSourceRegisters(mapper);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/DeadCodeRemover.java b/dx/src/com/android/dx/ssa/DeadCodeRemover.java
new file mode 100644
index 0000000..2a29050
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/DeadCodeRemover.java
@@ -0,0 +1,227 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.Insn;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * A variation on Appel Algorithm 19.12 "Dead code elimination in SSA form".
+ *
+ * TODO this algorithm is more efficient if run in reverse from exit
+ * block to entry block.
+ */
+public class DeadCodeRemover {
+    /** method we're processing */
+    private final SsaMethod ssaMeth;
+
+    /** ssaMeth.getRegCount() */
+    private final int regCount;
+
+    /**
+     * indexed by register: whether reg should be examined
+     * (does it correspond to a no-side-effect insn?)
+     */
+    private final BitSet worklist;
+
+    /** use list indexed by register; modified during operation */
+    private final ArrayList<SsaInsn>[] useList;
+
+    /**
+     * Process a method with the dead-code remver
+     *
+     * @param ssaMethod method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        DeadCodeRemover dc = new DeadCodeRemover(ssaMethod);
+        dc.run();
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param ssaMethod method to process
+     */
+    private DeadCodeRemover(SsaMethod ssaMethod) {
+        this.ssaMeth = ssaMethod;
+
+        regCount = ssaMethod.getRegCount();
+        worklist = new BitSet(regCount);
+        useList = ssaMeth.getUseListCopy();
+    }
+
+    /**
+     * Runs the dead code remover.
+     */
+    private void run() {
+        HashSet<SsaInsn> deletedInsns = (HashSet<SsaInsn>) new HashSet();
+
+        ssaMeth.forEachInsn(new NoSideEffectVisitor(worklist));
+
+        int regV;
+
+        while ( 0 <= (regV = worklist.nextSetBit(0)) ) {
+            worklist.clear(regV);
+
+            if (useList[regV].size() == 0
+                    || isCircularNoSideEffect(regV, null)) {
+
+                SsaInsn insnS = ssaMeth.getDefinitionForRegister(regV);
+
+                // This insn has already been deleted.
+                if (deletedInsns.contains(insnS)) {
+                    continue;
+                }
+
+                RegisterSpecList sources = insnS.getSources();
+
+                int sz = sources.size();
+                for (int i = 0; i < sz; i++) {
+                    // Delete this insn from all usage lists.
+                    RegisterSpec source = sources.get(i);
+                    useList[source.getReg()].remove(insnS);
+
+                    if (!hasSideEffect(
+                            ssaMeth.getDefinitionForRegister(
+                                    source.getReg()))) {
+                        /*
+                         * Only registers whose definition has no side effect
+                         * should be added back to the worklist.
+                         */
+                        worklist.set(source.getReg());
+                    }
+                }
+
+                // Schedule this insn for later deletion.
+                deletedInsns.add(insnS);
+            }
+        }
+
+        ssaMeth.deleteInsns(deletedInsns);
+    }
+
+    /**
+     * Returns true if the only uses of this register form a circle of
+     * operations with no side effects.
+     *
+     * @param regV register to examine
+     * @param set a set of registers that we've already determined
+     * are only used as sources in operations with no side effect or null
+     * if this is the first recursion
+     * @return true if usage is circular without side effect
+     */
+    private boolean isCircularNoSideEffect(int regV, BitSet set) {
+        if ((set != null) && set.get(regV)) {
+            return true;
+        }
+
+        for (SsaInsn use : useList[regV]) {
+            if (hasSideEffect(use)) {
+                return false;
+            }
+        }
+
+        if (set == null) {
+            set = new BitSet(regCount);
+        }
+
+        // This register is only used in operations that have no side effect.
+        set.set(regV);
+
+        for (SsaInsn use : useList[regV]) {
+            RegisterSpec result = use.getResult();
+
+            if (result == null
+                    || !isCircularNoSideEffect(result.getReg(), set)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns true if this insn has a side-effect. Returns true
+     * if the insn is null for reasons stated in the code block.
+     *
+     * @param insn {@code null-ok;} instruction in question
+     * @return true if it has a side-effect
+     */
+    private static boolean hasSideEffect(SsaInsn insn) {
+        if (insn == null) {
+            /* While false would seem to make more sense here, true
+             * prevents us from adding this back to a worklist unnecessarally.
+             */
+            return true;
+        }
+
+        return insn.hasSideEffect();
+    }
+
+    /**
+     * A callback class used to build up the initial worklist of
+     * registers defined by an instruction with no side effect.
+     */
+    static private class NoSideEffectVisitor implements SsaInsn.Visitor {
+        BitSet noSideEffectRegs;
+
+        /**
+         * Passes in data structures that will be filled out after
+         * ssaMeth.forEachInsn() is called with this instance.
+         *
+         * @param noSideEffectRegs to-build bitset of regs that are
+         * results of regs with no side effects
+         */
+        public NoSideEffectVisitor(BitSet noSideEffectRegs) {
+            this.noSideEffectRegs = noSideEffectRegs;
+        }
+
+        /** {@inheritDoc} */
+        public void visitMoveInsn (NormalSsaInsn insn) {
+            // If we're tracking local vars, some moves have side effects.
+            if (!hasSideEffect(insn)) {
+                noSideEffectRegs.set(insn.getResult().getReg());
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitPhiInsn (PhiInsn phi) {
+            // If we're tracking local vars, then some phis have side effects.
+            if (!hasSideEffect(phi)) {
+                noSideEffectRegs.set(phi.getResult().getReg());
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void visitNonMoveInsn (NormalSsaInsn insn) {
+            RegisterSpec result = insn.getResult();
+            if (!hasSideEffect(insn) && result != null) {
+                noSideEffectRegs.set(result.getReg());
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/DomFront.java b/dx/src/com/android/dx/ssa/DomFront.java
new file mode 100644
index 0000000..941a317
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/DomFront.java
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * Calculates the dominance-frontiers of a methot's basic blocks.
+ * Algorithm from "A Simple, Fast Dominance Algorithm" by Cooper,
+ * Harvey, and Kennedy; transliterated to Java.
+ */
+public class DomFront {
+    /** local debug flag */
+    private static boolean DEBUG = false;
+
+    /** {@code non-null;} method being processed */
+    private final SsaMethod meth;
+
+    private final ArrayList<SsaBasicBlock> nodes;
+
+    private final DomInfo[] domInfos;
+
+    /**
+     * Dominance-frontier information for a single basic block.
+     */
+    public static class DomInfo {
+        /**
+         * {@code null-ok;} the dominance frontier set indexed by
+         * block index
+         */
+        public IntSet dominanceFrontiers;
+
+        /** {@code >= 0 after run();} the index of the immediate dominator */
+        public int idom = -1;
+    }
+
+    /**
+     * Constructs instance. Call {@link DomFront#run} to process.
+     *
+     * @param meth {@code non-null;} method to process
+     */
+    public DomFront(SsaMethod meth) {
+        this.meth = meth;
+        nodes = meth.getBlocks();
+
+        int szNodes = nodes.size();
+        domInfos = new DomInfo[szNodes];
+
+        for (int i = 0; i < szNodes; i++) {
+            domInfos[i] = new DomInfo();
+        }
+    }
+
+    /**
+     * Calculates the dominance frontier information for the method.
+     *
+     * @return {@code non-null;} an array of DomInfo structures
+     */
+    public DomInfo[] run() {
+        int szNodes = nodes.size();
+
+        if (DEBUG) {
+            for (int i = 0; i < szNodes; i++) {
+                SsaBasicBlock node = nodes.get(i);
+                System.out.println("pred[" + i + "]: "
+                        + node.getPredecessors());
+            }
+        }
+
+        Dominators methDom = Dominators.make(meth, domInfos, false);
+
+        if (DEBUG) {
+            for (int i = 0; i < szNodes; i++) {
+                DomInfo info = domInfos[i];
+                System.out.println("idom[" + i + "]: "
+                        + info.idom);
+            }
+        }
+
+        buildDomTree();
+
+        if (DEBUG) {
+            debugPrintDomChildren();
+        }
+
+        for (int i = 0; i < szNodes; i++) {
+            domInfos[i].dominanceFrontiers
+                    = SetFactory.makeDomFrontSet(szNodes);
+        }
+
+        calcDomFronts();
+
+        if (DEBUG) {
+            for (int i = 0; i < szNodes; i++) {
+                System.out.println("df[" + i + "]: "
+                        + domInfos[i].dominanceFrontiers);
+            }
+        }
+
+        return domInfos;
+    }
+
+    private void debugPrintDomChildren() {
+        int szNodes = nodes.size();
+
+        for (int i = 0; i < szNodes; i++) {
+            SsaBasicBlock node = nodes.get(i);
+            StringBuffer sb = new StringBuffer();
+
+            sb.append('{');
+            boolean comma = false;
+            for (SsaBasicBlock child : node.getDomChildren()) {
+                if (comma) {
+                    sb.append(',');
+                }
+                sb.append(child);
+                comma = true;
+            }
+            sb.append('}');
+
+            System.out.println("domChildren[" + node + "]: "
+                    + sb);
+        }
+    }
+
+    /**
+     * The dominators algorithm leaves us knowing who the immediate dominator
+     * is for each node. This sweeps the node list and builds the proper
+     * dominance tree.
+     */
+    private void buildDomTree() {
+        int szNodes = nodes.size();
+
+        for (int i = 0; i < szNodes; i++) {
+            DomInfo info = domInfos[i];
+
+            if (info.idom == -1) continue;
+
+            SsaBasicBlock domParent = nodes.get(info.idom);
+            domParent.addDomChild(nodes.get(i));
+        }
+    }
+
+    /**
+     * Calculates the dominance-frontier set.
+     * from "A Simple, Fast Dominance Algorithm" by Cooper,
+     * Harvey, and Kennedy; transliterated to Java.
+     */
+    private void calcDomFronts() {
+        int szNodes = nodes.size();
+
+        for (int b = 0; b < szNodes; b++) {
+            SsaBasicBlock nb = nodes.get(b);
+            DomInfo nbInfo = domInfos[b];
+            BitSet pred = nb.getPredecessors();
+
+            if (pred.cardinality() > 1) {
+                for (int i = pred.nextSetBit(0); i >= 0;
+                     i = pred.nextSetBit(i + 1)) {
+
+                    for (int runnerIndex = i;
+                         runnerIndex != nbInfo.idom; /* empty */) {
+                        /*
+                         * We can stop if we hit a block we already
+                         * added label to, since we must be at a part
+                         * of the dom tree we have seen before.
+                         */
+                        if (runnerIndex == -1) break;
+
+                        DomInfo runnerInfo = domInfos[runnerIndex];
+
+                        if (runnerInfo.dominanceFrontiers.has(b)) {
+                            break;
+                        }
+
+                        // Add b to runner's dominance frontier set.
+                        runnerInfo.dominanceFrontiers.add(b);
+                        runnerIndex = runnerInfo.idom;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/Dominators.java b/dx/src/com/android/dx/ssa/Dominators.java
new file mode 100644
index 0000000..503e857
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/Dominators.java
@@ -0,0 +1,285 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * This class computes dominator and post-dominator information using the
+ * Lengauer-Tarjan method.
+ *
+ * See A Fast Algorithm for Finding Dominators in a Flowgraph
+ * T. Lengauer & R. Tarjan, ACM TOPLAS July 1979, pgs 121-141.
+ *
+ * This implementation runs in time O(n log n).  The time bound
+ * could be changed to O(n * ack(n)) with a small change to the link and eval,
+ * and an addition of a child field to the DFS info. In reality, the constant
+ * overheads are high enough that the current method is faster in all but the
+ * strangest artificially constructed examples.
+ *
+ * The basic idea behind this algorithm is to perform a DFS walk, keeping track
+ * of various info about parents.  We then use this info to calculate the
+ * dominators, using union-find structures to link together the DFS info,
+ * then finally evaluate the union-find results to get the dominators.
+ * This implementation is m log n because it does not perform union by
+ * rank to keep the union-find tree balanced.
+ */
+public final class Dominators {
+    /* postdom is true if we want post dominators */
+    private final boolean postdom;
+
+    /* {@code non-null;} method being processed */
+    private final SsaMethod meth;
+
+    /* Method's basic blocks. */
+    private final ArrayList<SsaBasicBlock> blocks;
+
+    /** indexed by basic block index */
+    private final DFSInfo[] info;
+
+    private final ArrayList<SsaBasicBlock> vertex;
+
+    /** {@code non-null;} the raw dominator info */
+    private final DomFront.DomInfo domInfos[];
+
+    /**
+     * Constructs an instance.
+     *
+     * @param meth {@code non-null;} method to process
+     * @param domInfos {@code non-null;} the raw dominator info
+     * @param postdom true for postdom information, false for normal dom info
+     */
+    private Dominators(SsaMethod meth, DomFront.DomInfo[] domInfos,
+            boolean postdom) {
+        this.meth = meth;
+        this.domInfos = domInfos;
+        this.postdom = postdom;
+        this.blocks = meth.getBlocks();
+        this.info = new DFSInfo[blocks.size() + 2];
+        this.vertex = new ArrayList<SsaBasicBlock>();
+    }
+
+    /**
+     * Constructs a fully-initialized instance. (This method exists so as
+     * to avoid calling a large amount of code in the constructor.)
+     *
+     * @param meth {@code non-null;} method to process
+     * @param domInfos {@code non-null;} the raw dominator info
+     * @param postdom true for postdom information, false for normal dom info
+     */
+    public static Dominators make(SsaMethod meth, DomFront.DomInfo[] domInfos,
+            boolean postdom) {
+        Dominators result = new Dominators(meth, domInfos, postdom);
+
+        result.run();
+        return result;
+    }
+
+    private BitSet getSuccs(SsaBasicBlock block) {
+        if (postdom) {
+            return block.getPredecessors();
+        } else {
+            return block.getSuccessors();
+        }
+    }
+
+    private BitSet getPreds(SsaBasicBlock block) {
+        if (postdom) {
+            return block.getSuccessors();
+        } else {
+            return block.getPredecessors();
+        }
+    }
+
+    /**
+     * Performs path compress on the DFS info.
+     *
+     * @param in Basic block whose DFS info we are path compressing.
+     */
+    private void compress(SsaBasicBlock in) {
+        DFSInfo bbInfo = info[in.getIndex()];
+        DFSInfo ancestorbbInfo = info[bbInfo.ancestor.getIndex()];
+
+        if (ancestorbbInfo.ancestor != null) {
+            ArrayList<SsaBasicBlock> worklist = new ArrayList<SsaBasicBlock>();
+            HashSet<SsaBasicBlock> visited = new HashSet<SsaBasicBlock>();
+            worklist.add(in);
+
+            while (!worklist.isEmpty()) {
+                int wsize = worklist.size();
+                SsaBasicBlock v = worklist.get(wsize - 1);
+                DFSInfo vbbInfo = info[v.getIndex()];
+                SsaBasicBlock vAncestor = vbbInfo.ancestor;
+                DFSInfo vabbInfo = info[vAncestor.getIndex()];
+
+                // Make sure we process our ancestor before ourselves.
+                if (visited.add(vAncestor) && vabbInfo.ancestor != null) {
+                    worklist.add(vAncestor);
+                    continue;
+                }
+                worklist.remove(wsize - 1);
+
+                // Update based on ancestor info.
+                if (vabbInfo.ancestor == null) {
+                    continue;
+                }
+                SsaBasicBlock vAncestorRep = vabbInfo.rep;
+                SsaBasicBlock vRep = vbbInfo.rep;
+                if (info[vAncestorRep.getIndex()].semidom
+                        < info[vRep.getIndex()].semidom) {
+                    vbbInfo.rep = vAncestorRep;
+                }
+                vbbInfo.ancestor = vabbInfo.ancestor;
+            }
+        }
+    }
+
+    private SsaBasicBlock eval(SsaBasicBlock v) {
+        DFSInfo bbInfo = info[v.getIndex()];
+
+        if (bbInfo.ancestor == null) {
+            return v;
+        }
+
+        compress(v);
+        return bbInfo.rep;
+    }
+
+    /**
+     * Performs dominator/post-dominator calculation for the control
+     * flow graph.
+     *
+     * @param meth {@code non-null;} method to analyze
+     */
+    private void run() {
+        SsaBasicBlock root = postdom
+                ? meth.getExitBlock() : meth.getEntryBlock();
+
+        if (root != null) {
+            vertex.add(root);
+            domInfos[root.getIndex()].idom = root.getIndex();
+        }
+
+        /*
+         * First we perform a DFS numbering of the blocks, by
+         * numbering the dfs tree roots.
+         */
+
+        DfsWalker walker = new DfsWalker();
+        meth.forEachBlockDepthFirst(postdom, walker);
+
+        // the largest semidom number assigned
+        int dfsMax = vertex.size() - 1;
+
+        // Now calculate semidominators.
+        for (int i = dfsMax; i >= 2; --i) {
+            SsaBasicBlock w = vertex.get(i);
+            DFSInfo wInfo = info[w.getIndex()];
+
+            BitSet preds = getPreds(w);
+            for (int j = preds.nextSetBit(0);
+                 j >= 0;
+                 j = preds.nextSetBit(j + 1)) {
+                SsaBasicBlock predBlock = blocks.get(j);
+                DFSInfo predInfo = info[predBlock.getIndex()];
+
+                /*
+                 * PredInfo may not exist in case the predecessor is
+                 * not reachable.
+                 */
+                if (predInfo != null) {
+                    int predSemidom = info[eval(predBlock).getIndex()].semidom;
+                    if (predSemidom < wInfo.semidom) {
+                        wInfo.semidom = predSemidom;
+                    }
+                }
+            }
+            info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w);
+
+            /*
+             * Normally we would call link here, but in our O(m log n)
+             * implementation this is equivalent to the following
+             * single line.
+             */
+            wInfo.ancestor = wInfo.parent;
+
+            // Implicity define idom for each vertex.
+            ArrayList<SsaBasicBlock> wParentBucket;
+            wParentBucket = info[wInfo.parent.getIndex()].bucket;
+
+            while (!wParentBucket.isEmpty()) {
+                int lastItem = wParentBucket.size() - 1;
+                SsaBasicBlock last = wParentBucket.remove(lastItem);
+                SsaBasicBlock U = eval(last);
+                if (info[U.getIndex()].semidom
+                        < info[last.getIndex()].semidom) {
+                    domInfos[last.getIndex()].idom = U.getIndex();
+                } else {
+                    domInfos[last.getIndex()].idom = wInfo.parent.getIndex();
+                }
+            }
+        }
+
+        // Now explicitly define the immediate dominator of each vertex
+        for (int i =  2; i <= dfsMax; ++i) {
+            SsaBasicBlock w = vertex.get(i);
+            if (domInfos[w.getIndex()].idom
+                    != vertex.get(info[w.getIndex()].semidom).getIndex()) {
+                domInfos[w.getIndex()].idom
+                        = domInfos[domInfos[w.getIndex()].idom].idom;
+            }
+        }
+    }
+
+    /**
+     * Callback for depth-first walk through control flow graph (either
+     * from the entry block or the exit block). Records the traversal order
+     * in the {@code info}list.
+     */
+    private class DfsWalker implements SsaBasicBlock.Visitor {
+        private int dfsNum = 0;
+
+        public void visitBlock(SsaBasicBlock v, SsaBasicBlock parent) {
+            DFSInfo bbInfo = new DFSInfo();
+            bbInfo.semidom = ++dfsNum;
+            bbInfo.rep = v;
+            bbInfo.parent = parent;
+            vertex.add(v);
+            info[v.getIndex()] = bbInfo;
+        }
+    }
+
+    private static final class DFSInfo {
+        public int semidom;
+        public SsaBasicBlock parent;
+
+        /**
+         * rep(resentative) is known as "label" in the paper. It is the node
+         * that our block's DFS info has been unioned to.
+         */
+        public SsaBasicBlock rep;
+
+        public SsaBasicBlock ancestor;
+        public ArrayList<SsaBasicBlock> bucket;
+
+        public DFSInfo() {
+            bucket = new ArrayList<SsaBasicBlock>();
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/EscapeAnalysis.java b/dx/src/com/android/dx/ssa/EscapeAnalysis.java
new file mode 100644
index 0000000..d270857
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/EscapeAnalysis.java
@@ -0,0 +1,843 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.Exceptions;
+import com.android.dx.rop.code.FillArrayDataInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.PlainCstInsn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.cst.Zeroes;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Simple intraprocedural escape analysis. Finds new arrays that don't escape
+ * the method they are created in and replaces the array values with registers.
+ */
+public class EscapeAnalysis {
+    /**
+     * Struct used to generate and maintain escape analysis results.
+     */
+    static class EscapeSet {
+        /** set containing all registers related to an object */
+        BitSet regSet;
+        /** escape state of the object */
+        EscapeState escape;
+        /** list of objects that are put into this object */
+        ArrayList<EscapeSet> childSets;
+        /** list of objects that this object is put into */
+        ArrayList<EscapeSet> parentSets;
+        /** flag to indicate this object is a scalar replaceable array */
+        boolean replaceableArray;
+
+        /**
+         * Constructs an instance of an EscapeSet
+         *
+         * @param reg the SSA register that defines the object
+         * @param size the number of registers in the method
+         * @param escState the lattice value to initially set this to
+         */
+        EscapeSet(int reg, int size, EscapeState escState) {
+            regSet = new BitSet(size);
+            regSet.set(reg);
+            escape = escState;
+            childSets = new ArrayList<EscapeSet>();
+            parentSets = new ArrayList<EscapeSet>();
+            replaceableArray = false;
+        }
+    }
+
+    /**
+     * Lattice values used to indicate escape state for an object. Analysis can
+     * only raise escape state values, not lower them.
+     *
+     * TOP - Used for objects that haven't been analyzed yet
+     * NONE - Object does not escape, and is eligible for scalar replacement.
+     * METHOD - Object remains local to method, but can't be scalar replaced.
+     * INTER - Object is passed between methods. (treated as globally escaping
+     *         since this is an intraprocedural analysis)
+     * GLOBAL - Object escapes globally.
+     */
+    public enum EscapeState {
+        TOP, NONE, METHOD, INTER, GLOBAL
+    }
+
+    /** method we're processing */
+    private SsaMethod ssaMeth;
+    /** ssaMeth.getRegCount() */
+    private int regCount;
+    /** Lattice values for each object register group */
+    private ArrayList<EscapeSet> latticeValues;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param ssaMeth method to process
+     */
+    private EscapeAnalysis(SsaMethod ssaMeth) {
+        this.ssaMeth = ssaMeth;
+        this.regCount = ssaMeth.getRegCount();
+        this.latticeValues = new ArrayList<EscapeSet>();
+    }
+
+    /**
+     * Finds the index in the lattice for a particular register.
+     * Returns the size of the lattice if the register wasn't found.
+     *
+     * @param reg {@code non-null;} register being looked up
+     * @return index of the register or size of the lattice if it wasn't found.
+     */
+    private int findSetIndex(RegisterSpec reg) {
+        int i;
+        for (i = 0; i < latticeValues.size(); i++) {
+            EscapeSet e = latticeValues.get(i);
+            if (e.regSet.get(reg.getReg())) {
+                return i;
+            }
+        }
+        return i;
+    }
+
+    /**
+     * Finds the corresponding instruction for a given move result
+     *
+     * @param moveInsn {@code non-null;} a move result instruction
+     * @return {@code non-null;} the instruction that produces the result for
+     * the move
+     */
+    private SsaInsn getInsnForMove(SsaInsn moveInsn) {
+        int pred = moveInsn.getBlock().getPredecessors().nextSetBit(0);
+        ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns();
+        return predInsns.get(predInsns.size()-1);
+    }
+
+    /**
+     * Finds the corresponding move result for a given instruction
+     *
+     * @param insn {@code non-null;} an instruction that must always be
+     * followed by a move result
+     * @return {@code non-null;} the move result for the given instruction
+     */
+    private SsaInsn getMoveForInsn(SsaInsn insn) {
+        int succ = insn.getBlock().getSuccessors().nextSetBit(0);
+        ArrayList<SsaInsn> succInsns = ssaMeth.getBlocks().get(succ).getInsns();
+        return succInsns.get(0);
+    }
+
+    /**
+     * Creates a link in the lattice between two EscapeSets due to a put
+     * instruction. The object being put is the child and the object being put
+     * into is the parent. A child set must always have an escape state at
+     * least as high as its parent.
+     *
+     * @param parentSet {@code non-null;} the EscapeSet for the object being put
+     * into
+     * @param childSet {@code non-null;} the EscapeSet for the object being put
+     */
+    private void addEdge(EscapeSet parentSet, EscapeSet childSet) {
+        if (!childSet.parentSets.contains(parentSet)) {
+            childSet.parentSets.add(parentSet);
+        }
+        if (!parentSet.childSets.contains(childSet)) {
+            parentSet.childSets.add(childSet);
+        }
+    }
+
+    /**
+     * Merges all links in the lattice among two EscapeSets. On return, the
+     * newNode will have its old links as well as all links from the oldNode.
+     * The oldNode has all its links removed.
+     *
+     * @param newNode {@code non-null;} the EscapeSet to merge all links into
+     * @param oldNode {@code non-null;} the EscapeSet to remove all links from
+     */
+    private void replaceNode(EscapeSet newNode, EscapeSet oldNode) {
+        for (EscapeSet e : oldNode.parentSets) {
+            e.childSets.remove(oldNode);
+            e.childSets.add(newNode);
+            newNode.parentSets.add(e);
+        }
+        for (EscapeSet e : oldNode.childSets) {
+            e.parentSets.remove(oldNode);
+            e.parentSets.add(newNode);
+            newNode.childSets.add(e);
+        }
+    }
+
+    /**
+     * Performs escape analysis on a method. Finds scalar replaceable arrays and
+     * replaces them with equivalent registers.
+     *
+     * @param ssaMethod {@code non-null;} method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        new EscapeAnalysis(ssaMethod).run();
+    }
+
+    /**
+     * Process a single instruction, looking for new objects resulting from
+     * move result or move param.
+     *
+     * @param insn {@code non-null;} instruction to process
+     */
+    private void processInsn(SsaInsn insn) {
+        int op = insn.getOpcode().getOpcode();
+        RegisterSpec result = insn.getResult();
+        EscapeSet escSet;
+
+        // Identify new objects
+        if (op == RegOps.MOVE_RESULT_PSEUDO &&
+                result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+            // Handle objects generated through move_result_pseudo
+            escSet = processMoveResultPseudoInsn(insn);
+            processRegister(result, escSet);
+        } else if (op == RegOps.MOVE_PARAM &&
+                      result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+            // Track method arguments that are objects
+            escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE);
+            latticeValues.add(escSet);
+            processRegister(result, escSet);
+        } else if (op == RegOps.MOVE_RESULT &&
+                result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+            // Track method return values that are objects
+            escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE);
+            latticeValues.add(escSet);
+            processRegister(result, escSet);
+        }
+    }
+
+    /**
+     * Determine the origin of a move result pseudo instruction that generates
+     * an object. Creates a new EscapeSet for the new object accordingly.
+     *
+     * @param insn {@code non-null;} move result pseudo instruction to process
+     * @return {@code non-null;} an EscapeSet for the object referred to by the
+     * move result pseudo instruction
+     */
+    private EscapeSet processMoveResultPseudoInsn(SsaInsn insn) {
+        RegisterSpec result = insn.getResult();
+        SsaInsn prevSsaInsn = getInsnForMove(insn);
+        int prevOpcode = prevSsaInsn.getOpcode().getOpcode();
+        EscapeSet escSet;
+        RegisterSpec prevSource;
+
+        switch(prevOpcode) {
+           // New instance / Constant
+            case RegOps.NEW_INSTANCE:
+            case RegOps.CONST:
+                escSet = new EscapeSet(result.getReg(), regCount,
+                                           EscapeState.NONE);
+                break;
+            // New array
+            case RegOps.NEW_ARRAY:
+            case RegOps.FILLED_NEW_ARRAY:
+                prevSource = prevSsaInsn.getSources().get(0);
+                if (prevSource.getTypeBearer().isConstant()) {
+                    // New fixed array
+                    escSet = new EscapeSet(result.getReg(), regCount,
+                                               EscapeState.NONE);
+                    escSet.replaceableArray = true;
+                } else {
+                    // New variable array
+                    escSet = new EscapeSet(result.getReg(), regCount,
+                                               EscapeState.GLOBAL);
+                }
+                break;
+            // Loading a static object
+            case RegOps.GET_STATIC:
+                escSet = new EscapeSet(result.getReg(), regCount,
+                                           EscapeState.GLOBAL);
+                break;
+            // Type cast / load an object from a field or array
+            case RegOps.CHECK_CAST:
+            case RegOps.GET_FIELD:
+            case RegOps.AGET:
+                prevSource = prevSsaInsn.getSources().get(0);
+                int setIndex = findSetIndex(prevSource);
+
+                // Set should already exist, try to find it
+                if (setIndex != latticeValues.size()) {
+                    escSet = latticeValues.get(setIndex);
+                    escSet.regSet.set(result.getReg());
+                    return escSet;
+                }
+
+                // Set not found, must be either null or unknown
+                if (prevSource.getType() == Type.KNOWN_NULL) {
+                    escSet = new EscapeSet(result.getReg(), regCount,
+                                               EscapeState.NONE);
+               } else {
+                    escSet = new EscapeSet(result.getReg(), regCount,
+                                               EscapeState.GLOBAL);
+                }
+                break;
+            default:
+                return null;
+        }
+
+        // Add the newly created escSet to the lattice and return it
+        latticeValues.add(escSet);
+        return escSet;
+    }
+
+    /**
+     * Iterate through all the uses of a new object.
+     *
+     * @param result {@code non-null;} register where new object is stored
+     * @param escSet {@code non-null;} EscapeSet for the new object
+     */
+    private void processRegister(RegisterSpec result, EscapeSet escSet) {
+        ArrayList<RegisterSpec> regWorklist = new ArrayList<RegisterSpec>();
+        regWorklist.add(result);
+
+        // Go through the worklist
+        while (!regWorklist.isEmpty()) {
+            int listSize = regWorklist.size() - 1;
+            RegisterSpec def = regWorklist.remove(listSize);
+            List<SsaInsn> useList = ssaMeth.getUseListForRegister(def.getReg());
+
+            // Handle all the uses of this register
+            for (SsaInsn use : useList) {
+                Rop useOpcode = use.getOpcode();
+
+                if (useOpcode == null) {
+                    // Handle phis
+                    processPhiUse(use, escSet, regWorklist);
+                } else {
+                    // Handle other opcodes
+                    processUse(def, use, escSet, regWorklist);
+                }
+            }
+        }
+    }
+
+    /**
+     * Handles phi uses of new objects. Will merge together the sources of a phi
+     * into a single EscapeSet. Adds the result of the phi to the worklist so
+     * its uses can be followed.
+     *
+     * @param use {@code non-null;} phi use being processed
+     * @param escSet {@code non-null;} EscapeSet for the object
+     * @param regWorklist {@code non-null;} worklist of instructions left to
+     * process for this object
+     */
+    private void processPhiUse(SsaInsn use, EscapeSet escSet,
+                                   ArrayList<RegisterSpec> regWorklist) {
+        int setIndex = findSetIndex(use.getResult());
+        if (setIndex != latticeValues.size()) {
+            // Check if result is in a set already
+            EscapeSet mergeSet = latticeValues.get(setIndex);
+            if (mergeSet != escSet) {
+                // If it is, merge the sets and states, then delete the copy
+                escSet.replaceableArray = false;
+                escSet.regSet.or(mergeSet.regSet);
+                if (escSet.escape.compareTo(mergeSet.escape) < 0) {
+                    escSet.escape = mergeSet.escape;
+                }
+                replaceNode(escSet, mergeSet);
+                latticeValues.remove(setIndex);
+            }
+        } else {
+            // If no set is found, add it to this escSet and the worklist
+            escSet.regSet.set(use.getResult().getReg());
+            regWorklist.add(use.getResult());
+        }
+    }
+
+    /**
+     * Handles non-phi uses of new objects. Checks to see how instruction is
+     * used and updates the escape state accordingly.
+     *
+     * @param def {@code non-null;} register holding definition of new object
+     * @param use {@code non-null;} use of object being processed
+     * @param escSet {@code non-null;} EscapeSet for the object
+     * @param regWorklist {@code non-null;} worklist of instructions left to
+     * process for this object
+     */
+    private void processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet,
+                                ArrayList<RegisterSpec> regWorklist) {
+        int useOpcode = use.getOpcode().getOpcode();
+        switch (useOpcode) {
+            case RegOps.MOVE:
+                // Follow uses of the move by adding it to the worklist
+                escSet.regSet.set(use.getResult().getReg());
+                regWorklist.add(use.getResult());
+                break;
+            case RegOps.IF_EQ:
+            case RegOps.IF_NE:
+            case RegOps.CHECK_CAST:
+                // Compared objects can't be replaced, so promote if necessary
+                if (escSet.escape.compareTo(EscapeState.METHOD) < 0) {
+                    escSet.escape = EscapeState.METHOD;
+                }
+                break;
+            case RegOps.APUT:
+                // For array puts, check for a constant array index
+                RegisterSpec putIndex = use.getSources().get(2);
+                if (!putIndex.getTypeBearer().isConstant()) {
+                    // If not constant, array can't be replaced
+                    escSet.replaceableArray = false;
+                }
+                // Intentional fallthrough
+            case RegOps.PUT_FIELD:
+                // Skip non-object puts
+                RegisterSpec putValue = use.getSources().get(0);
+                if (putValue.getTypeBearer().getBasicType() != Type.BT_OBJECT) {
+                    break;
+                }
+                escSet.replaceableArray = false;
+
+                // Raise 1st object's escape state to 2nd if 2nd is higher
+                RegisterSpecList sources = use.getSources();
+                if (sources.get(0).getReg() == def.getReg()) {
+                    int setIndex = findSetIndex(sources.get(1));
+                    if (setIndex != latticeValues.size()) {
+                        EscapeSet parentSet = latticeValues.get(setIndex);
+                        addEdge(parentSet, escSet);
+                        if (escSet.escape.compareTo(parentSet.escape) < 0) {
+                            escSet.escape = parentSet.escape;
+                        }
+                    }
+                } else {
+                    int setIndex = findSetIndex(sources.get(0));
+                    if (setIndex != latticeValues.size()) {
+                        EscapeSet childSet = latticeValues.get(setIndex);
+                        addEdge(escSet, childSet);
+                        if (childSet.escape.compareTo(escSet.escape) < 0) {
+                            childSet.escape = escSet.escape;
+                        }
+                    }
+                }
+                break;
+            case RegOps.AGET:
+                // For array gets, check for a constant array index
+                RegisterSpec getIndex = use.getSources().get(1);
+                if (!getIndex.getTypeBearer().isConstant()) {
+                    // If not constant, array can't be replaced
+                    escSet.replaceableArray = false;
+                }
+                break;
+            case RegOps.PUT_STATIC:
+                // Static puts cause an object to escape globally
+                escSet.escape = EscapeState.GLOBAL;
+                break;
+            case RegOps.INVOKE_STATIC:
+            case RegOps.INVOKE_VIRTUAL:
+            case RegOps.INVOKE_SUPER:
+            case RegOps.INVOKE_DIRECT:
+            case RegOps.INVOKE_INTERFACE:
+            case RegOps.RETURN:
+            case RegOps.THROW:
+                // These operations cause an object to escape interprocedurally
+                escSet.escape = EscapeState.INTER;
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Performs scalar replacement on all eligible arrays.
+     */
+    private void scalarReplacement() {
+        // Iterate through lattice, looking for non-escaping replaceable arrays
+        for (EscapeSet escSet : latticeValues) {
+            if (!escSet.replaceableArray || escSet.escape != EscapeState.NONE) {
+                continue;
+            }
+
+            // Get the instructions for the definition and move of the array
+            int e = escSet.regSet.nextSetBit(0);
+            SsaInsn def = ssaMeth.getDefinitionForRegister(e);
+            SsaInsn prev = getInsnForMove(def);
+
+            // Create a map for the new registers that will be created
+            TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer();
+            int length = ((CstLiteralBits) lengthReg).getIntBits();
+            ArrayList<RegisterSpec> newRegs =
+                new ArrayList<RegisterSpec>(length);
+            HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
+
+            // Replace the definition of the array with registers
+            replaceDef(def, prev, length, newRegs);
+
+            // Mark definition instructions for deletion
+            deletedInsns.add(prev);
+            deletedInsns.add(def);
+
+            // Go through all uses of the array
+            List<SsaInsn> useList = ssaMeth.getUseListForRegister(e);
+            for (SsaInsn use : useList) {
+                // Replace the use with scalars and then mark it for deletion
+                replaceUse(use, prev, newRegs, deletedInsns);
+                deletedInsns.add(use);
+            }
+
+            // Delete all marked instructions
+            ssaMeth.deleteInsns(deletedInsns);
+            ssaMeth.onInsnsChanged();
+
+            // Convert the method back to SSA form
+            SsaConverter.updateSsaMethod(ssaMeth, regCount);
+
+            // Propagate and remove extra moves added by scalar replacement
+            movePropagate();
+        }
+    }
+
+    /**
+     * Replaces the instructions that define an array with equivalent registers.
+     * For each entry in the array, a register is created, initialized to zero.
+     * A mapping between this register and the corresponding array index is
+     * added.
+     *
+     * @param def {@code non-null;} move result instruction for array
+     * @param prev {@code non-null;} instruction for instantiating new array
+     * @param length size of the new array
+     * @param newRegs {@code non-null;} mapping of array indices to new
+     * registers to be populated
+     */
+    private void replaceDef(SsaInsn def, SsaInsn prev, int length,
+                                ArrayList<RegisterSpec> newRegs) {
+        Type resultType = def.getResult().getType();
+
+        // Create new zeroed out registers for each element in the array
+        for (int i = 0; i < length; i++) {
+            Constant newZero = Zeroes.zeroFor(resultType.getComponentType());
+            TypedConstant typedZero = (TypedConstant) newZero;
+            RegisterSpec newReg =
+                RegisterSpec.make(ssaMeth.makeNewSsaReg(), typedZero);
+            newRegs.add(newReg);
+            insertPlainInsnBefore(def, RegisterSpecList.EMPTY, newReg,
+                                      RegOps.CONST, newZero);
+        }
+    }
+
+    /**
+     * Replaces the use for a scalar replaceable array. Gets and puts become
+     * move instructions, and array lengths and fills are handled. Can also
+     * identify ArrayIndexOutOfBounds exceptions and throw them if detected.
+     *
+     * @param use {@code non-null;} move result instruction for array
+     * @param prev {@code non-null;} instruction for instantiating new array
+     * @param newRegs {@code non-null;} mapping of array indices to new
+     * registers
+     * @param deletedInsns {@code non-null;} set of instructions marked for
+     * deletion
+     */
+    private void replaceUse(SsaInsn use, SsaInsn prev,
+                                ArrayList<RegisterSpec> newRegs,
+                                HashSet<SsaInsn> deletedInsns) {
+        int index;
+        int length = newRegs.size();
+        SsaInsn next;
+        RegisterSpecList sources;
+        RegisterSpec source, result;
+        CstLiteralBits indexReg;
+
+        switch (use.getOpcode().getOpcode()) {
+            case RegOps.AGET:
+                // Replace array gets with moves
+                next = getMoveForInsn(use);
+                sources = use.getSources();
+                indexReg = ((CstLiteralBits) sources.get(1).getTypeBearer());
+                index = indexReg.getIntBits();
+                if (index < length) {
+                    source = newRegs.get(index);
+                    result = source.withReg(next.getResult().getReg());
+                    insertPlainInsnBefore(next, RegisterSpecList.make(source),
+                                              result, RegOps.MOVE, null);
+                } else {
+                    // Throw an exception if the index is out of bounds
+                    insertExceptionThrow(next, sources.get(1), deletedInsns);
+                    deletedInsns.add(next.getBlock().getInsns().get(2));
+                }
+                deletedInsns.add(next);
+                break;
+            case RegOps.APUT:
+                // Replace array puts with moves
+                sources = use.getSources();
+                indexReg = ((CstLiteralBits) sources.get(2).getTypeBearer());
+                index = indexReg.getIntBits();
+                if (index < length) {
+                    source = sources.get(0);
+                    result = source.withReg(newRegs.get(index).getReg());
+                    insertPlainInsnBefore(use, RegisterSpecList.make(source),
+                                              result, RegOps.MOVE, null);
+                    // Update the newReg entry to mark value as unknown now
+                    newRegs.set(index, result.withSimpleType());
+                } else {
+                    // Throw an exception if the index is out of bounds
+                    insertExceptionThrow(use, sources.get(2), deletedInsns);
+                }
+                break;
+            case RegOps.ARRAY_LENGTH:
+                // Replace array lengths with const instructions
+                TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer();
+                //CstInteger lengthReg = CstInteger.make(length);
+                next = getMoveForInsn(use);
+                insertPlainInsnBefore(next, RegisterSpecList.EMPTY,
+                                          next.getResult(), RegOps.CONST,
+                                          (Constant) lengthReg);
+                deletedInsns.add(next);
+                break;
+            case RegOps.MARK_LOCAL:
+                // Remove mark local instructions
+                break;
+            case RegOps.FILL_ARRAY_DATA:
+                // Create const instructions for each fill value
+                Insn ropUse = use.getOriginalRopInsn();
+                FillArrayDataInsn fill = (FillArrayDataInsn) ropUse;
+                ArrayList<Constant> constList = fill.getInitValues();
+                for (int i = 0; i < length; i++) {
+                    RegisterSpec newFill =
+                        RegisterSpec.make(newRegs.get(i).getReg(),
+                                              (TypeBearer) constList.get(i));
+                    insertPlainInsnBefore(use, RegisterSpecList.EMPTY, newFill,
+                                              RegOps.CONST, constList.get(i));
+                    // Update the newRegs to hold the new const value
+                    newRegs.set(i, newFill);
+                }
+                break;
+            default:
+        }
+    }
+
+    /**
+     * Identifies extra moves added by scalar replacement and propagates the
+     * source of the move to any users of the result.
+     */
+    private void movePropagate() {
+        for (int i = 0; i < ssaMeth.getRegCount(); i++) {
+            SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+            // Look for move instructions only
+            if (insn == null || insn.getOpcode() == null ||
+                insn.getOpcode().getOpcode() != RegOps.MOVE) {
+                continue;
+            }
+
+            final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy();
+            final RegisterSpec source = insn.getSources().get(0);
+            final RegisterSpec result = insn.getResult();
+
+            // Ignore moves that weren't added due to scalar replacement
+            if (source.getReg() < regCount && result.getReg() < regCount) {
+                continue;
+            }
+
+            // Create a mapping from source to result
+            RegisterMapper mapper = new RegisterMapper() {
+                @Override
+                public int getNewRegisterCount() {
+                    return ssaMeth.getRegCount();
+                }
+
+                @Override
+                public RegisterSpec map(RegisterSpec registerSpec) {
+                    if (registerSpec.getReg() == result.getReg()) {
+                        return source;
+                    }
+
+                    return registerSpec;
+                }
+            };
+
+            // Modify all uses of the move to use the source of the move instead
+            for (SsaInsn use : useList[result.getReg()]) {
+                use.mapSourceRegisters(mapper);
+            }
+        }
+    }
+
+    /**
+     * Runs escape analysis and scalar replacement of arrays.
+     */
+    private void run() {
+        ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
+            public void visitBlock (SsaBasicBlock block,
+                    SsaBasicBlock unused) {
+                block.forEachInsn(new SsaInsn.Visitor() {
+                    public void visitMoveInsn(NormalSsaInsn insn) {
+                        // do nothing
+                    }
+
+                    public void visitPhiInsn(PhiInsn insn) {
+                        // do nothing
+                    }
+
+                    public void visitNonMoveInsn(NormalSsaInsn insn) {
+                        processInsn(insn);
+                    }
+                });
+            }
+        });
+
+        // Go through lattice and promote fieldSets as necessary
+        for (EscapeSet e : latticeValues) {
+            if (e.escape != EscapeState.NONE) {
+                for (EscapeSet field : e.childSets) {
+                    if (e.escape.compareTo(field.escape) > 0) {
+                        field.escape = e.escape;
+                    }
+                }
+            }
+        }
+
+        // Perform scalar replacement for arrays
+        scalarReplacement();
+    }
+
+    /**
+     * Replaces instructions that trigger an ArrayIndexOutofBounds exception
+     * with an actual throw of the exception.
+     *
+     * @param insn {@code non-null;} instruction causing the exception
+     * @param index {@code non-null;} index value that is out of bounds
+     * @param deletedInsns {@code non-null;} set of instructions marked for
+     * deletion
+     */
+    private void insertExceptionThrow(SsaInsn insn, RegisterSpec index,
+                                          HashSet<SsaInsn> deletedInsns) {
+        // Create a new ArrayIndexOutOfBoundsException
+        CstType exception =
+            new CstType(Exceptions.TYPE_ArrayIndexOutOfBoundsException);
+        insertThrowingInsnBefore(insn, RegisterSpecList.EMPTY, null,
+                                     RegOps.NEW_INSTANCE, exception);
+
+        // Add a successor block with a move result pseudo for the exception
+        SsaBasicBlock currBlock = insn.getBlock();
+        SsaBasicBlock newBlock =
+            currBlock.insertNewSuccessor(currBlock.getPrimarySuccessor());
+        SsaInsn newInsn = newBlock.getInsns().get(0);
+        RegisterSpec newReg =
+            RegisterSpec.make(ssaMeth.makeNewSsaReg(), exception);
+        insertPlainInsnBefore(newInsn, RegisterSpecList.EMPTY, newReg,
+                                  RegOps.MOVE_RESULT_PSEUDO, null);
+
+        // Add another successor block to initialize the exception
+        SsaBasicBlock newBlock2 =
+            newBlock.insertNewSuccessor(newBlock.getPrimarySuccessor());
+        SsaInsn newInsn2 = newBlock2.getInsns().get(0);
+        CstNat newNat = new CstNat(new CstUtf8("<init>"), new CstUtf8("(I)V"));
+        CstMethodRef newRef = new CstMethodRef(exception, newNat);
+        insertThrowingInsnBefore(newInsn2, RegisterSpecList.make(newReg, index),
+                                     null, RegOps.INVOKE_DIRECT, newRef);
+        deletedInsns.add(newInsn2);
+
+        // Add another successor block to throw the new exception
+        SsaBasicBlock newBlock3 =
+            newBlock2.insertNewSuccessor(newBlock2.getPrimarySuccessor());
+        SsaInsn newInsn3 = newBlock3.getInsns().get(0);
+        insertThrowingInsnBefore(newInsn3, RegisterSpecList.make(newReg), null,
+                                     RegOps.THROW, null);
+        newBlock3.replaceSuccessor(newBlock3.getPrimarySuccessorIndex(),
+                                       ssaMeth.getExitBlock().getIndex());
+        deletedInsns.add(newInsn3);
+    }
+
+    /**
+     * Inserts a new PlainInsn before the given instruction.
+     * TODO: move this somewhere more appropriate
+     *
+     * @param insn {@code non-null;} instruction to insert before
+     * @param newSources {@code non-null;} sources of new instruction
+     * @param newResult {@code non-null;} result of new instruction
+     * @param newOpcode opcode of new instruction
+     * @param cst {@code null-ok;} constant for new instruction, if any
+     */
+    private void insertPlainInsnBefore(SsaInsn insn,
+        RegisterSpecList newSources, RegisterSpec newResult, int newOpcode,
+        Constant cst) {
+
+        Insn originalRopInsn = insn.getOriginalRopInsn();
+        Rop newRop;
+        if (newOpcode == RegOps.MOVE_RESULT_PSEUDO) {
+            newRop = Rops.opMoveResultPseudo(newResult.getType());
+        } else {
+            newRop = Rops.ropFor(newOpcode, newResult, newSources, cst);
+        }
+
+        Insn newRopInsn;
+        if (cst == null) {
+            newRopInsn = new PlainInsn(newRop,
+                    originalRopInsn.getPosition(), newResult, newSources);
+        } else {
+            newRopInsn = new PlainCstInsn(newRop,
+                originalRopInsn.getPosition(), newResult, newSources, cst);
+        }
+
+        NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
+        List<SsaInsn> insns = insn.getBlock().getInsns();
+
+        insns.add(insns.lastIndexOf(insn), newInsn);
+        ssaMeth.onInsnAdded(newInsn);
+    }
+
+    /**
+     * Inserts a new ThrowingInsn before the given instruction.
+     * TODO: move this somewhere more appropriate
+     *
+     * @param insn {@code non-null;} instruction to insert before
+     * @param newSources {@code non-null;} sources of new instruction
+     * @param newResult {@code non-null;} result of new instruction
+     * @param newOpcode opcode of new instruction
+     * @param cst {@code null-ok;} constant for new instruction, if any
+     */
+    private void insertThrowingInsnBefore(SsaInsn insn,
+        RegisterSpecList newSources, RegisterSpec newResult, int newOpcode,
+        Constant cst) {
+
+        Insn origRopInsn = insn.getOriginalRopInsn();
+        Rop newRop = Rops.ropFor(newOpcode, newResult, newSources, cst);
+        Insn newRopInsn;
+        if (cst == null) {
+            newRopInsn = new ThrowingInsn(newRop,
+                origRopInsn.getPosition(), newSources, StdTypeList.EMPTY);
+        } else {
+            newRopInsn = new ThrowingCstInsn(newRop,
+                origRopInsn.getPosition(), newSources, StdTypeList.EMPTY, cst);
+        }
+
+        NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
+        List<SsaInsn> insns = insn.getBlock().getInsns();
+
+        insns.add(insns.lastIndexOf(insn), newInsn);
+        ssaMeth.onInsnAdded(newInsn);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java b/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java
new file mode 100644
index 0000000..851249b
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.ssa.back.InterferenceGraph;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * A register mapper that keeps track of the accumulated interference
+ * information for the registers in the new namespace.
+ *
+ * Please note that this mapper requires that the old namespace does not
+ * have variable register widths/categories, and the new namespace does.
+ */
+public class InterferenceRegisterMapper extends BasicRegisterMapper {
+    /**
+     * Array of interference sets. ArrayList is indexed by new namespace
+     * and BitIntSet's are indexed by old namespace.  The list expands
+     * as needed and missing items are assumed to interfere with nothing.
+     *
+     * Bit sets are always used here, unlike elsewhere, because the max
+     * size of this matrix will be (countSsaRegs * countRopRegs), which may
+     * grow to hundreds of K but not megabytes.
+     */
+    private final ArrayList<BitIntSet> newRegInterference;
+
+    /** the interference graph for the old namespace */
+    private final InterferenceGraph oldRegInterference;
+
+    /**
+     * Constructs an instance
+     *
+     * @param countOldRegisters number of registers in old namespace
+     */
+    public InterferenceRegisterMapper(InterferenceGraph oldRegInterference,
+            int countOldRegisters) {
+        super(countOldRegisters);
+
+        newRegInterference = new ArrayList<BitIntSet>();
+        this.oldRegInterference = oldRegInterference;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void addMapping(int oldReg, int newReg, int category) {
+        super.addMapping(oldReg, newReg, category);
+
+        addInterfence(newReg, oldReg);
+
+        if (category == 2) {
+            addInterfence(newReg + 1, oldReg);
+        }
+    }
+
+    /**
+     * Checks to see if old namespace reg {@code oldReg} interferes
+     * with what currently maps to {@code newReg}.
+     *
+     * @param oldReg old namespace register
+     * @param newReg new namespace register
+     * @param category category of old namespace register
+     * @return true if oldReg will interfere with newReg
+     */
+    public boolean interferes(int oldReg, int newReg, int category) {
+        if (newReg >= newRegInterference.size()) {
+            return false;
+        } else {
+            IntSet existing = newRegInterference.get(newReg);
+
+            if (existing == null) {
+                return false;
+            } else if (category == 1) {
+                return existing.has(oldReg);
+            } else {
+                return existing.has(oldReg)
+                        || (interferes(oldReg, newReg+1, category-1));
+            }
+        }
+    }
+
+    /**
+     * Checks to see if old namespace reg {@code oldReg} interferes
+     * with what currently maps to {@code newReg}.
+     *
+     * @param oldSpec {@code non-null;} old namespace register
+     * @param newReg new namespace register
+     * @return true if oldReg will interfere with newReg
+     */
+    public boolean interferes(RegisterSpec oldSpec, int newReg) {
+        return interferes(oldSpec.getReg(), newReg, oldSpec.getCategory());
+    }
+
+    /**
+     * Adds a register's interference set to the interference list,
+     * growing it if necessary.
+     *
+     * @param newReg register in new namespace
+     * @param oldReg register in old namespace
+     */
+    private void addInterfence(int newReg, int oldReg) {
+        newRegInterference.ensureCapacity(newReg + 1);
+
+        while (newReg >= newRegInterference.size()) {
+            newRegInterference.add(new BitIntSet(newReg +1));
+        }
+
+        oldRegInterference.mergeInterferenceSet(
+                oldReg, newRegInterference.get(newReg));
+    }
+
+    /**
+     * Checks to see if any of a set of old-namespace registers are
+     * pinned to the specified new-namespace reg + category. Takes into
+     * account the category of the old-namespace registers.
+     *
+     * @param oldSpecs {@code non-null;} set of old-namespace regs
+     * @param newReg {@code >= 0;} new-namespace register
+     * @param targetCategory {@code 1..2;} the number of adjacent new-namespace
+     * registers (starting at ropReg) to consider
+     * @return true if any of the old-namespace register have been mapped
+     * to the new-namespace register + category
+     */
+    public boolean areAnyPinned(RegisterSpecList oldSpecs,
+            int newReg, int targetCategory) {
+        int sz = oldSpecs.size();
+
+        for (int i = 0; i < sz; i++) {
+            RegisterSpec oldSpec = oldSpecs.get(i);
+            int r = oldToNew(oldSpec.getReg());
+
+            /*
+             * If oldSpec is a category-2 register, then check both newReg
+             * and newReg - 1.
+             */
+            if (r == newReg
+                || (oldSpec.getCategory() == 2 && (r + 1) == newReg)
+                || (targetCategory == 2 && (r == newReg + 1))) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java
new file mode 100644
index 0000000..01d818d
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.List;
+
+/**
+ * Upgrades insn to their literal (constant-immediate) equivilent if possible.
+ * Also switches IF instructions that compare with a constant zero or null
+ * to be their IF_*Z equivalents.
+ */
+public class LiteralOpUpgrader {
+
+    /** method we're processing */
+    private final SsaMethod ssaMeth;
+
+    /**
+     * Process a method.
+     *
+     * @param ssaMethod {@code non-null;} method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        LiteralOpUpgrader dc;
+
+        dc = new LiteralOpUpgrader(ssaMethod);
+
+        dc.run();
+    }
+
+    private LiteralOpUpgrader(SsaMethod ssaMethod) {
+        this.ssaMeth = ssaMethod;
+    }
+
+    /**
+     * Returns true if the register contains an integer 0 or a known-null
+     * object reference
+     *
+     * @param spec non-null spec
+     * @return true for 0 or null type bearers
+     */
+    private static boolean isConstIntZeroOrKnownNull(RegisterSpec spec) {
+        TypeBearer tb = spec.getTypeBearer();
+        if (tb instanceof CstLiteralBits) {
+            CstLiteralBits clb = (CstLiteralBits) tb;
+            return (clb.getLongBits() == 0);
+        }
+        return false;
+    }
+
+    /**
+     * Run the literal op upgrader
+     */
+    private void run() {
+        final TranslationAdvice advice = Optimizer.getAdvice();
+
+        ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+            public void visitMoveInsn(NormalSsaInsn insn) {
+                // do nothing
+            }
+
+            public void visitPhiInsn(PhiInsn insn) {
+                // do nothing
+            }
+
+            public void visitNonMoveInsn(NormalSsaInsn insn) {
+
+                Insn originalRopInsn = insn.getOriginalRopInsn();
+                Rop opcode = originalRopInsn.getOpcode();
+                RegisterSpecList sources = insn.getSources();
+
+                if (sources.size() != 2 ) {
+                    // We're only dealing with two-source insns here.
+                    return;
+                }
+
+                if (opcode.getBranchingness() == Rop.BRANCH_IF) {
+                    /*
+                     * An if instruction can become an if-*z instruction.
+                     */
+                    if (isConstIntZeroOrKnownNull(sources.get(0))) {
+                        replacePlainInsn(insn, sources.withoutFirst(),
+                                RegOps.flippedIfOpcode(opcode.getOpcode()));
+                    } else if (isConstIntZeroOrKnownNull(sources.get(1))) {
+                        replacePlainInsn(insn, sources.withoutLast(),
+                                opcode.getOpcode());
+                    }
+                } else if (advice.hasConstantOperation(
+                        opcode, sources.get(0), sources.get(1))) {
+                    insn.upgradeToLiteral();
+                } else  if (opcode.isCommutative()
+                        && advice.hasConstantOperation(
+                        opcode, sources.get(1), sources.get(0))) {
+                    /*
+                     * An instruction can be commuted to a literal operation
+                     */
+
+                    insn.setNewSources(
+                            RegisterSpecList.make(
+                                    sources.get(1), sources.get(0)));
+
+                    insn.upgradeToLiteral();
+                }
+            }
+        });
+    }
+
+    /**
+     * Replaces an SsaInsn containing a PlainInsn with a new PlainInsn. The
+     * new PlainInsn is contructed with a new RegOp and new sources.
+     *
+     * TODO move this somewhere else.
+     *
+     * @param insn {@code non-null;} an SsaInsn containing a PlainInsn
+     * @param newSources {@code non-null;} new sources list for new insn
+     * @param newOpcode A RegOp from {@link RegOps}
+     */
+    private void replacePlainInsn(NormalSsaInsn insn,
+            RegisterSpecList newSources, int newOpcode) {
+
+        Insn originalRopInsn = insn.getOriginalRopInsn();
+        Rop newRop = Rops.ropFor(newOpcode,
+                insn.getResult(), newSources, null);
+        Insn newRopInsn = new PlainInsn(newRop,
+                originalRopInsn.getPosition(), insn.getResult(),
+                newSources);
+        NormalSsaInsn newInsn
+                = new NormalSsaInsn(newRopInsn, insn.getBlock());
+
+        List<SsaInsn> insns = insn.getBlock().getInsns();
+
+        ssaMeth.onInsnRemoved(insn);
+        insns.set(insns.lastIndexOf(insn), newInsn);
+        ssaMeth.onInsnAdded(newInsn);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/LocalVariableExtractor.java b/dx/src/com/android/dx/ssa/LocalVariableExtractor.java
new file mode 100644
index 0000000..11d53cf
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LocalVariableExtractor.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Code to figure out which local variables are active at which points in
+ * a method. Stolen and retrofitted from
+ * com.android.dx.rop.code.LocalVariableExtractor
+ *
+ * TODO remove this. Allow Rop-form LocalVariableInfo to be passed in,
+ * converted, and adapted through edge-splitting.
+ */
+public class LocalVariableExtractor {
+    /** {@code non-null;} method being extracted from */
+    private final SsaMethod method;
+
+    /** {@code non-null;} block list for the method */
+    private final ArrayList<SsaBasicBlock> blocks;
+
+    /** {@code non-null;} result in-progress */
+    private final LocalVariableInfo resultInfo;
+
+    /** {@code non-null;} work set indicating blocks needing to be processed */
+    private final BitSet workSet;
+
+    /**
+     * Extracts out all the local variable information from the given method.
+     *
+     * @param method {@code non-null;} the method to extract from
+     * @return {@code non-null;} the extracted information
+     */
+    public static LocalVariableInfo extract(SsaMethod method) {
+        LocalVariableExtractor lve = new LocalVariableExtractor(method);
+        return lve.doit();
+    }
+
+    /**
+     * Constructs an instance. This method is private. Use {@link #extract}.
+     *
+     * @param method {@code non-null;} the method to extract from
+     */
+    private LocalVariableExtractor(SsaMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        ArrayList<SsaBasicBlock> blocks = method.getBlocks();
+
+        this.method = method;
+        this.blocks = blocks;
+        this.resultInfo = new LocalVariableInfo(method);
+        this.workSet = new BitSet(blocks.size());
+    }
+
+    /**
+     * Does the extraction.
+     *
+     * @return {@code non-null;} the extracted information
+     */
+    private LocalVariableInfo doit() {
+
+        //FIXME why is this needed here?
+        if (method.getRegCount() > 0 ) {
+            for (int bi = method.getEntryBlockIndex();
+                 bi >= 0;
+                 bi = workSet.nextSetBit(0)) {
+                workSet.clear(bi);
+                processBlock(bi);
+            }
+        }
+
+        resultInfo.setImmutable();
+        return resultInfo;
+    }
+
+    /**
+     * Processes a single block.
+     *
+     * @param blockIndex {@code >= 0;} block index of the block to process
+     */
+    private void processBlock(int blockIndex) {
+        RegisterSpecSet primaryState
+                = resultInfo.mutableCopyOfStarts(blockIndex);
+        SsaBasicBlock block = blocks.get(blockIndex);
+        List<SsaInsn> insns = block.getInsns();
+        int insnSz = insns.size();
+
+        // The exit block has no insns and no successors
+        if (blockIndex == method.getExitBlockIndex()) {
+            return;
+        }
+
+        /*
+         * We may have to treat the last instruction specially: If it
+         * can (but doesn't always) throw, and the exception can be
+         * caught within the same method, then we need to use the
+         * state *before* executing it to be what is merged into
+         * exception targets.
+         */
+        SsaInsn lastInsn = insns.get(insnSz - 1);
+        boolean hasExceptionHandlers
+                = lastInsn.getOriginalRopInsn().getCatches().size() !=0 ;
+        boolean canThrowDuringLastInsn = hasExceptionHandlers
+                && (lastInsn.getResult() != null);
+        int freezeSecondaryStateAt = insnSz - 1;
+        RegisterSpecSet secondaryState = primaryState;
+
+        /*
+         * Iterate over the instructions, adding information for each place
+         * that the active variable set changes.
+         */
+
+        for (int i = 0; i < insnSz; i++) {
+            if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) {
+                // Until this point, primaryState == secondaryState.
+                primaryState.setImmutable();
+                primaryState = primaryState.mutableCopy();
+            }
+
+            SsaInsn insn = insns.get(i);
+            RegisterSpec result;
+
+            result = insn.getLocalAssignment();
+
+            if (result == null) {
+                // We may be nuking an existing local
+
+                result = insn.getResult();
+
+                if (result != null && primaryState.get(result.getReg()) != null) {
+                    primaryState.remove(primaryState.get(result.getReg()));
+                }
+                continue;
+            }
+
+            result = result.withSimpleType();
+
+            RegisterSpec already = primaryState.get(result);
+            /*
+             * The equals() check ensures we only add new info if
+             * the instruction causes a change to the set of
+             * active variables.
+             */
+            if (!result.equals(already)) {
+                /*
+                 * If this insn represents a local moving from one register
+                 * to another, remove the association between the old register
+                 * and the local.
+                 */
+                RegisterSpec previous
+                        = primaryState.localItemToSpec(result.getLocalItem());
+
+                if (previous != null
+                        && (previous.getReg() != result.getReg())) {
+
+                    primaryState.remove(previous);
+                }
+
+                resultInfo.addAssignment(insn, result);
+                primaryState.put(result);
+            }
+        }
+
+        primaryState.setImmutable();
+
+        /*
+         * Merge this state into the start state for each successor,
+         * and update the work set where required (that is, in cases
+         * where the start state for a block changes).
+         */
+
+        IntList successors = block.getSuccessorList();
+        int succSz = successors.size();
+        int primarySuccessor = block.getPrimarySuccessorIndex();
+
+        for (int i = 0; i < succSz; i++) {
+            int succ = successors.get(i);
+            RegisterSpecSet state = (succ == primarySuccessor) ?
+                primaryState : secondaryState;
+
+            if (resultInfo.mergeStarts(succ, state)) {
+                workSet.set(succ);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/LocalVariableInfo.java b/dx/src/com/android/dx/ssa/LocalVariableInfo.java
new file mode 100644
index 0000000..8845270
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LocalVariableInfo.java
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.RegisterSpec;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Container for local variable information for a particular {@link
+ * com.android.dx.ssa.SsaMethod}.
+ * Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}.
+ */
+public class LocalVariableInfo         extends MutabilityControl {
+    /** {@code >= 0;} the register count for the method */
+    private final int regCount;
+
+    /**
+     * {@code non-null;} {@link com.android.dx.rop.code.RegisterSpecSet} to use when indicating a block
+     * that has no locals; it is empty and immutable but has an appropriate
+     * max size for the method
+     */
+    private final RegisterSpecSet emptySet;
+
+    /**
+     * {@code non-null;} array consisting of register sets representing the
+     * sets of variables already assigned upon entry to each block,
+     * where array indices correspond to block indices
+     */
+    private final RegisterSpecSet[] blockStarts;
+
+    /** {@code non-null;} map from instructions to the variable each assigns */
+    private final HashMap<SsaInsn, RegisterSpec> insnAssignments;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param method {@code non-null;} the method being represented by this instance
+     */
+    public LocalVariableInfo(SsaMethod method) {
+        if (method == null) {
+            throw new NullPointerException("method == null");
+        }
+
+        List<SsaBasicBlock> blocks = method.getBlocks();
+
+        this.regCount = method.getRegCount();
+        this.emptySet = new RegisterSpecSet(regCount);
+        this.blockStarts = new RegisterSpecSet[blocks.size()];
+        this.insnAssignments =
+            new HashMap<SsaInsn, RegisterSpec>(/*hint here*/);
+
+        emptySet.setImmutable();
+    }
+
+    /**
+     * Sets the register set associated with the start of the block with
+     * the given index.
+     *
+     * @param index {@code >= 0;} the block index
+     * @param specs {@code non-null;} the register set to associate with the block
+     */
+    public void setStarts(int index, RegisterSpecSet specs) {
+        throwIfImmutable();
+
+        if (specs == null) {
+            throw new NullPointerException("specs == null");
+        }
+
+        try {
+            blockStarts[index] = specs;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus index");
+        }
+    }
+
+    /**
+     * Merges the given register set into the set for the block with the
+     * given index. If there was not already an associated set, then this
+     * is the same as calling {@link #setStarts}. Otherwise, this will
+     * merge the two sets and call {@link #setStarts} on the result of the
+     * merge.
+     *
+     * @param index {@code >= 0;} the block index
+     * @param specs {@code non-null;} the register set to merge into the start set
+     * for the block
+     * @return {@code true} if the merge resulted in an actual change
+     * to the associated set (including storing one for the first time) or
+     * {@code false} if there was no change
+     */
+    public boolean mergeStarts(int index, RegisterSpecSet specs) {
+        RegisterSpecSet start = getStarts0(index);
+        boolean changed = false;
+
+        if (start == null) {
+            setStarts(index, specs);
+            return true;
+        }
+
+        RegisterSpecSet newStart = start.mutableCopy();
+        newStart.intersect(specs, true);
+
+        if (start.equals(newStart)) {
+            return false;
+        }
+
+        newStart.setImmutable();
+        setStarts(index, newStart);
+
+        return true;
+    }
+
+    /**
+     * Gets the register set associated with the start of the block
+     * with the given index. This returns an empty set with the appropriate
+     * max size if no set was associated with the block in question.
+     *
+     * @param index {@code >= 0;} the block index
+     * @return {@code non-null;} the associated register set
+     */
+    public RegisterSpecSet getStarts(int index) {
+        RegisterSpecSet result = getStarts0(index);
+
+        return (result != null) ? result : emptySet;
+    }
+
+    /**
+     * Gets the register set associated with the start of the given
+     * block. This is just convenient shorthand for
+     * {@code getStarts(block.getLabel())}.
+     *
+     * @param block {@code non-null;} the block in question
+     * @return {@code non-null;} the associated register set
+     */
+    public RegisterSpecSet getStarts(SsaBasicBlock block) {
+        return getStarts(block.getIndex());
+    }
+
+    /**
+     * Gets a mutable copy of the register set associated with the
+     * start of the block with the given index. This returns a
+     * newly-allocated empty {@link RegisterSpecSet} of appropriate
+     * max size if there is not yet any set associated with the block.
+     *
+     * @param index {@code >= 0;} the block index
+     * @return {@code non-null;} the associated register set
+     */
+    public RegisterSpecSet mutableCopyOfStarts(int index) {
+        RegisterSpecSet result = getStarts0(index);
+
+        return (result != null) ?
+            result.mutableCopy() : new RegisterSpecSet(regCount);
+    }
+
+    /**
+     * Adds an assignment association for the given instruction and
+     * register spec. This throws an exception if the instruction
+     * doesn't actually perform a named variable assignment.
+     *
+     * <b>Note:</b> Although the instruction contains its own spec for
+     * the result, it still needs to be passed in explicitly to this
+     * method, since the spec that is stored here should always have a
+     * simple type and the one in the instruction can be an arbitrary
+     * {@link com.android.dx.rop.type.TypeBearer} (such as a constant value).
+     *
+     * @param insn {@code non-null;} the instruction in question
+     * @param spec {@code non-null;} the associated register spec
+     */
+    public void addAssignment(SsaInsn insn, RegisterSpec spec) {
+        throwIfImmutable();
+
+        if (insn == null) {
+            throw new NullPointerException("insn == null");
+        }
+
+        if (spec == null) {
+            throw new NullPointerException("spec == null");
+        }
+
+        insnAssignments.put(insn, spec);
+    }
+
+    /**
+     * Gets the named register being assigned by the given instruction, if
+     * previously stored in this instance.
+     *
+     * @param insn {@code non-null;} instruction in question
+     * @return {@code null-ok;} the named register being assigned, if any
+     */
+    public RegisterSpec getAssignment(SsaInsn insn) {
+        return insnAssignments.get(insn);
+    }
+
+    /**
+     * Gets the number of assignments recorded by this instance.
+     *
+     * @return {@code >= 0;} the number of assignments
+     */
+    public int getAssignmentCount() {
+        return insnAssignments.size();
+    }
+
+    public void debugDump() {
+        for (int index = 0 ; index < blockStarts.length; index++) {
+            if (blockStarts[index] == null) {
+                continue;
+            }
+
+            if (blockStarts[index] == emptySet) {
+                System.out.printf("%04x: empty set\n", index);
+            } else {
+                System.out.printf("%04x: %s\n", index, blockStarts[index]);
+            }
+        }
+    }
+
+    /**
+     * Helper method, to get the starts for a index, throwing the
+     * right exception for range problems.
+     *
+     * @param index {@code >= 0;} the block index
+     * @return {@code null-ok;} associated register set or {@code null} if there
+     * is none
+     */
+    private RegisterSpecSet getStarts0(int index) {
+        try {
+            return blockStarts[index];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("bogus index");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/MoveParamCombiner.java b/dx/src/com/android/dx/ssa/MoveParamCombiner.java
new file mode 100644
index 0000000..41660d2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/MoveParamCombiner.java
@@ -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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.cst.CstInteger;
+
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Combine identical move-param insns, which may result from Ropper's
+ * handling of synchronized methods.
+ */
+public class MoveParamCombiner {
+
+    /** method to process */
+    private final SsaMethod ssaMeth;
+
+    /**
+     * Processes a method with this optimization step.
+     *
+     * @param ssaMethod method to process
+     */
+    public static void process(SsaMethod ssaMethod) {
+        new MoveParamCombiner(ssaMethod).run();
+    }
+
+    private MoveParamCombiner(SsaMethod ssaMeth) {
+        this.ssaMeth = ssaMeth;
+    }
+
+    /**
+     * Runs this optimization step.
+     */
+    private void run() {
+        // This will contain the definition specs for each parameter
+        final RegisterSpec[] paramSpecs
+                = new RegisterSpec[ssaMeth.getParamWidth()];
+
+        // Insns to delete when all done
+        final HashSet<SsaInsn> deletedInsns = new HashSet();
+
+        ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+            public void visitMoveInsn (NormalSsaInsn insn) {
+            }
+            public void visitPhiInsn (PhiInsn phi) {
+            }
+            public void visitNonMoveInsn (NormalSsaInsn insn) {
+                if (insn.getOpcode().getOpcode() != RegOps.MOVE_PARAM) {
+                    return;
+                }
+
+                int param = getParamIndex(insn);
+
+                if (paramSpecs[param] == null) {
+                    paramSpecs[param] = insn.getResult();
+                } else {
+                    final RegisterSpec specA = paramSpecs[param];
+                    final RegisterSpec specB = insn.getResult();
+                    LocalItem localA = specA.getLocalItem();
+                    LocalItem localB = specB.getLocalItem();
+                    LocalItem newLocal;
+
+                    /*
+                     * Is there local information to preserve?
+                     */
+
+                    if (localA == null) {
+                        newLocal = localB;
+                    } else if (localB == null) {
+                        newLocal = localA;
+                    } else if (localA.equals(localB)) {
+                        newLocal = localA;
+                    } else {
+                        /*
+                         * Oddly, these two identical move-params have distinct
+                         * debug info. We'll just keep them distinct.
+                         */
+                        return;
+                    }
+
+                    ssaMeth.getDefinitionForRegister(specA.getReg())
+                            .setResultLocal(newLocal);
+
+                    /*
+                     * Map all uses of specB to specA
+                     */
+
+                    RegisterMapper mapper = new RegisterMapper() {
+                        /** @inheritDoc */
+                        public int getNewRegisterCount() {
+                            return ssaMeth.getRegCount();
+                        }
+
+                        /** @inheritDoc */
+                        public RegisterSpec map(RegisterSpec registerSpec) {
+                            if (registerSpec.getReg() == specB.getReg()) {
+                                return specA;
+                            }
+
+                            return registerSpec;
+                        }
+                    };
+
+                    List<SsaInsn> uses
+                            = ssaMeth.getUseListForRegister(specB.getReg());
+
+                    // Use list is modified by mapSourceRegisters
+                    for (int i = uses.size() - 1; i >= 0; i--) {
+                        SsaInsn use = uses.get(i);
+                        use.mapSourceRegisters(mapper);
+                    }
+
+                    deletedInsns.add(insn);
+                }
+
+            }
+        });
+
+        ssaMeth.deleteInsns(deletedInsns);
+    }
+
+    /**
+     * Returns the parameter index associated with a move-param insn. Does
+     * not verify that the insn is a move-param insn.
+     *
+     * @param insn {@code non-null;} a move-param insn
+     * @return {@code >=0;} parameter index
+     */
+    private int getParamIndex(NormalSsaInsn insn) {
+        CstInsn cstInsn = (CstInsn)(insn.getOriginalRopInsn());
+
+        int param = ((CstInteger)cstInsn.getConstant()).getValue();
+        return param;
+    }
+
+}
diff --git a/dx/src/com/android/dx/ssa/NormalSsaInsn.java b/dx/src/com/android/dx/ssa/NormalSsaInsn.java
new file mode 100644
index 0000000..93d3647
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/NormalSsaInsn.java
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+
+/**
+ * A "normal" (non-phi) instruction in SSA form. Always wraps a rop insn.
+ */
+public final class NormalSsaInsn extends SsaInsn implements Cloneable {
+    /** {@code non-null;} rop insn that we're wrapping */
+    private Insn insn;
+
+    /**
+     * Creates an instance.
+     *
+     * @param insn Rop insn to wrap
+     * @param block block that contains this insn
+     */
+    NormalSsaInsn(final Insn insn, final SsaBasicBlock block) {
+        super(insn.getResult(), block);
+        this.insn = insn;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void mapSourceRegisters(RegisterMapper mapper) {
+        RegisterSpecList oldSources = insn.getSources();
+        RegisterSpecList newSources = mapper.map(oldSources);
+
+        if (newSources != oldSources) {
+            insn = insn.withNewRegisters(getResult(), newSources);
+            getBlock().getParent().onSourcesChanged(this, oldSources);
+        }
+    }
+
+    /**
+     * Changes one of the insn's sources. New source should be of same type
+     * and category.
+     *
+     * @param index {@code >=0;} index of source to change
+     * @param newSpec spec for new source
+     */
+    public final void changeOneSource(int index, RegisterSpec newSpec) {
+        RegisterSpecList origSources = insn.getSources();
+        int sz = origSources.size();
+        RegisterSpecList newSources = new RegisterSpecList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            newSources.set(i, i == index ? newSpec : origSources.get(i));
+        }
+
+        newSources.setImmutable();
+
+        RegisterSpec origSpec = origSources.get(index);
+        if (origSpec.getReg() != newSpec.getReg()) {
+            /*
+             * If the register remains unchanged, we're only changing
+             * the type or local var name so don't update use list
+             */
+            getBlock().getParent().onSourceChanged(this, origSpec, newSpec);
+        }
+
+        insn = insn.withNewRegisters(getResult(), newSources);
+    }
+
+    /**
+     * Changes the source list of the insn. New source list should be the
+     * same size and consist of sources of identical types.
+     *
+     * @param newSources non-null new sources list.
+     */
+    public final void setNewSources (RegisterSpecList newSources) {
+        RegisterSpecList origSources = insn.getSources();
+
+        if (origSources.size() != newSources.size()) {
+            throw new RuntimeException("Sources counts don't match");
+        }
+
+        insn = insn.withNewRegisters(getResult(), newSources);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NormalSsaInsn clone() {
+        return (NormalSsaInsn) super.clone();
+    }
+
+    /**
+     * Like rop.Insn.getSources().
+     *
+     * @return {@code null-ok;} sources list
+     */
+    public RegisterSpecList getSources() {
+        return insn.getSources();
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toRopInsn().toHuman();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn toRopInsn() {
+        return insn.withNewRegisters(getResult(), insn.getSources());
+    }
+
+    /**
+     * @return the Rop opcode for this insn
+     */
+    @Override
+    public Rop getOpcode() {
+        return insn.getOpcode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Insn getOriginalRopInsn() {
+        return insn;
+    }
+
+    /** {@inheritDoc} */
+    public RegisterSpec getLocalAssignment() {
+        RegisterSpec assignment;
+
+        if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
+            assignment = insn.getSources().get(0);
+        } else {
+            assignment = getResult();
+        }
+
+        if (assignment == null) {
+            return null;
+        }
+
+        LocalItem local = assignment.getLocalItem();
+
+        if (local == null) {
+            return null;
+        }
+
+        return assignment;
+    }
+
+    /**
+     * Upgrades this insn to a version that represents the constant last
+     * source literally. If the upgrade is not possible, this does nothing.
+     *
+     * @see Insn#withLastSourceLiteral
+     */
+    public void upgradeToLiteral() {
+        RegisterSpecList oldSources = insn.getSources();
+
+        insn = insn.withLastSourceLiteral();
+        getBlock().getParent().onSourcesChanged(this, oldSources);
+    }
+
+    /**
+     * @return true if this is a move (but not a move-operand) instruction
+     */
+    @Override
+    public boolean isNormalMoveInsn() {
+        return insn.getOpcode().getOpcode() == RegOps.MOVE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isMoveException() {
+        return insn.getOpcode().getOpcode() == RegOps.MOVE_EXCEPTION;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean canThrow() {
+        return insn.canThrow();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(Visitor v) {
+        if (isNormalMoveInsn()) {
+            v.visitMoveInsn(this);
+        } else {
+            v.visitNonMoveInsn(this);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public  boolean isPhiOrMove() {
+        return isNormalMoveInsn();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * TODO: Increase the scope of this.
+     */
+    @Override
+    public boolean hasSideEffect() {
+        Rop opcode = getOpcode();
+
+        if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+            return true;
+        }
+
+        boolean hasLocalSideEffect
+            = Optimizer.getPreserveLocals() && getLocalAssignment() != null;
+
+        switch (opcode.getOpcode()) {
+            case RegOps.MOVE_RESULT:
+            case RegOps.MOVE:
+            case RegOps.CONST:
+                return hasLocalSideEffect;
+            default:
+                return true;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/Optimizer.java b/dx/src/com/android/dx/ssa/Optimizer.java
new file mode 100644
index 0000000..06ed138
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/Optimizer.java
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.ssa.back.LivenessAnalyzer;
+import com.android.dx.ssa.back.SsaToRop;
+
+import java.util.EnumSet;
+
+/**
+ * Runs a method through the SSA form conversion, any optimization algorithms,
+ * and returns it to rop form.
+ */
+public class Optimizer {
+    private static boolean preserveLocals = true;
+
+    private static TranslationAdvice advice;
+
+    /** optional optimizer steps */
+    public enum OptionalStep {
+        MOVE_PARAM_COMBINER, SCCP, LITERAL_UPGRADE, CONST_COLLECTOR,
+            ESCAPE_ANALYSIS
+    }
+
+    /**
+     * @return true if local variable information should be preserved, even
+     * at code size/register size cost
+     */
+    public static boolean getPreserveLocals() {
+        return preserveLocals;
+    }
+
+    /**
+     * @return {@code non-null;} translation advice
+     */
+    public static TranslationAdvice getAdvice() {
+        return advice;
+    }
+
+    /**
+     * Runs optimization algorthims over this method, and returns a new
+     * instance of RopMethod with the changes.
+     *
+     * @param rmeth method to process
+     * @param paramWidth the total width, in register-units, of this method's
+     * parameters
+     * @param isStatic true if this method has no 'this' pointer argument.
+     * @param inPreserveLocals true if local variable info should be preserved,
+     * at the cost of some registers and insns
+     * @param inAdvice {@code non-null;} translation advice
+     * @return optimized method
+     */
+    public static RopMethod optimize(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        return optimize(rmeth, paramWidth, isStatic, inPreserveLocals, inAdvice,
+                EnumSet.allOf(OptionalStep.class));
+    }
+
+    /**
+     * Runs optimization algorthims over this method, and returns a new
+     * instance of RopMethod with the changes.
+     *
+     * @param rmeth method to process
+     * @param paramWidth the total width, in register-units, of this method's
+     * parameters
+     * @param isStatic true if this method has no 'this' pointer argument.
+     * @param inPreserveLocals true if local variable info should be preserved,
+     * at the cost of some registers and insns
+     * @param inAdvice {@code non-null;} translation advice
+     * @param steps set of optional optimization steps to run
+     * @return optimized method
+     */
+    public static RopMethod optimize(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) {
+        SsaMethod ssaMeth = null;
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+        runSsaFormSteps(ssaMeth, steps);
+
+        RopMethod resultMeth = SsaToRop.convertToRopMethod(ssaMeth, false);
+
+        if (resultMeth.getBlocks().getRegCount()
+                > advice.getMaxOptimalRegisterCount()) {
+            // Try to see if we can squeeze it under the register count bar
+            resultMeth = optimizeMinimizeRegisters(rmeth, paramWidth, isStatic,
+                    steps);
+        }
+        return resultMeth;
+    }
+
+    /**
+     * Runs the optimizer with a strategy to minimize the number of rop-form
+     * registers used by the end result. Dex bytecode does not have instruction
+     * forms that take register numbers larger than 15 for all instructions.
+     * If we've produced a method that uses more than 16 registers, try again
+     * with a different strategy to see if we can get under the bar. The end
+     * result will be much more efficient.
+     *
+     * @param rmeth method to process
+     * @param paramWidth the total width, in register-units, of this method's
+     * parameters
+     * @param isStatic true if this method has no 'this' pointer argument.
+     * @param steps set of optional optimization steps to run
+     * @return optimized method
+     */
+    private static RopMethod optimizeMinimizeRegisters(RopMethod rmeth,
+            int paramWidth, boolean isStatic,
+            EnumSet<OptionalStep> steps) {
+        SsaMethod ssaMeth;
+        RopMethod resultMeth;
+
+        ssaMeth = SsaConverter.convertToSsaMethod(
+                rmeth, paramWidth, isStatic);
+
+        EnumSet<OptionalStep> newSteps = steps.clone();
+
+        /*
+         * CONST_COLLECTOR trades insns for registers, which is not an
+         * appropriate strategy here.
+         */
+        newSteps.remove(OptionalStep.CONST_COLLECTOR);
+
+        runSsaFormSteps(ssaMeth, newSteps);
+
+        resultMeth = SsaToRop.convertToRopMethod(ssaMeth, true);
+        return resultMeth;
+    }
+
+    private static void runSsaFormSteps(SsaMethod ssaMeth,
+            EnumSet<OptionalStep> steps) {
+        boolean needsDeadCodeRemover = true;
+
+        if (steps.contains(OptionalStep.MOVE_PARAM_COMBINER)) {
+            MoveParamCombiner.process(ssaMeth);
+        }
+
+        if (steps.contains(OptionalStep.SCCP)) {
+            SCCP.process(ssaMeth);
+        }
+
+        if (steps.contains(OptionalStep.LITERAL_UPGRADE)) {
+            LiteralOpUpgrader.process(ssaMeth);
+            DeadCodeRemover.process(ssaMeth);
+            needsDeadCodeRemover = false;
+        }
+
+        /*
+         * ESCAPE_ANALYSIS impacts debuggability, so left off by default
+         */
+        steps.remove(OptionalStep.ESCAPE_ANALYSIS);
+        if (steps.contains(OptionalStep.ESCAPE_ANALYSIS)) {
+            EscapeAnalysis.process(ssaMeth);
+            DeadCodeRemover.process(ssaMeth);
+            needsDeadCodeRemover = false;
+        }
+
+        if (steps.contains(OptionalStep.CONST_COLLECTOR)) {
+            ConstCollector.process(ssaMeth);
+            DeadCodeRemover.process(ssaMeth);
+            needsDeadCodeRemover = false;
+        }
+
+        // dead code remover must be run before phi type resolver
+        if (needsDeadCodeRemover) {
+            DeadCodeRemover.process(ssaMeth);
+        }
+
+        PhiTypeResolver.process(ssaMeth);
+    }
+
+    public static SsaMethod debugEdgeSplit(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        return SsaConverter.testEdgeSplit(rmeth, paramWidth, isStatic);
+    }
+
+    public static SsaMethod debugPhiPlacement(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        return SsaConverter.testPhiPlacement(rmeth, paramWidth, isStatic);
+    }
+
+    public static SsaMethod debugRenaming(RopMethod rmeth, int paramWidth,
+            boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        return SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+    }
+
+    public static SsaMethod debugDeadCodeRemover(RopMethod rmeth,
+            int paramWidth, boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice) {
+
+        SsaMethod ssaMeth;
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+        DeadCodeRemover.process(ssaMeth);
+
+        return ssaMeth;
+    }
+
+    public static SsaMethod debugNoRegisterAllocation(RopMethod rmeth,
+            int paramWidth, boolean isStatic, boolean inPreserveLocals,
+            TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) {
+
+        SsaMethod ssaMeth;
+
+        preserveLocals = inPreserveLocals;
+        advice = inAdvice;
+
+        ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+
+        runSsaFormSteps(ssaMeth, steps);
+
+        LivenessAnalyzer.constructInterferenceGraph(ssaMeth);
+
+        return ssaMeth;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/PhiInsn.java b/dx/src/com/android/dx/ssa/PhiInsn.java
new file mode 100644
index 0000000..3ea876f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/PhiInsn.java
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Phi instruction (magical post-control-flow-merge) instruction
+ * in SSA form. Will be converted to moves in predecessor blocks before
+ * conversion back to ROP form.
+ */
+public final class PhiInsn extends SsaInsn {
+    /**
+     * result register. The original result register of the phi insn
+     * is needed during the renaming process after the new result
+     * register has already been chosen.
+     */
+    private final int ropResultReg;
+
+    /**
+     * {@code non-null;} operands of the instruction; built up by
+     * {@link #addPhiOperand}
+     */
+    private final ArrayList<Operand> operands = new ArrayList<Operand>();
+
+    /** {@code null-ok;} source registers; constructed lazily */
+    private RegisterSpecList sources;
+
+    /**
+     * Constructs a new phi insn with no operands.
+     *
+     * @param resultReg the result reg for this phi insn
+     * @param block block containing this insn.
+     */
+    public PhiInsn(RegisterSpec resultReg, SsaBasicBlock block) {
+        super(resultReg, block);
+        ropResultReg = resultReg.getReg();
+    }
+
+    /**
+     * Makes a phi insn with a void result type.
+     *
+     * @param resultReg the result register for this phi insn.
+     * @param block block containing this insn.
+     */
+    public PhiInsn(final int resultReg, final SsaBasicBlock block) {
+        /*
+         * The result type here is bogus: The type depends on the
+         * operand and will be derived later.
+         */
+        super(RegisterSpec.make(resultReg, Type.VOID), block);
+        ropResultReg = resultReg;
+    }
+
+    /** {@inheritDoc} */
+    public PhiInsn clone() {
+        throw new UnsupportedOperationException("can't clone phi");
+    }
+
+    /**
+     * Updates the TypeBearers of all the sources (phi operands) to be
+     * the current TypeBearer of the register-defining instruction's result.
+     * This is used during phi-type resolution.<p>
+     *
+     * Note that local association of operands are preserved in this step.
+     *
+     * @param ssaMeth method that contains this insn
+     */
+    public void updateSourcesToDefinitions(SsaMethod ssaMeth) {
+        for (Operand o : operands) {
+            RegisterSpec def
+                = ssaMeth.getDefinitionForRegister(
+                    o.regSpec.getReg()).getResult();
+
+            o.regSpec = o.regSpec.withType(def.getType());
+        }
+
+        sources = null;
+    }
+
+    /**
+     * Changes the result type. Used during phi type resolution
+     *
+     * @param type {@code non-null;} new TypeBearer
+     * @param local {@code null-ok;} new local info, if available
+     */
+    public void changeResultType(TypeBearer type, LocalItem local) {
+        setResult(RegisterSpec.makeLocalOptional(
+                          getResult().getReg(), type, local));
+    }
+
+    /**
+     * Gets the original rop-form result reg. This is useful during renaming.
+     *
+     * @return the original rop-form result reg
+     */
+    public int getRopResultReg() {
+        return ropResultReg;
+    }
+
+    /**
+     * Adds an operand to this phi instruction.
+     *
+     * @param registerSpec register spec, including type and reg of operand
+     * @param predBlock predecessor block to be associated with this operand
+     */
+    public void addPhiOperand(RegisterSpec registerSpec,
+            SsaBasicBlock predBlock) {
+        operands.add(new Operand(registerSpec, predBlock.getIndex(),
+                predBlock.getRopLabel()));
+
+        // Un-cache sources, in case someone has already called getSources().
+        sources = null;
+    }
+
+    /**
+     * Gets the index of the pred block associated with the RegisterSpec
+     * at the particular getSources() index.
+     *
+     * @param sourcesIndex index of source in getSources()
+     * @return block index
+     */
+    public int predBlockIndexForSourcesIndex(int sourcesIndex) {
+        return operands.get(sourcesIndex).blockIndex;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Always returns null for {@code PhiInsn}s.
+     */
+    @Override
+    public Rop getOpcode() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Always returns null for {@code PhiInsn}s.
+     */
+    @Override
+    public Insn getOriginalRopInsn() {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Always returns false for {@code PhiInsn}s.
+     */
+    @Override
+    public boolean canThrow() {
+        return false;
+    }
+
+    /**
+     * Gets sources. Constructed lazily from phi operand data structures and
+     * then cached.
+     *
+     * @return {@code non-null;} sources list
+     */
+    public RegisterSpecList getSources() {
+        if (sources != null) {
+            return sources;
+        }
+
+        if (operands.size() == 0) {
+            // How'd this happen? A phi insn with no operand?
+            return RegisterSpecList.EMPTY;
+        }
+
+        int szSources = operands.size();
+        sources = new RegisterSpecList(szSources);
+
+        for (int i = 0; i < szSources; i++) {
+            Operand o = operands.get(i);
+
+            sources.set(i, o.regSpec);
+        }
+
+        sources.setImmutable();
+        return sources;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isRegASource(int reg) {
+        /*
+         * Avoid creating a sources list in case it has not already been
+         * created.
+         */
+
+        for (Operand o : operands) {
+            if (o.regSpec.getReg() == reg) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @return true if all operands use the same register
+     */
+    public boolean areAllOperandsEqual() {
+        if (operands.size() == 0 ) {
+            // This should never happen.
+            return true;
+        }
+
+        int firstReg = operands.get(0).regSpec.getReg();
+        for (Operand o : operands) {
+            if (firstReg != o.regSpec.getReg()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final void mapSourceRegisters(RegisterMapper mapper) {
+        for (Operand o : operands) {
+            RegisterSpec old = o.regSpec;
+            o.regSpec = mapper.map(old);
+            if (old != o.regSpec) {
+                getBlock().getParent().onSourceChanged(this, old, o.regSpec);
+            }
+        }
+        sources = null;
+    }
+
+    /**
+     * Always throws an exeption, since a phi insn may not be
+     * converted back to rop form.
+     *
+     * @return always throws exception
+     */
+    @Override
+    public Insn toRopInsn() {
+        throw new IllegalArgumentException(
+                "Cannot convert phi insns to rop form");
+    }
+
+    /**
+     * Returns the list of predecessor blocks associated with all operands
+     * that have {@code reg} as an operand register.
+     *
+     * @param reg register to look up
+     * @param ssaMeth method we're operating on
+     * @return list of predecessor blocks, empty if none
+     */
+    public List<SsaBasicBlock> predBlocksForReg(int reg, SsaMethod ssaMeth) {
+        ArrayList<SsaBasicBlock> ret = new ArrayList<SsaBasicBlock>();
+
+        for (Operand o : operands) {
+            if (o.regSpec.getReg() == reg) {
+                ret.add(ssaMeth.getBlocks().get(o.blockIndex));
+            }
+        }
+
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isPhiOrMove() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean hasSideEffect() {
+        return Optimizer.getPreserveLocals() && getLocalAssignment() != null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void accept(SsaInsn.Visitor v) {
+        v.visitPhiInsn(this);
+    }
+
+    /** {@inheritDoc} */
+    public String toHuman() {
+        return toHumanWithInline(null);
+    }
+
+    /**
+     * Returns human-readable string for listing dumps. This method
+     * allows sub-classes to specify extra text.
+     *
+     * @param extra {@code null-ok;} the argument to print after the opcode
+     * @return human-readable string for listing dumps
+     */
+    protected final String toHumanWithInline(String extra) {
+        StringBuffer sb = new StringBuffer(80);
+
+        sb.append(SourcePosition.NO_INFO);
+        sb.append(": phi");
+
+        if (extra != null) {
+            sb.append("(");
+            sb.append(extra);
+            sb.append(")");
+        }
+
+        RegisterSpec result = getResult();
+
+        if (result == null) {
+            sb.append(" .");
+        } else {
+            sb.append(" ");
+            sb.append(result.toHuman());
+        }
+
+        sb.append(" <-");
+
+        int sz = getSources().size();
+        if (sz == 0) {
+            sb.append(" .");
+        } else {
+            for (int i = 0; i < sz; i++) {
+                sb.append(" ");
+                sb.append(sources.get(i).toHuman()
+                        + "[b="
+                        + Hex.u2(operands.get(i).ropLabel)  + "]");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * A single phi operand, consiting of source register and block index
+     * for move.
+     */
+    private static class Operand {
+        public RegisterSpec regSpec;
+        public final int blockIndex;
+        public final int ropLabel;       // only used for debugging
+
+        public Operand(RegisterSpec regSpec, int blockIndex, int ropLabel) {
+            this.regSpec = regSpec;
+            this.blockIndex = blockIndex;
+            this.ropLabel = ropLabel;
+        }
+    }
+
+    /**
+     * Visitor interface for instances of this (outer) class.
+     */
+    public static interface Visitor {
+        public void visitPhiInsn(PhiInsn insn);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/PhiTypeResolver.java b/dx/src/com/android/dx/ssa/PhiTypeResolver.java
new file mode 100644
index 0000000..4b8b4e3
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/PhiTypeResolver.java
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.cf.code.Merger;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Resolves the result types of phi instructions. When phi instructions
+ * are inserted, their result types are set to BT_VOID (which is a nonsensical
+ * type for a register) but must be resolve to a real type before converting
+ * out of SSA form.<p>
+ *
+ * The resolve is done as an iterative merge of each phi's operand types.
+ * Phi operands may be themselves be the result of unresolved phis,
+ * and the algorithm tries to find the most-fit type (for example, if every
+ * operand is the same constant value or the same local variable info, we want
+ * that to be reflected).<p>
+ *
+ * This algorithm assumes a dead-code remover has already removed all
+ * circular-only phis that may have been inserted.
+ */
+public class PhiTypeResolver {
+
+    SsaMethod ssaMeth;
+    /** indexed by register; all registers still defined by unresolved phis */
+    private final BitSet worklist;
+
+    /**
+     * Resolves all phi types in the method
+     * @param ssaMeth method to process
+     */
+    public static void process (SsaMethod ssaMeth) {
+        new PhiTypeResolver(ssaMeth).run();
+    }
+
+    private PhiTypeResolver(SsaMethod ssaMeth) {
+        this.ssaMeth = ssaMeth;
+        worklist = new BitSet(ssaMeth.getRegCount());
+    }
+
+    /**
+     * Runs the phi-type resolver.
+     */
+    private void run() {
+
+        int regCount = ssaMeth.getRegCount();
+
+        for (int reg = 0; reg < regCount; reg++) {
+            SsaInsn definsn = ssaMeth.getDefinitionForRegister(reg);
+
+            if (definsn != null
+                    && (definsn.getResult().getBasicType() == Type.BT_VOID)) {
+                worklist.set(reg);
+            }
+        }
+
+        int reg;
+        while ( 0 <= (reg = worklist.nextSetBit(0))) {
+            worklist.clear(reg);
+
+            /*
+             * definitions on the worklist have a type of BT_VOID, which
+             * must have originated from a PhiInsn.
+             */
+            PhiInsn definsn = (PhiInsn)ssaMeth.getDefinitionForRegister(reg);
+
+            if (resolveResultType(definsn)) {
+                /*
+                 * If the result type has changed, re-resolve all phis
+                 * that use this.
+                 */
+
+                List<SsaInsn> useList = ssaMeth.getUseListForRegister(reg);
+
+                int sz = useList.size();
+                for (int i = 0; i < sz; i++ ) {
+                    SsaInsn useInsn = useList.get(i);
+                    RegisterSpec resultReg = useInsn.getResult();
+                    if (resultReg != null && useInsn instanceof PhiInsn) {
+                        worklist.set(resultReg.getReg());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if a and b are equal, whether
+     * or not either of them are null.
+     * @param a
+     * @param b
+     * @return true if equal
+     */
+    private static boolean equalsHandlesNulls(LocalItem a, LocalItem b) {
+        return (a == b) || ((a != null) && a.equals(b));
+    }
+
+    /**
+     * Resolves the result of a phi insn based on its operands. The "void"
+     * type, which is a nonsensical type for a register, is used for
+     * registers defined by as-of-yet-unresolved phi operations.
+     *
+     * @return true if the result type changed, false if no change
+     */
+    boolean resolveResultType(PhiInsn insn) {
+        insn.updateSourcesToDefinitions(ssaMeth);
+
+        RegisterSpecList sources = insn.getSources();
+
+        // Start by finding the first non-void operand
+        RegisterSpec first = null;
+        int firstIndex = -1;
+
+        int szSources = sources.size();
+        for (int i = 0 ; i <szSources ; i++) {
+            RegisterSpec rs = sources.get(i);
+
+            if (rs.getBasicType() != Type.BT_VOID) {
+                first = rs;
+                firstIndex = i;
+            }
+        }
+
+        if (first == null) {
+            // All operands are void -- we're not ready to resolve yet
+            return false;
+        }
+
+        LocalItem firstLocal = first.getLocalItem();
+        TypeBearer mergedType = first.getType();
+        boolean sameLocals = true;
+        for (int i = 0 ; i < szSources ; i++) {
+            if (i == firstIndex) {
+                continue;
+            }
+
+            RegisterSpec rs = sources.get(i);
+
+            // Just skip void (unresolved phi results) for now
+            if (rs.getBasicType() == Type.BT_VOID){
+                continue;
+            }
+
+            sameLocals = sameLocals
+                    && equalsHandlesNulls(firstLocal, rs.getLocalItem());
+
+            mergedType = Merger.mergeType(mergedType, rs.getType());
+        }
+
+        TypeBearer newResultType;
+
+        if (mergedType != null) {
+            newResultType = mergedType;
+        } else {
+            StringBuilder sb = new StringBuilder();
+
+            for (int i = 0; i < szSources; i++) {
+                sb.append(sources.get(i).toString());
+                sb.append(' ');
+            }
+
+            throw new RuntimeException ("Couldn't map types in phi insn:" + sb);
+        }
+
+        LocalItem newLocal = sameLocals ? firstLocal : null;
+
+        RegisterSpec result = insn.getResult();
+
+        if ((result.getTypeBearer() == newResultType)
+                && equalsHandlesNulls(newLocal, result.getLocalItem())) {
+            return false;
+        }
+
+        insn.changeResultType(newResultType, newLocal);
+
+        return true;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/RegisterMapper.java b/dx/src/com/android/dx/ssa/RegisterMapper.java
new file mode 100644
index 0000000..bef941f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/RegisterMapper.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Represents a mapping between two register numbering schemes.
+ * Subclasses of this may be mutable, and as such the mapping provided
+ * is only valid for the lifetime of the method call in which
+ * instances of this class are passed.
+ */
+public abstract class RegisterMapper {
+    /**
+     * Gets the count of registers (really, the total register width, since
+     * category width is counted) in the new namespace.
+     * @return >= 0 width of new namespace.
+     */
+    public abstract int getNewRegisterCount();
+
+    /**
+     * @param registerSpec old register
+     * @return register in new space
+     */
+    public abstract RegisterSpec map(RegisterSpec registerSpec);
+
+    /**
+     *
+     * @param sources old register list
+     * @return new mapped register list, or old if nothing has changed.
+     */
+    public final RegisterSpecList map(RegisterSpecList sources) {
+        int sz = sources.size();
+        RegisterSpecList newSources = new RegisterSpecList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            newSources.set(i, map(sources.get(i)));
+        }
+
+        newSources.setImmutable();
+
+        // Return the old sources if nothing has changed.
+        return newSources.equals(sources) ? sources : newSources;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SCCP.java b/dx/src/com/android/dx/ssa/SCCP.java
new file mode 100644
index 0000000..42abbb2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SCCP.java
@@ -0,0 +1,483 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.Type;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * A small variant of Wegman and Zadeck's Sparse Conditional Constant
+ * Propagation algorithm.
+ */
+public class SCCP {
+    /** Lattice values  */
+    private static final int TOP = 0;
+    private static final int CONSTANT = 1;
+    private static final int VARYING = 2;
+    /** method we're processing */
+    private SsaMethod ssaMeth;
+    /** ssaMeth.getRegCount() */
+    private int regCount;
+    /** Lattice values for each SSA register */
+    private int[] latticeValues;
+    /** For those registers that are constant, this is the constant value */
+    private Constant[] latticeConstants;
+    /** Worklist of basic blocks to be processed */
+    private ArrayList<SsaBasicBlock> cfgWorklist;
+    /** Bitset containing bits for each block that has been found executable */
+    private BitSet executableBlocks;
+    /** Worklist for SSA edges.  This is a list of registers to process */
+    private ArrayList<SsaInsn> ssaWorklist;
+    /**
+     * Worklist for SSA edges that represent varying values.  It makes the
+     * algorithm much faster if you move all values to VARYING as fast as
+     * possible.
+     */
+    private ArrayList<SsaInsn> varyingWorklist;
+
+    private SCCP(SsaMethod ssaMeth) {
+        this.ssaMeth = ssaMeth;
+        this.regCount = ssaMeth.getRegCount();
+        this.latticeValues = new int[this.regCount];
+        this.latticeConstants = new Constant[this.regCount];
+        this.cfgWorklist = new ArrayList<SsaBasicBlock>();
+        this.executableBlocks = new BitSet(ssaMeth.getBlocks().size());
+        this.ssaWorklist = new ArrayList<SsaInsn>();
+        this.varyingWorklist = new ArrayList<SsaInsn>();
+        for (int i = 0; i < this.regCount; i++) {
+            latticeValues[i] = TOP;
+            latticeConstants[i] = null;
+        }
+    }
+
+    /**
+     * Performs sparse conditional constant propagation on a method.
+     * @param ssaMethod Method to process
+     */
+    public static void process (SsaMethod ssaMethod) {
+        new SCCP(ssaMethod).run();
+    }
+
+    /**
+     * Add a new SSA basic block to the CFG worklist
+     * @param ssaBlock Block to add
+     */
+    private void addBlockToWorklist(SsaBasicBlock ssaBlock) {
+        if (!executableBlocks.get(ssaBlock.getIndex())) {
+            cfgWorklist.add(ssaBlock);
+            executableBlocks.set(ssaBlock.getIndex());
+        }
+    }
+
+    /**
+     * Adds an SSA register's uses to the SSA worklist.
+     * @param reg SSA register
+     * @param latticeValue new lattice value for @param reg.
+     */
+    private void addUsersToWorklist(int reg, int latticeValue) {
+        if (latticeValue == VARYING) {
+            for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+                varyingWorklist.add(insn);
+            }
+        } else {
+            for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+                ssaWorklist.add(insn);
+            }
+        }
+    }
+
+    /**
+     * Sets a lattice value for a register to value.
+     * @param reg SSA register
+     * @param value Lattice value
+     * @param cst Constant value (may be null)
+     * @return true if the lattice value changed.
+     */
+    private boolean setLatticeValueTo(int reg, int value, Constant cst) {
+        if (value != CONSTANT) {
+            if (latticeValues[reg] != value) {
+                latticeValues[reg] = value;
+                return true;
+            }
+            return false;
+        } else {
+            if (latticeValues[reg] != value
+                    || !latticeConstants[reg].equals(cst)) {
+                latticeValues[reg] = value;
+                latticeConstants[reg] = cst;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Simulates a PHI node and set the lattice for the result
+     * to the approriate value.
+     * Meet values:
+     * TOP x anything = anything
+     * VARYING x anything = VARYING
+     * CONSTANT x CONSTANT = CONSTANT if equal constants, VARYING otherwise
+     * @param insn PHI to simulate.
+     */
+    private void simulatePhi(PhiInsn insn) {
+        int phiResultReg = insn.getResult().getReg();
+
+        if (latticeValues[phiResultReg] == VARYING) {
+            return;
+        }
+
+        RegisterSpecList sources = insn.getSources();
+        int phiResultValue = TOP;
+        Constant phiConstant = null;
+        int sourceSize = sources.size();
+
+        for (int i = 0; i < sourceSize; i++) {
+            int predBlockIndex = insn.predBlockIndexForSourcesIndex(i);
+            int sourceReg = sources.get(i).getReg();
+            int sourceRegValue = latticeValues[sourceReg];
+
+            if (!executableBlocks.get(predBlockIndex)
+                    || sourceRegValue == TOP) {
+                continue;
+            }
+
+            if (sourceRegValue == CONSTANT) {
+                if (phiConstant == null) {
+                    phiConstant = latticeConstants[sourceReg];
+                    phiResultValue = CONSTANT;
+                 } else if (!latticeConstants[sourceReg].equals(phiConstant)){
+                    phiResultValue = VARYING;
+                    break;
+                }
+
+            } else if (sourceRegValue == VARYING) {
+                phiResultValue = VARYING;
+                break;
+            }
+        }
+        if (setLatticeValueTo(phiResultReg, phiResultValue, phiConstant)) {
+            addUsersToWorklist(phiResultReg, phiResultValue);
+        }
+    }
+
+    /**
+     * Simulate a block and note the results in the lattice.
+     * @param block Block to visit
+     */
+    private void simulateBlock(SsaBasicBlock block) {
+        for (SsaInsn insn : block.getInsns()) {
+            if (insn instanceof PhiInsn) {
+                simulatePhi((PhiInsn) insn);
+            } else {
+                simulateStmt(insn);
+            }
+        }
+    }
+    private static String latticeValName(int latticeVal) {
+        switch (latticeVal) {
+            case TOP: return "TOP";
+            case CONSTANT: return "CONSTANT";
+            case VARYING: return "VARYING";
+            default: return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Simplifies a jump statement.
+     * @param insn jump to simplify
+     * @return an instruction representing the simplified jump.
+     */
+    private Insn simplifyJump (Insn insn) {
+        return insn;
+    }
+
+    /**
+     * Simulates math insns, if possible.
+     *
+     * @param insn non-null insn to simulate
+     * @return constant result or null if not simulatable.
+     */
+    private Constant simulateMath(SsaInsn insn) {
+        Insn ropInsn = insn.getOriginalRopInsn();
+        int opcode = insn.getOpcode().getOpcode();
+        RegisterSpecList sources = insn.getSources();
+        int regA = sources.get(0).getReg();
+        Constant cA;
+        Constant cB;
+
+        if (latticeValues[regA] != CONSTANT) {
+            cA = null;
+        } else {
+            cA = latticeConstants[regA];
+        }
+
+        if (sources.size() == 1) {
+            CstInsn cstInsn = (CstInsn) ropInsn;
+            cB = cstInsn.getConstant();
+        } else { /* sources.size() == 2 */
+            int regB = sources.get(1).getReg();
+            if (latticeValues[regB] != CONSTANT) {
+                cB = null;
+            } else {
+                cB = latticeConstants[regB];
+            }
+        }
+
+        if (cA == null || cB == null) {
+            //TODO handle a constant of 0 with MUL or AND
+            return null;
+        }
+
+        switch (insn.getResult().getBasicType()) {
+            case Type.BT_INT:
+                int vR;
+                boolean skip=false;
+
+                int vA = ((CstInteger) cA).getValue();
+                int vB = ((CstInteger) cB).getValue();
+
+                switch (opcode) {
+                    case RegOps.ADD:
+                        vR = vA + vB;
+                        break;
+                    case RegOps.SUB:
+                        vR = vA - vB;
+                        break;
+                    case RegOps.MUL:
+                        vR = vA * vB;
+                        break;
+                    case RegOps.DIV:
+                        if (vB == 0) {
+                            skip = true;
+                            vR = 0; // just to hide a warning
+                        } else {
+                            vR = vA / vB;
+                        }
+                        break;
+                    case RegOps.AND:
+                        vR = vA & vB;
+                        break;
+                    case RegOps.OR:
+                        vR = vA | vB;
+                        break;
+                    case RegOps.XOR:
+                        vR = vA ^ vB;
+                        break;
+                    case RegOps.SHL:
+                        vR = vA << vB;
+                        break;
+                    case RegOps.SHR:
+                        vR = vA >> vB;
+                        break;
+                    case RegOps.USHR:
+                        vR = vA >>> vB;
+                        break;
+                    case RegOps.REM:
+                        vR = vA % vB;
+                        break;
+                    default:
+                        throw new RuntimeException("Unexpected op");
+                }
+
+                return skip ? null : CstInteger.make(vR);
+
+            default:
+                // not yet supported
+                return null;
+        }
+    }
+
+    /**
+     * Simulates a statement and set the result lattice value.
+     * @param insn instruction to simulate
+     */
+    private void simulateStmt(SsaInsn insn) {
+        Insn ropInsn = insn.getOriginalRopInsn();
+        if (ropInsn.getOpcode().getBranchingness() != Rop.BRANCH_NONE
+                || ropInsn.getOpcode().isCallLike()) {
+            ropInsn = simplifyJump (ropInsn);
+            /* TODO: If jump becomes constant, only take true edge. */
+            SsaBasicBlock block = insn.getBlock();
+            int successorSize = block.getSuccessorList().size();
+            for (int i = 0; i < successorSize; i++) {
+                int successor = block.getSuccessorList().get(i);
+                addBlockToWorklist(ssaMeth.getBlocks().get(successor));
+            }
+        }
+
+        if (insn.getResult() == null) {
+            return;
+        }
+
+        /* TODO: Simplify statements when possible using the constants. */
+        int resultReg = insn.getResult().getReg();
+        int resultValue = VARYING;
+        Constant resultConstant = null;
+        int opcode = insn.getOpcode().getOpcode();
+        switch (opcode) {
+            case RegOps.CONST: {
+                CstInsn cstInsn = (CstInsn)ropInsn;
+                resultValue = CONSTANT;
+                resultConstant = cstInsn.getConstant();
+                break;
+            }
+            case RegOps.MOVE: {
+                if (insn.getSources().size() == 1) {
+                    int sourceReg = insn.getSources().get(0).getReg();
+                    resultValue = latticeValues[sourceReg];
+                    resultConstant = latticeConstants[sourceReg];
+                }
+                break;
+            }
+
+            case RegOps.ADD:
+            case RegOps.SUB:
+            case RegOps.MUL:
+            case RegOps.DIV:
+            case RegOps.AND:
+            case RegOps.OR:
+            case RegOps.XOR:
+            case RegOps.SHL:
+            case RegOps.SHR:
+            case RegOps.USHR:
+            case RegOps.REM:
+
+                resultConstant = simulateMath(insn);
+
+                if (resultConstant == null) {
+                    resultValue = VARYING;
+                } else {
+                    resultValue = CONSTANT;
+                }
+            break;
+            /* TODO: Handle non-int arithmetic.
+               TODO: Eliminate check casts that we can prove the type of. */
+            default: {}
+        }
+        if (setLatticeValueTo(resultReg, resultValue, resultConstant)) {
+            addUsersToWorklist(resultReg, resultValue);
+        }
+    }
+
+    private void run() {
+        SsaBasicBlock firstBlock = ssaMeth.getEntryBlock();
+        addBlockToWorklist(firstBlock);
+
+        /* Empty all the worklists by propagating our values */
+        while (!cfgWorklist.isEmpty()
+                || !ssaWorklist.isEmpty()
+                || !varyingWorklist.isEmpty()) {
+            while (!cfgWorklist.isEmpty()) {
+                int listSize = cfgWorklist.size() - 1;
+                SsaBasicBlock block = cfgWorklist.remove(listSize);
+                simulateBlock(block);
+            }
+            while (!varyingWorklist.isEmpty()) {
+                int listSize = varyingWorklist.size() - 1;
+                SsaInsn insn = varyingWorklist.remove(listSize);
+
+                if (!executableBlocks.get(insn.getBlock().getIndex())) {
+                    continue;
+                }
+
+                if (insn instanceof PhiInsn) {
+                    simulatePhi((PhiInsn)insn);
+                } else {
+                    simulateStmt(insn);
+                }
+            }
+            while (!ssaWorklist.isEmpty()) {
+                int listSize = ssaWorklist.size() - 1;
+                SsaInsn insn = ssaWorklist.remove(listSize);
+
+                if (!executableBlocks.get(insn.getBlock().getIndex())) {
+                    continue;
+                }
+
+                if (insn instanceof PhiInsn) {
+                    simulatePhi((PhiInsn)insn);
+                } else {
+                    simulateStmt(insn);
+                }
+            }
+        }
+
+        replaceConstants();
+    }
+
+    /**
+     * Replaces TypeBearers in source register specs with constant type
+     * bearers if possible. These are then referenced in later optimization
+     * steps.
+     */
+    private void replaceConstants() {
+        for (int reg = 0; reg < regCount; reg++) {
+            if (latticeValues[reg] != CONSTANT) {
+                continue;
+            }
+            if (!(latticeConstants[reg] instanceof TypedConstant)) {
+                // We can't do much with these
+                continue;
+            }
+
+            SsaInsn defn = ssaMeth.getDefinitionForRegister(reg);
+            TypeBearer typeBearer = defn.getResult().getTypeBearer();
+
+            if (typeBearer.isConstant()) {
+                /*
+                 * The definition was a constant already.
+                 * The uses should be as well.
+                 */
+                continue;
+            }
+
+            /*
+             * Update the sources RegisterSpec's of all non-move uses.
+             * These will be used in later steps.
+             */
+            for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+                if (insn.isPhiOrMove()) {
+                    continue;
+                }
+
+                NormalSsaInsn nInsn = (NormalSsaInsn) insn;
+                RegisterSpecList sources = insn.getSources();
+
+                int index = sources.indexOfRegister(reg);
+
+                RegisterSpec spec = sources.get(index);
+                RegisterSpec newSpec
+                        = spec.withType((TypedConstant)latticeConstants[reg]);
+
+                nInsn.changeOneSource(index, newSpec);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SetFactory.java b/dx/src/com/android/dx/ssa/SetFactory.java
new file mode 100644
index 0000000..92e965f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SetFactory.java
@@ -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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.ListIntSet;
+
+
+/**
+ * Makes int sets for various parts of the optimizer.
+ */
+public final class SetFactory {
+
+    /**
+     * BitIntSet/ListIntSet threshold for dominance frontier sets. These
+     * sets are kept per basic block until phi placement and tend to be,
+     * like the CFG itself, very sparse at large sizes.
+     *
+     * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+     */
+    private static final int DOMFRONT_SET_THRESHOLD_SIZE = 3072;
+
+    /**
+     * BitIntSet/ListIntSet threshold for interference graph sets. These
+     * sets are kept per register until register allocation is done.
+     *
+     * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+     */
+    private static final int INTERFERENCE_SET_THRESHOLD_SIZE = 3072;
+
+    /**
+     * BitIntSet/ListIntSet threshold for the live in/out sets kept by
+     * {@link SsaBasicBlock}. These are sets of SSA registers kept per basic
+     * block during register allocation.
+     *
+     * The total size of a bitset for this would be the count of blocks
+     * times the size of registers. The threshold value here is merely
+     * the register count, which is typically on the order of the block
+     * count as well.
+     */
+    private static final int LIVENESS_SET_THRESHOLD_SIZE = 3072;
+
+
+    /**
+     * Make IntSet for the dominance-frontier sets.
+     *
+     * @param szBlocks {@code >=0;} count of basic blocks in method
+     * @return {@code non-null;} appropriate set
+     */
+    /*package*/ static IntSet makeDomFrontSet(int szBlocks) {
+        return szBlocks <= DOMFRONT_SET_THRESHOLD_SIZE
+                ? new BitIntSet(szBlocks)
+                : new ListIntSet();
+    }
+
+    /**
+     * Make IntSet for the interference graph sets. Public because
+     * InterferenceGraph is in another package.
+     *
+     * @param countRegs {@code >=0;} count of SSA registers used in method
+     * @return {@code non-null;} appropriate set
+     */
+    public static IntSet makeInterferenceSet(int countRegs) {
+        return countRegs <= INTERFERENCE_SET_THRESHOLD_SIZE
+                ? new BitIntSet(countRegs)
+                : new ListIntSet();
+    }
+
+    /**
+     * Make IntSet for register live in/out sets.
+     *
+     * @param countRegs {@code >=0;} count of SSA registers used in method
+     * @return {@code non-null;} appropriate set
+     */
+    /*package*/ static IntSet makeLivenessSet(int countRegs) {
+        return countRegs <= LIVENESS_SET_THRESHOLD_SIZE
+                ? new BitIntSet(countRegs)
+                : new ListIntSet();
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaBasicBlock.java b/dx/src/com/android/dx/ssa/SsaBasicBlock.java
new file mode 100644
index 0000000..499f59f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaBasicBlock.java
@@ -0,0 +1,1032 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.IntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An SSA representation of a basic block.
+ */
+public final class SsaBasicBlock {
+    /**
+     * {@code non-null;} comparator for instances of this class that
+     * just compares block labels
+     */
+    public static final Comparator<SsaBasicBlock> LABEL_COMPARATOR =
+        new LabelComparator();
+
+    /** {@code non-null;} insn list associated with this instance */
+    private ArrayList<SsaInsn> insns;
+
+    /** {@code non-null;} predecessor set (by block list index) */
+    private BitSet predecessors;
+
+    /** {@code non-null;} successor set (by block list index) */
+    private BitSet successors;
+
+    /**
+     * {@code non-null;} ordered successor list
+     * (same block may be listed more than once)
+     */
+    private IntList successorList;
+
+    /**
+     * block list index of primary successor, or {@code -1} for no primary
+     * successor
+     */
+    private int primarySuccessor = -1;
+
+    /** label of block in rop form */
+    private int ropLabel;
+
+    /** {@code non-null;} method we belong to */
+    private SsaMethod parent;
+
+    /** our index into parent.getBlock() */
+    private int index;
+
+    /** list of dom children */
+    private final ArrayList<SsaBasicBlock> domChildren;
+
+    /**
+     * the number of moves added to the end of the block during the
+     * phi-removal process. Retained for subsequent move scheduling.
+     */
+    private int movesFromPhisAtEnd = 0;
+
+    /**
+     * the number of moves added to the beginning of the block during the
+     * phi-removal process. Retained for subsequent move scheduling.
+     */
+    private int movesFromPhisAtBeginning = 0;
+
+    /**
+     * contains last computed value of reachability of this block, or -1
+     * if reachability hasn't been calculated yet
+     */
+    private int reachable = -1;
+
+    /**
+     * {@code null-ok;} indexed by reg: the regs that are live-in at
+     * this block
+     */
+    private IntSet liveIn;
+
+    /**
+     * {@code null-ok;} indexed by reg: the regs that are live-out at
+     * this block
+     */
+    private IntSet liveOut;
+
+    /**
+     * Creates a new empty basic block.
+     *
+     * @param basicBlockIndex index this block will have
+     * @param ropLabel original rop-form label
+     * @param parent method of this block
+     */
+    public SsaBasicBlock(final int basicBlockIndex, final int ropLabel,
+            final SsaMethod parent) {
+        this.parent = parent;
+        this.index = basicBlockIndex;
+        this.insns = new ArrayList<SsaInsn>();
+        this.ropLabel = ropLabel;
+
+        this.predecessors = new BitSet(parent.getBlocks().size());
+        this.successors = new BitSet(parent.getBlocks().size());
+        this.successorList = new IntList();
+
+        domChildren = new ArrayList<SsaBasicBlock>();
+    }
+
+    /**
+     * Creates a new SSA basic block from a ROP form basic block.
+     *
+     * @param rmeth original method
+     * @param basicBlockIndex index this block will have
+     * @param parent method of this block predecessor set will be
+     * updated
+     * @return new instance
+     */
+    public static SsaBasicBlock newFromRop(RopMethod rmeth,
+            int basicBlockIndex, final SsaMethod parent) {
+        BasicBlockList ropBlocks = rmeth.getBlocks();
+        BasicBlock bb = ropBlocks.get(basicBlockIndex);
+        SsaBasicBlock result =
+            new SsaBasicBlock(basicBlockIndex, bb.getLabel(), parent);
+        InsnList ropInsns = bb.getInsns();
+
+        result.insns.ensureCapacity(ropInsns.size());
+
+        for (int i = 0, sz = ropInsns.size() ; i < sz ; i++) {
+            result.insns.add(new NormalSsaInsn (ropInsns.get(i), result));
+        }
+
+        result.predecessors = SsaMethod.bitSetFromLabelList(
+                ropBlocks,
+                rmeth.labelToPredecessors(bb.getLabel()));
+
+        result.successors
+                = SsaMethod.bitSetFromLabelList(ropBlocks, bb.getSuccessors());
+
+        result.successorList
+                = SsaMethod.indexListFromLabelList(ropBlocks,
+                    bb.getSuccessors());
+
+        if (result.successorList.size() != 0) {
+            int primarySuccessor = bb.getPrimarySuccessor();
+
+            result.primarySuccessor = (primarySuccessor < 0)
+                    ? -1 : ropBlocks.indexOfLabel(primarySuccessor);
+        }
+
+        return result;
+    }
+
+    /**
+     * Adds a basic block as a dom child for this block. Used when constructing
+     * the dom tree.
+     *
+     * @param child {@code non-null;} new dom child
+     */
+    public void addDomChild(SsaBasicBlock child) {
+        domChildren.add(child);
+    }
+
+    /**
+     * Gets the dom children for this node. Don't modify this list.
+     *
+     * @return {@code non-null;} list of dom children
+     */
+    public ArrayList<SsaBasicBlock> getDomChildren() {
+        return domChildren;
+    }
+
+    /**
+     * Adds a phi insn to the beginning of this block. The result type of
+     * the phi will be set to void, to indicate that it's currently unknown.
+     *
+     * @param reg {@code >=0;} result reg
+     */
+    public void addPhiInsnForReg(int reg) {
+        insns.add(0, new PhiInsn(reg, this));
+    }
+
+    /**
+     * Adds a phi insn to the beginning of this block. This is to be used
+     * when the result type or local-association can be determined at phi
+     * insert time.
+     *
+     * @param resultSpec {@code non-null;} reg
+     */
+    public void addPhiInsnForReg(RegisterSpec resultSpec) {
+        insns.add(0, new PhiInsn(resultSpec, this));
+    }
+
+    /**
+     * Adds an insn to the head of this basic block, just after any phi
+     * insns.
+     *
+     * @param insn {@code non-null;} rop-form insn to add
+     */
+    public void addInsnToHead(Insn insn) {
+        SsaInsn newInsn = SsaInsn.makeFromRop(insn, this);
+        insns.add(getCountPhiInsns(), newInsn);
+        parent.onInsnAdded(newInsn);
+    }
+
+    /**
+     * Replaces the last insn in this block. The provided insn must have
+     * some branchingness.
+     *
+     * @param insn {@code non-null;} rop-form insn to add, which must branch.
+     */
+    public void replaceLastInsn(Insn insn) {
+        if (insn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) {
+            throw new IllegalArgumentException("last insn must branch");
+        }
+
+        SsaInsn oldInsn = insns.get(insns.size() - 1);
+        SsaInsn newInsn = SsaInsn.makeFromRop(insn, this);
+
+        insns.set(insns.size() - 1, newInsn);
+
+        parent.onInsnRemoved(oldInsn);
+        parent.onInsnAdded(newInsn);
+    }
+
+    /**
+     * Visits each phi insn.
+     *
+     * @param v {@code non-null;} the callback
+     */
+    public void forEachPhiInsn(PhiInsn.Visitor v) {
+        int sz = insns.size();
+
+        for (int i = 0; i < sz; i++) {
+            SsaInsn insn = insns.get(i);
+            if (insn instanceof PhiInsn) {
+                v.visitPhiInsn((PhiInsn) insn);
+            } else {
+                /*
+                 * Presently we assume PhiInsn's are in a continuous
+                 * block at the top of the list
+                 */
+                break;
+            }
+        }
+    }
+
+    /**
+     * Deletes all phi insns. Do this after adding appropriate move insns.
+     */
+    public void removeAllPhiInsns() {
+        /*
+         * Presently we assume PhiInsn's are in a continuous
+         * block at the top of the list.
+         */
+
+        insns.subList(0, getCountPhiInsns()).clear();
+    }
+
+    /**
+     * Gets the number of phi insns at the top of this basic block.
+     *
+     * @return count of phi insns
+     */
+    private int getCountPhiInsns() {
+        int countPhiInsns;
+
+        int sz = insns.size();
+        for (countPhiInsns = 0; countPhiInsns < sz; countPhiInsns++) {
+            SsaInsn insn = insns.get(countPhiInsns);
+            if (!(insn instanceof PhiInsn)) {
+                break;
+            }
+        }
+
+        return countPhiInsns;
+    }
+
+    /**
+     * @return {@code non-null;} the (mutable) instruction list for this block,
+     * with phi insns at the beginning
+     */
+    public ArrayList<SsaInsn> getInsns() {
+        return insns;
+    }
+
+    /**
+     * @return {@code non-null;} the (mutable) list of phi insns for this block
+     */
+    public List<SsaInsn> getPhiInsns() {
+        return insns.subList(0, getCountPhiInsns());
+    }
+
+    /**
+     * @return the block index of this block
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * @return the label of this block in rop form
+     */
+    public int getRopLabel() {
+        return ropLabel;
+    }
+
+    /**
+     * @return the label of this block in rop form as a hex string
+     */
+    public String getRopLabelString() {
+        return Hex.u2(ropLabel);
+    }
+
+    /**
+     * @return {@code non-null;} predecessors set, indexed by block index
+     */
+    public BitSet getPredecessors() {
+        return predecessors;
+    }
+
+    /**
+     * @return {@code non-null;} successors set, indexed by block index
+     */
+    public BitSet getSuccessors() {
+        return successors;
+    }
+
+    /**
+     * @return {@code non-null;} ordered successor list, containing block
+     * indicies
+     */
+    public IntList getSuccessorList() {
+        return successorList;
+    }
+
+    /**
+     * @return {@code >= -1;} block index of primary successor or
+     * {@code -1} if no primary successor
+     */
+    public int getPrimarySuccessorIndex() {
+        return primarySuccessor;
+    }
+
+    /**
+     * @return rop label of primary successor
+     */
+    public int getPrimarySuccessorRopLabel() {
+        return parent.blockIndexToRopLabel(primarySuccessor);
+    }
+
+    /**
+     * @return {@code null-ok;} the primary successor block or {@code null}
+     * if there is none
+     */
+    public SsaBasicBlock getPrimarySuccessor() {
+        if (primarySuccessor < 0) {
+            return null;
+        } else {
+            return parent.getBlocks().get(primarySuccessor);
+        }
+    }
+
+    /**
+     * @return successor list of rop labels
+     */
+    public IntList getRopLabelSuccessorList() {
+        IntList result = new IntList(successorList.size());
+
+        int sz = successorList.size();
+
+        for (int i = 0; i < sz; i++) {
+            result.add(parent.blockIndexToRopLabel(successorList.get(i)));
+        }
+        return result;
+    }
+
+    /**
+     * @return {@code non-null;} method that contains this block
+     */
+    public SsaMethod getParent() {
+        return parent;
+    }
+
+    /**
+     * Inserts a new empty GOTO block as a predecessor to this block.
+     * All previous predecessors will be predecessors to the new block.
+     *
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public SsaBasicBlock insertNewPredecessor() {
+        SsaBasicBlock newPred = parent.makeNewGotoBlock();
+
+        // Update the new block.
+        newPred.predecessors = predecessors;
+        newPred.successors.set(index) ;
+        newPred.successorList.add(index);
+        newPred.primarySuccessor = index;
+
+
+        // Update us.
+        predecessors = new BitSet(parent.getBlocks().size());
+        predecessors.set(newPred.index);
+
+        // Update our (soon-to-be) old predecessors.
+        for (int i = newPred.predecessors.nextSetBit(0); i >= 0;
+                i = newPred.predecessors.nextSetBit(i + 1)) {
+
+            SsaBasicBlock predBlock = parent.getBlocks().get(i);
+
+            predBlock.replaceSuccessor(index, newPred.index);
+        }
+
+        return newPred;
+    }
+
+    /**
+     * Constructs and inserts a new empty GOTO block {@code Z} between
+     * this block ({@code A}) and a current successor block
+     * ({@code B}). The new block will replace B as A's successor and
+     * A as B's predecessor. A and B will no longer be directly connected.
+     * If B is listed as a successor multiple times, all references
+     * are replaced.
+     *
+     * @param other current successor (B)
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public SsaBasicBlock insertNewSuccessor(SsaBasicBlock other) {
+        SsaBasicBlock newSucc = parent.makeNewGotoBlock();
+
+        if (!successors.get(other.index)) {
+            throw new RuntimeException("Block " + other.getRopLabelString()
+                    + " not successor of " + getRopLabelString());
+        }
+
+        // Update the new block.
+        newSucc.predecessors.set(this.index);
+        newSucc.successors.set(other.index) ;
+        newSucc.successorList.add(other.index);
+        newSucc.primarySuccessor = other.index;
+
+        // Update us.
+        for (int i = successorList.size() - 1 ;  i >= 0; i--) {
+            if (successorList.get(i) == other.index) {
+                successorList.set(i, newSucc.index);
+            }
+        }
+
+        if (primarySuccessor == other.index) {
+            primarySuccessor = newSucc.index;
+        }
+        successors.clear(other.index);
+        successors.set(newSucc.index);
+
+        // Update "other".
+        other.predecessors.set(newSucc.index);
+        other.predecessors.set(index, successors.get(other.index));
+
+        return newSucc;
+    }
+
+    /**
+     * Replaces an old successor with a new successor. This will throw
+     * RuntimeException if {@code oldIndex} was not a successor.
+     *
+     * @param oldIndex index of old successor block
+     * @param newIndex index of new successor block
+     */
+    public void replaceSuccessor(int oldIndex, int newIndex) {
+        if (oldIndex == newIndex) {
+            return;
+        }
+
+        // Update us.
+        successors.set(newIndex);
+
+        if (primarySuccessor == oldIndex) {
+            primarySuccessor = newIndex;
+        }
+
+        for (int i = successorList.size() - 1 ;  i >= 0; i--) {
+            if (successorList.get(i) == oldIndex) {
+                successorList.set(i, newIndex);
+            }
+        }
+
+        successors.clear(oldIndex);
+
+        // Update new successor.
+        parent.getBlocks().get(newIndex).predecessors.set(index);
+
+        // Update old successor.
+        parent.getBlocks().get(oldIndex).predecessors.clear(index);
+    }
+
+    /**
+     * Removes a successor from this block's successor list.
+     *
+     * @param oldIndex index of successor block to remove
+     */
+    public void removeSuccessor(int oldIndex) {
+        int removeIndex = 0;
+
+        for (int i = successorList.size() - 1; i >= 0; i--) {
+            if (successorList.get(i) == oldIndex) {
+                removeIndex = i;
+            } else {
+                primarySuccessor = successorList.get(i);
+            }
+        }
+
+        successorList.removeIndex(removeIndex);
+        successors.clear(oldIndex);
+        parent.getBlocks().get(oldIndex).predecessors.clear(index);
+    }
+
+    /**
+     * Attaches block to an exit block if necessary. If this block
+     * is not an exit predecessor or is the exit block, this block does
+     * nothing. For use by {@link com.android.dx.ssa.SsaMethod#makeExitBlock}
+     *
+     * @param exitBlock {@code non-null;} exit block
+     */
+    public void exitBlockFixup(SsaBasicBlock exitBlock) {
+        if (this == exitBlock) {
+            return;
+        }
+
+        if (successorList.size() == 0) {
+            /*
+             * This is an exit predecessor.
+             * Set the successor to the exit block
+             */
+            successors.set(exitBlock.index);
+            successorList.add(exitBlock.index);
+            primarySuccessor = exitBlock.index;
+            exitBlock.predecessors.set(this.index);
+        }
+    }
+
+    /**
+     * Adds a move instruction to the end of this basic block, just
+     * before the last instruction. If the result of the final instruction
+     * is the source in question, then the move is placed at the beginning of
+     * the primary successor block. This is for unversioned registers.
+     *
+     * @param result move destination
+     * @param source move source
+     */
+    public void addMoveToEnd(RegisterSpec result, RegisterSpec source) {
+
+        if (result.getReg() == source.getReg()) {
+            // Sometimes we end up with no-op moves. Ignore them here.
+            return;
+        }
+
+        /*
+         * The last Insn has to be a normal SSA insn: a phi can't branch
+         * or return or cause an exception, etc.
+         */
+        NormalSsaInsn lastInsn;
+        lastInsn = (NormalSsaInsn)insns.get(insns.size()-1);
+
+        if (lastInsn.getResult() != null || lastInsn.getSources().size() > 0) {
+            /*
+             * The final insn in this block has a source or result
+             * register, and the moves we may need to place and
+             * schedule may interfere. We need to insert this
+             * instruction at the beginning of the primary successor
+             * block instead. We know this is safe, because when we
+             * edge-split earlier, we ensured that each successor has
+             * only us as a predecessor.
+             */
+
+            for (int i = successors.nextSetBit(0)
+                    ; i >= 0
+                    ; i = successors.nextSetBit(i + 1)) {
+
+                SsaBasicBlock succ;
+
+                succ = parent.getBlocks().get(i);
+                succ.addMoveToBeginning(result, source);
+            }
+        } else {
+            /*
+             * We can safely add a move to the end of the block just
+             * before the last instruction, because the final insn does
+             * not assign to anything.
+             */
+            RegisterSpecList sources = RegisterSpecList.make(source);
+            NormalSsaInsn toAdd = new NormalSsaInsn(
+                    new PlainInsn(Rops.opMove(result.getType()),
+                            SourcePosition.NO_INFO, result, sources), this);
+
+            insns.add(insns.size() - 1, toAdd);
+
+            movesFromPhisAtEnd++;
+        }
+    }
+
+    /**
+     * Adds a move instruction after the phi insn block.
+     *
+     * @param result move destination
+     * @param source move source
+     */
+    public void addMoveToBeginning (RegisterSpec result, RegisterSpec source) {
+        if (result.getReg() == source.getReg()) {
+            // Sometimes we end up with no-op moves. Ignore them here.
+            return;
+        }
+
+        RegisterSpecList sources = RegisterSpecList.make(source);
+        NormalSsaInsn toAdd = new NormalSsaInsn(
+                new PlainInsn(Rops.opMove(result.getType()),
+                        SourcePosition.NO_INFO, result, sources), this);
+
+        insns.add(getCountPhiInsns(), toAdd);
+        movesFromPhisAtBeginning++;
+    }
+
+    /**
+     * Sets the register as used in a bitset, taking into account its
+     * category/width.
+     *
+     * @param regsUsed set, indexed by register number
+     * @param rs register to mark as used
+     */
+    private static void setRegsUsed (BitSet regsUsed, RegisterSpec rs) {
+        regsUsed.set(rs.getReg());
+        if (rs.getCategory() > 1) {
+            regsUsed.set(rs.getReg() + 1);
+        }
+    }
+
+    /**
+     * Checks to see if the register is used in a bitset, taking
+     * into account its category/width.
+     *
+     * @param regsUsed set, indexed by register number
+     * @param rs register to mark as used
+     * @return true if register is fully or partially (for the case of wide
+     * registers) used.
+     */
+    private static boolean checkRegUsed (BitSet regsUsed, RegisterSpec rs) {
+        int reg = rs.getReg();
+        int category = rs.getCategory();
+
+        return regsUsed.get(reg)
+                || (category == 2 ? regsUsed.get(reg + 1) : false);
+    }
+
+    /**
+     * Ensures that all move operations in this block occur such that
+     * reads of any register happen before writes to that register.
+     * NOTE: caller is expected to returnSpareRegisters()!
+     *
+     * TODO: See Briggs, et al "Practical Improvements to the Construction and
+     * Destruction of Static Single Assignment Form" section 5. a) This can
+     * be done in three passes.
+     *
+     * @param toSchedule List of instructions. Must consist only of moves.
+     */
+    private void scheduleUseBeforeAssigned(List<SsaInsn> toSchedule) {
+        BitSet regsUsedAsSources = new BitSet(parent.getRegCount());
+
+        // TODO: Get rid of this.
+        BitSet regsUsedAsResults = new BitSet(parent.getRegCount());
+
+        int sz = toSchedule.size();
+
+        int insertPlace = 0;
+
+        while (insertPlace < sz) {
+            int oldInsertPlace = insertPlace;
+
+            // Record all registers used as sources in this block.
+            for (int i = insertPlace; i < sz; i++) {
+                setRegsUsed(regsUsedAsSources,
+                        toSchedule.get(i).getSources().get(0));
+
+                setRegsUsed(regsUsedAsResults,
+                        toSchedule.get(i).getResult());
+            }
+
+            /*
+             * If there are no circular dependencies, then there exists
+             * n instructions where n > 1 whose result is not used as a source.
+             */
+            for (int i = insertPlace; i <sz; i++) {
+                SsaInsn insn = toSchedule.get(i);
+
+                /*
+                 * Move these n registers to the front, since they overwrite
+                 * nothing.
+                 */
+                if (!checkRegUsed(regsUsedAsSources, insn.getResult())) {
+                    Collections.swap(toSchedule, i, insertPlace++);
+                }
+            }
+
+            /*
+             * If we've made no progress in this iteration, there's a
+             * circular dependency. Split it using the temp reg.
+             */
+            if (oldInsertPlace == insertPlace) {
+
+                SsaInsn insnToSplit = null;
+
+                // Find an insn whose result is used as a source.
+                for (int i = insertPlace; i < sz; i++) {
+                    SsaInsn insn = toSchedule.get(i);
+                    if (checkRegUsed(regsUsedAsSources, insn.getResult())
+                            && checkRegUsed(regsUsedAsResults,
+                                insn.getSources().get(0))) {
+
+                        insnToSplit = insn;
+                        /*
+                         * We're going to split this insn; move it to the
+                         * front.
+                         */
+                        Collections.swap(toSchedule, insertPlace, i);
+                        break;
+                    }
+                }
+
+                // At least one insn will be set above.
+
+                RegisterSpec result = insnToSplit.getResult();
+                RegisterSpec tempSpec = result.withReg(
+                        parent.borrowSpareRegister(result.getCategory()));
+
+                NormalSsaInsn toAdd = new NormalSsaInsn(
+                        new PlainInsn(Rops.opMove(result.getType()),
+                                SourcePosition.NO_INFO,
+                                tempSpec,
+                                insnToSplit.getSources()), this);
+
+                toSchedule.add(insertPlace++, toAdd);
+
+                RegisterSpecList newSources = RegisterSpecList.make(tempSpec);
+
+                NormalSsaInsn toReplace = new NormalSsaInsn(
+                        new PlainInsn(Rops.opMove(result.getType()),
+                                SourcePosition.NO_INFO,
+                                result,
+                                newSources), this);
+
+                toSchedule.set(insertPlace, toReplace);
+
+                // The size changed.
+                sz = toSchedule.size();
+            }
+
+            regsUsedAsSources.clear();
+            regsUsedAsResults.clear();
+        }
+    }
+
+    /**
+     * Adds {@code regV} to the live-out list for this block. This is called
+     * by the liveness analyzer.
+     *
+     * @param regV register that is live-out for this block.
+     */
+    public void addLiveOut (int regV) {
+        if (liveOut == null) {
+            liveOut = SetFactory.makeLivenessSet(parent.getRegCount());
+        }
+
+        liveOut.add(regV);
+    }
+
+    /**
+     * Adds {@code regV} to the live-in list for this block. This is
+     * called by the liveness analyzer.
+     *
+     * @param regV register that is live-in for this block.
+     */
+    public void addLiveIn (int regV) {
+        if (liveIn == null) {
+            liveIn = SetFactory.makeLivenessSet(parent.getRegCount());
+        }
+
+        liveIn.add(regV);
+    }
+
+    /**
+     * Returns the set of live-in registers. Valid after register
+     * interference graph has been generated, otherwise empty.
+     *
+     * @return {@code non-null;} live-in register set.
+     */
+    public IntSet getLiveInRegs() {
+        if (liveIn == null) {
+            liveIn = SetFactory.makeLivenessSet(parent.getRegCount());
+        }
+        return liveIn;
+    }
+
+    /**
+     * Returns the set of live-out registers. Valid after register
+     * interference graph has been generated, otherwise empty.
+     *
+     * @return {@code non-null;} live-out register set
+     */
+    public IntSet getLiveOutRegs() {
+        if (liveOut == null) {
+            liveOut = SetFactory.makeLivenessSet(parent.getRegCount());
+        }
+        return liveOut;
+    }
+
+    /**
+     * @return true if this is the one-and-only exit block for this method
+     */
+    public boolean isExitBlock() {
+        return index == parent.getExitBlockIndex();
+    }
+
+    /**
+     * Returns true if this block was last calculated to be reachable.
+     * Recalculates reachability if value has never been computed.
+     *
+     * @return {@code true} if reachable
+     */
+    public boolean isReachable() {
+        if (reachable == -1) {
+            parent.computeReachability();
+        }
+        return (reachable == 1);
+    }
+
+    /**
+     * Sets reachability of block to specified value
+     *
+     * @param reach new value of reachability for block
+     */
+    public void setReachable(int reach) {
+        reachable = reach;
+    }
+
+    /**
+     * Sorts move instructions added via {@code addMoveToEnd} during
+     * phi removal so that results don't overwrite sources that are used.
+     * For use after all phis have been removed and all calls to
+     * addMoveToEnd() have been made.<p>
+     *
+     * This is necessary because copy-propogation may have left us in a state
+     * where the same basic block has the same register as a phi operand
+     * and a result. In this case, the register in the phi operand always
+     * refers value before any other phis have executed.
+     */
+    public void scheduleMovesFromPhis() {
+        if (movesFromPhisAtBeginning > 1) {
+            List<SsaInsn> toSchedule;
+
+            toSchedule = insns.subList(0, movesFromPhisAtBeginning);
+
+            scheduleUseBeforeAssigned(toSchedule);
+
+            SsaInsn firstNonPhiMoveInsn = insns.get(movesFromPhisAtBeginning);
+
+            /*
+             * TODO: It's actually possible that this case never happens,
+             * because a move-exception block, having only one predecessor
+             * in SSA form, perhaps is never on a dominance frontier.
+             */
+            if (firstNonPhiMoveInsn.isMoveException()) {
+                if (true) {
+                    /*
+                     * We've yet to observe this case, and if it can
+                     * occur the code written to handle it probably
+                     * does not work.
+                     */
+                    throw new RuntimeException(
+                            "Unexpected: moves from "
+                                    +"phis before move-exception");
+                } else {
+                    /*
+                     * A move-exception insn must be placed first in this block
+                     * We need to move it there, and deal with possible
+                     * interference.
+                     */
+                    boolean moveExceptionInterferes = false;
+
+                    int moveExceptionResult
+                            = firstNonPhiMoveInsn.getResult().getReg();
+
+                    /*
+                     * Does the move-exception result reg interfere with the
+                     * phi moves?
+                     */
+                    for (SsaInsn insn : toSchedule) {
+                        if (insn.isResultReg(moveExceptionResult)
+                                || insn.isRegASource(moveExceptionResult)) {
+                            moveExceptionInterferes = true;
+                            break;
+                        }
+                    }
+
+                    if (!moveExceptionInterferes) {
+                        // This is the easy case.
+                        insns.remove(movesFromPhisAtBeginning);
+                        insns.add(0, firstNonPhiMoveInsn);
+                    } else {
+                        /*
+                         * We need to move the result to a spare reg
+                         * and move it back.
+                         */
+                        RegisterSpec originalResultSpec
+                            = firstNonPhiMoveInsn.getResult();
+                        int spareRegister = parent.borrowSpareRegister(
+                                originalResultSpec.getCategory());
+
+                        // We now move it to a spare register.
+                        firstNonPhiMoveInsn.changeResultReg(spareRegister);
+                        RegisterSpec tempSpec =
+                            firstNonPhiMoveInsn.getResult();
+
+                        insns.add(0, firstNonPhiMoveInsn);
+
+                        // And here we move it back.
+
+                        NormalSsaInsn toAdd = new NormalSsaInsn(
+                                new PlainInsn(
+                                        Rops.opMove(tempSpec.getType()),
+                                        SourcePosition.NO_INFO,
+                                        originalResultSpec,
+                                        RegisterSpecList.make(tempSpec)),
+                                this);
+
+
+                        /*
+                         * Place it immediately after the phi-moves,
+                         * overwriting the move-exception that was there.
+                         */
+                        insns.set(movesFromPhisAtBeginning + 1, toAdd);
+                    }
+                }
+            }
+        }
+
+        if (movesFromPhisAtEnd > 1) {
+            scheduleUseBeforeAssigned(
+                    insns.subList(insns.size() - movesFromPhisAtEnd - 1,
+                                insns.size() - 1));
+        }
+
+        // Return registers borrowed here and in scheduleUseBeforeAssigned().
+        parent.returnSpareRegisters();
+
+    }
+
+    /**
+     * Visits all insns in this block.
+     *
+     * @param visitor {@code non-null;} callback interface
+     */
+    public void forEachInsn(SsaInsn.Visitor visitor) {
+        // This gets called a LOT, and not using an iterator
+        // saves a lot of allocations and reduces memory usage
+        int len = insns.size();
+        for (int i = 0; i < len; i++) {
+            insns.get(i).accept(visitor);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "{" + index + ":" + Hex.u2(ropLabel) + '}';
+    }
+
+    /**
+     * Visitor interface for basic blocks.
+     */
+    public interface Visitor {
+        /**
+         * Indicates a block has been visited by an iterator method.
+         *
+         * @param v {@code non-null;} block visited
+         * @param parent {@code null-ok;} parent node if applicable
+         */
+        void visitBlock (SsaBasicBlock v, SsaBasicBlock parent);
+    }
+
+    /**
+     * Label comparator.
+     */
+    public static final class LabelComparator
+            implements Comparator<SsaBasicBlock> {
+        /** {@inheritDoc} */
+        public int compare(SsaBasicBlock b1, SsaBasicBlock b2) {
+            int label1 = b1.ropLabel;
+            int label2 = b2.ropLabel;
+
+            if (label1 < label2) {
+                return -1;
+            } else if (label1 > label2) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaConverter.java b/dx/src/com/android/dx/ssa/SsaConverter.java
new file mode 100644
index 0000000..5cd8b6f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaConverter.java
@@ -0,0 +1,391 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.util.IntIterator;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * Converts ROP methods to SSA Methods
+ */
+public class SsaConverter {
+    public static final boolean DEBUG = false;
+
+    /**
+     * Returns an SSA representation, edge-split and with phi
+     * functions placed.
+     *
+     * @param rmeth input
+     * @param paramWidth the total width, in register-units, of the method's
+     * parameters
+     * @param isStatic {@code true} if this method has no {@code this}
+     * pointer argument
+     * @return output in SSA form
+     */
+    public static SsaMethod convertToSsaMethod(RopMethod rmeth,
+            int paramWidth, boolean isStatic) {
+        SsaMethod result
+            = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+        edgeSplit(result);
+
+        LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
+
+        placePhiFunctions(result, localInfo, 0);
+        new SsaRenamer(result).run();
+
+        /*
+         * The exit block, added here, is not considered for edge splitting
+         * or phi placement since no actual control flows to it.
+         */
+        result.makeExitBlock();
+
+        return result;
+    }
+
+    /**
+     * Updates an SSA representation, placing phi functions and renaming all
+     * registers above a certain threshold number.
+     *
+     * @param ssaMeth input
+     * @param threshold registers below this number are unchanged
+     */
+    public static void updateSsaMethod(SsaMethod ssaMeth, int threshold) {
+        LocalVariableInfo localInfo = LocalVariableExtractor.extract(ssaMeth);
+        placePhiFunctions(ssaMeth, localInfo, threshold);
+        new SsaRenamer(ssaMeth, threshold).run();
+    }
+
+    /**
+     * Returns an SSA represention with only the edge-splitter run.
+     *
+     * @param rmeth method to process
+     * @param paramWidth width of all arguments in the method
+     * @param isStatic {@code true} if this method has no {@code this}
+     * pointer argument
+     * @return an SSA represention with only the edge-splitter run
+     */
+    public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth,
+            boolean isStatic) {
+        SsaMethod result;
+
+        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+        edgeSplit(result);
+        return result;
+    }
+
+    /**
+     * Returns an SSA represention with only the steps through the
+     * phi placement run.
+     *
+     * @param rmeth method to process
+     * @param paramWidth width of all arguments in the method
+     * @param isStatic {@code true} if this method has no {@code this}
+     * pointer argument
+     * @return an SSA represention with only the edge-splitter run
+     */
+    public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth,
+            boolean isStatic) {
+        SsaMethod result;
+
+        result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+        edgeSplit(result);
+
+        LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
+
+        placePhiFunctions(result, localInfo, 0);
+        return result;
+    }
+
+    /**
+     * See Appel section 19.1:
+     *
+     * Converts CFG into "edge-split" form, such that each node either a
+     * unique successor or unique predecessor.<p>
+     *
+     * In addition, the SSA form we use enforces a further constraint,
+     * requiring each block with a final instruction that returns a
+     * value to have a primary successor that has no other
+     * predecessor. This ensures move statements can always be
+     * inserted correctly when phi statements are removed.
+     *
+     * @param result method to process
+     */
+    private static void edgeSplit(SsaMethod result) {
+        edgeSplitPredecessors(result);
+        edgeSplitMoveExceptionsAndResults(result);
+        edgeSplitSuccessors(result);
+    }
+
+    /**
+     * Inserts Z nodes as new predecessors for every node that has multiple
+     * successors and multiple predecessors.
+     *
+     * @param result {@code non-null;} method to process
+     */
+    private static void edgeSplitPredecessors(SsaMethod result) {
+        ArrayList<SsaBasicBlock> blocks = result.getBlocks();
+
+        /*
+         * New blocks are added to the end of the block list during
+         * this iteration.
+         */
+        for (int i = blocks.size() - 1; i >= 0; i-- ) {
+            SsaBasicBlock block = blocks.get(i);
+            if (nodeNeedsUniquePredecessor(block)) {
+                block.insertNewPredecessor();
+            }
+        }
+    }
+
+    /**
+     * @param block {@code non-null;} block in question
+     * @return {@code true} if this node needs to have a unique
+     * predecessor created for it
+     */
+    private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) {
+        /*
+         * Any block with that has both multiple successors and multiple
+         * predecessors needs a new predecessor node.
+         */
+
+        int countPredecessors = block.getPredecessors().cardinality();
+        int countSuccessors = block.getSuccessors().cardinality();
+
+        return  (countPredecessors > 1 && countSuccessors > 1);
+    }
+
+    /**
+     * In ROP form, move-exception must occur as the first insn in a block
+     * immediately succeeding the insn that could thrown an exception.
+     * We may need room to insert move insns later, so make sure to split
+     * any block that starts with a move-exception such that there is a
+     * unique move-exception block for each predecessor.
+     *
+     * @param ssaMeth method to process
+     */
+    private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) {
+        ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+        /*
+         * New blocks are added to the end of the block list during
+         * this iteration.
+         */
+        for (int i = blocks.size() - 1; i >= 0; i-- ) {
+            SsaBasicBlock block = blocks.get(i);
+
+            /*
+             * Any block that starts with a move-exception and has more than
+             * one predecessor...
+             */
+            if (!block.isExitBlock()
+                    && block.getPredecessors().cardinality() > 1
+                    && block.getInsns().get(0).isMoveException()) {
+
+                // block.getPredecessors() is changed in the loop below.
+                BitSet preds = (BitSet)block.getPredecessors().clone();
+                for (int j = preds.nextSetBit(0); j >= 0;
+                     j = preds.nextSetBit(j + 1)) {
+                    SsaBasicBlock predecessor = blocks.get(j);
+                    SsaBasicBlock zNode
+                        = predecessor.insertNewSuccessor(block);
+
+                    /*
+                     * Make sure to place the move-exception as the
+                     * first insn.
+                     */
+                    zNode.getInsns().add(0, block.getInsns().get(0).clone());
+                }
+
+                // Remove the move-exception from the original block.
+                block.getInsns().remove(0);
+            }
+        }
+    }
+
+    /**
+     * Inserts Z nodes for every node that needs a new
+     * successor.
+     *
+     * @param result {@code non-null;} method to process
+     */
+    private static void edgeSplitSuccessors(SsaMethod result) {
+        ArrayList<SsaBasicBlock> blocks = result.getBlocks();
+
+        /*
+         * New blocks are added to the end of the block list during
+         * this iteration.
+         */
+        for (int i = blocks.size() - 1; i >= 0; i-- ) {
+            SsaBasicBlock block = blocks.get(i);
+
+            // Successors list is modified in loop below.
+            BitSet successors = (BitSet)block.getSuccessors().clone();
+            for (int j = successors.nextSetBit(0);
+                 j >= 0; j = successors.nextSetBit(j+1)) {
+
+                SsaBasicBlock succ = blocks.get(j);
+
+                if (needsNewSuccessor(block, succ)) {
+                    block.insertNewSuccessor(succ);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns {@code true} if block and successor need a Z-node
+     * between them. Presently, this is {@code true} if the final
+     * instruction has any sources or results and the current
+     * successor block has more than one predecessor.
+     *
+     * @param block predecessor node
+     * @param succ successor node
+     * @return {@code true} if a Z node is needed
+     */
+    private static boolean needsNewSuccessor(SsaBasicBlock block,
+            SsaBasicBlock succ) {
+        ArrayList<SsaInsn> insns = block.getInsns();
+        SsaInsn lastInsn = insns.get(insns.size() - 1);
+
+        return ((lastInsn.getResult() != null)
+                    || (lastInsn.getSources().size() > 0))
+                && succ.getPredecessors().cardinality() > 1;
+    }
+
+    /**
+     * See Appel algorithm 19.6:
+     *
+     * Place Phi functions in appropriate locations.
+     *
+     * @param ssaMeth {@code non-null;} method to process.
+     * Modifications are made in-place.
+     * @param localInfo {@code non-null;} local variable info, used
+     * when placing phis
+     * @param threshold registers below this number are ignored
+     */
+    private static void placePhiFunctions (SsaMethod ssaMeth,
+            LocalVariableInfo localInfo, int threshold) {
+        ArrayList<SsaBasicBlock> ssaBlocks;
+        int regCount;
+        int blockCount;
+
+        ssaBlocks = ssaMeth.getBlocks();
+        blockCount = ssaBlocks.size();
+        regCount = ssaMeth.getRegCount() - threshold;
+
+        DomFront df = new DomFront(ssaMeth);
+        DomFront.DomInfo[] domInfos = df.run();
+
+        // Bit set of registers vs block index "definition sites"
+        BitSet[] defsites = new BitSet[regCount];
+
+        // Bit set of registers vs block index "phi placement sites"
+        BitSet[] phisites = new BitSet[regCount];
+
+        for (int i = 0; i < regCount; i++) {
+            defsites[i] = new BitSet(blockCount);
+            phisites[i] = new BitSet(blockCount);
+        }
+
+        /*
+         * For each register, build a set of all basic blocks where
+         * containing an assignment to that register.
+         */
+        for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) {
+            SsaBasicBlock b = ssaBlocks.get(bi);
+
+            for (SsaInsn insn : b.getInsns()) {
+                RegisterSpec rs = insn.getResult();
+
+                if (rs != null && rs.getReg() - threshold >= 0) {
+                    defsites[rs.getReg() - threshold].set(bi);
+                }
+            }
+        }
+
+        if (DEBUG) {
+            System.out.println("defsites");
+
+            for (int i = 0; i < regCount; i++) {
+                StringBuilder sb = new StringBuilder();
+                sb.append('v').append(i).append(": ");
+                sb.append(defsites[i].toString());
+                System.out.println(sb);
+            }
+        }
+
+        BitSet worklist;
+
+        /*
+         * For each register, compute all locations for phi placement
+         * based on dominance-frontier algorithm.
+         */
+        for (int reg = 0, s = regCount; reg < s; reg++) {
+            int workBlockIndex;
+
+            /* Worklist set starts out with each node where reg is assigned. */
+
+            worklist = (BitSet) (defsites[reg].clone());
+
+            while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
+                worklist.clear(workBlockIndex);
+                IntIterator dfIterator
+                    = domInfos[workBlockIndex].dominanceFrontiers.iterator();
+
+                while (dfIterator.hasNext()) {
+                    int dfBlockIndex = dfIterator.next();
+
+                    if (!phisites[reg].get(dfBlockIndex)) {
+                        phisites[reg].set(dfBlockIndex);
+
+                        int tReg = reg + threshold;
+                        RegisterSpec rs
+                            = localInfo.getStarts(dfBlockIndex).get(tReg);
+
+                        if (rs == null) {
+                            ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg);
+                        } else {
+                            ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs);
+                        }
+
+                        if (!defsites[reg].get(dfBlockIndex)) {
+                            worklist.set(dfBlockIndex);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (DEBUG) {
+            System.out.println("phisites");
+
+            for (int i = 0; i < regCount; i++) {
+                StringBuilder sb = new StringBuilder();
+                sb.append('v').append(i).append(": ");
+                sb.append(phisites[i].toString());
+                System.out.println(sb);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaInsn.java b/dx/src/com/android/dx/ssa/SsaInsn.java
new file mode 100644
index 0000000..ca7a1a2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaInsn.java
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.util.ToHuman;
+
+/**
+ * An instruction in SSA form
+ */
+public abstract class SsaInsn implements ToHuman, Cloneable {
+    /** {@code non-null;} the block that contains this instance */
+    private final SsaBasicBlock block;
+
+    /** {@code null-ok;} result register */
+    private RegisterSpec result;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param result {@code null-ok;} initial result register. May be changed.
+     * @param block {@code non-null;} block containing this insn. Can
+     * never change.
+     */
+    protected SsaInsn(RegisterSpec result, SsaBasicBlock block) {
+        if (block == null) {
+            throw new NullPointerException("block == null");
+        }
+
+        this.block = block;
+        this.result = result;
+    }
+
+    /**
+     * Makes a new SSA insn form a rop insn.
+     *
+     * @param insn {@code non-null;} rop insn
+     * @param block {@code non-null;} owning block
+     * @return {@code non-null;} an appropriately constructed instance
+     */
+    public static SsaInsn makeFromRop(Insn insn, SsaBasicBlock block) {
+        return new NormalSsaInsn(insn, block);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public SsaInsn clone() {
+        try {
+            return (SsaInsn)super.clone();
+        } catch (CloneNotSupportedException ex) {
+            throw new RuntimeException ("unexpected", ex);
+        }
+    }
+
+    /**
+     * Like {@link com.android.dx.rop.code.Insn getResult()}.
+     *
+     * @return result register
+     */
+    public RegisterSpec getResult() {
+        return result;
+    }
+
+    /**
+     * Set the result register.
+     *
+     * @param result {@code non-null;} the new result register
+     */
+    protected void setResult(RegisterSpec result) {
+        if (result == null) {
+            throw new NullPointerException("result == null");
+        }
+
+        this.result = result;
+    }
+
+    /**
+     * Like {@link com.android.dx.rop.code.Insn getSources()}.
+     *
+     * @return {@code non-null;} sources list
+     */
+    abstract public RegisterSpecList getSources();
+
+    /**
+     * Gets the block to which this insn instance belongs.
+     *
+     * @return owning block
+     */
+    public SsaBasicBlock getBlock() {
+        return block;
+    }
+
+    /**
+     * Returns whether or not the specified reg is the result reg.
+     *
+     * @param reg register to test
+     * @return true if there is a result and it is stored in the specified
+     * register
+     */
+    public boolean isResultReg(int reg) {
+        return result != null && result.getReg() == reg;
+    }
+
+
+    /**
+     * Changes the result register if this insn has a result. This is used
+     * during renaming.
+     *
+     * @param reg new result register
+     */
+    public void changeResultReg(int reg) {
+        if (result != null) {
+            result = result.withReg(reg);
+        }
+    }
+
+    /**
+     * Sets the local association for the result of this insn. This is
+     * sometimes updated during the SsaRenamer process.
+     *
+     * @param local {@code null-ok;} new debug/local variable info
+     */
+    public final void setResultLocal(LocalItem local) {
+        LocalItem oldItem = result.getLocalItem();
+
+        if (local != oldItem && (local == null
+                || !local.equals(result.getLocalItem()))) {
+            result = RegisterSpec.makeLocalOptional(
+                    result.getReg(), result.getType(), local);
+        }
+    }
+
+    /**
+     * Map registers after register allocation.
+     *
+     * @param mapper {@code non-null;} mapping from old to new registers
+     */
+    public final void mapRegisters(RegisterMapper mapper) {
+        RegisterSpec oldResult = result;
+
+        result = mapper.map(result);
+        block.getParent().updateOneDefinition(this, oldResult);
+        mapSourceRegisters(mapper);
+    }
+
+    /**
+     * Maps only source registers.
+     *
+     * @param mapper new mapping
+     */
+    abstract public void mapSourceRegisters(RegisterMapper mapper);
+
+    /**
+     * Returns the Rop opcode for this insn, or null if this is a phi insn.
+     *
+     * TODO: Move this up into NormalSsaInsn.
+     *
+     * @return {@code null-ok;} Rop opcode if there is one.
+     */
+    abstract public Rop getOpcode();
+
+    /**
+     * Returns the original Rop insn for this insn, or null if this is
+     * a phi insn.
+     *
+     * TODO: Move this up into NormalSsaInsn.
+     *
+     * @return {@code null-ok;} Rop insn if there is one.
+     */
+    abstract public Insn getOriginalRopInsn();
+
+    /**
+     * Gets the spec of a local variable assignment that occurs at this
+     * instruction, or null if no local variable assignment occurs. This
+     * may be the result register, or for {@code mark-local} insns
+     * it may be the source.
+     *
+     * @see com.android.dx.rop.code.Insn#getLocalAssignment()
+     *
+     * @return {@code null-ok;} a local-associated register spec or null
+     */
+    public RegisterSpec getLocalAssignment() {
+        if (result != null && result.getLocalItem() != null) {
+            return result;
+        }
+
+        return null;
+    }
+
+    /**
+     * Indicates whether the specified register is amongst the registers
+     * used as sources for this instruction.
+     *
+     * @param reg the register in question
+     * @return true if the reg is a source
+     */
+    public boolean isRegASource(int reg) {
+        return null != getSources().specForRegister(reg);
+    }
+
+    /**
+     * Transform back to ROP form.
+     *
+     * TODO: Move this up into NormalSsaInsn.
+     *
+     * @return {@code non-null;} a ROP representation of this instruction, with
+     * updated registers.
+     */
+    public abstract Insn toRopInsn();
+
+    /**
+     * @return true if this is a PhiInsn or a normal move insn
+     */
+    public abstract boolean isPhiOrMove();
+
+    /**
+     * Returns true if this insn is considered to have a side effect beyond
+     * that of assigning to the result reg.
+     *
+     * @return true if this insn is considered to have a side effect beyond
+     * that of assigning to the result reg.
+     */
+    public abstract boolean hasSideEffect();
+
+    /**
+     * @return true if this is a move (but not a move-operand or
+     * move-exception) instruction
+     */
+    public boolean isNormalMoveInsn() {
+        return false;
+    }
+
+    /**
+     * @return true if this is a move-exception instruction.
+     * These instructions must immediately follow a preceeding invoke*
+     */
+    public boolean isMoveException() {
+        return false;
+    }
+
+    /**
+     * @return true if this instruction can throw.
+     */
+    abstract public boolean canThrow();
+
+    /**
+     * Accepts a visitor.
+     *
+     * @param v {@code non-null} the visitor
+     */
+    public abstract void accept(Visitor v);
+
+    /**
+     * Visitor interface for this class.
+     */
+    public static interface Visitor {
+        /**
+         * Any non-phi move instruction
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitMoveInsn(NormalSsaInsn insn);
+
+        /**
+         * Any phi insn
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitPhiInsn(PhiInsn insn);
+
+        /**
+         * Any insn that isn't a move or a phi (which is also a move).
+         * @param insn {@code non-null;} the instruction to visit
+         */
+        public void visitNonMoveInsn(NormalSsaInsn insn);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaMethod.java b/dx/src/com/android/dx/ssa/SsaMethod.java
new file mode 100644
index 0000000..4c2bd85
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaMethod.java
@@ -0,0 +1,873 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * A method in SSA form.
+ */
+public final class SsaMethod {
+    /** basic blocks, indexed by block index */
+    private ArrayList<SsaBasicBlock> blocks;
+
+    /** Index of first executed block in method */
+    private int entryBlockIndex;
+
+    /**
+     * Index of exit block, which exists only in SSA form,
+     * or or {@code -1} if there is none
+     */
+    private int exitBlockIndex;
+
+    /** total number of registers required */
+    private int registerCount;
+
+    /** first register number to use for any temporary "spares" */
+    private int spareRegisterBase;
+
+    /** current count of spare registers used */
+    private int borrowedSpareRegisters;
+
+    /** really one greater than the max label */
+    private int maxLabel;
+
+    /** the total width, in register-units, of the method's parameters */
+    private final int paramWidth;
+
+    /** true if this method has no {@code this} pointer argument */
+    private final boolean isStatic;
+
+    /**
+     * indexed by register: the insn where said register is defined or null
+     * if undefined. null until (lazily) created.
+     */
+    private SsaInsn[] definitionList;
+
+    /** indexed by register: the list of all insns that use a register */
+    private ArrayList<SsaInsn>[] useList;
+
+    /** A version of useList with each List unmodifiable */
+    private List<SsaInsn>[] unmodifiableUseList;
+
+    /**
+     * "back-convert mode". Set during back-conversion when registers
+     * are about to be mapped into a non-SSA namespace. When true,
+     * use and def lists are unavailable.
+     *
+     * TODO: Remove this mode, and place the functionality elsewhere
+     */
+    private boolean backMode;
+
+    /**
+     * @param ropMethod rop-form method to convert from
+     * @param paramWidth the total width, in register-units, of the
+     * method's parameters
+     * @param isStatic {@code true} if this method has no {@code this}
+     * pointer argument
+     */
+    public static SsaMethod newFromRopMethod(RopMethod ropMethod,
+            int paramWidth, boolean isStatic) {
+        SsaMethod result = new SsaMethod(ropMethod, paramWidth, isStatic);
+
+        result.convertRopToSsaBlocks(ropMethod);
+
+        return result;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param ropMethod {@code non-null;} the original rop-form method that
+     * this instance is based on
+     * @param paramWidth the total width, in register-units, of the
+     * method's parameters
+     * @param isStatic {@code true} if this method has no {@code this}
+     * pointer argument
+     */
+    private SsaMethod(RopMethod ropMethod, int paramWidth, boolean isStatic) {
+        this.paramWidth = paramWidth;
+        this.isStatic = isStatic;
+        this.backMode = false;
+        this.maxLabel = ropMethod.getBlocks().getMaxLabel();
+        this.registerCount = ropMethod.getBlocks().getRegCount();
+        this.spareRegisterBase = registerCount;
+    }
+
+    /**
+     * Builds a BitSet of block indices from a basic block list and a list
+     * of labels taken from Rop form.
+     *
+     * @param blocks Rop blocks
+     * @param labelList list of rop block labels
+     * @return BitSet of block indices
+     */
+    static BitSet bitSetFromLabelList(BasicBlockList blocks,
+            IntList labelList) {
+        BitSet result = new BitSet(blocks.size());
+
+        for (int i = 0, sz = labelList.size(); i < sz; i++) {
+            result.set(blocks.indexOfLabel(labelList.get(i)));
+        }
+
+        return result;
+    }
+
+    /**
+     * Builds an IntList of block indices from a basic block list and a list
+     * of labels taken from Rop form.
+     *
+     * @param ropBlocks Rop blocks
+     * @param labelList list of rop block labels
+     * @return IntList of block indices
+     */
+    public static IntList indexListFromLabelList(BasicBlockList ropBlocks,
+            IntList labelList) {
+
+        IntList result = new IntList(labelList.size());
+
+        for (int i = 0, sz = labelList.size(); i < sz; i++) {
+            result.add(ropBlocks.indexOfLabel(labelList.get(i)));
+        }
+
+        return result;
+    }
+
+    private void convertRopToSsaBlocks(RopMethod rmeth) {
+        BasicBlockList ropBlocks = rmeth.getBlocks();
+        int sz = ropBlocks.size();
+
+        blocks = new ArrayList<SsaBasicBlock>(sz + 2);
+
+        for (int i = 0; i < sz; i++) {
+            SsaBasicBlock sbb = SsaBasicBlock.newFromRop(rmeth, i, this);
+            blocks.add(sbb);
+        }
+
+        // Add an no-op entry block.
+        int origEntryBlockIndex = rmeth.getBlocks()
+                .indexOfLabel(rmeth.getFirstLabel());
+
+        SsaBasicBlock entryBlock
+                = blocks.get(origEntryBlockIndex).insertNewPredecessor();
+
+        entryBlockIndex = entryBlock.getIndex();
+        exitBlockIndex = -1; // This gets made later.
+    }
+
+    /**
+     * Creates an exit block and attaches it to the CFG if this method
+     * exits. Methods that never exit will not have an exit block. This
+     * is called after edge-splitting and phi insertion, since the edges
+     * going into the exit block should not be considered in those steps.
+     */
+    /*package*/ void makeExitBlock() {
+        if (exitBlockIndex >= 0) {
+            throw new RuntimeException("must be called at most once");
+        }
+
+        exitBlockIndex = blocks.size();
+        SsaBasicBlock exitBlock
+                = new SsaBasicBlock(exitBlockIndex, maxLabel++, this);
+
+        blocks.add(exitBlock);
+
+        for (SsaBasicBlock block : blocks) {
+            block.exitBlockFixup(exitBlock);
+        }
+
+        if (exitBlock.getPredecessors().cardinality() == 0) {
+            // In cases where there is no exit...
+            blocks.remove(exitBlockIndex);
+            exitBlockIndex = -1;
+            maxLabel--;
+        }
+    }
+
+    /**
+     * Gets a new {@code GOTO} insn.
+     *
+     * @param block block to which this GOTO will be added
+     * (not it's destination!)
+     * @return an appropriately-constructed instance.
+     */
+    private static SsaInsn getGoto(SsaBasicBlock block) {
+        return new NormalSsaInsn (
+                new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO,
+                    null, RegisterSpecList.EMPTY), block);
+    }
+
+    /**
+     * Makes a new basic block for this method, which is empty besides
+     * a single {@code GOTO}. Successors and predecessors are not yet
+     * set.
+     *
+     * @return new block
+     */
+    public SsaBasicBlock makeNewGotoBlock() {
+        int newIndex = blocks.size();
+        SsaBasicBlock newBlock = new SsaBasicBlock(newIndex, maxLabel++, this);
+
+        newBlock.getInsns().add(getGoto(newBlock));
+        blocks.add(newBlock);
+
+        return newBlock;
+    }
+
+    /**
+     * @return block index of first execution block
+     */
+    public int getEntryBlockIndex() {
+        return entryBlockIndex;
+    }
+
+    /**
+     * @return first execution block
+     */
+    public SsaBasicBlock getEntryBlock() {
+        return blocks.get(entryBlockIndex);
+    }
+
+    /**
+     * @return block index of exit block or {@code -1} if there is none
+     */
+    public int getExitBlockIndex() {
+        return exitBlockIndex;
+    }
+
+    /**
+     * @return {@code null-ok;} block of exit block or {@code null} if
+     * there is none
+     */
+    public SsaBasicBlock getExitBlock() {
+        return exitBlockIndex < 0 ? null : blocks.get(exitBlockIndex);
+    }
+
+    /**
+     * @param bi block index or {@code -1} for none
+     * @return rop label or {code -1} if {@code bi} was {@code -1}
+     */
+    public int blockIndexToRopLabel(int bi) {
+        if (bi < 0) {
+            return -1;
+        }
+        return blocks.get(bi).getRopLabel();
+    }
+
+    /**
+     * @return count of registers used in this method
+     */
+    public int getRegCount() {
+        return registerCount;
+    }
+
+    /**
+     * @return the total width, in register units, of the method's
+     * parameters
+     */
+    public int getParamWidth() {
+        return paramWidth;
+    }
+
+    /**
+     * Returns {@code true} if this is a static method.
+     *
+     * @return {@code true} if this is a static method
+     */
+    public boolean isStatic() {
+        return isStatic;
+    }
+
+    /**
+     * Borrows a register to use as a temp. Used in the phi removal process.
+     * Call returnSpareRegisters() when done.
+     *
+     * @param category width (1 or 2) of the register
+     * @return register number to use
+     */
+    public int borrowSpareRegister(int category) {
+        int result = spareRegisterBase + borrowedSpareRegisters;
+
+        borrowedSpareRegisters += category;
+        registerCount = Math.max(registerCount, result + category);
+
+        return result;
+    }
+
+    /**
+     * Returns all borrowed registers.
+     */
+    public void returnSpareRegisters() {
+        borrowedSpareRegisters = 0;
+    }
+
+    /**
+     * @return {@code non-null;} basic block list. Do not modify.
+     */
+    public ArrayList<SsaBasicBlock> getBlocks() {
+        return blocks;
+    }
+
+    /**
+     * Returns the count of reachable blocks in this method: blocks that have
+     * predecessors (or are the start block)
+     *
+     * @return {@code >= 0;} number of reachable basic blocks
+     */
+    public int getCountReachableBlocks() {
+        int ret = 0;
+
+        for (SsaBasicBlock b : blocks) {
+            // Blocks that have been disconnected don't count.
+            if (b.isReachable()) {
+                ret++;
+            }
+        }
+
+        return ret;
+    }
+
+    /**
+     * Computes reachability for all blocks in the method. First clears old
+     * values from all blocks, then starts with the entry block and walks down
+     * the control flow graph, marking all blocks it finds as reachable.
+     */
+    public void computeReachability() {
+        for (SsaBasicBlock block : blocks) {
+            block.setReachable(0);
+        }
+
+        ArrayList<SsaBasicBlock> blockList = new ArrayList<SsaBasicBlock>();
+        blockList.add(this.getEntryBlock());
+
+        while (!blockList.isEmpty()) {
+            SsaBasicBlock block = blockList.remove(0);
+            if (block.isReachable()) continue;
+
+            block.setReachable(1);
+            BitSet succs = block.getSuccessors();
+            for (int i = succs.nextSetBit(0); i >= 0;
+                     i = succs.nextSetBit(i + 1)) {
+                blockList.add(blocks.get(i));
+            }
+        }
+    }
+
+    /**
+     * Remaps unversioned registers.
+     *
+     * @param mapper maps old registers to new.
+     */
+    public void mapRegisters(RegisterMapper mapper) {
+        for (SsaBasicBlock block : getBlocks()) {
+            for (SsaInsn insn : block.getInsns()) {
+                insn.mapRegisters(mapper);
+            }
+        }
+
+        registerCount = mapper.getNewRegisterCount();
+        spareRegisterBase = registerCount;
+    }
+
+    /**
+     * Returns the insn that defines the given register
+     * @param reg register in question
+     * @return insn (actual instance from code) that defined this reg or null
+     * if reg is not defined.
+     */
+    public SsaInsn getDefinitionForRegister(int reg) {
+        if (backMode) {
+            throw new RuntimeException("No def list in back mode");
+        }
+
+        if (definitionList != null) {
+            return definitionList[reg];
+        }
+
+        definitionList = new SsaInsn[getRegCount()];
+
+        forEachInsn(new SsaInsn.Visitor() {
+            public void visitMoveInsn (NormalSsaInsn insn) {
+                definitionList[insn.getResult().getReg()] = insn;
+            }
+            public void visitPhiInsn (PhiInsn phi) {
+                definitionList[phi.getResult().getReg()] = phi;
+            }
+            public void visitNonMoveInsn (NormalSsaInsn insn) {
+                RegisterSpec result = insn.getResult();
+                if (result != null) {
+                    definitionList[insn.getResult().getReg()] = insn;
+                }
+            }
+        });
+
+        return definitionList[reg];
+    }
+
+    /**
+     * Builds useList and unmodifiableUseList.
+     */
+    private void buildUseList() {
+        if (backMode) {
+            throw new RuntimeException("No use list in back mode");
+        }
+
+        useList = new ArrayList[registerCount];
+
+        for (int i = 0; i < registerCount; i++) {
+            useList[i] = new ArrayList();
+        }
+
+        forEachInsn(new SsaInsn.Visitor() {
+            /** {@inheritDoc} */
+            public void visitMoveInsn (NormalSsaInsn insn) {
+                addToUses(insn);
+            }
+            /** {@inheritDoc} */
+            public void visitPhiInsn (PhiInsn phi) {
+                addToUses(phi);
+            }
+            /** {@inheritDoc} */
+            public void visitNonMoveInsn (NormalSsaInsn insn) {
+                addToUses(insn);
+            }
+            /**
+             * Adds specified insn to the uses list for all of its sources.
+             * @param insn {@code non-null;} insn to process
+             */
+            private void addToUses(SsaInsn insn) {
+                RegisterSpecList rl = insn.getSources();
+                int sz = rl.size();
+
+                for (int i = 0; i < sz; i++) {
+                    useList[rl.get(i).getReg()].add(insn);
+                }
+            }
+        });
+
+        unmodifiableUseList = new List[registerCount];
+
+        for (int i = 0; i < registerCount; i++) {
+            unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]);
+        }
+    }
+
+    /**
+     * Updates the use list for a single change in source register.
+     *
+     * @param insn {@code non-null;} insn being changed
+     * @param oldSource {@code null-ok;} The source that was used, if
+     * applicable
+     * @param newSource {@code non-null;} the new source being used
+     */
+    /*package*/ void onSourceChanged(SsaInsn insn,
+            RegisterSpec oldSource, RegisterSpec newSource) {
+        if (useList == null) return;
+
+        if (oldSource != null) {
+            int reg = oldSource.getReg();
+            useList[reg].remove(insn);
+        }
+
+        int reg = newSource.getReg();
+        if (useList.length <= reg) {
+            useList = null;
+            return;
+        }
+        useList[reg].add(insn);
+    }
+
+    /**
+     * Updates the use list for a source list change.
+     *
+     * @param insn {@code insn non-null;} insn being changed.
+     * {@code insn.getSources()} must return the new source list.
+     * @param oldSources {@code null-ok;} list of sources that were
+     * previously used
+     */
+    /*package*/ void onSourcesChanged(SsaInsn insn,
+            RegisterSpecList oldSources) {
+        if (useList == null) return;
+
+        if (oldSources != null) {
+            removeFromUseList(insn, oldSources);
+        }
+
+        RegisterSpecList sources = insn.getSources();
+        int szNew = sources.size();
+
+        for (int i = 0; i < szNew; i++) {
+            int reg = sources.get(i).getReg();
+            useList[reg].add(insn);
+        }
+    }
+
+    /**
+     * Removes a given {@code insn} from the use lists for the given
+     * {@code oldSources} (rather than the sources currently
+     * returned by insn.getSources()).
+     *
+     * @param insn {@code non-null;} insn in question
+     * @param oldSources {@code null-ok;} registers whose use lists
+     * {@code insn} should be removed form
+     */
+    private void removeFromUseList(SsaInsn insn, RegisterSpecList oldSources) {
+        if (oldSources == null) {
+            return;
+        }
+
+        int szNew = oldSources.size();
+        for (int i = 0; i < szNew; i++) {
+            if (!useList[oldSources.get(i).getReg()].remove(insn)) {
+                throw new RuntimeException("use not found");
+            }
+        }
+    }
+
+    /**
+     * Adds an insn to both the use and def lists. For use when adding
+     * a new insn to the method.
+     *
+     * @param insn {@code non-null;} insn to add
+     */
+    /*package*/ void onInsnAdded(SsaInsn insn) {
+        onSourcesChanged(insn, null);
+        updateOneDefinition(insn, null);
+    }
+
+    /**
+     * Removes an instruction from use and def lists. For use during
+     * instruction removal.
+     *
+     * @param insn {@code non-null;} insn to remove
+     */
+    /*package*/ void onInsnRemoved(SsaInsn insn) {
+        if (useList != null) {
+            removeFromUseList(insn, insn.getSources());
+        }
+
+        RegisterSpec resultReg = insn.getResult();
+        if (definitionList != null && resultReg != null) {
+            definitionList[resultReg.getReg()] = null;
+        }
+    }
+
+    /**
+     * Indicates that the instruction list has changed or the SSA register
+     * count has increased, so that internal datastructures that rely on
+     * it should be rebuild. In general, the various other on* methods
+     * should be called in preference when changes occur if they are
+     * applicable.
+     */
+    public void onInsnsChanged() {
+        // Definition list will need to be recomputed
+        definitionList = null;
+
+        // Use list will need to be recomputed
+        useList = null;
+        unmodifiableUseList = null;
+    }
+
+    /**
+     * Updates a single definition.
+     *
+     * @param insn {@code non-null;} insn who's result should be recorded as
+     * a definition
+     * @param oldResult {@code null-ok;} a previous result that should
+     * be no longer considered a definition by this insn
+     */
+    /*package*/ void updateOneDefinition(SsaInsn insn,
+            RegisterSpec oldResult) {
+        if (definitionList == null) return;
+
+        if (oldResult != null) {
+            int reg = oldResult.getReg();
+            definitionList[reg] = null;
+        }
+
+        RegisterSpec resultReg = insn.getResult();
+
+        if (resultReg != null) {
+            int reg = resultReg.getReg();
+
+            if (definitionList[reg] != null) {
+                throw new RuntimeException("Duplicate add of insn");
+            } else {
+                definitionList[resultReg.getReg()] = insn;
+            }
+        }
+    }
+
+    /**
+     * Returns the list of all source uses (not results) for a register.
+     *
+     * @param reg register in question
+     * @return unmodifiable instruction list
+     */
+    public List<SsaInsn> getUseListForRegister(int reg) {
+
+        if (unmodifiableUseList == null) {
+            buildUseList();
+        }
+
+        return unmodifiableUseList[reg];
+    }
+
+    /**
+     * Returns a modifiable copy of the register use list.
+     *
+     * @return modifiable copy of the use-list, indexed by register
+     */
+    public ArrayList<SsaInsn>[] getUseListCopy() {
+        if (useList == null) {
+            buildUseList();
+        }
+
+        ArrayList<SsaInsn>[] useListCopy
+                = (ArrayList<SsaInsn>[])(new ArrayList[registerCount]);
+
+        for (int i = 0; i < registerCount; i++) {
+            useListCopy[i] = (ArrayList<SsaInsn>)(new ArrayList(useList[i]));
+        }
+
+        return useListCopy;
+    }
+
+    /**
+     * Checks to see if the given SSA reg is ever associated with a local
+     * local variable. Each SSA reg may be associated with at most one
+     * local var.
+     *
+     * @param spec {@code non-null;} ssa reg
+     * @return true if reg is ever associated with a local
+     */
+    public boolean isRegALocal(RegisterSpec spec) {
+        SsaInsn defn = getDefinitionForRegister(spec.getReg());
+
+        if (defn == null) {
+            // version 0 registers are never used as locals
+            return false;
+        }
+
+        // Does the definition have a local associated with it?
+        if (defn.getLocalAssignment() != null) return true;
+
+        // If not, is there a mark-local insn?
+        for (SsaInsn use : getUseListForRegister(spec.getReg())) {
+            Insn insn = use.getOriginalRopInsn();
+
+            if (insn != null
+                    && insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Sets the new register count after renaming.
+     *
+     * @param newRegCount new register count
+     */
+    /*package*/ void setNewRegCount(int newRegCount) {
+        registerCount = newRegCount;
+        spareRegisterBase = registerCount;
+        onInsnsChanged();
+    }
+
+    /**
+     * Makes a new SSA register. For use after renaming has completed.
+     *
+     * @return {@code >=0;} new SSA register.
+     */
+    public int makeNewSsaReg() {
+        int reg = registerCount++;
+        spareRegisterBase = registerCount;
+        onInsnsChanged();
+        return reg;
+    }
+
+    /**
+     * Visits all insns in this method.
+     *
+     * @param visitor {@code non-null;} callback interface
+     */
+    public void forEachInsn(SsaInsn.Visitor visitor) {
+        for (SsaBasicBlock block : blocks) {
+            block.forEachInsn(visitor);
+        }
+    }
+
+    /**
+     * Visits each phi insn in this method
+     * @param v {@code non-null;} callback.
+     *
+     */
+    public void forEachPhiInsn(PhiInsn.Visitor v) {
+        for (SsaBasicBlock block : blocks) {
+            block.forEachPhiInsn(v);
+        }
+    }
+
+
+    /**
+     * Walks the basic block tree in depth-first order, calling the visitor
+     * method once for every block. This depth-first walk may be run forward
+     * from the method entry point or backwards from the method exit points.
+     *
+     * @param reverse true if this should walk backwards from the exit points
+     * @param v {@code non-null;} callback interface. {@code parent} is set
+     * unless this is the root node
+     */
+    public void forEachBlockDepthFirst(boolean reverse,
+            SsaBasicBlock.Visitor v) {
+        BitSet visited = new BitSet(blocks.size());
+
+        // We push the parent first, then the child on the stack.
+        Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
+
+        SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock();
+
+        if (rootBlock == null) {
+            // in the case there's no exit block
+            return;
+        }
+
+        stack.add(null);    // Start with null parent.
+        stack.add(rootBlock);
+
+        while (stack.size() > 0) {
+            SsaBasicBlock cur = stack.pop();
+            SsaBasicBlock parent = stack.pop();
+
+            if (!visited.get(cur.getIndex())) {
+                BitSet children
+                    = reverse ? cur.getPredecessors() : cur.getSuccessors();
+                for (int i = children.nextSetBit(0); i >= 0
+                        ; i = children.nextSetBit(i + 1)) {
+                    stack.add(cur);
+                    stack.add(blocks.get(i));
+                }
+                visited.set(cur.getIndex());
+                v.visitBlock(cur, parent);
+            }
+        }
+    }
+
+    /**
+     * Visits blocks in dom-tree order, starting at the current node.
+     * The {@code parent} parameter of the Visitor.visitBlock callback
+     * is currently always set to null.
+     *
+     * @param v {@code non-null;} callback interface
+     */
+    public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) {
+        BitSet visited = new BitSet(getBlocks().size());
+        Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
+
+        stack.add(getEntryBlock());
+
+        while (stack.size() > 0) {
+            SsaBasicBlock cur = stack.pop();
+            ArrayList<SsaBasicBlock> curDomChildren = cur.getDomChildren();
+
+            if (!visited.get(cur.getIndex())) {
+                // We walk the tree this way for historical reasons...
+                for (int i = curDomChildren.size() - 1; i >= 0; i--) {
+                    SsaBasicBlock child = curDomChildren.get(i);
+                    stack.add(child);
+                }
+                visited.set(cur.getIndex());
+                v.visitBlock(cur, null);
+            }
+        }
+    }
+
+    /**
+     * Deletes all insns in the set from this method.
+     *
+     * @param deletedInsns {@code non-null;} insns to delete
+     */
+    public void deleteInsns(Set<SsaInsn> deletedInsns) {
+        for (SsaBasicBlock block : getBlocks()) {
+            ArrayList<SsaInsn> insns = block.getInsns();
+
+            for (int i = insns.size() - 1; i >= 0; i--) {
+                SsaInsn insn = insns.get(i);
+
+                if (deletedInsns.contains(insn)) {
+                    onInsnRemoved(insn);
+                    insns.remove(i);
+                }
+            }
+
+            // Check to see if we need to add a GOTO
+
+            int insnsSz = insns.size();
+            SsaInsn lastInsn = (insnsSz == 0) ? null : insns.get(insnsSz - 1);
+
+            if (block != getExitBlock() && (insnsSz == 0
+                    || lastInsn.getOriginalRopInsn() == null
+                    || lastInsn.getOriginalRopInsn().getOpcode()
+                        .getBranchingness() == Rop.BRANCH_NONE)) {
+                // We managed to eat a throwable insn
+
+                Insn gotoInsn = new PlainInsn(Rops.GOTO,
+                        SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY);
+                insns.add(SsaInsn.makeFromRop(gotoInsn, block));
+
+                // Remove secondary successors from this block
+                BitSet succs = block.getSuccessors();
+                for (int i = succs.nextSetBit(0); i >= 0;
+                         i = succs.nextSetBit(i + 1)) {
+                    if (i != block.getPrimarySuccessorIndex()) {
+                        block.removeSuccessor(i);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets "back-convert mode". Set during back-conversion when registers
+     * are about to be mapped into a non-SSA namespace. When true,
+     * use and def lists are unavailable.
+     */
+    public void setBackMode() {
+        backMode = true;
+        useList = null;
+        definitionList = null;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaRenamer.java b/dx/src/com/android/dx/ssa/SsaRenamer.java
new file mode 100644
index 0000000..58e4142
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaRenamer.java
@@ -0,0 +1,662 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Complete transformation to SSA form by renaming all registers accessed.<p>
+ *
+ * See Appel algorithm 19.7<p>
+ *
+ * Unlike the original algorithm presented in Appel, this renamer converts
+ * to a new flat (versionless) register space. The "version 0" registers,
+ * which represent the initial state of the Rop registers and should never
+ * actually be meaningfully accessed in a legal program, are represented
+ * as the first N registers in the SSA namespace. Subsequent assignments
+ * are assigned new unique names. Note that the incoming Rop representation
+ * has a concept of register widths, where 64-bit values are stored into
+ * two adjoining Rop registers. This adjoining register representation is
+ * ignored in SSA form conversion and while in SSA form, each register can be e
+ * either 32 or 64 bits wide depending on use. The adjoining-register
+ * represention is re-created later when converting back to Rop form. <p>
+ *
+ * But, please note, the SSA Renamer's ignoring of the adjoining-register ROP
+ * representation means that unaligned accesses to 64-bit registers are not
+ * supported. For example, you cannot do a 32-bit operation on a portion of
+ * a 64-bit register. This will never be observed to happen when coming
+ * from Java code, of course.<p>
+ *
+ * The implementation here, rather than keeping a single register version
+ * stack for the entire method as the dom tree is walked, instead keeps
+ * a mapping table for the current block being processed. Once the
+ * current block has been processed, this mapping table is then copied
+ * and used as the initial state for child blocks.<p>
+ */
+public class SsaRenamer implements Runnable {
+    /** debug flag */
+    private static final boolean DEBUG = false;
+
+    /** method we're processing */
+    private final SsaMethod ssaMeth;
+
+    /** next available SSA register */
+    private int nextSsaReg;
+
+    /** the number of original rop registers */
+    private final int ropRegCount;
+
+    /** work only on registers above this value */
+    private int threshold;
+
+    /**
+     * indexed by block index; register version state for each block start.
+     * This list is updated by each dom parent for its children. The only
+     * sub-arrays that exist at any one time are the start states for blocks
+     * yet to be processed by a {@code BlockRenamer} instance.
+     */
+    private final RegisterSpec[][] startsForBlocks;
+
+    /** map of SSA register number to debug (local var names) or null of n/a */
+    private final ArrayList<LocalItem> ssaRegToLocalItems;
+
+    /**
+     * maps SSA registers back to the original rop number. Used for
+     * debug only.
+     */
+    private IntList ssaRegToRopReg;
+
+    /**
+     * Constructs an instance of the renamer
+     *
+     * @param ssaMeth {@code non-null;} un-renamed SSA method that will
+     * be renamed.
+     */
+    public SsaRenamer(SsaMethod ssaMeth) {
+        ropRegCount = ssaMeth.getRegCount();
+
+        this.ssaMeth = ssaMeth;
+
+        /*
+         * Reserve the first N registers in the SSA register space for
+         * "version 0" registers.
+         */
+        nextSsaReg = ropRegCount;
+        threshold = 0;
+        startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][];
+
+        ssaRegToLocalItems = new ArrayList<LocalItem>();
+
+        if (DEBUG) {
+            ssaRegToRopReg = new IntList(ropRegCount);
+        }
+
+        /*
+         * Appel 19.7
+         *
+         * Initialization:
+         *   for each variable a        // register i
+         *      Count[a] <- 0           // nextSsaReg, flattened
+         *      Stack[a] <- 0           // versionStack
+         *      push 0 onto Stack[a]
+         *
+         */
+
+        // top entry for the version stack is version 0
+        RegisterSpec[] initialRegMapping = new RegisterSpec[ropRegCount];
+        for (int i = 0; i < ropRegCount; i++) {
+            // everyone starts with a version 0 register
+            initialRegMapping[i] = RegisterSpec.make(i, Type.VOID);
+
+            if (DEBUG) {
+                ssaRegToRopReg.add(i);
+            }
+        }
+
+        // Initial state for entry block
+        startsForBlocks[ssaMeth.getEntryBlockIndex()] = initialRegMapping;
+    }
+
+    /**
+    * Constructs an instance of the renamer with threshold set
+    *
+    * @param ssaMeth {@code non-null;} un-renamed SSA method that will
+    * be renamed.
+    * @param thresh registers below this number are unchanged
+    */
+   public SsaRenamer(SsaMethod ssaMeth, int thresh) {
+       this(ssaMeth);
+       threshold = thresh;
+   }
+
+    /**
+     * Performs renaming transformation, modifying the method's instructions
+     * in-place.
+     */
+    public void run() {
+        // Rename each block in dom-tree DFS order.
+        ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
+            public void visitBlock (SsaBasicBlock block,
+                    SsaBasicBlock unused) {
+                new BlockRenamer(block).process();
+            }
+        });
+
+        ssaMeth.setNewRegCount(nextSsaReg);
+        ssaMeth.onInsnsChanged();
+
+        if (DEBUG) {
+            System.out.println("SSA\tRop");
+            /*
+             * We're going to compute the version of the rop register
+             * by keeping a running total of how many times the rop
+             * register has been mapped.
+             */
+            int[] versions = new int[ropRegCount];
+
+            int sz = ssaRegToRopReg.size();
+            for (int i = 0; i < sz; i++) {
+                int ropReg = ssaRegToRopReg.get(i);
+                System.out.println(i + "\t" + ropReg + "["
+                        + versions[ropReg] + "]");
+                versions[ropReg]++;
+            }
+        }
+    }
+
+    /**
+     * Duplicates a RegisterSpec array.
+     *
+     * @param orig {@code non-null;} array to duplicate
+     * @return {@code non-null;} new instance
+     */
+    private static  RegisterSpec[] dupArray(RegisterSpec[] orig) {
+        RegisterSpec[] copy = new RegisterSpec[orig.length];
+
+        System.arraycopy(orig, 0, copy, 0, orig.length);
+
+        return copy;
+    }
+
+    /**
+     * Gets a local variable item for a specified register.
+     *
+     * @param ssaReg register in SSA name space
+     * @return {@code null-ok;} Local variable name or null if none
+     */
+    private LocalItem getLocalForNewReg(int ssaReg) {
+        if (ssaReg < ssaRegToLocalItems.size()) {
+            return ssaRegToLocalItems.get(ssaReg);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Records a debug (local variable) name for a specified register.
+     *
+     * @param ssaReg non-null named register spec in SSA name space
+     */
+    private void setNameForSsaReg(RegisterSpec ssaReg) {
+        int reg = ssaReg.getReg();
+        LocalItem local = ssaReg.getLocalItem();
+
+        ssaRegToLocalItems.ensureCapacity(reg + 1);
+        while (ssaRegToLocalItems.size() <= reg) {
+            ssaRegToLocalItems.add(null);
+        }
+
+        ssaRegToLocalItems.set(reg, local);
+    }
+
+    /**
+     * Returns true if this SSA register is below the specified threshold.
+     * Used when most code is already in SSA form, and renaming is needed only
+     * for registers above a certain threshold.
+     *
+     * @param ssaReg the SSA register in question
+     * @return {@code true} if its register number is below the threshold
+     */
+    private boolean isBelowThresholdRegister(int ssaReg) {
+        return ssaReg < threshold;
+    }
+
+    /**
+     * Returns true if this SSA register is a "version 0"
+     * register. All version 0 registers are assigned the first N register
+     * numbers, where N is the count of original rop registers.
+     *
+     * @param ssaReg the SSA register in question
+     * @return true if it is a version 0 register.
+     */
+    private boolean isVersionZeroRegister(int ssaReg) {
+        return ssaReg < ropRegCount;
+    }
+
+    /**
+     * Returns true if a and b are equal or are both null.
+     *
+     * @param a null-ok
+     * @param b null-ok
+     * @return Returns true if a and b are equal or are both null
+     */
+    private static boolean equalsHandlesNulls(Object a, Object b) {
+        return a == b ||  (a != null && a.equals(b));
+    }
+
+    /**
+     * Processes all insns in a block and renames their registers
+     * as appropriate.
+     */
+    private class BlockRenamer implements SsaInsn.Visitor{
+        /** {@code non-null;} block we're processing. */
+        private final SsaBasicBlock block;
+
+        /**
+         * {@code non-null;} indexed by old register name. The current
+         * top of the version stack as seen by this block. It's
+         * initialized from the ending state of its dom parent,
+         * updated as the block's instructions are processed, and then
+         * copied to each one of its dom children.
+         */
+        private final RegisterSpec[] currentMapping;
+
+        /**
+         * contains the set of moves we need to keep to preserve local
+         * var info. All other moves will be deleted.
+         */
+        private final HashSet<SsaInsn> movesToKeep;
+
+        /**
+         * maps the set of insns to replace after renaming is finished
+         * on the block.
+         */
+        private final HashMap<SsaInsn, SsaInsn> insnsToReplace;
+
+        private final RenamingMapper mapper;
+
+        /**
+         * Constructs a block renamer instance. Call {@code process}
+         * to process.
+         *
+         * @param block {@code non-null;} block to process
+         */
+        BlockRenamer(final SsaBasicBlock block) {
+            this.block = block;
+            currentMapping = startsForBlocks[block.getIndex()];
+            movesToKeep = new HashSet<SsaInsn>();
+            insnsToReplace = new HashMap<SsaInsn, SsaInsn>();
+            mapper =  new RenamingMapper();
+
+            // We don't need our own start state anymore
+            startsForBlocks[block.getIndex()] = null;
+        }
+
+        /**
+         * Provides a register mapping between the old register space
+         * and the current renaming mapping. The mapping is updated
+         * as the current block's instructions are processed.
+         */
+        private class RenamingMapper extends RegisterMapper {
+            public RenamingMapper() {
+                // This space intentionally left blank.
+            }
+
+            /** {@inheritDoc} */
+            @Override
+            public int getNewRegisterCount() {
+                return nextSsaReg;
+            }
+
+            /** {@inheritDoc} */
+            @Override
+            public RegisterSpec map(RegisterSpec registerSpec) {
+                if (registerSpec == null) return null;
+
+                int reg = registerSpec.getReg();
+
+                // For debugging: assert that the mapped types are compatible.
+                if (DEBUG) {
+                    RegisterSpec newVersion = currentMapping[reg];
+                    if (newVersion.getBasicType() != Type.BT_VOID
+                            && registerSpec.getBasicFrameType()
+                                != newVersion.getBasicFrameType()) {
+
+                        throw new RuntimeException(
+                                "mapping registers of incompatible types! "
+                                + registerSpec
+                                + " " + currentMapping[reg]);
+                    }
+                }
+
+                return registerSpec.withReg(currentMapping[reg].getReg());
+            }
+        }
+
+        /**
+         * Renames all the variables in this block and inserts appriopriate
+         * phis in successor blocks.
+         */
+        public void process() {
+            /*
+             * From Appel:
+             *
+             * Rename(n) =
+             *   for each statement S in block n   // 'statement' in 'block'
+             */
+
+            block.forEachInsn(this);
+
+            updateSuccessorPhis();
+
+            // Delete all move insns in this block.
+            ArrayList<SsaInsn> insns = block.getInsns();
+            int szInsns = insns.size();
+
+            for (int i = szInsns - 1; i >= 0 ; i--) {
+                SsaInsn insn = insns.get(i);
+                SsaInsn replaceInsn;
+
+                replaceInsn = insnsToReplace.get(insn);
+
+                if (replaceInsn != null) {
+                    insns.set(i, replaceInsn);
+                } else if (insn.isNormalMoveInsn()
+                        && !movesToKeep.contains(insn)) {
+                    insns.remove(i);
+                }
+            }
+
+            // Store the start states for our dom children.
+            boolean first = true;
+            for (SsaBasicBlock child : block.getDomChildren()) {
+                if (child != block) {
+                    // Don't bother duplicating the array for the first child.
+                    RegisterSpec[] childStart = first ? currentMapping
+                        : dupArray(currentMapping);
+
+                    startsForBlocks[child.getIndex()] = childStart;
+                    first = false;
+                }
+            }
+
+            // currentMapping is owned by a child now.
+        }
+
+        /**
+         * Enforces a few contraints when a register mapping is added.
+         *
+         * <ol>
+         * <li> Ensures that all new SSA registers specs in the mapping
+         * table with the same register number are identical. In effect, once
+         * an SSA register spec has received or lost a local variable name,
+         * then every old-namespace register that maps to it should gain or
+         * lose its local variable name as well.
+         * <li> Records the local name associated with the
+         * register so that a register is never associated with more than one
+         * local.
+         * <li> ensures that only one SSA register
+         * at a time is considered to be associated with a local variable. When
+         * {@code currentMapping} is updated and the newly added element
+         * is named, strip that name from any other SSA registers.
+         * </ol>
+         *
+         * @param ropReg {@code >= 0;} rop register number
+         * @param ssaReg {@code non-null;} an SSA register that has just
+         * been added to {@code currentMapping}
+         */
+        private void addMapping(int ropReg, RegisterSpec ssaReg) {
+            int ssaRegNum = ssaReg.getReg();
+            LocalItem ssaRegLocal = ssaReg.getLocalItem();
+
+            currentMapping[ropReg] = ssaReg;
+
+            /*
+             * Ensure all SSA register specs with the same reg are identical.
+             */
+            for (int i = currentMapping.length - 1; i >= 0; i--) {
+                RegisterSpec cur = currentMapping[i];
+
+                if (ssaRegNum == cur.getReg()) {
+                    currentMapping[i] = ssaReg;
+                }
+            }
+
+            // All further steps are for registers with local information.
+            if (ssaRegLocal == null) {
+                return;
+            }
+
+            // Record that this SSA reg has been associated with a local.
+            setNameForSsaReg(ssaReg);
+
+            // Ensure that no other SSA regs are associated with this local.
+            for (int i = currentMapping.length - 1; i >= 0; i--) {
+                RegisterSpec cur = currentMapping[i];
+
+                if (ssaRegNum != cur.getReg()
+                        && ssaRegLocal.equals(cur.getLocalItem())) {
+                    currentMapping[i] = cur.withLocalItem(null);
+                }
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * Phi insns have their result registers renamed.
+         */
+        public void visitPhiInsn(PhiInsn phi) {
+            /* don't process sources for phi's */
+            processResultReg(phi);
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * Move insns are treated as a simple mapping operation, and
+         * will later be removed unless they represent a local variable
+         * assignment. If they represent a local variable assignement, they
+         * are preserved.
+         */
+        public void visitMoveInsn(NormalSsaInsn insn) {
+            /*
+             * For moves: copy propogate the move if we can, but don't
+             * if we need to preserve local variable info and the
+             * result has a different name than the source.
+             */
+
+            RegisterSpec ropResult = insn.getResult();
+            int ropResultReg = ropResult.getReg();
+            int ropSourceReg = insn.getSources().get(0).getReg();
+
+            insn.mapSourceRegisters(mapper);
+            int ssaSourceReg = insn.getSources().get(0).getReg();
+
+            LocalItem sourceLocal
+                = currentMapping[ropSourceReg].getLocalItem();
+            LocalItem resultLocal = ropResult.getLocalItem();
+
+            /*
+             * A move from a register that's currently associated with a local
+             * to one that will not be associated with a local does not need
+             * to be preserved, but the local association should remain.
+             * Hence, we inherit the sourceLocal where the resultLocal is null.
+             */
+
+            LocalItem newLocal
+                = (resultLocal == null) ? sourceLocal : resultLocal;
+            LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg);
+
+            /*
+             * If we take the new local, will only one local have ever
+             * been associated with this SSA reg?
+             */
+            boolean onlyOneAssociatedLocal
+                    = associatedLocal == null || newLocal == null
+                    || newLocal.equals(associatedLocal);
+
+            /*
+             * If we're going to copy-propogate, then the ssa register
+             * spec that's going to go into the mapping is made up of
+             * the source register number mapped from above, the type
+             * of the result, and the name either from the result (if
+             * specified) or inherited from the existing mapping.
+             *
+             * The move source has incomplete type information in null
+             * object cases, so the result type is used.
+             */
+            RegisterSpec ssaReg
+                    = RegisterSpec.makeLocalOptional(
+                        ssaSourceReg, ropResult.getType(), newLocal);
+
+            if (!Optimizer.getPreserveLocals() || (onlyOneAssociatedLocal
+                    && equalsHandlesNulls(newLocal, sourceLocal)) &&
+                    threshold == 0) {
+                /*
+                 * We don't have to keep this move to preserve local
+                 * information. Either the name is the same, or the result
+                 * register spec is unnamed.
+                 */
+
+                addMapping(ropResultReg, ssaReg);
+            } else if (onlyOneAssociatedLocal && sourceLocal == null &&
+                    threshold == 0) {
+                /*
+                 * The register was previously unnamed. This means that a
+                 * local starts after it's first assignment in SSA form
+                 */
+
+                RegisterSpecList ssaSources = RegisterSpecList.make(
+                        RegisterSpec.make(ssaReg.getReg(),
+                                ssaReg.getType(), newLocal));
+
+                SsaInsn newInsn
+                        = SsaInsn.makeFromRop(
+                            new PlainInsn(Rops.opMarkLocal(ssaReg),
+                            SourcePosition.NO_INFO, null, ssaSources),block);
+
+                insnsToReplace.put(insn, newInsn);
+
+                // Just map as above.
+                addMapping(ropResultReg, ssaReg);
+            } else {
+                /*
+                 * Do not copy-propogate, since the two registers have
+                 * two different local-variable names.
+                 */
+                processResultReg(insn);
+
+                movesToKeep.add(insn);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * All insns that are not move or phi insns have their source registers
+         * mapped ot the current mapping. Their result registers are then
+         * renamed to a new SSA register which is then added to the current
+         * register mapping.
+         */
+        public void visitNonMoveInsn(NormalSsaInsn insn) {
+            /* for each use of some variable X in S */
+            insn.mapSourceRegisters(mapper);
+
+            processResultReg(insn);
+        }
+
+        /**
+         * Renames the result register of this insn and updates the
+         * current register mapping. Does nothing if this insn has no result.
+         * Applied to all non-move insns.
+         *
+         * @param insn insn to process.
+         */
+        void processResultReg(SsaInsn insn) {
+            RegisterSpec ropResult = insn.getResult();
+
+            if (ropResult == null) {
+                return;
+            }
+
+            int ropReg = ropResult.getReg();
+            if (isBelowThresholdRegister(ropReg)) {
+                return;
+            }
+
+            insn.changeResultReg(nextSsaReg);
+            addMapping(ropReg, insn.getResult());
+
+            if (DEBUG) {
+                ssaRegToRopReg.add(ropReg);
+            }
+
+            nextSsaReg++;
+        }
+
+        /**
+         * Updates the phi insns in successor blocks with operands based
+         * on the current mapping of the rop register the phis represent.
+         */
+        private void updateSuccessorPhis() {
+            PhiInsn.Visitor visitor = new PhiInsn.Visitor() {
+                public void visitPhiInsn (PhiInsn insn) {
+                    int ropReg;
+
+                    ropReg = insn.getRopResultReg();
+                    if (isBelowThresholdRegister(ropReg)) {
+                        return;
+                    }
+
+                    /*
+                     * Never add a version 0 register as a phi
+                     * operand. Version 0 registers represent the
+                     * initial register state, and thus are never
+                     * significant. Furthermore, the register liveness
+                     * algorithm doesn't properly count them as "live
+                     * in" at the beginning of the method.
+                     */
+
+                    RegisterSpec stackTop = currentMapping[ropReg];
+                    if (!isVersionZeroRegister(stackTop.getReg())) {
+                        insn.addPhiOperand(stackTop, block);
+                    }
+                }
+            };
+
+            BitSet successors = block.getSuccessors();
+            for (int i = successors.nextSetBit(0); i >= 0;
+                    i = successors.nextSetBit(i + 1)) {
+                SsaBasicBlock successor = ssaMeth.getBlocks().get(i);
+                successor.forEachPhiInsn(visitor);
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/_tests/_DomFront.java b/dx/src/com/android/dx/ssa/_tests/_DomFront.java
new file mode 100644
index 0000000..3d891c9
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/_tests/_DomFront.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa._tests;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class {@code com.android.dx.ssa.DomFront}.
+ */
+public class _DomFront
+        extends TestCase {
+
+    public void test_one() {
+
+    }
+
+
+}
diff --git a/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java b/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java
new file mode 100644
index 0000000..6416e84
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Allocates registers via a naive n^2 register allocator.
+ * This allocator does not try to co-locate local variables or deal
+ * intelligently with different size register uses.
+ */
+public class FirstFitAllocator extends RegisterAllocator {
+    /**
+     * If true, allocator places parameters at the top of the frame
+     * in calling-convention order.
+     */
+    private static final boolean PRESLOT_PARAMS = true;
+
+    /** indexed by old reg; the set of old regs we've mapped */
+    private final BitSet mapped;
+
+    /** {@inheritDoc} */
+    public FirstFitAllocator(
+            final SsaMethod ssaMeth, final InterferenceGraph interference) {
+        super(ssaMeth, interference);
+
+        mapped = new BitSet(ssaMeth.getRegCount());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean wantsParamsMovedHigh() {
+        return PRESLOT_PARAMS;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RegisterMapper allocateRegisters() {
+        int oldRegCount = ssaMeth.getRegCount();
+
+        BasicRegisterMapper mapper
+                = new BasicRegisterMapper(oldRegCount);
+
+        int nextNewRegister = 0;
+
+        if (PRESLOT_PARAMS) {
+            /*
+             * Reserve space for the params at the bottom of the register
+             * space. Later, we'll flip the params to the end of the register
+             * space.
+             */
+
+            nextNewRegister = ssaMeth.getParamWidth();
+        }
+
+        for (int i = 0; i < oldRegCount; i++) {
+            if (mapped.get(i)) {
+                // we already got this one
+                continue;
+            }
+
+            int maxCategory = getCategoryForSsaReg(i);
+            IntSet current = new BitIntSet(oldRegCount);
+
+            interference.mergeInterferenceSet(i, current);
+
+            boolean isPreslotted = false;
+            int newReg = 0;
+
+            if (PRESLOT_PARAMS && isDefinitionMoveParam(i)) {
+                // Any move-param definition must be a NormalSsaInsn
+                NormalSsaInsn defInsn = (NormalSsaInsn)
+                       ssaMeth.getDefinitionForRegister(i);
+
+                newReg = paramNumberFromMoveParam(defInsn);
+
+                mapper.addMapping(i, newReg, maxCategory);
+                isPreslotted = true;
+            } else {
+                mapper.addMapping(i, nextNewRegister, maxCategory);
+                newReg = nextNewRegister;
+            }
+
+            for (int j = i + 1; j < oldRegCount; j++) {
+                if (mapped.get(j) || isDefinitionMoveParam(j)) {
+                    continue;
+                }
+
+                /*
+                 * If reg j doesn't interfere with the current mapping.
+                 * Also, if this is a pre-slotted method parameter, we
+                 * can't use more than the original param width.
+                 */
+                if (!current.has(j)
+                        && !(isPreslotted
+                            && (maxCategory < getCategoryForSsaReg(j)))) {
+
+                    interference.mergeInterferenceSet(j, current);
+
+                    maxCategory = Math.max(maxCategory,
+                            getCategoryForSsaReg(j));
+
+                    mapper.addMapping(j, newReg, maxCategory);
+                    mapped.set(j);
+                }
+            }
+
+            mapped.set(i);
+            if (!isPreslotted) {
+                nextNewRegister += maxCategory;
+            }
+        }
+
+        return mapper;
+    }
+
+    /**
+     * Returns the parameter number that this move-param insn refers to
+     * @param ndefInsn a move-param insn (otherwise, exceptions will be thrown)
+     * @return parameter number (offset in the total parameter width)
+     */
+    private int paramNumberFromMoveParam(NormalSsaInsn ndefInsn) {
+        CstInsn origInsn = (CstInsn) ndefInsn.getOriginalRopInsn();
+
+        return ((CstInteger) origInsn.getConstant()).getValue();
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java
new file mode 100644
index 0000000..0cffcfa
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java
@@ -0,0 +1,958 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.InterferenceRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.IntIterator;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Allocates registers in a first-fit fashion, with the bottom reserved for
+ * method parameters and all SSAregisters representing the same local variable
+ * kept together if possible.
+ */
+public class FirstFitLocalCombiningAllocator extends RegisterAllocator {
+    /** local debug flag */
+    private static final boolean DEBUG = false;
+
+    /** maps local variable to a list of associated SSA registers */
+    private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables;
+
+    /** list of move-result-pesudo instructions seen in this method */
+    private final ArrayList<NormalSsaInsn> moveResultPseudoInsns;
+
+    /** list of invoke-range instructions seen in this method */
+    private final ArrayList<NormalSsaInsn> invokeRangeInsns;
+
+    /** indexed by SSA reg; the set of SSA regs we've mapped */
+    private final BitSet ssaRegsMapped;
+
+    /** Register mapper which will be our result */
+    private final InterferenceRegisterMapper mapper;
+
+    /** end of rop registers range (starting at 0) reserved for parameters */
+    private final int paramRangeEnd;
+
+    /** set of rop registers reserved for parameters or local variables */
+    private final BitSet reservedRopRegs;
+
+    /** set of rop registers that have been used by anything */
+    private final BitSet usedRopRegs;
+
+    /** true if converter should take steps to minimize rop-form registers */
+    private final boolean minimizeRegisters;
+
+    /**
+     * Constructs instance.
+     *
+     * @param ssaMeth {@code non-null;} method to process
+     * @param interference non-null interference graph for SSA registers
+     * @param minimizeRegisters true if converter should take steps to
+     * minimize rop-form registers
+     */
+    public FirstFitLocalCombiningAllocator(
+            SsaMethod ssaMeth, InterferenceGraph interference,
+            boolean minimizeRegisters) {
+        super(ssaMeth, interference);
+
+        ssaRegsMapped = new BitSet(ssaMeth.getRegCount());
+
+        mapper = new InterferenceRegisterMapper(
+                interference, ssaMeth.getRegCount());
+
+        this.minimizeRegisters = minimizeRegisters;
+
+        /*
+         * Reserve space for the params at the bottom of the register
+         * space. Later, we'll flip the params to the end of the register
+         * space.
+         */
+
+        paramRangeEnd = ssaMeth.getParamWidth();
+
+        reservedRopRegs = new BitSet(paramRangeEnd * 2);
+        reservedRopRegs.set(0, paramRangeEnd);
+        usedRopRegs = new BitSet(paramRangeEnd * 2);
+        localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>();
+        moveResultPseudoInsns = new ArrayList<NormalSsaInsn>();
+        invokeRangeInsns = new ArrayList<NormalSsaInsn>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean wantsParamsMovedHigh() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RegisterMapper allocateRegisters() {
+
+        analyzeInstructions();
+
+        if (DEBUG) {
+            printLocalVars();
+        }
+
+        if (DEBUG) System.out.println("--->Mapping local-associated params");
+        handleLocalAssociatedParams();
+
+        if (DEBUG) System.out.println("--->Mapping other params");
+        handleUnassociatedParameters();
+
+        if (DEBUG) System.out.println("--->Mapping invoke-range");
+        handleInvokeRangeInsns();
+
+        if (DEBUG) {
+            System.out.println("--->Mapping local-associated non-params");
+        }
+        handleLocalAssociatedOther();
+
+        if (DEBUG) System.out.println("--->Mapping check-cast results");
+        handleCheckCastResults();
+
+        if (DEBUG) System.out.println("--->Mapping others");
+        handleNormalUnassociated();
+
+        return mapper;
+    }
+
+    /**
+     * Dumps local variable table to stdout for debugging.
+     */
+    private void printLocalVars() {
+        System.out.println("Printing local vars");
+        for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e :
+                localVariables.entrySet()) {
+            StringBuilder regs = new StringBuilder();
+
+            regs.append('{');
+            regs.append(' ');
+            for (RegisterSpec reg : e.getValue()) {
+                regs.append('v');
+                regs.append(reg.getReg());
+                regs.append(' ');
+            }
+            regs.append('}');
+            System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs);
+        }
+    }
+
+    /**
+     * Maps all local-associated parameters to rop registers.
+     */
+    private void handleLocalAssociatedParams() {
+        for (ArrayList<RegisterSpec> ssaRegs : localVariables.values()) {
+            int sz = ssaRegs.size();
+            int paramIndex = -1;
+            int paramCategory = 0;
+
+            // First, find out if this local variable is a parameter.
+            for (int i = 0; i < sz; i++) {
+                RegisterSpec ssaSpec = ssaRegs.get(i);
+                int ssaReg = ssaSpec.getReg();
+
+                paramIndex = getParameterIndexForReg(ssaReg);
+
+                if (paramIndex >= 0) {
+                    paramCategory = ssaSpec.getCategory();
+                    addMapping(ssaSpec, paramIndex);
+                    break;
+                }
+            }
+
+            if (paramIndex < 0) {
+                // This local wasn't a parameter.
+                continue;
+            }
+
+            // Any remaining local-associated registers will be mapped later.
+            tryMapRegs(ssaRegs, paramIndex, paramCategory, true);
+        }
+    }
+
+    /**
+     * Gets the parameter index for SSA registers that are method parameters.
+     * {@code -1} is returned for non-parameter registers.
+     *
+     * @param ssaReg {@code >=0;} SSA register to look up
+     * @return parameter index or {@code -1} if not a parameter
+     */
+    private int getParameterIndexForReg(int ssaReg) {
+        SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg);
+        if (defInsn == null) {
+            return -1;
+        }
+
+        Rop opcode = defInsn.getOpcode();
+
+        // opcode == null for phi insns.
+        if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) {
+            CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn();
+            return  ((CstInteger) origInsn.getConstant()).getValue();
+        }
+
+        return -1;
+    }
+
+    /**
+     * Maps all local-associated registers that are not parameters.
+     * Tries to find an unreserved range that's wide enough for all of
+     * the SSA registers, and then tries to map them all to that
+     * range. If not all fit, a new range is tried until all registers
+     * have been fit.
+     */
+    private void handleLocalAssociatedOther() {
+        for (ArrayList<RegisterSpec> specs : localVariables.values()) {
+            int ropReg = 0;
+
+            boolean done;
+            do {
+                int maxCategory = 1;
+
+                // Compute max category for remaining unmapped registers.
+                int sz = specs.size();
+                for (int i = 0; i < sz; i++) {
+                    RegisterSpec ssaSpec = specs.get(i);
+                    int category = ssaSpec.getCategory();
+                    if (!ssaRegsMapped.get(ssaSpec.getReg())
+                            && category > maxCategory) {
+                        maxCategory = category;
+                    }
+                }
+
+                ropReg = findRopRegForLocal(ropReg, maxCategory);
+
+                done = tryMapRegs(specs, ropReg, maxCategory, true);
+
+                // Increment for next call to findNext.
+                ropReg++;
+            } while (!done);
+        }
+    }
+
+    /**
+     * Tries to map a list of SSA registers into the a rop reg, marking
+     * used rop space as reserved. SSA registers that don't fit are left
+     * unmapped.
+     *
+     * @param specs {@code non-null;} SSA registers to attempt to map
+     * @param ropReg {@code >=0;} rop register to map to
+     * @param maxAllowedCategory {@code 1..2;} maximum category
+     * allowed in mapping.
+     * @param markReserved do so if {@code true}
+     * @return {@code true} if all registers were mapped, {@code false}
+     * if some remain unmapped
+     */
+    private boolean tryMapRegs(
+            ArrayList<RegisterSpec> specs, int ropReg,
+            int maxAllowedCategory, boolean markReserved) {
+        boolean remaining = false;
+        for (RegisterSpec spec : specs) {
+            if (ssaRegsMapped.get(spec.getReg())) {
+                continue;
+            }
+
+            boolean succeeded;
+            succeeded = tryMapReg(spec, ropReg, maxAllowedCategory);
+            remaining = !succeeded || remaining;
+            if (succeeded && markReserved) {
+                // This only needs to be called once really with
+                // the widest category used, but <shrug>
+                markReserved(ropReg, spec.getCategory());
+            }
+        }
+        return !remaining;
+    }
+
+    /**
+     * Tries to map an SSA register to a rop register.
+     *
+     * @param ssaSpec {@code non-null;} SSA register
+     * @param ropReg {@code >=0;} rop register
+     * @param maxAllowedCategory {@code 1..2;} the maximum category
+     * that the SSA register is allowed to be
+     * @return {@code true} if map succeeded, {@code false} if not
+     */
+    private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg,
+            int maxAllowedCategory) {
+        if (ssaSpec.getCategory() <= maxAllowedCategory
+                && !ssaRegsMapped.get(ssaSpec.getReg())
+                && canMapReg(ssaSpec, ropReg)) {
+            addMapping(ssaSpec, ropReg);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Marks a range of rop registers as "reserved for a local variable."
+     *
+     * @param ropReg {@code >= 0;} rop register to reserve
+     * @param category {@code > 0;} width to reserve
+     */
+    private void markReserved(int ropReg, int category) {
+        reservedRopRegs.set(ropReg, ropReg + category, true);
+    }
+
+    /**
+     * Checks to see if any rop registers in the specified range are reserved
+     * for local variables or parameters.
+     *
+     * @param ropRangeStart {@code >= 0;} lowest rop register
+     * @param width {@code > 0;} number of rop registers in range.
+     * @return {@code true} if any register in range is marked reserved
+     */
+    private boolean rangeContainsReserved(int ropRangeStart, int width) {
+        for (int i = ropRangeStart; i < (ropRangeStart + width); i++) {
+            if (reservedRopRegs.get(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if given rop register represents the {@code this} pointer
+     * for a non-static method.
+     *
+     * @param startReg rop register
+     * @return true if the "this" pointer is located here.
+     */
+    private boolean isThisPointerReg(int startReg) {
+        // "this" is always the first parameter.
+        return startReg == 0 && !ssaMeth.isStatic();
+    }
+
+    /**
+     * Finds a range of unreserved rop registers.
+     *
+     * @param startReg {@code >= 0;} a rop register to start the search at
+     * @param width {@code > 0;} the width, in registers, required.
+     * @return {@code >= 0;} start of available register range.
+     */
+    private int findNextUnreservedRopReg(int startReg, int width) {
+        if (minimizeRegisters && !isThisPointerReg(startReg)) {
+            return startReg;
+        }
+
+        int reg;
+
+        reg = reservedRopRegs.nextClearBit(startReg);
+
+        while (true) {
+            int i = 1;
+
+            while (i < width && !reservedRopRegs.get(reg + i)) {
+                i++;
+            }
+
+            if (i == width) {
+                return reg;
+            }
+
+            reg = reservedRopRegs.nextClearBit(reg + i);
+        }
+    }
+
+    /**
+     * Finds a range of rop regs that can be used for local variables.
+     * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any
+     * rop register that has not yet been used.
+     *
+     * @param startReg {@code >= 0;} a rop register to start the search at
+     * @param width {@code > 0;} the width, in registers, required.
+     * @return {@code >= 0;} start of available register range.
+     */
+    private int findRopRegForLocal(int startReg, int width) {
+        if (minimizeRegisters && !isThisPointerReg(startReg)) {
+            return startReg;
+        }
+
+        int reg;
+
+        reg = usedRopRegs.nextClearBit(startReg);
+
+        while (true) {
+            int i = 1;
+
+            while (i < width && !usedRopRegs.get(reg + i)) {
+                i++;
+            }
+
+            if (i == width) {
+                return reg;
+            }
+
+            reg = usedRopRegs.nextClearBit(reg + i);
+        }
+    }
+
+    /**
+     * Maps any parameter that isn't local-associated, which can happen
+     * in the case where there is no java debug info.
+     */
+    private void handleUnassociatedParameters() {
+        int szSsaRegs = ssaMeth.getRegCount();
+
+        for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
+            if (ssaRegsMapped.get(ssaReg)) {
+                // We already did this one above
+                continue;
+            }
+
+            int paramIndex = getParameterIndexForReg(ssaReg);
+
+            RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
+            if (paramIndex >= 0) {
+                addMapping(ssaSpec, paramIndex);
+            }
+        }
+    }
+
+    /**
+     * Handles all insns that want a register range for their sources.
+     */
+    private void handleInvokeRangeInsns() {
+        for (NormalSsaInsn insn : invokeRangeInsns) {
+            adjustAndMapSourceRangeRange(insn);
+        }
+    }
+
+    /**
+     * Handles check cast results to reuse the same source register if
+     * possible.
+     */
+    private void handleCheckCastResults() {
+        for (NormalSsaInsn insn : moveResultPseudoInsns) {
+            RegisterSpec moveRegSpec = insn.getResult();
+            int moveReg = moveRegSpec.getReg();
+            BitSet predBlocks = insn.getBlock().getPredecessors();
+
+            // Expect one predecessor block only
+            if (predBlocks.cardinality() != 1) {
+                continue;
+            }
+
+            SsaBasicBlock predBlock =
+                    ssaMeth.getBlocks().get(predBlocks.nextSetBit(0));
+            ArrayList<SsaInsn> insnList = predBlock.getInsns();
+
+            /**
+             * If the predecessor block has a check-cast, it will be the last
+             * instruction
+             */
+            SsaInsn checkCastInsn = insnList.get(insnList.size() - 1);
+            if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) {
+                continue;
+            }
+
+            RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0);
+            int checkReg = checkRegSpec.getReg();
+
+            // Assume none of the register is mapped yet
+            int ropReg = 0;
+
+            /**
+             * See if either register is already mapped. Most likely the move
+             * result will be mapped already since the cast result is stored
+             * in a local variable.
+             */
+            if (ssaRegsMapped.get(moveReg)) {
+                ropReg = mapper.oldToNew(moveReg);
+            } else if (ssaRegsMapped.get(checkReg)) {
+                ropReg = mapper.oldToNew(checkReg);
+            }
+
+            ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>(2);
+            ssaRegs.add(moveRegSpec);
+            ssaRegs.add(checkRegSpec);
+            int category = checkRegSpec.getCategory();
+
+            while (!tryMapRegs(ssaRegs, ropReg, category, false)) {
+                ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+            }
+        }
+    }
+
+    /**
+     * Maps all non-parameter, non-local variable registers.
+     */
+    private void handleNormalUnassociated() {
+        int szSsaRegs = ssaMeth.getRegCount();
+
+        for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
+            if (ssaRegsMapped.get(ssaReg)) {
+                // We already did this one
+                continue;
+            }
+
+            RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
+
+            if (ssaSpec == null) continue;
+
+            int category = ssaSpec.getCategory();
+            // Find a rop reg that does not interfere
+            int ropReg = findNextUnreservedRopReg(0, category);
+            while (!canMapReg(ssaSpec, ropReg)) {
+                ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+            }
+
+            addMapping(ssaSpec, ropReg);
+        }
+    }
+
+    /**
+     * Checks to see if {@code ssaSpec} can be mapped to
+     * {@code ropReg}. Checks interference graph and ensures
+     * the range does not cross the parameter range.
+     *
+     * @param ssaSpec {@code non-null;} SSA spec
+     * @param ropReg prosepctive new-namespace reg
+     * @return {@code true} if mapping is possible
+     */
+    private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) {
+        int category = ssaSpec.getCategory();
+        return !(spansParamRange(ropReg, category)
+                || mapper.interferes(ssaSpec, ropReg));
+    }
+
+    /**
+     * Returns true if the specified rop register + category
+     * will cross the boundry between the lower {@code paramWidth}
+     * registers reserved for method params and the upper registers. We cannot
+     * allocate a register that spans the param block and the normal block,
+     * because we will be moving the param block to high registers later.
+     *
+     * @param ssaReg register in new namespace
+     * @param category width that the register will have
+     * @return {@code true} in the case noted above
+     */
+    private boolean spansParamRange(int ssaReg, int category) {
+        return ((ssaReg < paramRangeEnd)
+                && ((ssaReg + category) > paramRangeEnd));
+    }
+
+    /**
+     * Analyze each instruction and find out all the local variable assignments
+     * and move-result-pseudo/invoke-range instrucitons.
+     */
+    private void analyzeInstructions() {
+        ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+            /** {@inheritDoc} */
+            public void visitMoveInsn(NormalSsaInsn insn) {
+                processInsn(insn);
+            }
+
+            /** {@inheritDoc} */
+            public void visitPhiInsn(PhiInsn insn) {
+                processInsn(insn);
+            }
+
+            /** {@inheritDoc} */
+            public void visitNonMoveInsn(NormalSsaInsn insn) {
+                processInsn(insn);
+            }
+
+            /**
+             * This method collects three types of instructions:
+             *
+             * 1) Adds a local variable assignment to the
+             *    {@code localVariables} map.
+             * 2) Add move-result-pseudo to the
+             *    {@code moveResultPseudoInsns} list.
+             * 3) Add invoke-range to the
+             *    {@code invokeRangeInsns} list.
+             *
+             * @param insn {@code non-null;} insn that may represent a
+             * local variable assignment
+             */
+            private void processInsn(SsaInsn insn) {
+                RegisterSpec assignment;
+                assignment = insn.getLocalAssignment();
+
+                if (assignment != null) {
+                    LocalItem local = assignment.getLocalItem();
+
+                    ArrayList<RegisterSpec> regList
+                        = localVariables.get(local);
+
+                    if (regList == null) {
+                        regList = new ArrayList<RegisterSpec>();
+                        localVariables.put(local, regList);
+                    }
+
+                    regList.add(assignment);
+                }
+
+                if (insn instanceof NormalSsaInsn) {
+                    if (insn.getOpcode().getOpcode() ==
+                            RegOps.MOVE_RESULT_PSEUDO) {
+                        moveResultPseudoInsns.add((NormalSsaInsn) insn);
+                    } else if (Optimizer.getAdvice().requiresSourcesInOrder(
+                            insn.getOriginalRopInsn().getOpcode(),
+                            insn.getSources())) {
+                        invokeRangeInsns.add((NormalSsaInsn) insn);
+                    }
+                }
+
+            }
+        });
+    }
+
+    /**
+     * Adds a mapping from an SSA register to a rop register.
+     * {@link #canMapReg} should have already been called.
+     *
+     * @param ssaSpec {@code non-null;} SSA register to map from
+     * @param ropReg {@code >=0;} rop register to map to
+     */
+    private void addMapping(RegisterSpec ssaSpec, int ropReg) {
+        int ssaReg = ssaSpec.getReg();
+
+        // An assertion.
+        if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) {
+            throw new RuntimeException(
+                    "attempt to add invalid register mapping");
+        }
+
+        if (DEBUG) {
+            System.out.printf("Add mapping s%d -> v%d c:%d\n",
+                    ssaSpec.getReg(), ropReg, ssaSpec.getCategory());
+        }
+
+        int category = ssaSpec.getCategory();
+        mapper.addMapping(ssaSpec.getReg(), ropReg, category);
+        ssaRegsMapped.set(ssaReg);
+        usedRopRegs.set(ropReg, ropReg + category);
+    }
+
+
+    /**
+     * Maps the source registers of the specified instruction such that they
+     * will fall in a contiguous range in rop form. Moves are inserted as
+     * necessary to allow the range to be allocated.
+     *
+     * @param insn {@code non-null;} insn whos sources to process
+     */
+    private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) {
+        int newRegStart = findRangeAndAdjust(insn);
+
+        RegisterSpecList sources = insn.getSources();
+        int szSources = sources.size();
+        int nextRopReg = newRegStart;
+
+        for (int i = 0; i < szSources; i++) {
+            RegisterSpec source = sources.get(i);
+            int sourceReg = source.getReg();
+            int category = source.getCategory();
+            int curRopReg = nextRopReg;
+            nextRopReg += category;
+
+            if (ssaRegsMapped.get(sourceReg)) {
+                continue;
+            }
+
+            LocalItem localItem = getLocalItemForReg(sourceReg);
+            addMapping(source, curRopReg);
+
+            if (localItem != null) {
+                markReserved(curRopReg, category);
+                ArrayList<RegisterSpec> similarRegisters
+                        = localVariables.get(localItem);
+
+                int szSimilar = similarRegisters.size();
+
+                /*
+                 * Try to map all SSA registers also associated with
+                 * this local.
+                 */
+                for (int j = 0; j < szSimilar; j++) {
+                    RegisterSpec similarSpec = similarRegisters.get(j);
+                    int similarReg = similarSpec.getReg();
+
+                    // Don't map anything that's also a source.
+                    if (-1 != sources.indexOfRegister(similarReg)) {
+                        continue;
+                    }
+
+                    // Registers left unmapped will get handled later.
+                    tryMapReg(similarSpec, curRopReg, category);
+                }
+            }
+        }
+    }
+
+    /**
+     * Find a contiguous rop register range that fits the specified
+     * instruction's sources. First, try to center the range around
+     * sources that have already been mapped to rop registers. If that fails,
+     * just find a new contiguous range that doesn't interfere.
+     *
+     * @param insn {@code non-null;} the insn whose sources need to
+     * fit. Must be last insn in basic block.
+     * @return {@code >= 0;} rop register of start of range
+     */
+    private int findRangeAndAdjust(NormalSsaInsn insn) {
+        RegisterSpecList sources = insn.getSources();
+        int szSources = sources.size();
+        // the category for each source index
+        int categoriesForIndex[] = new int[szSources];
+        int rangeLength = 0;
+
+        // Compute rangeLength and categoriesForIndex
+        for (int i = 0; i < szSources; i++) {
+            int category = sources.get(i).getCategory();
+            categoriesForIndex[i] = category;
+            rangeLength += categoriesForIndex[i];
+        }
+
+        // the highest score of fits tried so far
+        int maxScore = Integer.MIN_VALUE;
+        // the high scoring range's start
+        int resultRangeStart = -1;
+        // by source index: set of sources needing moves in high scoring plan
+        BitSet resultMovesRequired = null;
+
+        /*
+         * First, go through each source that's already been mapped. Try
+         * to center the range around the rop register this source is mapped
+         * to.
+         */
+        int rangeStartOffset = 0;
+        for (int i = 0; i < szSources; i++) {
+            int ssaCenterReg = sources.get(i).getReg();
+
+            if (i != 0) {
+                rangeStartOffset -= categoriesForIndex[i - 1];
+            }
+            if (!ssaRegsMapped.get(ssaCenterReg)) {
+                continue;
+            }
+
+            int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset;
+
+            if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) {
+                continue;
+            }
+
+            BitSet curMovesRequired = new BitSet(szSources);
+
+            int fitWidth
+                    = fitPlanForRange(rangeStart, insn, categoriesForIndex,
+                    curMovesRequired);
+
+            if (fitWidth < 0) {
+                continue;
+            }
+
+            int score = fitWidth - curMovesRequired.cardinality();
+
+            if (score > maxScore) {
+                maxScore = score;
+                resultRangeStart = rangeStart;
+                resultMovesRequired = curMovesRequired;
+            }
+
+            if (fitWidth == rangeLength) {
+                // We can't do any better than this, so stop here
+                break;
+            }
+        }
+
+        /*
+         * If we were unable to find a plan for a fit centered around
+         * an already-mapped source, just try to find a range of
+         * registers we can move the range into.
+         */
+
+        if (resultRangeStart == -1) {
+            resultMovesRequired = new BitSet(szSources);
+
+            resultRangeStart = findAnyFittingRange(insn, rangeLength,
+                    categoriesForIndex, resultMovesRequired);
+        }
+
+        /*
+         * Now, insert any moves required.
+         */
+
+        for (int i = resultMovesRequired.nextSetBit(0); i >= 0;
+             i = resultMovesRequired.nextSetBit(i+1)) {
+            insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i)));
+        }
+
+        return resultRangeStart;
+    }
+
+    /**
+     * Finds an unreserved range that will fit the sources of the
+     * specified instruction. Does not bother trying to center the range
+     * around an already-mapped source register;
+     *
+     * @param insn {@code non-null;} insn to build range for
+     * @param rangeLength {@code >=0;} length required in register units
+     * @param categoriesForIndex {@code non-null;} indexed by source index;
+     * the category for each source
+     * @param outMovesRequired {@code non-null;} an output parameter indexed by
+     * source index that will contain the set of sources which need
+     * moves inserted
+     * @return the rop register that starts the fitting range
+     */
+    private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength,
+            int[] categoriesForIndex, BitSet outMovesRequired) {
+        int rangeStart = 0;
+        while (true) {
+            rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength);
+            int fitWidth
+                    = fitPlanForRange(rangeStart, insn,
+                    categoriesForIndex, outMovesRequired);
+
+            if (fitWidth >= 0) {
+                break;
+            }
+            rangeStart++;
+            outMovesRequired.clear();
+        }
+        return rangeStart;
+    }
+
+    /**
+     * Attempts to build a plan for fitting a range of sources into rop
+     * registers.
+     *
+     * @param ropReg {@code >= 0;} rop reg that begins range
+     * @param insn {@code non-null;} insn to plan range for
+     * @param categoriesForIndex {@code non-null;} indexed by source index;
+     * the category for each source
+     * @param outMovesRequired {@code non-null;} an output parameter indexed by
+     * source index that will contain the set of sources which need
+     * moves inserted
+     * @return the width of the fit that that does not involve added moves or
+     * {@code -1} if "no fit possible"
+     */
+    private int fitPlanForRange(int ropReg, NormalSsaInsn insn,
+            int[] categoriesForIndex, BitSet outMovesRequired) {
+        RegisterSpecList sources = insn.getSources();
+        int szSources = sources.size();
+        int fitWidth = 0;
+        IntSet liveOut = insn.getBlock().getLiveOutRegs();
+        RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut);
+
+        // An SSA reg may only be mapped into a range once.
+        BitSet seen = new BitSet(ssaMeth.getRegCount());
+
+        for (int i = 0; i < szSources ; i++) {
+            RegisterSpec ssaSpec = sources.get(i);
+            int ssaReg = ssaSpec.getReg();
+            int category = categoriesForIndex[i];
+
+            if (i != 0) {
+                ropReg += categoriesForIndex[i-1];
+            }
+
+            if (ssaRegsMapped.get(ssaReg)
+                    && mapper.oldToNew(ssaReg) == ropReg) {
+                // This is a register that is already mapped appropriately.
+                fitWidth += category;
+            } else if (rangeContainsReserved(ropReg, category)) {
+                fitWidth = -1;
+                break;
+            } else if (!ssaRegsMapped.get(ssaReg)
+                    && canMapReg(ssaSpec, ropReg)
+                    && !seen.get(ssaReg)) {
+                // This is a register that can be mapped appropriately.
+                fitWidth += category;
+            } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category)
+                    && !mapper.areAnyPinned(sources, ropReg, category)) {
+                /*
+                 * This is a source that can be moved. We can insert a
+                 * move as long as:
+                 *
+                 *   * no SSA register pinned to the desired rop reg
+                 *     is live out on the block
+                 *
+                 *   * no SSA register pinned to desired rop reg is
+                 *     a source of this insn (since this may require
+                 *     overlapping moves, which we can't presently handle)
+                 */
+
+                outMovesRequired.set(i);
+            } else {
+                fitWidth = -1;
+                break;
+            }
+
+            seen.set(ssaReg);
+        }
+        return fitWidth;
+    }
+
+    /**
+     * Converts a bit set of SSA registers into a RegisterSpecList containing
+     * the definition specs of all the registers.
+     *
+     * @param ssaSet {@code non-null;} set of SSA registers
+     * @return list of RegisterSpecs as noted above
+     */
+    RegisterSpecList ssaSetToSpecs(IntSet ssaSet) {
+        RegisterSpecList result = new RegisterSpecList(ssaSet.elements());
+
+        IntIterator iter = ssaSet.iterator();
+
+        int i = 0;
+        while (iter.hasNext()) {
+            result.set(i++, getDefinitionSpecForSsaReg(iter.next()));
+        }
+
+        return result;
+    }
+
+    /**
+     * Gets a local item associated with an ssa register, if one exists.
+     *
+     * @param ssaReg {@code >= 0;} SSA register
+     * @return {@code null-ok;} associated local item or null
+     */
+    private LocalItem getLocalItemForReg(int ssaReg) {
+        for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry :
+                 localVariables.entrySet()) {
+            for (RegisterSpec spec : entry.getValue()) {
+                if (spec.getReg() == ssaReg) {
+                    return entry.getKey();
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java b/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java
new file mode 100644
index 0000000..515b04f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.util.IntList;
+
+import java.util.BitSet;
+
+/**
+ * Searches for basic blocks that all have the same successor and insns
+ * but different predecessors. These blocks are then combined into a single
+ * block and the now-unused blocks are deleted. These identical blocks
+ * frequently are created when catch blocks are edge-split.
+ */
+public class IdenticalBlockCombiner {
+    private final RopMethod ropMethod;
+    private final BasicBlockList blocks;
+    private final BasicBlockList newBlocks;
+
+    /**
+     * Constructs instance. Call {@code process()} to run.
+     *
+     * @param rm {@code non-null;} instance to process
+     */
+    public IdenticalBlockCombiner(RopMethod rm) {
+        ropMethod = rm;
+        blocks = ropMethod.getBlocks();
+        newBlocks = blocks.getMutableCopy();
+    }
+
+    /**
+     * Runs algorithm. TODO: This is n^2, and could be made linear-ish with
+     * a hash. In particular, hash the contents of each block and only
+     * compare blocks with the same hash.
+     *
+     * @return {@code non-null;} new method that has been processed
+     */
+    public RopMethod process() {
+        int szBlocks = blocks.size();
+        // indexed by label
+        BitSet toDelete = new BitSet(blocks.getMaxLabel());
+
+        // For each non-deleted block...
+        for (int bindex = 0; bindex < szBlocks; bindex++) {
+            BasicBlock b = blocks.get(bindex);
+
+            if (toDelete.get(b.getLabel())) {
+                // doomed block
+                continue;
+            }
+
+            IntList preds = ropMethod.labelToPredecessors(b.getLabel());
+
+            // ...look at all of it's predecessors that have only one succ...
+            int szPreds = preds.size();
+            for (int i = 0; i < szPreds; i++) {
+                int iLabel = preds.get(i);
+
+                BasicBlock iBlock = blocks.labelToBlock(iLabel);
+
+                if (toDelete.get(iLabel)
+                        || iBlock.getSuccessors().size() > 1
+                        || iBlock.getFirstInsn().getOpcode().getOpcode() ==
+                            RegOps.MOVE_RESULT) {
+                    continue;
+                }
+
+                IntList toCombine = new IntList();
+
+                // ...and see if they can be combined with any other preds...
+                for (int j = i + 1; j < szPreds; j++) {
+                    int jLabel = preds.get(j);
+                    BasicBlock jBlock = blocks.labelToBlock(jLabel);
+
+                    if (jBlock.getSuccessors().size() == 1
+                            && compareInsns(iBlock, jBlock)) {
+
+                        toCombine.add(jLabel);
+                        toDelete.set(jLabel);
+                    }
+                }
+
+                combineBlocks(iLabel, toCombine);
+            }
+        }
+
+        for (int i = szBlocks - 1; i >= 0; i--) {
+            if (toDelete.get(newBlocks.get(i).getLabel())) {
+                newBlocks.set(i, null);
+            }
+        }
+
+        newBlocks.shrinkToFit();
+        newBlocks.setImmutable();
+
+        return new RopMethod(newBlocks, ropMethod.getFirstLabel());
+    }
+
+    /**
+     * Helper method to compare the contents of two blocks.
+     *
+     * @param a {@code non-null;} a block to compare
+     * @param b {@code non-null;} another block to compare
+     * @return {@code true} iff the two blocks' instructions are the same
+     */
+    private static boolean compareInsns(BasicBlock a, BasicBlock b) {
+        return a.getInsns().contentEquals(b.getInsns());
+    }
+
+    /**
+     * Combines blocks proven identical into one alpha block, re-writing
+     * all of the successor links that point to the beta blocks to point
+     * to the alpha block instead.
+     *
+     * @param alphaLabel block that will replace all the beta block
+     * @param betaLabels label list of blocks to combine
+     */
+    private void combineBlocks(int alphaLabel, IntList betaLabels) {
+        int szBetas = betaLabels.size();
+
+        for (int i = 0; i < szBetas; i++) {
+            int betaLabel = betaLabels.get(i);
+            BasicBlock bb = blocks.labelToBlock(betaLabel);
+            IntList preds = ropMethod.labelToPredecessors(bb.getLabel());
+            int szPreds = preds.size();
+
+            for (int j = 0; j < szPreds; j++) {
+                BasicBlock predBlock = newBlocks.labelToBlock(preds.get(j));
+                replaceSucc(predBlock, betaLabel, alphaLabel);
+            }
+        }
+    }
+
+    /**
+     * Replaces one of a block's successors with a different label. Constructs
+     * an updated BasicBlock instance and places it in {@code newBlocks}.
+     *
+     * @param block block to replace
+     * @param oldLabel label of successor to replace
+     * @param newLabel label of new successor
+     */
+    private void replaceSucc(BasicBlock block, int oldLabel, int newLabel) {
+        IntList newSuccessors = block.getSuccessors().mutableCopy();
+        int newPrimarySuccessor;
+
+        newSuccessors.set(newSuccessors.indexOf(oldLabel), newLabel);
+        newPrimarySuccessor = block.getPrimarySuccessor();
+
+        if (newPrimarySuccessor == oldLabel) {
+            newPrimarySuccessor = newLabel;
+        }
+
+        newSuccessors.setImmutable();
+
+        BasicBlock newBB = new BasicBlock(block.getLabel(),
+                block.getInsns(), newSuccessors, newPrimarySuccessor);
+
+        newBlocks.set(newBlocks.indexOfLabel(block.getLabel()), newBB);
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/InterferenceGraph.java b/dx/src/com/android/dx/ssa/back/InterferenceGraph.java
new file mode 100644
index 0000000..e6cde62
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/InterferenceGraph.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.SetFactory;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A register interference graph
+ */
+public class InterferenceGraph {
+    /**
+     * {@code non-null;} interference graph, indexed by register in
+     * both dimensions
+     */
+    private final ArrayList<IntSet> interference;
+
+    /**
+     * Creates a new graph.
+     *
+     * @param countRegs {@code >= 0;} the start count of registers in
+     * the namespace. New registers can be added subsequently.
+     */
+    public InterferenceGraph(int countRegs) {
+        interference = new ArrayList<IntSet>(countRegs);
+
+        for (int i = 0; i < countRegs; i++) {
+            interference.add(SetFactory.makeInterferenceSet(countRegs));
+        }
+    }
+
+    /**
+     * Adds a register pair to the interference/liveness graph. Parameter
+     * order is insignificant.
+     *
+     * @param regV one register index
+     * @param regW another register index
+     */
+    public void add(int regV, int regW) {
+        ensureCapacity(Math.max(regV, regW) + 1);
+
+        interference.get(regV).add(regW);
+        interference.get(regW).add(regV);
+    }
+
+    /**
+     * Dumps interference graph to stdout for debugging.
+     */
+    public void dumpToStdout() {
+        int oldRegCount = interference.size();
+
+        for (int i = 0; i < oldRegCount; i++) {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append("Reg " + i + ":" + interference.get(i).toString());
+
+            System.out.println(sb.toString());
+        }
+    }
+
+    /**
+     * Merges the interference set for a register into a given bit set
+     *
+     * @param reg {@code >= 0;} register
+     * @param set {@code non-null;} interference set; will be merged
+     * with set for given register
+     */
+    public void mergeInterferenceSet(int reg, IntSet set) {
+        if (reg < interference.size()) {
+            set.merge(interference.get(reg));
+        }
+    }
+
+    /**
+     * Ensures that the interference graph is appropriately sized.
+     *
+     * @param size requested minumum size
+     */
+    private void ensureCapacity(int size) {
+        int countRegs = interference.size();
+
+        interference.ensureCapacity(size);
+
+        for (int i = countRegs; i < size; i++) {
+            interference.add(SetFactory.makeInterferenceSet(size));
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java b/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java
new file mode 100644
index 0000000..a293e6f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.rop.code.RegisterSpec;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * From Appel "Modern Compiler Implementation in Java" algorithm 19.17
+ * Calculate the live ranges for register {@code reg}.<p>
+ *
+ * v = regV <p>
+ * s = insn <p>
+ * M = visitedBlocks <p>
+ */
+public class LivenessAnalyzer {
+    /**
+     * {@code non-null;} index by basic block indexed set of basic blocks
+     * that have already been visited. "M" as written in the original Appel
+     * algorithm.
+     */
+    private final BitSet visitedBlocks;
+
+    /**
+     * {@code non-null;} set of blocks remaing to visit as "live out as block"
+     */
+    private final BitSet liveOutBlocks;
+
+    /**
+     * {@code >=0;} SSA register currently being analyzed.
+     * "v" in the original Appel algorithm.
+     */
+    private final int regV;
+
+    /** method to process */
+    private final SsaMethod ssaMeth;
+
+    /** interference graph being updated */
+    private final InterferenceGraph interference;
+
+    /** block "n" in Appel 19.17 */
+    private SsaBasicBlock blockN;
+
+    /** index of statement {@code s} in {@code blockN} */
+    private int statementIndex;
+
+    /** the next function to call */
+    private NextFunction nextFunction;
+
+    /** constants for {@link #nextFunction} */
+    private static enum NextFunction {
+        LIVE_IN_AT_STATEMENT,
+            LIVE_OUT_AT_STATEMENT,
+            LIVE_OUT_AT_BLOCK,
+            DONE;
+    }
+
+    /**
+     * Runs register liveness algorithm for a method, updating the
+     * live in/out information in {@code SsaBasicBlock} instances and
+     * returning an interference graph.
+     *
+     * @param ssaMeth {@code non-null;} method to process
+     * @return {@code non-null;} interference graph indexed by SSA
+     * registers in both directions
+     */
+    public static InterferenceGraph constructInterferenceGraph(
+            SsaMethod ssaMeth) {
+        int szRegs = ssaMeth.getRegCount();
+        InterferenceGraph interference = new InterferenceGraph(szRegs);
+
+        for (int i = 0; i < szRegs; i++) {
+            new LivenessAnalyzer(ssaMeth, i, interference).run();
+        }
+
+        coInterferePhis(ssaMeth, interference);
+
+        return interference;
+    }
+
+    /**
+     * Makes liveness analyzer instance for specific register.
+     *
+     * @param ssaMeth {@code non-null;} method to process
+     * @param reg register whose liveness to analyze
+     * @param interference {@code non-null;} indexed by SSA reg in
+     * both dimensions; graph to update
+     *
+     */
+    private LivenessAnalyzer(SsaMethod ssaMeth, int reg,
+            InterferenceGraph interference) {
+        int blocksSz = ssaMeth.getBlocks().size();
+
+        this.ssaMeth = ssaMeth;
+        this.regV = reg;
+        visitedBlocks = new BitSet(blocksSz);
+        liveOutBlocks = new BitSet(blocksSz);
+        this.interference = interference;
+    }
+
+    /**
+     * The algorithm in Appel is presented in partial tail-recursion
+     * form. Obviously, that's not efficient in java, so this function
+     * serves as the dispatcher instead.
+     */
+    private void handleTailRecursion() {
+        while (nextFunction != NextFunction.DONE) {
+            switch (nextFunction) {
+                case LIVE_IN_AT_STATEMENT:
+                    nextFunction = NextFunction.DONE;
+                    liveInAtStatement();
+                    break;
+
+                case LIVE_OUT_AT_STATEMENT:
+                    nextFunction = NextFunction.DONE;
+                    liveOutAtStatement();
+                    break;
+
+                case LIVE_OUT_AT_BLOCK:
+                    nextFunction = NextFunction.DONE;
+                    liveOutAtBlock();
+                    break;
+
+                default:
+            }
+        }
+    }
+
+    /**
+     * From Appel algorithm 19.17.
+     */
+    public void run() {
+        List<SsaInsn> useList = ssaMeth.getUseListForRegister(regV);
+
+        for (SsaInsn insn : useList) {
+            nextFunction = NextFunction.DONE;
+
+            if (insn instanceof PhiInsn) {
+                // If s is a phi-function with V as it's ith argument.
+                PhiInsn phi = (PhiInsn) insn;
+
+                for (SsaBasicBlock pred :
+                         phi.predBlocksForReg(regV, ssaMeth)) {
+                    blockN = pred;
+
+                    nextFunction = NextFunction.LIVE_OUT_AT_BLOCK;
+                    handleTailRecursion();
+                }
+            } else {
+                blockN = insn.getBlock();
+                statementIndex = blockN.getInsns().indexOf(insn);
+
+                if (statementIndex < 0) {
+                    throw new RuntimeException(
+                            "insn not found in it's own block");
+                }
+
+                nextFunction = NextFunction.LIVE_IN_AT_STATEMENT;
+                handleTailRecursion();
+            }
+        }
+
+        int nextLiveOutBlock;
+        while ((nextLiveOutBlock = liveOutBlocks.nextSetBit(0)) >= 0) {
+            blockN = ssaMeth.getBlocks().get(nextLiveOutBlock);
+            liveOutBlocks.clear(nextLiveOutBlock);
+            nextFunction = NextFunction.LIVE_OUT_AT_BLOCK;
+            handleTailRecursion();
+        }
+    }
+
+    /**
+     * "v is live-out at n."
+     */
+    private void liveOutAtBlock() {
+        if (! visitedBlocks.get(blockN.getIndex())) {
+            visitedBlocks.set(blockN.getIndex());
+
+            blockN.addLiveOut(regV);
+
+            ArrayList<SsaInsn> insns;
+
+            insns = blockN.getInsns();
+
+            // Live out at last statement in blockN
+            statementIndex = insns.size() - 1;
+            nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT;
+        }
+    }
+
+    /**
+     * "v is live-in at s."
+     */
+    private void liveInAtStatement() {
+        // if s is the first statement in block N
+        if (statementIndex == 0) {
+            // v is live-in at n
+            blockN.addLiveIn(regV);
+
+            BitSet preds = blockN.getPredecessors();
+
+            liveOutBlocks.or(preds);
+        } else {
+            // Let s' be the statement preceeding s
+            statementIndex -= 1;
+            nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT;
+        }
+    }
+
+    /**
+     * "v is live-out at s."
+     */
+    private void liveOutAtStatement() {
+        SsaInsn statement = blockN.getInsns().get(statementIndex);
+        RegisterSpec rs = statement.getResult();
+
+        if (!statement.isResultReg(regV)) {
+            if (rs != null) {
+                interference.add(regV, rs.getReg());
+            }
+            nextFunction = NextFunction.LIVE_IN_AT_STATEMENT;
+        }
+    }
+
+    /**
+     * Ensures that all the phi result registers for all the phis in the
+     * same basic block interfere with each other. This is needed since
+     * the dead code remover has allowed through "dead-end phis" whose
+     * results are not used except as local assignments. Without this step,
+     * a the result of a dead-end phi might be assigned the same register
+     * as the result of another phi, and the phi removal move scheduler may
+     * generate moves that over-write the live result.
+     *
+     * @param ssaMeth {@code non-null;} method to pricess
+     * @param interference {@code non-null;} interference graph
+     */
+    private static void coInterferePhis(SsaMethod ssaMeth,
+            InterferenceGraph interference) {
+        for (SsaBasicBlock b : ssaMeth.getBlocks()) {
+            List<SsaInsn> phis = b.getPhiInsns();
+
+            int szPhis = phis.size();
+
+            for (int i = 0; i < szPhis; i++) {
+                for (int j = 0; j < szPhis; j++) {
+                    if (i == j) {
+                        continue;
+                    }
+
+                    interference.add(phis.get(i).getResult().getReg(),
+                        phis.get(j).getResult().getReg());
+                }
+            }
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java b/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java
new file mode 100644
index 0000000..0205c11
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaMethod;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * A register allocator that maps SSA register n to Rop register 2*n,
+ * essentially preserving the original mapping and remaining agnostic
+ * about normal or wide categories. Used for debugging.
+ */
+public class NullRegisterAllocator extends RegisterAllocator {
+    /** {@inheritDoc} */
+    public NullRegisterAllocator(SsaMethod ssaMeth,
+            InterferenceGraph interference) {
+        super(ssaMeth, interference);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean wantsParamsMovedHigh() {
+        // We're not smart enough for this.
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public RegisterMapper allocateRegisters() {
+        int oldRegCount = ssaMeth.getRegCount();
+
+        BasicRegisterMapper mapper = new BasicRegisterMapper(oldRegCount);
+
+        for (int i = 0; i < oldRegCount; i++) {
+            mapper.addMapping(i, i*2, 2);
+        }
+
+        return mapper;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/RegisterAllocator.java b/dx/src/com/android/dx/ssa/back/RegisterAllocator.java
new file mode 100644
index 0000000..1f9f70f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/RegisterAllocator.java
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.IntIterator;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Base class of all register allocators.
+ */
+public abstract class RegisterAllocator {
+    /** method being processed */
+    protected final SsaMethod ssaMeth;
+
+    /** interference graph, indexed by register in both dimensions */
+    protected final InterferenceGraph interference;
+
+    /**
+     * Creates an instance. Call {@code allocateRegisters} to run.
+     * @param ssaMeth method to process.
+     * @param interference Interference graph, indexed by register in both
+     * dimensions.
+     */
+    public RegisterAllocator(SsaMethod ssaMeth,
+            InterferenceGraph interference) {
+        this.ssaMeth = ssaMeth;
+        this.interference = interference;
+    }
+
+    /**
+     * Indicates whether the method params were allocated at the bottom
+     * of the namespace, and thus should be moved up to the top of the
+     * namespace after phi removal.
+     *
+     * @return {@code true} if params should be moved from low to high
+     */
+    public abstract boolean wantsParamsMovedHigh();
+
+    /**
+     * Runs the algorithm.
+     *
+     * @return a register mapper to apply to the {@code SsaMethod}
+     */
+    public abstract RegisterMapper allocateRegisters();
+
+    /**
+     * Returns the category (width) of the definition site of the register.
+     * Returns {@code 1} for undefined registers.
+     *
+     * @param reg register
+     * @return {@code 1..2}
+     */
+    protected final int getCategoryForSsaReg(int reg) {
+        SsaInsn definition = ssaMeth.getDefinitionForRegister(reg);
+
+        if (definition == null) {
+            // an undefined reg
+            return 1;
+        } else {
+            return definition.getResult().getCategory();
+        }
+    }
+
+    /**
+     * Returns the RegisterSpec of the definition of the register.
+     *
+     * @param reg {@code >= 0;} SSA register
+     * @return definition spec of the register or null if it is never defined
+     * (for the case of "version 0" SSA registers)
+     */
+    protected final RegisterSpec getDefinitionSpecForSsaReg(int reg) {
+        SsaInsn definition = ssaMeth.getDefinitionForRegister(reg);
+
+        return definition == null ? null : definition.getResult();
+    }
+
+    /**
+     * Returns true if the definition site of this register is a
+     * move-param (ie, this is a method parameter).
+     *
+     * @param reg register in question
+     * @return {@code true} if this is a method parameter
+     */
+    protected boolean isDefinitionMoveParam(int reg) {
+        SsaInsn defInsn = ssaMeth.getDefinitionForRegister(reg);
+
+        if (defInsn instanceof NormalSsaInsn) {
+            NormalSsaInsn ndefInsn = (NormalSsaInsn) defInsn;
+
+            return ndefInsn.getOpcode().getOpcode() == RegOps.MOVE_PARAM;
+        }
+
+        return false;
+    }
+
+    /**
+     * Inserts a move instruction for a specified SSA register before a
+     * specified instruction, creating a new SSA register and adjusting the
+     * interference graph in the process. The insn currently must be the
+     * last insn in a block.
+     *
+     * @param insn {@code non-null;} insn to insert move before, must
+     * be last insn in block
+     * @param reg {@code non-null;} SSA register to duplicate
+     * @return {@code non-null;} spec of new SSA register created by move
+     */
+    protected final RegisterSpec insertMoveBefore(SsaInsn insn,
+            RegisterSpec reg) {
+        SsaBasicBlock block = insn.getBlock();
+        ArrayList<SsaInsn> insns = block.getInsns();
+        int insnIndex = insns.indexOf(insn);
+
+        if (insnIndex < 0) {
+            throw new IllegalArgumentException (
+                    "specified insn is not in this block");
+        }
+
+        if (insnIndex != insns.size() - 1) {
+            /*
+             * Presently, the interference updater only works when
+             * adding before the last insn, and the last insn must have no
+             * result
+             */
+            throw new IllegalArgumentException(
+                    "Adding move here not supported:" + insn.toHuman());
+        }
+
+        /*
+         * Get new register and make new move instruction.
+         */
+
+        // The new result must not have an associated local variable.
+        RegisterSpec newRegSpec = RegisterSpec.make(ssaMeth.makeNewSsaReg(),
+                reg.getTypeBearer());
+
+        SsaInsn toAdd = SsaInsn.makeFromRop(
+                new PlainInsn(Rops.opMove(newRegSpec.getType()),
+                        SourcePosition.NO_INFO, newRegSpec,
+                        RegisterSpecList.make(reg)), block);
+
+        insns.add(insnIndex, toAdd);
+
+        int newReg = newRegSpec.getReg();
+
+        /*
+         * Adjust interference graph based on what's live out of the current
+         * block and what's used by the final instruction.
+         */
+
+        IntSet liveOut = block.getLiveOutRegs();
+        IntIterator liveOutIter = liveOut.iterator();
+
+        while (liveOutIter.hasNext()) {
+            interference.add(newReg, liveOutIter.next());
+        }
+
+        // Everything that's a source in the last insn interferes.
+        RegisterSpecList sources = insn.getSources();
+        int szSources = sources.size();
+
+        for (int i = 0; i < szSources; i++) {
+            interference.add(newReg, sources.get(i).getReg());
+        }
+
+        ssaMeth.onInsnsChanged();
+
+        return newRegSpec;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/back/SsaToRop.java b/dx/src/com/android/dx/ssa/back/SsaToRop.java
new file mode 100644
index 0000000..d9d2c45
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/SsaToRop.java
@@ -0,0 +1,386 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.util.IntList;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Converts a method in SSA form to ROP form.
+ */
+public class SsaToRop {
+    /** local debug flag */
+    private static final boolean DEBUG = false;
+
+    /** {@code non-null;} method to process */
+    private final SsaMethod ssaMeth;
+
+    /**
+     * {@code true} if the converter should attempt to minimize
+     * the rop-form register count
+     */
+    private final boolean minimizeRegisters;
+
+    /** {@code non-null;} interference graph */
+    private final InterferenceGraph interference;
+
+    /**
+     * Converts a method in SSA form to ROP form.
+     *
+     * @param ssaMeth {@code non-null;} method to process
+     * @param minimizeRegisters {@code true} if the converter should
+     * attempt to minimize the rop-form register count
+     * @return {@code non-null;} rop-form output
+     */
+    public static RopMethod convertToRopMethod(SsaMethod ssaMeth,
+            boolean minimizeRegisters) {
+        return new SsaToRop(ssaMeth, minimizeRegisters).convert();
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param ssaMeth {@code non-null;} method to process
+     * @param minimizeRegisters {@code true} if the converter should
+     * attempt to minimize the rop-form register count
+     */
+    private SsaToRop(SsaMethod ssaMethod, boolean minimizeRegisters) {
+        this.minimizeRegisters = minimizeRegisters;
+        this.ssaMeth = ssaMethod;
+        this.interference =
+            LivenessAnalyzer.constructInterferenceGraph(ssaMethod);
+    }
+
+    /**
+     * Performs the conversion.
+     *
+     * @return {@code non-null;} rop-form output
+     */
+    private RopMethod convert() {
+        if (DEBUG) {
+            interference.dumpToStdout();
+        }
+
+        // These are other allocators for debugging or historical comparison:
+        // allocator = new NullRegisterAllocator(ssaMeth, interference);
+        // allocator = new FirstFitAllocator(ssaMeth, interference);
+
+        RegisterAllocator allocator =
+            new FirstFitLocalCombiningAllocator(ssaMeth, interference,
+                    minimizeRegisters);
+
+        RegisterMapper mapper = allocator.allocateRegisters();
+
+        if (DEBUG) {
+            System.out.println("Printing reg map");
+            System.out.println(((BasicRegisterMapper)mapper).toHuman());
+        }
+
+        ssaMeth.setBackMode();
+
+        ssaMeth.mapRegisters(mapper);
+
+        removePhiFunctions();
+
+        if (allocator.wantsParamsMovedHigh()) {
+            moveParametersToHighRegisters();
+        }
+
+        removeEmptyGotos();
+
+        RopMethod ropMethod = new RopMethod(convertBasicBlocks(),
+                ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex()));
+        ropMethod = new IdenticalBlockCombiner(ropMethod).process();
+
+        return ropMethod;
+    }
+
+    /**
+     * Removes all blocks containing only GOTOs from the control flow.
+     * Although much of this work will be done later when converting
+     * from rop to dex, not all simplification cases can be handled
+     * there. Furthermore, any no-op block between the exit block and
+     * blocks containing the real return or throw statements must be
+     * removed.
+     */
+    private void removeEmptyGotos() {
+        final ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+        ssaMeth.forEachBlockDepthFirst(false, new SsaBasicBlock.Visitor() {
+            public void visitBlock(SsaBasicBlock b, SsaBasicBlock parent) {
+                ArrayList<SsaInsn> insns = b.getInsns();
+
+                if ((insns.size() == 1)
+                        && (insns.get(0).getOpcode() == Rops.GOTO)) {
+                    BitSet preds = (BitSet) b.getPredecessors().clone();
+
+                    for (int i = preds.nextSetBit(0); i >= 0;
+                            i = preds.nextSetBit(i + 1)) {
+                        SsaBasicBlock pb = blocks.get(i);
+                        pb.replaceSuccessor(b.getIndex(),
+                                b.getPrimarySuccessorIndex());
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * See Appel 19.6. To remove the phi instructions in an edge-split
+     * SSA representation we know we can always insert a move in a
+     * predecessor block.
+     */
+    private void removePhiFunctions() {
+        ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+        for (SsaBasicBlock block : blocks) {
+            // Add moves in all the pred blocks for each phi insn.
+            block.forEachPhiInsn(new PhiVisitor(blocks));
+
+            // Delete the phi insns.
+            block.removeAllPhiInsns();
+        }
+
+        /*
+         * After all move insns have been added, sort them so they don't
+         * destructively interfere.
+         */
+        for (SsaBasicBlock block : blocks) {
+            block.scheduleMovesFromPhis();
+        }
+    }
+
+    /**
+     * Helper for {@link #removePhiFunctions}: PhiSuccessorUpdater for
+     * adding move instructions to predecessors based on phi insns.
+     */
+    private static class PhiVisitor implements PhiInsn.Visitor {
+        private final ArrayList<SsaBasicBlock> blocks;
+
+        public PhiVisitor(ArrayList<SsaBasicBlock> blocks) {
+            this.blocks = blocks;
+        }
+
+        public void visitPhiInsn(PhiInsn insn) {
+            RegisterSpecList sources = insn.getSources();
+            RegisterSpec result = insn.getResult();
+            int sz = sources.size();
+
+            for (int i = 0; i < sz; i++) {
+                RegisterSpec source = sources.get(i);
+                SsaBasicBlock predBlock = blocks.get(
+                        insn.predBlockIndexForSourcesIndex(i));
+
+                predBlock.addMoveToEnd(result, source);
+            }
+        }
+    }
+
+    /**
+     * Moves the parameter registers, which allocateRegisters() places
+     * at the bottom of the frame, up to the top of the frame to match
+     * Dalvik calling convention.
+     */
+    private void moveParametersToHighRegisters() {
+        int paramWidth = ssaMeth.getParamWidth();
+        BasicRegisterMapper mapper
+                = new BasicRegisterMapper(ssaMeth.getRegCount());
+        int regCount = ssaMeth.getRegCount();
+
+        for (int i = 0; i < regCount; i++) {
+            if (i < paramWidth) {
+                mapper.addMapping(i, regCount - paramWidth + i, 1);
+            } else {
+                mapper.addMapping(i, i - paramWidth, 1);
+            }
+        }
+
+        if (DEBUG) {
+            System.out.printf("Moving %d registers from 0 to %d\n",
+                    paramWidth, regCount - paramWidth);
+        }
+
+        ssaMeth.mapRegisters(mapper);
+    }
+
+    /**
+     * @return rop-form basic block list
+     */
+    private BasicBlockList convertBasicBlocks() {
+        ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+        // Exit block may be null.
+        SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
+
+        ssaMeth.computeReachability();
+        int ropBlockCount = ssaMeth.getCountReachableBlocks();
+
+        // Don't count the exit block, if it exists.
+        ropBlockCount -= (exitBlock == null) ? 0 : 1;
+
+        BasicBlockList result = new BasicBlockList(ropBlockCount);
+
+        // Convert all the reachable blocks except the exit block.
+        int ropBlockIndex = 0;
+        for (SsaBasicBlock b : blocks) {
+            if (b.isReachable() && b != exitBlock) {
+                result.set(ropBlockIndex++, convertBasicBlock(b));
+            }
+        }
+
+        // The exit block, which is discarded, must do nothing.
+        if (exitBlock != null && exitBlock.getInsns().size() != 0) {
+            throw new RuntimeException(
+                    "Exit block must have no insns when leaving SSA form");
+        }
+
+        return result;
+    }
+
+    /**
+     * Validates that a basic block is a valid end predecessor. It must
+     * end in a RETURN or a THROW. Throws a runtime exception on error.
+     *
+     * @param b {@code non-null;} block to validate
+     * @throws RuntimeException on error
+     */
+    private void verifyValidExitPredecessor(SsaBasicBlock b) {
+        ArrayList<SsaInsn> insns = b.getInsns();
+        SsaInsn lastInsn = insns.get(insns.size() - 1);
+        Rop opcode = lastInsn.getOpcode();
+
+        if (opcode.getBranchingness() != Rop.BRANCH_RETURN
+                && opcode != Rops.THROW) {
+            throw new RuntimeException("Exit predecessor must end"
+                    + " in valid exit statement.");
+        }
+    }
+
+    /**
+     * Converts a single basic block to rop form.
+     *
+     * @param block SSA block to process
+     * @return {@code non-null;} ROP block
+     */
+    private BasicBlock convertBasicBlock(SsaBasicBlock block) {
+        IntList successorList = block.getRopLabelSuccessorList();
+        int primarySuccessorLabel = block.getPrimarySuccessorRopLabel();
+
+        // Filter out any reference to the SSA form's exit block.
+
+        // Exit block may be null.
+        SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
+        int exitRopLabel = (exitBlock == null) ? -1 : exitBlock.getRopLabel();
+
+        if (successorList.contains(exitRopLabel)) {
+            if (successorList.size() > 1) {
+                throw new RuntimeException(
+                        "Exit predecessor must have no other successors"
+                                + Hex.u2(block.getRopLabel()));
+            } else {
+                successorList = IntList.EMPTY;
+                primarySuccessorLabel = -1;
+
+                verifyValidExitPredecessor(block);
+            }
+        }
+
+        successorList.setImmutable();
+
+        BasicBlock result = new BasicBlock(
+                block.getRopLabel(), convertInsns(block.getInsns()),
+                successorList,
+                primarySuccessorLabel);
+
+        return result;
+    }
+
+    /**
+     * Converts an insn list to rop form.
+     *
+     * @param ssaInsns {@code non-null;} old instructions
+     * @return {@code non-null;} immutable instruction list
+     */
+    private InsnList convertInsns(ArrayList<SsaInsn> ssaInsns) {
+        int insnCount = ssaInsns.size();
+        InsnList result = new InsnList(insnCount);
+
+        for (int i = 0; i < insnCount; i++) {
+            result.set(i, ssaInsns.get(i).toRopInsn());
+        }
+
+        result.setImmutable();
+
+        return result;
+    }
+
+    /**
+     * <b>Note:</b> This method is not presently used.
+     *
+     * @return a list of registers ordered by most-frequently-used to
+     * least-frequently-used. Each register is listed once and only
+     * once.
+     */
+    public int[] getRegistersByFrequency() {
+        int regCount = ssaMeth.getRegCount();
+        Integer[] ret = new Integer[regCount];
+
+        for (int i = 0; i < regCount; i++) {
+            ret[i] = i;
+        }
+
+        Arrays.sort(ret, new Comparator<Integer>() {
+            public int compare(Integer o1, Integer o2) {
+                return ssaMeth.getUseListForRegister(o2).size()
+                        - ssaMeth.getUseListForRegister(o1).size();
+            }
+        });
+
+        int result[] = new int[regCount];
+
+        for (int i = 0; i < regCount; i++) {
+            result[i] = ret[i];
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/ssa/package-info.java b/dx/src/com/android/dx/ssa/package-info.java
new file mode 100644
index 0000000..582a327
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/package-info.java
@@ -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.
+ */
+
+package com.android.dx.ssa;
+
+/**
+ * <h1>An introduction to SSA Form</h1>
+ *
+ * This package contains classes associated with dx's {@code SSA}
+ * intermediate form. This form is a static-single-assignment representation of
+ * Rop-form a method with Rop-form-like instructions (with the addition of a
+ * {@link PhiInsn phi instriction}. This form is intended to make it easy to
+ * implement basic optimization steps and register allocation so that a
+ * reasonably efficient register machine representation can be produced from a
+ * stack machine source bytecode.<p>
+ *
+ * <h2>Key Classes</h2>
+ *
+ * <h3>Classes related to conversion and lifetime</h3>
+ * <ul>
+ * <li> {@link Optimizer} is a singleton class containing methods for
+ * converting, optimizing, and then back-converting Rop-form methods. It's the
+ * typical gateway into the rest of the package.
+ * <li> {@link SsaConverter} converts a Rop-form method to SSA form.
+ * <li> {@link SsaToRop} converts an SSA-form method back to Rop form.
+ * </ul>
+ *
+ * <h3>Classes related to method representation</h3>
+ * <ul>
+ * <li> A {@link SsaMethod} instance represents a method.
+ * <li> A {@link SsaBasicBlock} instance represents a basic block, whose
+ * semantics are quite similar to basic blocks in
+ * {@link com.android.dx.rop Rop form}.
+ * <li> {@link PhiInsn} instances represent "phi" operators defined in SSA
+ * literature. They must be the first N instructions in a basic block.
+ * <li> {@link NormalSsaInsn} instances represent instructions that directly
+ * correspond to {@code Rop} form.
+ * </ul>
+ *
+ * <h3>Classes related to optimization steps</h3>
+ * <ul>
+ * <li> {@link MoveParamCombiner} is a simple step that ensures each method
+ * parameter is represented by at most one SSA register.
+ * <li> {@link SCCP} is a (partially implemented) sparse-conditional
+ * constant propogator.
+ * <li> {@link LiteralOpUpgrader} is a step that attempts to use constant
+ * information to convert math and comparison instructions into
+ * constant-bearing "literal ops" in cases where they can be represented in the
+ * output form (see {@link TranslationAdvice#hasConstantOperation}).
+ * <li> {@link ConstCollector} is a step that attempts to trade (modest)
+ * increased register space for decreased instruction count in cases where
+ * the same constant value is used repeatedly in a single method.
+ * <li> {@link DeadCodeRemover} is a dead code remover. This phase must
+ * always be run to remove unused phi instructions.
+ * </ul>
+ *
+ * <h2>SSA Lifetime</h2>
+ * The representation of a method in SSA form obeys slightly different
+ * constraints depending upon whether it is in the process of being converted
+ * into or out of SSA form.
+ *
+ * <h3>Conversion into SSA Form</h3>
+ *
+ * {@link SsaConverter#convertToSsaMethod} takes a {@code RopMethod} and
+ * returns a fully-converted {@code SsaMethod}. The conversion process
+ * is roughly as follows:
+ *
+ * <ol>
+ * <li> The Rop-form method, its blocks and their instructions are directly
+ * wrapped in {@code SsaMethod}, {@code SsaBasicBlock} and
+ * {@code SsaInsn} instances. Nothing else changes.
+ * <li> Critical control-flow graph edges are {@link SsaConverter#edgeSplit
+ * split} and new basic blocks inserted as required to meet the constraints
+ * necessary for the ultimate SSA representation.
+ * <li> A {@link LocalVariableExtractor} is run to produce a table of
+ * Rop registers to local variables necessary during phi placement. This
+ * step could also be done in Rop form and then updated through the preceding
+ * steps.
+ * <li> {@code Phi} instructions are {link SsaConverter#placePhiFunctions}
+ * placed in a semi-pruned fashion, which requires computation of {@link
+ * Dominators dominance graph} and each node's {@link DomFront
+ * dominance-frontier set}.
+ * <li> Finally, source and result registers for all instructions are {@link
+ * SsaRenamer renamed} such that each assignment is given a unique register
+ * number (register categories or widths, significant in Rop form, do not
+ * exist in SSA). Move instructions are eliminated except where necessary
+ * to preserve local variable assignments.
+ * </ol>
+ *
+ */
diff --git a/dx/src/com/android/dx/util/AnnotatedOutput.java b/dx/src/com/android/dx/util/AnnotatedOutput.java
new file mode 100644
index 0000000..7a9ea29
--- /dev/null
+++ b/dx/src/com/android/dx/util/AnnotatedOutput.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Interface for a binary output destination that may be augmented
+ * with textual annotations.
+ */
+public interface AnnotatedOutput
+        extends Output {
+    /**
+     * Get whether this instance will actually keep annotations.
+     *
+     * @return {@code true} iff annotations are being kept
+     */
+    public boolean annotates();
+
+    /**
+     * Get whether this instance is intended to keep verbose annotations.
+     * Annotators may use the result of calling this method to inform their
+     * annotation activity.
+     *
+     * @return {@code true} iff annotations are to be verbose
+     */
+    public boolean isVerbose();
+
+    /**
+     * Add an annotation for the subsequent output. Any previously
+     * open annotation will be closed by this call, and the new
+     * annotation marks all subsequent output until another annotation
+     * call.
+     *
+     * @param msg {@code non-null;} the annotation message
+     */
+    public void annotate(String msg);
+
+    /**
+     * Add an annotation for a specified amount of subsequent
+     * output. Any previously open annotation will be closed by this
+     * call. If there is already pending annotation from one or more
+     * previous calls to this method, the new call "consumes" output
+     * after all the output covered by the previous calls.
+     *
+     * @param amt {@code >= 0;} the amount of output for this annotation to
+     * cover
+     * @param msg {@code non-null;} the annotation message
+     */
+    public void annotate(int amt, String msg);
+
+    /**
+     * End the most recent annotation. Subsequent output will be unannotated,
+     * until the next call to {@link #annotate}.
+     */
+    public void endAnnotation();
+
+    /**
+     * Get the maximum width of the annotated output. This is advisory:
+     * Implementations of this interface are encouraged to deal with too-wide
+     * output, but annotaters are encouraged to attempt to avoid exceeding
+     * the indicated width.
+     *
+     * @return {@code >= 1;} the maximum width
+     */
+    public int getAnnotationWidth();
+}
diff --git a/dx/src/com/android/dx/util/BitIntSet.java b/dx/src/com/android/dx/util/BitIntSet.java
new file mode 100644
index 0000000..b364f0c
--- /dev/null
+++ b/dx/src/com/android/dx/util/BitIntSet.java
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A set of integers, represented by a bit set
+ */
+public class BitIntSet implements IntSet {
+
+    /** also accessed in ListIntSet */
+    int[] bits;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param max the maximum value of ints in this set.
+     */
+    public BitIntSet(int max) {
+        bits = Bits.makeBitSet(max);
+    }
+
+    /** @inheritDoc */
+    public void add(int value) {
+        ensureCapacity(value);
+        Bits.set(bits, value, true);
+    }
+
+    /**
+     * Ensures that the bit set has the capacity to represent the given value.
+     *
+     * @param value {@code >= 0;} value to represent
+     */
+    private void ensureCapacity(int value) {
+        if (value >= Bits.getMax(bits)) {
+            int[] newBits = Bits.makeBitSet(
+                    Math.max(value + 1, 2 * Bits.getMax(bits)));
+            System.arraycopy(bits, 0, newBits, 0, bits.length);
+            bits = newBits;
+        }
+    }
+
+    /** @inheritDoc */
+    public void remove(int value) {
+        if (value < Bits.getMax(bits)) {
+            Bits.set(bits, value, false);
+        }
+    }
+
+    /** @inheritDoc */
+    public boolean has(int value) {
+        return (value < Bits.getMax(bits)) && Bits.get(bits, value);
+    }
+
+    /** @inheritDoc */
+    public void merge(IntSet other) {
+        if (other instanceof BitIntSet) {
+            BitIntSet o = (BitIntSet) other;
+            ensureCapacity(Bits.getMax(o.bits) + 1);
+            Bits.or(bits, o.bits);
+        } else if (other instanceof ListIntSet) {
+            ListIntSet o = (ListIntSet) other;
+            int sz = o.ints.size();
+
+            if (sz > 0) {
+                ensureCapacity(o.ints.get(sz - 1));
+            }
+            for (int i = 0; i < o.ints.size(); i++) {
+                Bits.set(bits, o.ints.get(i), true);
+            }
+        } else {
+            IntIterator iter = other.iterator();
+            while (iter.hasNext()) {
+                add(iter.next());
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    public int elements() {
+        return Bits.bitCount(bits);
+    }
+
+    /** @inheritDoc */
+    public IntIterator iterator() {
+        return new IntIterator() {
+            private int idx = Bits.findFirst(bits, 0);
+
+            /** @inheritDoc */
+            public boolean hasNext() {
+                return idx >= 0;
+            }
+
+            /** @inheritDoc */
+            public int next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+
+                int ret = idx;
+
+                idx = Bits.findFirst(bits, idx+1);
+
+                return ret;
+            }
+        };
+    }
+
+    /** @inheritDoc */
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append('{');
+
+        boolean first = true;
+        for (int i = Bits.findFirst(bits, 0)
+                ; i >= 0
+                ; i = Bits.findFirst(bits, i + 1)) {
+            if (!first) {
+                sb.append(", ");
+            }
+            first = false;
+            sb.append(i);
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/util/Bits.java b/dx/src/com/android/dx/util/Bits.java
new file mode 100644
index 0000000..cbc0a5b
--- /dev/null
+++ b/dx/src/com/android/dx/util/Bits.java
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for treating {@code int[]}s as bit sets.
+ */
+public final class Bits {
+    /**
+     * This class is uninstantiable.
+     */
+    private Bits() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Constructs a bit set to contain bits up to the given index (exclusive).
+     *
+     * @param max {@code >= 0;} the maximum bit index (exclusive)
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public static int[] makeBitSet(int max) {
+        int size = (max + 0x1f) >> 5;
+        return new int[size];
+    }
+
+    /**
+     * Gets the maximum index (exclusive) for the given bit set.
+     *
+     * @param bits {@code non-null;} bit set in question
+     * @return {@code >= 0;} the maximum index (exclusive) that may be set
+     */
+    public static int getMax(int[] bits) {
+        return bits.length * 0x20;
+    }
+
+    /**
+     * Gets the value of the bit at the given index.
+     *
+     * @param bits {@code non-null;} bit set to operate on
+     * @param idx {@code >= 0, < getMax(set);} which bit
+     * @return the value of the indicated bit
+     */
+    public static boolean get(int[] bits, int idx) {
+        int arrayIdx = idx >> 5;
+        int bit = 1 << (idx & 0x1f);
+        return (bits[arrayIdx] & bit) != 0;
+    }
+
+    /**
+     * Sets the given bit to the given value.
+     *
+     * @param bits {@code non-null;} bit set to operate on
+     * @param idx {@code >= 0, < getMax(set);} which bit
+     * @param value the new value for the bit
+     */
+    public static void set(int[] bits, int idx, boolean value) {
+        int arrayIdx = idx >> 5;
+        int bit = 1 << (idx & 0x1f);
+
+        if (value) {
+            bits[arrayIdx] |= bit;
+        } else {
+            bits[arrayIdx] &= ~bit;
+        }
+    }
+
+    /**
+     * Sets the given bit to {@code true}.
+     *
+     * @param bits {@code non-null;} bit set to operate on
+     * @param idx {@code >= 0, < getMax(set);} which bit
+     */
+    public static void set(int[] bits, int idx) {
+        int arrayIdx = idx >> 5;
+        int bit = 1 << (idx & 0x1f);
+        bits[arrayIdx] |= bit;
+    }
+
+    /**
+     * Sets the given bit to {@code false}.
+     *
+     * @param bits {@code non-null;} bit set to operate on
+     * @param idx {@code >= 0, < getMax(set);} which bit
+     */
+    public static void clear(int[] bits, int idx) {
+        int arrayIdx = idx >> 5;
+        int bit = 1 << (idx & 0x1f);
+        bits[arrayIdx] &= ~bit;
+    }
+
+    /**
+     * Returns whether or not the given bit set is empty, that is, whether
+     * no bit is set to {@code true}.
+     *
+     * @param bits {@code non-null;} bit set to operate on
+     * @return {@code true} iff all bits are {@code false}
+     */
+    public static boolean isEmpty(int[] bits) {
+        int len = bits.length;
+
+        for (int i = 0; i < len; i++) {
+            if (bits[i] != 0) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Gets the number of bits set to {@code true} in the given bit set.
+     *
+     * @param bits {@code non-null;} bit set to operate on
+     * @return {@code >= 0;} the bit count (aka population count) of the set
+     */
+    public static int bitCount(int[] bits) {
+        int len = bits.length;
+        int count = 0;
+
+        for (int i = 0; i < len; i++) {
+            count += Integer.bitCount(bits[i]);
+        }
+
+        return count;
+    }
+
+    /**
+     * Returns whether any bits are set to {@code true} in the
+     * specified range.
+     *
+     * @param bits {@code non-null;} bit set to operate on
+     * @param start {@code >= 0;} index of the first bit in the range (inclusive)
+     * @param end {@code >= 0;} index of the last bit in the range (exclusive)
+     * @return {@code true} if any bit is set to {@code true} in
+     * the indicated range
+     */
+    public static boolean anyInRange(int[] bits, int start, int end) {
+        int idx = findFirst(bits, start);
+        return (idx >= 0) && (idx < end);
+    }
+
+    /**
+     * Finds the lowest-order bit set at or after the given index in the
+     * given bit set.
+     *
+     * @param bits {@code non-null;} bit set to operate on
+     * @param idx {@code >= 0;} minimum index to return
+     * @return {@code >= -1;} lowest-order bit set at or after {@code idx},
+     * or {@code -1} if there is no appropriate bit index to return
+     */
+    public static int findFirst(int[] bits, int idx) {
+        int len = bits.length;
+        int minBit = idx & 0x1f;
+
+        for (int arrayIdx = idx >> 5; arrayIdx < len; arrayIdx++) {
+            int word = bits[arrayIdx];
+            if (word != 0) {
+                int bitIdx = findFirst(word, minBit);
+                if (bitIdx >= 0) {
+                    return (arrayIdx << 5) + bitIdx;
+                }
+            }
+            minBit = 0;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Finds the lowest-order bit set at or after the given index in the
+     * given {@code int}.
+     *
+     * @param value the value in question
+     * @param idx 0..31 the minimum bit index to return
+     * @return {@code >= -1;} lowest-order bit set at or after {@code idx},
+     * or {@code -1} if there is no appropriate bit index to return
+     */
+    public static int findFirst(int value, int idx) {
+        value &= ~((1 << idx) - 1); // Mask off too-low bits.
+        int result = Integer.numberOfTrailingZeros(value);
+        return (result == 32) ? -1 : result;
+    }
+
+    /**
+     * Ors bit array {@code b} into bit array {@code a}.
+     * {@code a.length} must be greater than or equal to
+     * {@code b.length}.
+     *
+     * @param a {@code non-null;} int array to be ored with other argument. This
+     * argument is modified.
+     * @param b {@code non-null;} int array to be ored into {@code a}. This
+     * argument is not modified.
+     */
+    public static void or(int[] a, int[] b) {
+        for (int i = 0; i < b.length; i++) {
+            a[i] |= b[i];
+        }
+    }
+
+    public static String toHuman(int[] bits) {
+        StringBuilder sb = new StringBuilder();
+
+        boolean needsComma = false;
+
+        sb.append('{');
+
+        int bitsLength = 32 * bits.length;
+        for (int i = 0; i < bitsLength; i++) {
+            if (Bits.get(bits, i)) {
+                if (needsComma) {
+                    sb.append(',');
+                }
+                needsComma = true;
+                sb.append(i);
+            }
+        }
+        sb.append('}');
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/util/ByteArray.java b/dx/src/com/android/dx/util/ByteArray.java
new file mode 100644
index 0000000..21d0457
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArray.java
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Wrapper for a {@code byte[]}, which provides read-only access and
+ * can "reveal" a partial slice of the underlying array.
+ *
+ * <b>Note:</b> Multibyte accessors all use big-endian order.
+ */
+public final class ByteArray {
+    /** {@code non-null;} underlying array */
+    private final byte[] bytes;
+
+    /** {@code >= 0}; start index of the slice (inclusive) */
+    private final int start;
+
+    /** {@code >= 0, <= bytes.length}; size computed as
+     * {@code end - start} (in the constructor) */
+    private final int size;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param bytes {@code non-null;} the underlying array
+     * @param start {@code >= 0;} start index of the slice (inclusive)
+     * @param end {@code >= start, <= bytes.length;} end index of
+     * the slice (exclusive)
+     */
+    public ByteArray(byte[] bytes, int start, int end) {
+        if (bytes == null) {
+            throw new NullPointerException("bytes == null");
+        }
+
+        if (start < 0) {
+            throw new IllegalArgumentException("start < 0");
+        }
+
+        if (end < start) {
+            throw new IllegalArgumentException("end < start");
+        }
+
+        if (end > bytes.length) {
+            throw new IllegalArgumentException("end > bytes.length");
+        }
+
+        this.bytes = bytes;
+        this.start = start;
+        this.size = end - start;
+    }
+
+    /**
+     * Constructs an instance from an entire {@code byte[]}.
+     *
+     * @param bytes {@code non-null;} the underlying array
+     */
+    public ByteArray(byte[] bytes) {
+        this(bytes, 0, bytes.length);
+    }
+
+    /**
+     * Gets the size of the array, in bytes.
+     *
+     * @return {@code >= 0;} the size
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Returns a slice (that is, a sub-array) of this instance.
+     *
+     * @param start {@code >= 0;} start index of the slice (inclusive)
+     * @param end {@code >= start, <= size();} end index of
+     * the slice (exclusive)
+     * @return {@code non-null;} the slice
+     */
+    public ByteArray slice(int start, int end) {
+        checkOffsets(start, end);
+        return new ByteArray(bytes, start + this.start, end + this.start);
+    }
+
+    /**
+     * Returns the offset into the given array represented by the given
+     * offset into this instance.
+     *
+     * @param offset offset into this instance
+     * @param bytes {@code non-null;} (alleged) underlying array
+     * @return corresponding offset into {@code bytes}
+     * @throws IllegalArgumentException thrown if {@code bytes} is
+     * not the underlying array of this instance
+     */
+    public int underlyingOffset(int offset, byte[] bytes) {
+        if (bytes != this.bytes) {
+            throw new IllegalArgumentException("wrong bytes");
+        }
+
+        return start + offset;
+    }
+
+    /**
+     * Gets the {@code signed byte} value at a particular offset.
+     *
+     * @param off {@code >= 0, < size();} offset to fetch
+     * @return {@code signed byte} at that offset
+     */
+    public int getByte(int off) {
+        checkOffsets(off, off + 1);
+        return getByte0(off);
+    }
+
+    /**
+     * Gets the {@code signed short} value at a particular offset.
+     *
+     * @param off {@code >= 0, < (size() - 1);} offset to fetch
+     * @return {@code signed short} at that offset
+     */
+    public int getShort(int off) {
+        checkOffsets(off, off + 2);
+        return (getByte0(off) << 8) | getUnsignedByte0(off + 1);
+    }
+
+    /**
+     * Gets the {@code signed int} value at a particular offset.
+     *
+     * @param off {@code >= 0, < (size() - 3);} offset to fetch
+     * @return {@code signed int} at that offset
+     */
+    public int getInt(int off) {
+        checkOffsets(off, off + 4);
+        return (getByte0(off) << 24) |
+            (getUnsignedByte0(off + 1) << 16) |
+            (getUnsignedByte0(off + 2) << 8) |
+            getUnsignedByte0(off + 3);
+    }
+
+    /**
+     * Gets the {@code signed long} value at a particular offset.
+     *
+     * @param off {@code >= 0, < (size() - 7);} offset to fetch
+     * @return {@code signed int} at that offset
+     */
+    public long getLong(int off) {
+        checkOffsets(off, off + 8);
+        int part1 = (getByte0(off) << 24) |
+            (getUnsignedByte0(off + 1) << 16) |
+            (getUnsignedByte0(off + 2) << 8) |
+            getUnsignedByte0(off + 3);
+        int part2 = (getByte0(off + 4) << 24) |
+            (getUnsignedByte0(off + 5) << 16) |
+            (getUnsignedByte0(off + 6) << 8) |
+            getUnsignedByte0(off + 7);
+
+        return (part2 & 0xffffffffL) | ((long) part1) << 32;
+    }
+
+    /**
+     * Gets the {@code unsigned byte} value at a particular offset.
+     *
+     * @param off {@code >= 0, < size();} offset to fetch
+     * @return {@code unsigned byte} at that offset
+     */
+    public int getUnsignedByte(int off) {
+        checkOffsets(off, off + 1);
+        return getUnsignedByte0(off);
+    }
+
+    /**
+     * Gets the {@code unsigned short} value at a particular offset.
+     *
+     * @param off {@code >= 0, < (size() - 1);} offset to fetch
+     * @return {@code unsigned short} at that offset
+     */
+    public int getUnsignedShort(int off) {
+        checkOffsets(off, off + 2);
+        return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1);
+    }
+
+    /**
+     * Copies the contents of this instance into the given raw
+     * {@code byte[]} at the given offset. The given array must be
+     * large enough.
+     *
+     * @param out {@code non-null;} array to hold the output
+     * @param offset {@code non-null;} index into {@code out} for the first
+     * byte of output
+     */
+    public void getBytes(byte[] out, int offset) {
+        if ((out.length - offset) < size) {
+            throw new IndexOutOfBoundsException("(out.length - offset) < " +
+                                                "size()");
+        }
+
+        System.arraycopy(bytes, start, out, offset, size);
+    }
+
+    /**
+     * Checks a range of offsets for validity, throwing if invalid.
+     *
+     * @param s start offset (inclusive)
+     * @param e end offset (exclusive)
+     */
+    private void checkOffsets(int s, int e) {
+        if ((s < 0) || (e < s) || (e > size)) {
+            throw new IllegalArgumentException("bad range: " + s + ".." + e +
+                                               "; actual size " + size);
+        }
+    }
+
+    /**
+     * Gets the {@code signed byte} value at the given offset,
+     * without doing any argument checking.
+     *
+     * @param off offset to fetch
+     * @return byte at that offset
+     */
+    private int getByte0(int off) {
+        return bytes[start + off];
+    }
+
+    /**
+     * Gets the {@code unsigned byte} value at the given offset,
+     * without doing any argument checking.
+     *
+     * @param off offset to fetch
+     * @return byte at that offset
+     */
+    private int getUnsignedByte0(int off) {
+        return bytes[start + off] & 0xff;
+    }
+
+    /**
+     * Gets a {@code DataInputStream} that reads from this instance,
+     * with the cursor starting at the beginning of this instance's data.
+     * <b>Note:</b> The returned instance may be cast to {@link #GetCursor}
+     * if needed.
+     *
+     * @return {@code non-null;} an appropriately-constructed
+     * {@code DataInputStream} instance
+     */
+    public MyDataInputStream makeDataInputStream() {
+        return new MyDataInputStream(makeInputStream());
+    }
+
+    /**
+     * Gets a {@code InputStream} that reads from this instance,
+     * with the cursor starting at the beginning of this instance's data.
+     * <b>Note:</b> The returned instance may be cast to {@link #GetCursor}
+     * if needed.
+     *
+     * @return {@code non-null;} an appropriately-constructed
+     * {@code InputStream} instancex
+     */
+    public MyInputStream makeInputStream() {
+        return new MyInputStream();
+    }
+
+    /**
+     * Helper interface that allows one to get the cursor (of a stream).
+     */
+    public interface GetCursor {
+        /**
+         * Gets the current cursor.
+         *
+         * @return {@code 0..size();} the cursor
+         */
+        public int getCursor();
+    }
+
+    /**
+     * Helper class for {@link #makeInputStream}, which implements the
+     * stream functionality.
+     */
+    public class MyInputStream extends InputStream {
+        /** 0..size; the cursor */
+        private int cursor;
+
+        /** 0..size; the mark */
+        private int mark;
+
+        public MyInputStream() {
+            cursor = 0;
+            mark = 0;
+        }
+
+        public int read() throws IOException {
+            if (cursor >= size) {
+                return -1;
+            }
+
+            int result = getUnsignedByte0(cursor);
+            cursor++;
+            return result;
+        }
+
+        public int read(byte[] arr, int offset, int length) {
+            if ((offset + length) > arr.length) {
+                length = arr.length - offset;
+            }
+
+            int maxLength = size - cursor;
+            if (length > maxLength) {
+                length = maxLength;
+            }
+
+            System.arraycopy(bytes, cursor + start, arr, offset, length);
+            cursor += length;
+            return length;
+        }
+
+        public int available() {
+            return size - cursor;
+        }
+
+        public void mark(int reserve) {
+            mark = cursor;
+        }
+
+        public void reset() {
+            cursor = mark;
+        }
+
+        public boolean markSupported() {
+            return true;
+        }
+    }
+
+    /**
+     * Helper class for {@link #makeDataInputStream}. This is used
+     * simply so that the cursor of a wrapped {@link #MyInputStream}
+     * instance may be easily determined.
+     */
+    public static class MyDataInputStream extends DataInputStream {
+        /** {@code non-null;} the underlying {@link #MyInputStream} */
+        private final MyInputStream wrapped;
+
+        public MyDataInputStream(MyInputStream wrapped) {
+            super(wrapped);
+
+            this.wrapped = wrapped;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
new file mode 100644
index 0000000..6d0615e
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
@@ -0,0 +1,639 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * Implementation of {@link AnnotatedOutput} which stores the written data
+ * into a {@code byte[]}.
+ *
+ * <p><b>Note:</b> As per the {@link Output} interface, multi-byte
+ * writes all use little-endian order.</p>
+ */
+public final class ByteArrayAnnotatedOutput
+        implements AnnotatedOutput {
+    /** default size for stretchy instances */
+    private static final int DEFAULT_SIZE = 1000;
+
+    /**
+     * whether the instance is stretchy, that is, whether its array
+     * may be resized to increase capacity
+     */
+    private final boolean stretchy;
+
+    /** {@code non-null;} the data itself */
+    private byte[] data;
+
+    /** {@code >= 0;} current output cursor */
+    private int cursor;
+
+    /** whether annotations are to be verbose */
+    private boolean verbose;
+
+    /**
+     * {@code null-ok;} list of annotations, or {@code null} if this instance
+     * isn't keeping them
+     */
+    private ArrayList<Annotation> annotations;
+
+    /** {@code >= 40 (if used);} the desired maximum annotation width */
+    private int annotationWidth;
+
+    /**
+     * {@code >= 8 (if used);} the number of bytes of hex output to use
+     * in annotations
+     */
+    private int hexCols;
+
+    /**
+     * Constructs an instance with a fixed maximum size. Note that the
+     * given array is the only one that will be used to store data. In
+     * particular, no reallocation will occur in order to expand the
+     * capacity of the resulting instance. Also, the constructed
+     * instance does not keep annotations by default.
+     *
+     * @param data {@code non-null;} data array to use for output
+     */
+    public ByteArrayAnnotatedOutput(byte[] data) {
+        this(data, false);
+    }
+
+    /**
+     * Constructs a "stretchy" instance. The underlying array may be
+     * reallocated. The constructed instance does not keep annotations
+     * by default.
+     */
+    public ByteArrayAnnotatedOutput() {
+        this(new byte[DEFAULT_SIZE], true);
+    }
+
+    /**
+     * Internal constructor.
+     *
+     * @param data {@code non-null;} data array to use for output
+     * @param stretchy whether the instance is to be stretchy
+     */
+    private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) {
+        if (data == null) {
+            throw new NullPointerException("data == null");
+        }
+
+        this.stretchy = stretchy;
+        this.data = data;
+        this.cursor = 0;
+        this.verbose = false;
+        this.annotations = null;
+        this.annotationWidth = 0;
+        this.hexCols = 0;
+    }
+
+    /**
+     * Gets the underlying {@code byte[]} of this instance, which
+     * may be larger than the number of bytes written
+     *
+     * @see #toByteArray
+     *
+     * @return {@code non-null;} the {@code byte[]}
+     */
+    public byte[] getArray() {
+        return data;
+    }
+
+    /**
+     * Constructs and returns a new {@code byte[]} that contains
+     * the written contents exactly (that is, with no extra unwritten
+     * bytes at the end).
+     *
+     * @see #getArray
+     *
+     * @return {@code non-null;} an appropriately-constructed array
+     */
+    public byte[] toByteArray() {
+        byte[] result = new byte[cursor];
+        System.arraycopy(data, 0, result, 0, cursor);
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public int getCursor() {
+        return cursor;
+    }
+
+    /** {@inheritDoc} */
+    public void assertCursor(int expectedCursor) {
+        if (cursor != expectedCursor) {
+            throw new ExceptionWithContext("expected cursor " +
+                    expectedCursor + "; actual value: " + cursor);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void writeByte(int value) {
+        int writeAt = cursor;
+        int end = writeAt + 1;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        data[writeAt] = (byte) value;
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void writeShort(int value) {
+        int writeAt = cursor;
+        int end = writeAt + 2;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        data[writeAt] = (byte) value;
+        data[writeAt + 1] = (byte) (value >> 8);
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void writeInt(int value) {
+        int writeAt = cursor;
+        int end = writeAt + 4;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        data[writeAt] = (byte) value;
+        data[writeAt + 1] = (byte) (value >> 8);
+        data[writeAt + 2] = (byte) (value >> 16);
+        data[writeAt + 3] = (byte) (value >> 24);
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void writeLong(long value) {
+        int writeAt = cursor;
+        int end = writeAt + 8;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        int half = (int) value;
+        data[writeAt] = (byte) half;
+        data[writeAt + 1] = (byte) (half >> 8);
+        data[writeAt + 2] = (byte) (half >> 16);
+        data[writeAt + 3] = (byte) (half >> 24);
+
+        half = (int) (value >> 32);
+        data[writeAt + 4] = (byte) half;
+        data[writeAt + 5] = (byte) (half >> 8);
+        data[writeAt + 6] = (byte) (half >> 16);
+        data[writeAt + 7] = (byte) (half >> 24);
+
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public int writeUnsignedLeb128(int value) {
+        int remaining = value >> 7;
+        int count = 0;
+
+        while (remaining != 0) {
+            writeByte((value & 0x7f) | 0x80);
+            value = remaining;
+            remaining >>= 7;
+            count++;
+        }
+
+        writeByte(value & 0x7f);
+        return count + 1;
+    }
+
+    /** {@inheritDoc} */
+    public int writeSignedLeb128(int value) {
+        int remaining = value >> 7;
+        int count = 0;
+        boolean hasMore = true;
+        int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+        while (hasMore) {
+            hasMore = (remaining != end)
+                || ((remaining & 1) != ((value >> 6) & 1));
+
+            writeByte((value & 0x7f) | (hasMore ? 0x80 : 0));
+            value = remaining;
+            remaining >>= 7;
+            count++;
+        }
+
+        return count;
+    }
+
+    /** {@inheritDoc} */
+    public void write(ByteArray bytes) {
+        int blen = bytes.size();
+        int writeAt = cursor;
+        int end = writeAt + blen;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        bytes.getBytes(data, writeAt);
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void write(byte[] bytes, int offset, int length) {
+        int writeAt = cursor;
+        int end = writeAt + length;
+        int bytesEnd = offset + length;
+
+        // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+        if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
+            throw new IndexOutOfBoundsException("bytes.length " +
+                                                bytes.length + "; " +
+                                                offset + "..!" + end);
+        }
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        System.arraycopy(bytes, offset, data, writeAt, length);
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void write(byte[] bytes) {
+        write(bytes, 0, bytes.length);
+    }
+
+    /** {@inheritDoc} */
+    public void writeZeroes(int count) {
+        if (count < 0) {
+            throw new IllegalArgumentException("count < 0");
+        }
+
+        int end = cursor + count;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        /*
+         * There is no need to actually write zeroes, since the array is
+         * already preinitialized with zeroes.
+         */
+
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public void alignTo(int alignment) {
+        int mask = alignment - 1;
+
+        if ((alignment < 0) || ((mask & alignment) != 0)) {
+            throw new IllegalArgumentException("bogus alignment");
+        }
+
+        int end = (cursor + mask) & ~mask;
+
+        if (stretchy) {
+            ensureCapacity(end);
+        } else if (end > data.length) {
+            throwBounds();
+            return;
+        }
+
+        /*
+         * There is no need to actually write zeroes, since the array is
+         * already preinitialized with zeroes.
+         */
+
+        cursor = end;
+    }
+
+    /** {@inheritDoc} */
+    public boolean annotates() {
+        return (annotations != null);
+    }
+
+    /** {@inheritDoc} */
+    public boolean isVerbose() {
+        return verbose;
+    }
+
+    /** {@inheritDoc} */
+    public void annotate(String msg) {
+        if (annotations == null) {
+            return;
+        }
+
+        endAnnotation();
+        annotations.add(new Annotation(cursor, msg));
+    }
+
+    /** {@inheritDoc} */
+    public void annotate(int amt, String msg) {
+        if (annotations == null) {
+            return;
+        }
+
+        endAnnotation();
+
+        int asz = annotations.size();
+        int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
+        int startAt;
+
+        if (lastEnd <= cursor) {
+            startAt = cursor;
+        } else {
+            startAt = lastEnd;
+        }
+
+        annotations.add(new Annotation(startAt, startAt + amt, msg));
+    }
+
+    /** {@inheritDoc} */
+    public void endAnnotation() {
+        if (annotations == null) {
+            return;
+        }
+
+        int sz = annotations.size();
+
+        if (sz != 0) {
+            annotations.get(sz - 1).setEndIfUnset(cursor);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public int getAnnotationWidth() {
+        int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);
+
+        return annotationWidth - leftWidth;
+    }
+
+    /**
+     * Indicates that this instance should keep annotations. This method may
+     * be called only once per instance, and only before any data has been
+     * written to the it.
+     *
+     * @param annotationWidth {@code >= 40;} the desired maximum annotation width
+     * @param verbose whether or not to indicate verbose annotations
+     */
+    public void enableAnnotations(int annotationWidth, boolean verbose) {
+        if ((annotations != null) || (cursor != 0)) {
+            throw new RuntimeException("cannot enable annotations");
+        }
+
+        if (annotationWidth < 40) {
+            throw new IllegalArgumentException("annotationWidth < 40");
+        }
+
+        int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
+        if (hexCols < 6) {
+            hexCols = 6;
+        } else if (hexCols > 10) {
+            hexCols = 10;
+        }
+
+        this.annotations = new ArrayList<Annotation>(1000);
+        this.annotationWidth = annotationWidth;
+        this.hexCols = hexCols;
+        this.verbose = verbose;
+    }
+
+    /**
+     * Finishes up annotation processing. This closes off any open
+     * annotations and removes annotations that don't refer to written
+     * data.
+     */
+    public void finishAnnotating() {
+        // Close off the final annotation, if any.
+        endAnnotation();
+
+        // Remove annotations that refer to unwritten data.
+        if (annotations != null) {
+            int asz = annotations.size();
+            while (asz > 0) {
+                Annotation last = annotations.get(asz - 1);
+                if (last.getStart() > cursor) {
+                    annotations.remove(asz - 1);
+                    asz--;
+                } else if (last.getEnd() > cursor) {
+                    last.setEnd(cursor);
+                    break;
+                } else {
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Writes the annotated content of this instance to the given writer.
+     *
+     * @param out {@code non-null;} where to write to
+     */
+    public void writeAnnotationsTo(Writer out) throws IOException {
+        int width2 = getAnnotationWidth();
+        int width1 = annotationWidth - width2 - 1;
+
+        TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|");
+        Writer left = twoc.getLeft();
+        Writer right = twoc.getRight();
+        int leftAt = 0; // left-hand byte output cursor
+        int rightAt = 0; // right-hand annotation index
+        int rightSz = annotations.size();
+
+        while ((leftAt < cursor) && (rightAt < rightSz)) {
+            Annotation a = annotations.get(rightAt);
+            int start = a.getStart();
+            int end;
+            String text;
+
+            if (leftAt < start) {
+                // This is an area with no annotation.
+                end = start;
+                start = leftAt;
+                text = "";
+            } else {
+                // This is an area with an annotation.
+                end = a.getEnd();
+                text = a.getText();
+                rightAt++;
+            }
+
+            left.write(Hex.dump(data, start, end - start, start, hexCols, 6));
+            right.write(text);
+            twoc.flush();
+            leftAt = end;
+        }
+
+        if (leftAt < cursor) {
+            // There is unannotated output at the end.
+            left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt,
+                                hexCols, 6));
+        }
+
+        while (rightAt < rightSz) {
+            // There are zero-byte annotations at the end.
+            right.write(annotations.get(rightAt).getText());
+            rightAt++;
+        }
+
+        twoc.flush();
+    }
+
+    /**
+     * Throws the excpetion for when an attempt is made to write past the
+     * end of the instance.
+     */
+    private static void throwBounds() {
+        throw new IndexOutOfBoundsException("attempt to write past the end");
+    }
+
+    /**
+     * Reallocates the underlying array if necessary. Calls to this method
+     * should be guarded by a test of {@link #stretchy}.
+     *
+     * @param desiredSize {@code >= 0;} the desired minimum total size of the array
+     */
+    private void ensureCapacity(int desiredSize) {
+        if (data.length < desiredSize) {
+            byte[] newData = new byte[desiredSize * 2 + 1000];
+            System.arraycopy(data, 0, newData, 0, cursor);
+            data = newData;
+        }
+    }
+
+    /**
+     * Annotation on output.
+     */
+    private static class Annotation {
+        /** {@code >= 0;} start of annotated range (inclusive) */
+        private final int start;
+
+        /**
+         * {@code >= 0;} end of annotated range (exclusive);
+         * {@code Integer.MAX_VALUE} if unclosed
+         */
+        private int end;
+
+        /** {@code non-null;} annotation text */
+        private final String text;
+
+        /**
+         * Constructs an instance.
+         *
+         * @param start {@code >= 0;} start of annotated range
+         * @param end {@code >= start;} end of annotated range (exclusive) or
+         * {@code Integer.MAX_VALUE} if unclosed
+         * @param text {@code non-null;} annotation text
+         */
+        public Annotation(int start, int end, String text) {
+            this.start = start;
+            this.end = end;
+            this.text = text;
+        }
+
+        /**
+         * Constructs an instance. It is initally unclosed.
+         *
+         * @param start {@code >= 0;} start of annotated range
+         * @param text {@code non-null;} annotation text
+         */
+        public Annotation(int start, String text) {
+            this(start, Integer.MAX_VALUE, text);
+        }
+
+        /**
+         * Sets the end as given, but only if the instance is unclosed;
+         * otherwise, do nothing.
+         *
+         * @param end {@code >= start;} the end
+         */
+        public void setEndIfUnset(int end) {
+            if (this.end == Integer.MAX_VALUE) {
+                this.end = end;
+            }
+        }
+
+        /**
+         * Sets the end as given.
+         *
+         * @param end {@code >= start;} the end
+         */
+        public void setEnd(int end) {
+            this.end = end;
+        }
+
+        /**
+         * Gets the start.
+         *
+         * @return the start
+         */
+        public int getStart() {
+            return start;
+        }
+
+        /**
+         * Gets the end.
+         *
+         * @return the end
+         */
+        public int getEnd() {
+            return end;
+        }
+
+        /**
+         * Gets the text.
+         *
+         * @return {@code non-null;} the text
+         */
+        public String getText() {
+            return text;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/ExceptionWithContext.java b/dx/src/com/android/dx/util/ExceptionWithContext.java
new file mode 100644
index 0000000..7f8523c
--- /dev/null
+++ b/dx/src/com/android/dx/util/ExceptionWithContext.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Exception which carries around structured context.
+ */
+public class ExceptionWithContext
+        extends RuntimeException {
+    /** {@code non-null;} human-oriented context of the exception */
+    private StringBuffer context;
+
+    /**
+     * Augments the given exception with the given context, and return the
+     * result. The result is either the given exception if it was an
+     * {@link ExceptionWithContext}, or a newly-constructed exception if it
+     * was not.
+     *
+     * @param ex {@code non-null;} the exception to augment
+     * @param str {@code non-null;} context to add
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static ExceptionWithContext withContext(Throwable ex, String str) {
+        ExceptionWithContext ewc;
+
+        if (ex instanceof ExceptionWithContext) {
+            ewc = (ExceptionWithContext) ex;
+        } else {
+            ewc = new ExceptionWithContext(ex);
+        }
+
+        ewc.addContext(str);
+        return ewc;
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param message human-oriented message
+     */
+    public ExceptionWithContext(String message) {
+        this(message, null);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param cause {@code null-ok;} exception that caused this one
+     */
+    public ExceptionWithContext(Throwable cause) {
+        this(null, cause);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param message human-oriented message
+     * @param cause {@code null-ok;} exception that caused this one
+     */
+    public ExceptionWithContext(String message, Throwable cause) {
+        super((message != null) ? message :
+              (cause != null) ? cause.getMessage() : null,
+              cause);
+
+        if (cause instanceof ExceptionWithContext) {
+            String ctx = ((ExceptionWithContext) cause).context.toString();
+            context = new StringBuffer(ctx.length() + 200);
+            context.append(ctx);
+        } else {
+            context = new StringBuffer(200);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void printStackTrace(PrintStream out) {
+        super.printStackTrace(out);
+        out.println(context);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void printStackTrace(PrintWriter out) {
+        super.printStackTrace(out);
+        out.println(context);
+    }
+
+    /**
+     * Adds a line of context to this instance.
+     *
+     * @param str {@code non-null;} new context
+     */
+    public void addContext(String str) {
+        if (str == null) {
+            throw new NullPointerException("str == null");
+        }
+
+        context.append(str);
+        if (!str.endsWith("\n")) {
+            context.append('\n');
+        }
+    }
+
+    /**
+     * Gets the context.
+     *
+     * @return {@code non-null;} the context
+     */
+    public String getContext() {
+        return context.toString();
+    }
+
+    /**
+     * Prints the message and context.
+     *
+     * @param out {@code non-null;} where to print to
+     */
+    public void printContext(PrintStream out) {
+        out.println(getMessage());
+        out.print(context);
+    }
+
+    /**
+     * Prints the message and context.
+     *
+     * @param out {@code non-null;} where to print to
+     */
+    public void printContext(PrintWriter out) {
+        out.println(getMessage());
+        out.print(context);
+    }
+}
diff --git a/dx/src/com/android/dx/util/FileUtils.java b/dx/src/com/android/dx/util/FileUtils.java
new file mode 100644
index 0000000..098c5ab
--- /dev/null
+++ b/dx/src/com/android/dx/util/FileUtils.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * File I/O utilities.
+ */
+public final class FileUtils {
+    /**
+     * This class is uninstantiable.
+     */
+    private FileUtils() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Reads the named file, translating {@link IOException} to a
+     * {@link RuntimeException} of some sort.
+     *
+     * @param fileName {@code non-null;} name of the file to read
+     * @return {@code non-null;} contents of the file
+     */
+    public static byte[] readFile(String fileName) {
+        File file = new File(fileName);
+        return readFile(file);
+    }
+
+    /**
+     * Reads the given file, translating {@link IOException} to a
+     * {@link RuntimeException} of some sort.
+     *
+     * @param file {@code non-null;} the file to read
+     * @return {@code non-null;} contents of the file
+     */
+    public static byte[] readFile(File file) {
+        if (!file.exists()) {
+            throw new RuntimeException(file + ": file not found");
+        }
+
+        if (!file.isFile()) {
+            throw new RuntimeException(file + ": not a file");
+        }
+
+        if (!file.canRead()) {
+            throw new RuntimeException(file + ": file not readable");
+        }
+
+        long longLength = file.length();
+        int length = (int) longLength;
+        if (length != longLength) {
+            throw new RuntimeException(file + ": file too long");
+        }
+
+        byte[] result = new byte[length];
+
+        try {
+            FileInputStream in = new FileInputStream(file);
+            int at = 0;
+            while (length > 0) {
+                int amt = in.read(result, at, length);
+                if (amt == -1) {
+                    throw new RuntimeException(file + ": unexpected EOF");
+                }
+                at += amt;
+                length -= amt;
+            }
+            in.close();
+        } catch (IOException ex) {
+            throw new RuntimeException(file + ": trouble reading", ex);
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/util/FixedSizeList.java b/dx/src/com/android/dx/util/FixedSizeList.java
new file mode 100644
index 0000000..fb3af04
--- /dev/null
+++ b/dx/src/com/android/dx/util/FixedSizeList.java
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.util.Arrays;
+
+/**
+ * Simple (mostly) fixed-size list of objects, which may be made immutable.
+ */
+public class FixedSizeList
+        extends MutabilityControl implements ToHuman {
+    /** {@code non-null;} array of elements */
+    private Object[] arr;
+
+    /**
+     * Constructs an instance. All indices initially contain {@code null}.
+     *
+     * @param size the size of the list
+     */
+    public FixedSizeList(int size) {
+        super(size != 0);
+
+        try {
+            arr = new Object[size];
+        } catch (NegativeArraySizeException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("size < 0");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            // Easy out.
+            return true;
+        }
+
+        if ((other == null) || (getClass() != other.getClass())) {
+            // Another easy out.
+            return false;
+        }
+
+        FixedSizeList list = (FixedSizeList) other;
+        return Arrays.equals(arr, list.arr);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(arr);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        String name = getClass().getName();
+
+        return toString0(name.substring(name.lastIndexOf('.') + 1) + '{',
+                         ", ",
+                         "}",
+                         false);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This method will only work if every element of the list
+     * implements {@link ToHuman}.
+     */
+    public String toHuman() {
+        String name = getClass().getName();
+
+        return toString0(name.substring(name.lastIndexOf('.') + 1) + '{',
+                         ", ",
+                         "}",
+                         true);
+    }
+
+    /**
+     * Gets a customized string form for this instance.
+     *
+     * @param prefix {@code null-ok;} prefix for the start of the result
+     * @param separator {@code null-ok;} separator to insert between each item
+     * @param suffix {@code null-ok;} suffix for the end of the result
+     * @return {@code non-null;} the custom string
+     */
+    public String toString(String prefix, String separator, String suffix) {
+        return toString0(prefix, separator, suffix, false);
+    }
+
+    /**
+     * Gets a customized human string for this instance. This method will
+     * only work if every element of the list implements {@link
+     * ToHuman}.
+     *
+     * @param prefix {@code null-ok;} prefix for the start of the result
+     * @param separator {@code null-ok;} separator to insert between each item
+     * @param suffix {@code null-ok;} suffix for the end of the result
+     * @return {@code non-null;} the custom string
+     */
+    public String toHuman(String prefix, String separator, String suffix) {
+        return toString0(prefix, separator, suffix, true);
+    }
+
+    /**
+     * Gets the number of elements in this list.
+     */
+    public final int size() {
+        return arr.length;
+    }
+
+    /**
+     * Shrinks this instance to fit, by removing any unset
+     * ({@code null}) elements, leaving the remaining elements in
+     * their original order.
+     */
+    public void shrinkToFit() {
+        int sz = arr.length;
+        int newSz = 0;
+
+        for (int i = 0; i < sz; i++) {
+            if (arr[i] != null) {
+                newSz++;
+            }
+        }
+
+        if (sz == newSz) {
+            return;
+        }
+
+        throwIfImmutable();
+
+        Object[] newa = new Object[newSz];
+        int at = 0;
+
+        for (int i = 0; i < sz; i++) {
+            Object one = arr[i];
+            if (one != null) {
+                newa[at] = one;
+                at++;
+            }
+        }
+
+        arr = newa;
+        if (newSz == 0) {
+            setImmutable();
+        }
+    }
+
+    /**
+     * Gets the indicated element. It is an error to call this with the
+     * index for an element which was never set; if you do that, this
+     * will throw {@code NullPointerException}. This method is
+     * protected so that subclasses may offer a safe type-checked
+     * public interface to their clients.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @return {@code non-null;} the indicated element
+     */
+    protected final Object get0(int n) {
+        try {
+            Object result = arr[n];
+
+            if (result == null) {
+                throw new NullPointerException("unset: " + n);
+            }
+
+            return result;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            return throwIndex(n);
+        }
+    }
+
+    /**
+     * Gets the indicated element, allowing {@code null}s to be
+     * returned. This method is protected so that subclasses may
+     * (optionally) offer a safe type-checked public interface to
+     * their clients.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @return {@code null-ok;} the indicated element
+     */
+    protected final Object getOrNull0(int n) {
+        return arr[n];
+    }
+
+    /**
+     * Sets the element at the given index, but without doing any type
+     * checks on the element. This method is protected so that
+     * subclasses may offer a safe type-checked public interface to
+     * their clients.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param obj {@code null-ok;} the value to store
+     */
+    protected final void set0(int n, Object obj) {
+        throwIfImmutable();
+
+        try {
+            arr[n] = obj;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            throwIndex(n);
+        }
+    }
+
+    /**
+     * Throws the appropriate exception for the given index value.
+     *
+     * @param n the index value
+     * @return never
+     * @throws IndexOutOfBoundsException always thrown
+     */
+    private Object throwIndex(int n) {
+        if (n < 0) {
+            throw new IndexOutOfBoundsException("n < 0");
+        }
+
+        throw new IndexOutOfBoundsException("n >= size()");
+    }
+
+    /**
+     * Helper for {@link #toString} and {@link #toHuman}, which both of
+     * those call to pretty much do everything.
+     *
+     * @param prefix {@code null-ok;} prefix for the start of the result
+     * @param separator {@code null-ok;} separator to insert between each item
+     * @param suffix {@code null-ok;} suffix for the end of the result
+     * @param human whether the output is to be human
+     * @return {@code non-null;} the custom string
+     */
+    private String toString0(String prefix, String separator, String suffix,
+                             boolean human) {
+        int len = arr.length;
+        StringBuffer sb = new StringBuffer(len * 10 + 10);
+
+        if (prefix != null) {
+            sb.append(prefix);
+        }
+
+        for (int i = 0; i < len; i++) {
+            if ((i != 0) && (separator != null)) {
+                sb.append(separator);
+            }
+
+            if (human) {
+                sb.append(((ToHuman) arr[i]).toHuman());
+            } else {
+                sb.append(arr[i]);
+            }
+        }
+
+        if (suffix != null) {
+            sb.append(suffix);
+        }
+
+        return sb.toString();
+    }
+
+}
diff --git a/dx/src/com/android/dx/util/Hex.java b/dx/src/com/android/dx/util/Hex.java
new file mode 100644
index 0000000..e95669c
--- /dev/null
+++ b/dx/src/com/android/dx/util/Hex.java
@@ -0,0 +1,303 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for formatting numbers as hexadecimal.
+ */
+public final class Hex {
+    /**
+     * This class is uninstantiable.
+     */
+    private Hex() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Formats a {@code long} as an 8-byte unsigned hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String u8(long v) {
+        char[] result = new char[16];
+        for (int i = 0; i < 16; i++) {
+            result[15 - i] = Character.forDigit((int) v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an {@code int} as a 4-byte unsigned hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String u4(int v) {
+        char[] result = new char[8];
+        for (int i = 0; i < 8; i++) {
+            result[7 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an {@code int} as a 3-byte unsigned hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String u3(int v) {
+        char[] result = new char[6];
+        for (int i = 0; i < 6; i++) {
+            result[5 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an {@code int} as a 2-byte unsigned hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String u2(int v) {
+        char[] result = new char[4];
+        for (int i = 0; i < 4; i++) {
+            result[3 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an {@code int} as either a 2-byte unsigned hex value
+     * (if the value is small enough) or a 4-byte unsigned hex value (if
+     * not).
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String u2or4(int v) {
+        if (v == (char) v) {
+            return u2(v);
+        } else {
+            return u4(v);
+        }
+    }
+
+    /**
+     * Formats an {@code int} as a 1-byte unsigned hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String u1(int v) {
+        char[] result = new char[2];
+        for (int i = 0; i < 2; i++) {
+            result[1 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an {@code int} as a 4-bit unsigned hex nibble.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String uNibble(int v) {
+        char[] result = new char[1];
+
+        result[0] = Character.forDigit(v & 0x0f, 16);
+        return new String(result);
+    }
+
+    /**
+     * Formats a {@code long} as an 8-byte signed hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String s8(long v) {
+        char[] result = new char[17];
+
+        if (v < 0) {
+            result[0] = '-';
+            v = -v;
+        } else {
+            result[0] = '+';
+        }
+
+        for (int i = 0; i < 16; i++) {
+            result[16 - i] = Character.forDigit((int) v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an {@code int} as a 4-byte signed hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String s4(int v) {
+        char[] result = new char[9];
+
+        if (v < 0) {
+            result[0] = '-';
+            v = -v;
+        } else {
+            result[0] = '+';
+        }
+
+        for (int i = 0; i < 8; i++) {
+            result[8 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an {@code int} as a 2-byte signed hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String s2(int v) {
+        char[] result = new char[5];
+
+        if (v < 0) {
+            result[0] = '-';
+            v = -v;
+        } else {
+            result[0] = '+';
+        }
+
+        for (int i = 0; i < 4; i++) {
+            result[4 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats an {@code int} as a 1-byte signed hex value.
+     *
+     * @param v value to format
+     * @return {@code non-null;} formatted form
+     */
+    public static String s1(int v) {
+        char[] result = new char[3];
+
+        if (v < 0) {
+            result[0] = '-';
+            v = -v;
+        } else {
+            result[0] = '+';
+        }
+
+        for (int i = 0; i < 2; i++) {
+            result[2 - i] = Character.forDigit(v & 0x0f, 16);
+            v >>= 4;
+        }
+
+        return new String(result);
+    }
+
+    /**
+     * Formats a hex dump of a portion of a {@code byte[]}. The result
+     * is always newline-terminated, unless the passed-in length was zero,
+     * in which case the result is always the empty string ({@code ""}).
+     *
+     * @param arr {@code non-null;} array to format
+     * @param offset {@code >= 0;} offset to the part to dump
+     * @param length {@code >= 0;} number of bytes to dump
+     * @param outOffset {@code >= 0;} first output offset to print
+     * @param bpl {@code >= 0;} number of bytes of output per line
+     * @param addressLength {@code {2,4,6,8};} number of characters for each address
+     * header
+     * @return {@code non-null;} a string of the dump
+     */
+    public static String dump(byte[] arr, int offset, int length,
+                              int outOffset, int bpl, int addressLength) {
+        int end = offset + length;
+
+        // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+        if (((offset | length | end) < 0) || (end > arr.length)) {
+            throw new IndexOutOfBoundsException("arr.length " +
+                                                arr.length + "; " +
+                                                offset + "..!" + end);
+        }
+
+        if (outOffset < 0) {
+            throw new IllegalArgumentException("outOffset < 0");
+        }
+
+        if (length == 0) {
+            return "";
+        }
+
+        StringBuffer sb = new StringBuffer(length * 4 + 6);
+        boolean bol = true;
+        int col = 0;
+
+        while (length > 0) {
+            if (col == 0) {
+                String astr;
+                switch (addressLength) {
+                    case 2:  astr = Hex.u1(outOffset); break;
+                    case 4:  astr = Hex.u2(outOffset); break;
+                    case 6:  astr = Hex.u3(outOffset); break;
+                    default: astr = Hex.u4(outOffset); break;
+                }
+                sb.append(astr);
+                sb.append(": ");
+            } else if ((col & 1) == 0) {
+                sb.append(' ');
+            }
+            sb.append(Hex.u1(arr[offset]));
+            outOffset++;
+            offset++;
+            col++;
+            if (col == bpl) {
+                sb.append('\n');
+                col = 0;
+            }
+            length--;
+        }
+
+        if (col != 0) {
+            sb.append('\n');
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/util/HexParser.java b/dx/src/com/android/dx/util/HexParser.java
new file mode 100644
index 0000000..1b34345
--- /dev/null
+++ b/dx/src/com/android/dx/util/HexParser.java
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for parsing hexadecimal text.
+ */
+public final class HexParser {
+    /**
+     * This class is uninstantiable.
+     */
+    private HexParser() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Parses the given text as hex, returning a {@code byte[]}
+     * corresponding to the text. The format is simple: Each line may
+     * start with a hex offset followed by a colon (which is verified
+     * and presumably used just as a comment), and then consists of
+     * hex digits freely interspersed with whitespace. If a pound sign
+     * is encountered, it and the rest of the line are ignored as a
+     * comment. If a double quote is encountered, then the ASCII value
+     * of the subsequent characters is used, until the next double
+     * quote. Quoted strings may not span multiple lines.
+     *
+     * @param src {@code non-null;} the source string
+     * @return {@code non-null;} the parsed form
+     */
+    public static byte[] parse(String src) {
+        int len = src.length();
+        byte[] result = new byte[len / 2];
+        int at = 0;
+        int outAt = 0;
+
+        while (at < len) {
+            int nlAt = src.indexOf('\n', at);
+            if (nlAt < 0) {
+                nlAt = len;
+            }
+            int poundAt = src.indexOf('#', at);
+
+            String line;
+            if ((poundAt >= 0) && (poundAt < nlAt)) {
+                line = src.substring(at, poundAt);
+            } else {
+                line = src.substring(at, nlAt);
+            }
+            at = nlAt + 1;
+
+            int colonAt = line.indexOf(':');
+
+            atCheck:
+            if (colonAt != -1) {
+                int quoteAt = line.indexOf('\"');
+                if ((quoteAt != -1) && (quoteAt < colonAt)) {
+                    break atCheck;
+                }
+
+                String atStr = line.substring(0, colonAt).trim();
+                line = line.substring(colonAt + 1);
+                int alleged = Integer.parseInt(atStr, 16);
+                if (alleged != outAt) {
+                    throw new RuntimeException("bogus offset marker: " +
+                                               atStr);
+                }
+            }
+
+            int lineLen = line.length();
+            int value = -1;
+            boolean quoteMode = false;
+
+            for (int i = 0; i < lineLen; i++) {
+                char c = line.charAt(i);
+
+                if (quoteMode) {
+                    if (c == '\"') {
+                        quoteMode = false;
+                    } else {
+                        result[outAt] = (byte) c;
+                        outAt++;
+                    }
+                    continue;
+                }
+
+                if (c <= ' ') {
+                    continue;
+                }
+                if (c == '\"') {
+                    if (value != -1) {
+                        throw new RuntimeException("spare digit around " +
+                                                   "offset " + Hex.u4(outAt));
+                    }
+                    quoteMode = true;
+                    continue;
+                }
+
+                int digVal = Character.digit(c, 16);
+                if (digVal == -1) {
+                    throw new RuntimeException("bogus digit character: \"" +
+                                               c + "\"");
+                }
+                if (value == -1) {
+                    value = digVal;
+                } else {
+                    result[outAt] = (byte) ((value << 4) | digVal);
+                    outAt++;
+                    value = -1;
+                }
+            }
+
+            if (value != -1) {
+                throw new RuntimeException("spare digit around offset " +
+                                           Hex.u4(outAt));
+            }
+
+            if (quoteMode) {
+                throw new RuntimeException("unterminated quote around " +
+                                           "offset " + Hex.u4(outAt));
+            }
+        }
+
+        if (outAt < result.length) {
+            byte[] newr = new byte[outAt];
+            System.arraycopy(result, 0, newr, 0, outAt);
+            result = newr;
+        }
+
+        return result;
+    }
+}
diff --git a/dx/src/com/android/dx/util/IndentingWriter.java b/dx/src/com/android/dx/util/IndentingWriter.java
new file mode 100644
index 0000000..3424e37
--- /dev/null
+++ b/dx/src/com/android/dx/util/IndentingWriter.java
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Writer that wraps another writer and passes width-limited and
+ * optionally-prefixed output to its subordinate. When lines are
+ * wrapped they are automatically indented based on the start of the
+ * line.
+ */
+public final class IndentingWriter extends FilterWriter {
+    /** {@code null-ok;} optional prefix for every line */
+    private final String prefix;
+
+    /** {@code > 0;} the maximum output width */
+    private final int width;
+
+    /** {@code > 0;} the maximum indent */
+    private final int maxIndent;
+
+    /** {@code >= 0;} current output column (zero-based) */
+    private int column;
+
+    /** whether indent spaces are currently being collected */
+    private boolean collectingIndent;
+
+    /** {@code >= 0;} current indent amount */
+    private int indent;
+
+    /**
+     * Constructs an instance.
+     *
+     * @param out {@code non-null;} writer to send final output to
+     * @param width {@code >= 0;} the maximum output width (not including
+     * {@code prefix}), or {@code 0} for no maximum
+     * @param prefix {@code non-null;} the prefix for each line
+     */
+    public IndentingWriter(Writer out, int width, String prefix) {
+        super(out);
+
+        if (out == null) {
+            throw new NullPointerException("out == null");
+        }
+
+        if (width < 0) {
+            throw new IllegalArgumentException("width < 0");
+        }
+
+        if (prefix == null) {
+            throw new NullPointerException("prefix == null");
+        }
+
+        this.width = (width != 0) ? width : Integer.MAX_VALUE;
+        this.maxIndent = width >> 1;
+        this.prefix = (prefix.length() == 0) ? null : prefix;
+
+        bol();
+    }
+
+    /**
+     * Constructs a no-prefix instance.
+     *
+     * @param out {@code non-null;} writer to send final output to
+     * @param width {@code >= 0;} the maximum output width (not including
+     * {@code prefix}), or {@code 0} for no maximum
+     */
+    public IndentingWriter(Writer out, int width) {
+        this(out, width, "");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(int c) throws IOException {
+        synchronized (lock) {
+            if (collectingIndent) {
+                if (c == ' ') {
+                    indent++;
+                    if (indent >= maxIndent) {
+                        indent = maxIndent;
+                        collectingIndent = false;
+                    }
+                } else {
+                    collectingIndent = false;
+                }
+            }
+
+            if ((column == width) && (c != '\n')) {
+                out.write('\n');
+                column = 0;
+                /*
+                 * Note: No else, so this should fall through to the next
+                 * if statement.
+                 */
+            }
+
+            if (column == 0) {
+                if (prefix != null) {
+                    out.write(prefix);
+                }
+
+                if (!collectingIndent) {
+                    for (int i = 0; i < indent; i++) {
+                        out.write(' ');
+                    }
+                    column = indent;
+                }
+            }
+
+            out.write(c);
+
+            if (c == '\n') {
+                bol();
+            } else {
+                column++;
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        synchronized (lock) {
+            while (len > 0) {
+                write(cbuf[off]);
+                off++;
+                len--;
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(String str, int off, int len) throws IOException {
+        synchronized (lock) {
+            while (len > 0) {
+                write(str.charAt(off));
+                off++;
+                len--;
+            }
+        }
+    }
+
+    /**
+     * Indicates that output is at the beginning of a line.
+     */
+    private void bol() {
+        column = 0;
+        collectingIndent = (maxIndent != 0);
+        indent = 0;
+    }
+}
diff --git a/dx/src/com/android/dx/util/IntIterator.java b/dx/src/com/android/dx/util/IntIterator.java
new file mode 100644
index 0000000..4caa439
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntIterator.java
@@ -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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * An iterator for a list of ints.
+ */
+public interface IntIterator {
+
+    /**
+     * Checks to see if the iterator has a next value.
+     *
+     * @return true if next() will succeed
+     */
+    boolean hasNext();
+
+    /**
+     * Returns the next value in the iterator.
+     *
+     * @return next value
+     * @throws java.util.NoSuchElementException if no next element exists
+     */
+    int next();
+}
diff --git a/dx/src/com/android/dx/util/IntList.java b/dx/src/com/android/dx/util/IntList.java
new file mode 100644
index 0000000..089fead
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntList.java
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.util.Arrays;
+
+/**
+ * Simple list of {@code int}s.
+ */
+public final class IntList extends MutabilityControl {
+    /** {@code non-null;} immutable, no-element instance */
+    public static final IntList EMPTY = new IntList(0);
+
+    /** {@code non-null;} array of elements */
+    private int[] values;
+
+    /** {@code >= 0;} current size of the list */
+    private int size;
+
+    /** whether the values are currently sorted */
+    private boolean sorted;
+
+    static {
+        EMPTY.setImmutable();
+    }
+
+    /**
+     * Constructs a new immutable instance with the given element.
+     *
+     * @param value the sole value in the list
+     */
+    public static IntList makeImmutable(int value) {
+        IntList result = new IntList(1);
+
+        result.add(value);
+        result.setImmutable();
+
+        return result;
+    }
+
+    /**
+     * Constructs a new immutable instance with the given elements.
+     *
+     * @param value0 the first value in the list
+     * @param value1 the second value in the list
+     */
+    public static IntList makeImmutable(int value0, int value1) {
+        IntList result = new IntList(2);
+
+        result.add(value0);
+        result.add(value1);
+        result.setImmutable();
+
+        return result;
+    }
+
+    /**
+     * Constructs an empty instance with a default initial capacity.
+     */
+    public IntList() {
+        this(4);
+    }
+
+    /**
+     * Constructs an empty instance.
+     *
+     * @param initialCapacity {@code >= 0;} initial capacity of the list
+     */
+    public IntList(int initialCapacity) {
+        super(true);
+
+        try {
+            values = new int[initialCapacity];
+        } catch (NegativeArraySizeException ex) {
+            // Translate the exception.
+            throw new IllegalArgumentException("size < 0");
+        }
+
+        size = 0;
+        sorted = true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        int result = 0;
+
+        for (int i = 0; i < size; i++) {
+            result = (result * 31) + values[i];
+        }
+
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+
+        if (! (other instanceof IntList)) {
+            return false;
+        }
+
+        IntList otherList = (IntList) other;
+
+        if (sorted != otherList.sorted) {
+            return false;
+        }
+
+        if (size != otherList.size) {
+            return false;
+        }
+
+        for (int i = 0; i < size; i++) {
+            if (values[i] != otherList.values[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer(size * 5 + 10);
+
+        sb.append('{');
+
+        for (int i = 0; i < size; i++) {
+            if (i != 0) {
+                sb.append(", ");
+            }
+            sb.append(values[i]);
+        }
+
+        sb.append('}');
+
+        return sb.toString();
+    }
+
+    /**
+     * Gets the number of elements in this list.
+     */
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Gets the indicated value.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @return the indicated element's value
+     */
+    public int get(int n) {
+        if (n >= size) {
+            throw new IndexOutOfBoundsException("n >= size()");
+        }
+
+        try {
+            return values[n];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate exception.
+            throw new IndexOutOfBoundsException("n < 0");
+        }
+    }
+
+    /**
+     * Sets the value at the given index.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param value value to store
+     */
+    public void set(int n, int value) {
+        throwIfImmutable();
+
+        if (n >= size) {
+            throw new IndexOutOfBoundsException("n >= size()");
+        }
+
+        try {
+            values[n] = value;
+            sorted = false;
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // Translate the exception.
+            if (n < 0) {
+                throw new IllegalArgumentException("n < 0");
+            }
+        }
+    }
+
+    /**
+     * Adds an element to the end of the list. This will increase the
+     * list's capacity if necessary.
+     *
+     * @param value the value to add
+     */
+    public void add(int value) {
+        throwIfImmutable();
+
+        growIfNeeded();
+
+        values[size++] = value;
+
+        if (sorted && (size > 1)) {
+            sorted = (value >= values[size - 2]);
+        }
+    }
+
+    /**
+     * Inserts element into specified index, moving elements at and above
+     * that index up one. May not be used to insert at an index beyond the
+     * current size (that is, insertion as a last element is legal but
+     * no further).
+     *
+     * @param n {@code >= 0, <=size();} index of where to insert
+     * @param value value to insert
+     */
+    public void insert(int n, int value) {
+        if (n > size) {
+            throw new IndexOutOfBoundsException("n > size()");
+        }
+
+        growIfNeeded();
+
+        System.arraycopy (values, n, values, n+1, size - n);
+        values[n] = value;
+        size++;
+
+        sorted = sorted
+                && (n == 0 || value > values[n-1])
+                && (n == (size - 1) || value < values[n+1]);
+    }
+
+    /**
+     * Removes an element at a given index, shifting elements at greater
+     * indicies down one.
+     *
+     * @param n  {@code >=0, < size();} index of element to remove
+     */
+    public void removeIndex(int n) {
+        if (n >= size) {
+            throw new IndexOutOfBoundsException("n >= size()");
+        }
+
+        System.arraycopy (values, n + 1, values, n, size - n - 1);
+        size--;
+
+        // sort status is unchanged
+    }
+
+    /**
+     * Increases size of array if needed
+     */
+    private void growIfNeeded() {
+        if (size == values.length) {
+            // Resize.
+            int[] newv = new int[size * 3 / 2 + 10];
+            System.arraycopy(values, 0, newv, 0, size);
+            values = newv;
+        }
+    }
+
+    /**
+     * Returns the last element in the array without modifying the array
+     *
+     * @return last value in the array.
+     * @exception IndexOutOfBoundsException if stack is empty.
+     */
+    public int top() {
+        return get(size - 1);
+    }
+
+    /**
+     * Pops an element off the end of the list and decreasing the size by one.
+     *
+     * @return value from what was the last element.
+     * @exception IndexOutOfBoundsException if stack is empty.
+     */
+    public int pop() {
+        throwIfImmutable();
+
+        int result;
+
+        result = get(size-1);
+        size--;
+
+        return result;
+    }
+
+    /**
+     * Pops N elements off the end of the list and decreasing the size by N.
+     *
+     * @param n {@code >= 0;} number of elements to remove from end.
+     * @exception IndexOutOfBoundsException if stack is smaller than N
+     */
+    public void pop(int n) {
+        throwIfImmutable();
+
+        size -= n;
+    }
+
+    /**
+     * Shrinks the size of the list.
+     *
+     * @param newSize {@code >= 0;} the new size
+     */
+    public void shrink(int newSize) {
+        if (newSize < 0) {
+            throw new IllegalArgumentException("newSize < 0");
+        }
+
+        if (newSize > size) {
+            throw new IllegalArgumentException("newSize > size");
+        }
+
+        throwIfImmutable();
+
+        size = newSize;
+    }
+
+    /**
+     * Makes and returns a mutable copy of the list.
+     *
+     * @return {@code non-null;} an appropriately-constructed instance
+     */
+    public IntList mutableCopy() {
+        int sz = size;
+        IntList result = new IntList(sz);
+
+        for (int i = 0; i < sz; i++) {
+            result.add(values[i]);
+        }
+
+        return result;
+    }
+
+    /**
+     * Sorts the elements in the list in-place.
+     */
+    public void sort() {
+        throwIfImmutable();
+
+        if (!sorted) {
+            Arrays.sort(values, 0, size);
+            sorted = true;
+        }
+    }
+
+    /**
+     * Returns the index of the given value, or -1 if the value does not
+     * appear in the list.  This will do a binary search if the list is
+     * sorted or a linear search if not.
+     * @param value value to find
+     * @return index of value or -1
+     */
+    public int indexOf(int value) {
+        int ret = binarysearch(value);
+
+        return ret >= 0 ? ret : -1;
+
+    }
+
+    /**
+     * Performs a binary search on a sorted list, returning the index of
+     * the given value if it is present or
+     * {@code (-(insertion point) - 1)} if the value is not present.
+     * If the list is not sorted, then reverts to linear search and returns
+     * {@code -size()} if the element is not found.
+     *
+     * @param value value to find
+     * @return index of value or {@code (-(insertion point) - 1)} if the
+     * value is not present
+     */
+    public int binarysearch(int value) {
+        int sz = size;
+
+        if (!sorted) {
+            // Linear search.
+            for (int i = 0; i < sz; i++) {
+                if (values[i] == value) {
+                    return i;
+                }
+            }
+
+            return -sz;
+        }
+
+        /*
+         * Binary search. This variant does only one value comparison
+         * per iteration but does one more iteration on average than
+         * the variant that includes a value equality check per
+         * iteration.
+         */
+
+        int min = -1;
+        int max = sz;
+
+        while (max > (min + 1)) {
+            /*
+             * The guessIdx calculation is equivalent to ((min + max)
+             * / 2) but won't go wonky when min and max are close to
+             * Integer.MAX_VALUE.
+             */
+            int guessIdx = min + ((max - min) >> 1);
+            int guess = values[guessIdx];
+
+            if (value <= guess) {
+                max = guessIdx;
+            } else {
+                min = guessIdx;
+            }
+        }
+
+        if ((max != sz)) {
+            return (value == values[max]) ? max : (-max - 1);
+        } else {
+            return -sz - 1;
+        }
+    }
+
+
+    /**
+     * Returns whether or not the given value appears in the list.
+     * This will do a binary search if the list is sorted or a linear
+     * search if not.
+     *
+     * @see #sort
+     *
+     * @param value value to look for
+     * @return whether the list contains the given value
+     */
+    public boolean contains(int value) {
+        return indexOf(value) >= 0;
+    }
+}
diff --git a/dx/src/com/android/dx/util/IntSet.java b/dx/src/com/android/dx/util/IntSet.java
new file mode 100644
index 0000000..33b6bdd
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntSet.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * A set of integers
+ */
+public interface IntSet {
+
+    /**
+     * Adds an int to a set
+     *
+     * @param value int to add
+     */
+    void add(int value);
+
+    /**
+     * Removes an int from a set.
+     *
+     * @param value int to remove
+     */
+    void remove(int value);
+
+    /**
+     * Checks to see if a value is in the set
+     *
+     * @param value int to check
+     * @return true if in set
+     */
+    boolean has(int value);
+
+    /**
+     * Merges {@code other} into this set, so this set becomes the
+     * union of the two.
+     *
+     * @param other {@code non-null;} other set to merge with.
+     */
+    void merge(IntSet other);
+
+    /**
+     * Returns the count of unique elements in this set.
+     *
+     * @return {@code > = 0;} count of unique elements
+     */
+    int elements();
+
+    /**
+     * Iterates the set
+     *
+     * @return {@code non-null;} a set iterator
+     */
+    IntIterator iterator();
+}
diff --git a/dx/src/com/android/dx/util/LabeledItem.java b/dx/src/com/android/dx/util/LabeledItem.java
new file mode 100644
index 0000000..b4856cf
--- /dev/null
+++ b/dx/src/com/android/dx/util/LabeledItem.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * An item that has an integer label.
+ */
+public interface LabeledItem {
+
+    /*
+     * Gets the label of this block.
+     *
+     * @return {@code >= 0;} the label
+     */
+    public int getLabel();
+}
diff --git a/dx/src/com/android/dx/util/LabeledList.java b/dx/src/com/android/dx/util/LabeledList.java
new file mode 100644
index 0000000..5b6e125
--- /dev/null
+++ b/dx/src/com/android/dx/util/LabeledList.java
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import com.android.dx.cf.code.ByteBlock;
+
+/**
+ * A list of labeled items, allowing easy lookup by label.
+ */
+public class LabeledList extends FixedSizeList {
+
+    /**
+     * Sparse array indexed by label to FixedSizeList index.
+     * -1 = invalid label.
+     */
+    private final IntList labelToIndex;
+
+    /** @inheritDoc */
+    public LabeledList(int size) {
+        super(size);
+
+        labelToIndex = new IntList(size);
+    }
+
+    /**
+     * Constructs a new instance that is a copy of the old instance.
+     *
+     * @param old instance to copy
+     */
+    protected LabeledList(LabeledList old) {
+        super(old.size());
+        labelToIndex = old.labelToIndex.mutableCopy();
+
+        int sz = old.size();
+
+        for (int i = 0; i < sz; i++) {
+            Object one = old.get0(i);
+            if (one != null) {
+                set0(i, one);
+            }
+        }
+    }
+
+    /**
+     * Gets the maximum label (exclusive) of any block added to this instance.
+     *
+     * @return {@code >= 0;} the maximum label
+     */
+    public int getMaxLabel() {
+        int sz = labelToIndex.size();
+
+        // Gobble any deleted labels that may be at the end...
+        int i;
+        for (i = sz - 1; (i >= 0) && (labelToIndex.get(i) < 0); i--)
+            ;
+
+        int newSize = i+1;
+
+        labelToIndex.shrink(newSize);
+
+        return newSize;
+    }
+
+    /**
+     * Removes a label from the label-to-index mapping
+     * @param oldLabel label to remove
+     */
+    protected void removeLabel(int oldLabel) {
+        labelToIndex.set(oldLabel, -1);
+    }
+
+    /**
+     * Adds a label and index to the label-to-index mapping
+     * @param label new label
+     * @param index index of block.
+     */
+    protected void addLabelIndex(int label, int index) {
+        int origSz = labelToIndex.size();
+
+        for (int i = 0; i <= (label - origSz); i++) {
+            labelToIndex.add(-1);
+        }
+
+        labelToIndex.set(label, index);
+    }
+
+    /**
+     * Gets the index of the first item in the list with the given
+     * label, if any.
+     *
+     * @param label {@code >= 0;} the label to look for
+     * @return {@code >= -1;} the index of the so-labelled item, or {@code -1}
+     * if none is found
+     */
+    public int indexOfLabel(int label) {
+        if (label >= labelToIndex.size()) {
+            return -1;
+        } else {
+            return labelToIndex.get(label);
+        }
+    }
+
+    /** @inheritDoc */
+    @Override
+    public void shrinkToFit() {
+        super.shrinkToFit();
+
+        rebuildLabelToIndex();
+    }
+
+    /**
+     * Rebuilds the label-to-index mapping after a shrinkToFit().
+     * Note: assumes that the labels that are in the list are the same
+     * although the indicies may have changed.
+     */
+    protected void rebuildLabelToIndex() {
+        int szItems = size();
+
+        for (int i = 0; i < szItems; i++) {
+            LabeledItem li = (LabeledItem)get0(i);
+
+            if (li != null) {
+                labelToIndex.set(li.getLabel(), i);
+            }
+        }
+    }
+
+    /**
+     * Sets the element at the given index.
+     *
+     * @param n {@code >= 0, < size();} which element
+     * @param item {@code null-ok;} the value to store
+     */
+    protected void set(int n, LabeledItem item) {
+        LabeledItem old = (LabeledItem) getOrNull0(n);
+
+        set0(n, item);
+
+        if (old != null) {
+            removeLabel(old.getLabel());
+        }
+
+        if (item != null) {
+            addLabelIndex(item.getLabel(), n);
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/Leb128Utils.java b/dx/src/com/android/dx/util/Leb128Utils.java
new file mode 100644
index 0000000..5d450ea
--- /dev/null
+++ b/dx/src/com/android/dx/util/Leb128Utils.java
@@ -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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * LEB128 (little-endian base 128) utilities.
+ */
+public final class Leb128Utils {
+    /**
+     * This class is uninstantiable.
+     */
+    private Leb128Utils() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Gets the number of bytes in the unsigned LEB128 encoding of the
+     * given value.
+     *
+     * @param value the value in question
+     * @return its write size, in bytes
+     */
+    public static int unsignedLeb128Size(int value) {
+        // TODO: This could be much cleverer.
+
+        int remaining = value >> 7;
+        int count = 0;
+
+        while (remaining != 0) {
+            remaining >>= 7;
+            count++;
+        }
+
+        return count + 1;
+    }
+
+    /**
+     * Gets the number of bytes in the signed LEB128 encoding of the
+     * given value.
+     *
+     * @param value the value in question
+     * @return its write size, in bytes
+     */
+    public static int signedLeb128Size(int value) {
+        // TODO: This could be much cleverer.
+
+        int remaining = value >> 7;
+        int count = 0;
+        boolean hasMore = true;
+        int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+        while (hasMore) {
+            hasMore = (remaining != end)
+                || ((remaining & 1) != ((value >> 6) & 1));
+
+            value = remaining;
+            remaining >>= 7;
+            count++;
+        }
+
+        return count;
+    }
+}
diff --git a/dx/src/com/android/dx/util/ListIntSet.java b/dx/src/com/android/dx/util/ListIntSet.java
new file mode 100644
index 0000000..2b4fc21
--- /dev/null
+++ b/dx/src/com/android/dx/util/ListIntSet.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A set of integers, represented by a list
+ */
+public class ListIntSet implements IntSet {
+
+    /** also accessed in BitIntSet */
+    final IntList ints;
+
+    /**
+     * Constructs an instance
+     */
+    public ListIntSet() {
+        ints = new IntList();
+        ints.sort();
+    }
+
+    /** @inheritDoc */
+    public void add(int value) {
+        int index = ints.binarysearch(value);
+
+        if (index < 0) {
+            ints.insert(-(index + 1), value);
+        }
+    }
+
+    /** @inheritDoc */
+    public void remove(int value) {
+        int index = ints.indexOf(value);
+
+        if (index >= 0) {
+            ints.removeIndex(index);
+        }
+    }
+
+    /** @inheritDoc */
+    public boolean has(int value) {
+        return ints.indexOf(value) >= 0;
+    }
+
+    /** @inheritDoc */
+    public void merge(IntSet other) {
+        if (other instanceof ListIntSet) {
+            ListIntSet o = (ListIntSet) other;
+            int szThis = ints.size();
+            int szOther = o.ints.size();
+
+            int i = 0;
+            int j = 0;
+
+            while (j < szOther && i < szThis) {
+                while (j < szOther && o.ints.get(j) < ints.get(i)) {
+                    add(o.ints.get(j++));
+                }
+                if (j == szOther) {
+                    break;
+                }
+                while (i < szThis && o.ints.get(j) >= ints.get(i)) {
+                    i++;
+                }
+            }
+
+            while (j < szOther) {
+                add(o.ints.get(j++));
+            }
+
+            ints.sort();
+        } else if (other instanceof BitIntSet) {
+            BitIntSet o = (BitIntSet) other;
+
+            for (int i = 0; i >= 0; i = Bits.findFirst(o.bits, i + 1)) {
+                ints.add(i);
+            }
+            ints.sort();
+        } else {
+            IntIterator iter = other.iterator();
+            while (iter.hasNext()) {
+                add(iter.next());
+            }
+        }
+    }
+
+    /** @inheritDoc */
+    public int elements() {
+        return ints.size();
+    }
+
+    /** @inheritDoc */
+    public IntIterator iterator() {
+        return new IntIterator() {
+            private int idx = 0;
+
+            /** @inheritDoc */
+            public boolean hasNext() {
+                return idx < ints.size();
+            }
+
+            /** @inheritDoc */
+            public int next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+
+                return ints.get(idx++);
+            }
+        };
+    }
+
+    /** @inheritDoc */
+    public String toString() {
+        return ints.toString();
+    }
+}
diff --git a/dx/src/com/android/dx/util/MutabilityControl.java b/dx/src/com/android/dx/util/MutabilityControl.java
new file mode 100644
index 0000000..14e0f2e
--- /dev/null
+++ b/dx/src/com/android/dx/util/MutabilityControl.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Very simple base class that implements a flag to control the mutability
+ * of instances. This class just provides the flag and a utility to check
+ * and throw the right exception, but it is up to subclasses to place calls
+ * to the checker in all the right places.
+ */
+public class MutabilityControl {
+    /** whether this instance is mutable */
+    private boolean mutable;
+
+    /**
+     * Constructs an instance. It is initially mutable.
+     */
+    public MutabilityControl() {
+        mutable = true;
+    }
+
+    /**
+     * Constructs an instance, explicitly indicating the mutability.
+     *
+     * @param mutable {@code true} iff this instance is mutable
+     */
+    public MutabilityControl(boolean mutable) {
+        this.mutable = mutable;
+    }
+
+    /**
+     * Makes this instance immutable.
+     */
+    public void setImmutable() {
+        mutable = false;
+    }
+
+    /**
+     * Checks to see whether or not this instance is immutable. This is the
+     * same as calling {@code !isMutable()}.
+     *
+     * @return {@code true} iff this instance is immutable
+     */
+    public final boolean isImmutable() {
+        return !mutable;
+    }
+
+    /**
+     * Checks to see whether or not this instance is mutable.
+     *
+     * @return {@code true} iff this instance is mutable
+     */
+    public final boolean isMutable() {
+        return mutable;
+    }
+
+    /**
+     * Throws {@link MutabilityException} if this instance is
+     * immutable.
+     */
+    public final void throwIfImmutable() {
+        if (!mutable) {
+            throw new MutabilityException("immutable instance");
+        }
+    }
+
+    /**
+     * Throws {@link MutabilityException} if this instance is mutable.
+     */
+    public final void throwIfMutable() {
+        if (mutable) {
+            throw new MutabilityException("mutable instance");
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/MutabilityException.java b/dx/src/com/android/dx/util/MutabilityException.java
new file mode 100644
index 0000000..bd21651
--- /dev/null
+++ b/dx/src/com/android/dx/util/MutabilityException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Exception due to a mutability problem.
+ */
+public class MutabilityException
+        extends ExceptionWithContext {
+    public MutabilityException(String message) {
+        super(message);
+    }
+
+    public MutabilityException(Throwable cause) {
+        super(cause);
+    }
+
+    public MutabilityException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/dx/src/com/android/dx/util/Output.java b/dx/src/com/android/dx/util/Output.java
new file mode 100644
index 0000000..402fa83
--- /dev/null
+++ b/dx/src/com/android/dx/util/Output.java
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Interface for a sink for binary output. This is similar to
+ * {@code java.util.DataOutput}, but no {@code IOExceptions}
+ * are declared, and multibyte output is defined to be little-endian.
+ */
+public interface Output {
+    /**
+     * Gets the current cursor position. This is the same as the number of
+     * bytes written to this instance.
+     *
+     * @return {@code >= 0;} the cursor position
+     */
+    public int getCursor();
+
+    /**
+     * Asserts that the cursor is the given value.
+     *
+     * @param expectedCursor the expected cursor value
+     * @throws RuntimeException thrown if {@code getCursor() !=
+     * expectedCursor}
+     */
+    public void assertCursor(int expectedCursor);
+
+    /**
+     * Writes a {@code byte} to this instance.
+     *
+     * @param value the value to write; all but the low 8 bits are ignored
+     */
+    public void writeByte(int value);
+
+    /**
+     * Writes a {@code short} to this instance.
+     *
+     * @param value the value to write; all but the low 16 bits are ignored
+     */
+    public void writeShort(int value);
+
+    /**
+     * Writes an {@code int} to this instance.
+     *
+     * @param value the value to write
+     */
+    public void writeInt(int value);
+
+    /**
+     * Writes a {@code long} to this instance.
+     *
+     * @param value the value to write
+     */
+    public void writeLong(long value);
+
+    /**
+     * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+     * see the "Dalvik Executable Format" document or DWARF v3 section
+     * 7.6.
+     *
+     * @param value value to write, treated as an unsigned value
+     * @return {@code 1..5;} the number of bytes actually written
+     */
+    public int writeUnsignedLeb128(int value);
+
+    /**
+     * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+     * see the "Dalvik Executable Format" document or DWARF v3 section
+     * 7.6.
+     *
+     * @param value value to write
+     * @return {@code 1..5;} the number of bytes actually written
+     */
+    public int writeSignedLeb128(int value);
+
+    /**
+     * Writes a {@link ByteArray} to this instance.
+     *
+     * @param bytes {@code non-null;} the array to write
+     */
+    public void write(ByteArray bytes);
+
+    /**
+     * Writes a portion of a {@code byte[]} to this instance.
+     *
+     * @param bytes {@code non-null;} the array to write
+     * @param offset {@code >= 0;} offset into {@code bytes} for the first
+     * byte to write
+     * @param length {@code >= 0;} number of bytes to write
+     */
+    public void write(byte[] bytes, int offset, int length);
+
+    /**
+     * Writes a {@code byte[]} to this instance. This is just
+     * a convenient shorthand for {@code write(bytes, 0, bytes.length)}.
+     *
+     * @param bytes {@code non-null;} the array to write
+     */
+    public void write(byte[] bytes);
+
+    /**
+     * Writes the given number of {@code 0} bytes.
+     *
+     * @param count {@code >= 0;} the number of zeroes to write
+     */
+    public void writeZeroes(int count);
+
+    /**
+     * Adds extra bytes if necessary (with value {@code 0}) to
+     * force alignment of the output cursor as given.
+     *
+     * @param alignment {@code > 0;} the alignment; must be a power of two
+     */
+    public void alignTo(int alignment);
+}
diff --git a/dx/src/com/android/dx/util/ToHuman.java b/dx/src/com/android/dx/util/ToHuman.java
new file mode 100644
index 0000000..b3a31a5
--- /dev/null
+++ b/dx/src/com/android/dx/util/ToHuman.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Simple interface for objects that can return a "human" (as opposed to
+ * a complete but often hard to read) string form.
+ */
+public interface ToHuman {
+    /**
+     * Return the "human" string form of this instance.  This is
+     * generally less "debuggy" than {@code toString()}.
+     *
+     * @return {@code non-null;} the human string form
+     */
+    public String toHuman();
+}
diff --git a/dx/src/com/android/dx/util/TwoColumnOutput.java b/dx/src/com/android/dx/util/TwoColumnOutput.java
new file mode 100644
index 0000000..ed2ab9f
--- /dev/null
+++ b/dx/src/com/android/dx/util/TwoColumnOutput.java
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Class that takes a combined output destination and provides two
+ * output writers, one of which ends up writing to the left column and
+ * one which goes on the right.
+ */
+public final class TwoColumnOutput {
+    /** {@code non-null;} underlying writer for final output */
+    private final Writer out;
+
+    /** {@code > 0;} the left column width */
+    private final int leftWidth;
+
+    /** {@code non-null;} pending left column output */
+    private final StringBuffer leftBuf;
+
+    /** {@code non-null;} pending right column output */
+    private final StringBuffer rightBuf;
+
+    /** {@code non-null;} left column writer */
+    private final IndentingWriter leftColumn;
+
+    /** {@code non-null;} right column writer */
+    private final IndentingWriter rightColumn;
+
+    /**
+     * Turns the given two strings (with widths) and spacer into a formatted
+     * two-column string.
+     *
+     * @param s1 {@code non-null;} first string
+     * @param width1 {@code > 0;} width of the first column
+     * @param spacer {@code non-null;} spacer string
+     * @param s2 {@code non-null;} second string
+     * @param width2 {@code > 0;} width of the second column
+     * @return {@code non-null;} an appropriately-formatted string
+     */
+    public static String toString(String s1, int width1, String spacer,
+                                  String s2, int width2) {
+        int len1 = s1.length();
+        int len2 = s2.length();
+
+        StringWriter sw = new StringWriter((len1 + len2) * 3);
+        TwoColumnOutput twoOut =
+            new TwoColumnOutput(sw, width1, width2, spacer);
+
+        try {
+            twoOut.getLeft().write(s1);
+            twoOut.getRight().write(s2);
+        } catch (IOException ex) {
+            throw new RuntimeException("shouldn't happen", ex);
+        }
+
+        twoOut.flush();
+        return sw.toString();
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param out {@code non-null;} writer to send final output to
+     * @param leftWidth {@code > 0;} width of the left column, in characters
+     * @param rightWidth {@code > 0;} width of the right column, in characters
+     * @param spacer {@code non-null;} spacer string to sit between the two columns
+     */
+    public TwoColumnOutput(Writer out, int leftWidth, int rightWidth,
+                           String spacer) {
+        if (out == null) {
+            throw new NullPointerException("out == null");
+        }
+
+        if (leftWidth < 1) {
+            throw new IllegalArgumentException("leftWidth < 1");
+        }
+
+        if (rightWidth < 1) {
+            throw new IllegalArgumentException("rightWidth < 1");
+        }
+
+        if (spacer == null) {
+            throw new NullPointerException("spacer == null");
+        }
+
+        StringWriter leftWriter = new StringWriter(1000);
+        StringWriter rightWriter = new StringWriter(1000);
+
+        this.out = out;
+        this.leftWidth = leftWidth;
+        this.leftBuf = leftWriter.getBuffer();
+        this.rightBuf = rightWriter.getBuffer();
+        this.leftColumn = new IndentingWriter(leftWriter, leftWidth);
+        this.rightColumn =
+            new IndentingWriter(rightWriter, rightWidth, spacer);
+    }
+
+    /**
+     * Constructs an instance.
+     *
+     * @param out {@code non-null;} stream to send final output to
+     * @param leftWidth {@code >= 1;} width of the left column, in characters
+     * @param rightWidth {@code >= 1;} width of the right column, in characters
+     * @param spacer {@code non-null;} spacer string to sit between the two columns
+     */
+    public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth,
+                           String spacer) {
+        this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer);
+    }
+
+    /**
+     * Gets the writer to use to write to the left column.
+     *
+     * @return {@code non-null;} the left column writer
+     */
+    public Writer getLeft() {
+        return leftColumn;
+    }
+
+    /**
+     * Gets the writer to use to write to the right column.
+     *
+     * @return {@code non-null;} the right column writer
+     */
+    public Writer getRight() {
+        return rightColumn;
+    }
+
+    /**
+     * Flushes the output. If there are more lines of pending output in one
+     * column, then the other column will get filled with blank lines.
+     */
+    public void flush() {
+        try {
+            appendNewlineIfNecessary(leftBuf, leftColumn);
+            appendNewlineIfNecessary(rightBuf, rightColumn);
+            outputFullLines();
+            flushLeft();
+            flushRight();
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Outputs to the final destination as many full line pairs as
+     * there are in the pending output, removing those lines from
+     * their respective buffers. This method terminates when at
+     * least one of the two column buffers is empty.
+     */
+    private void outputFullLines() throws IOException {
+        for (;;) {
+            int leftLen = leftBuf.indexOf("\n");
+            if (leftLen < 0) {
+                return;
+            }
+
+            int rightLen = rightBuf.indexOf("\n");
+            if (rightLen < 0) {
+                return;
+            }
+
+            if (leftLen != 0) {
+                out.write(leftBuf.substring(0, leftLen));
+            }
+
+            if (rightLen != 0) {
+                writeSpaces(out, leftWidth - leftLen);
+                out.write(rightBuf.substring(0, rightLen));
+            }
+
+            out.write('\n');
+
+            leftBuf.delete(0, leftLen + 1);
+            rightBuf.delete(0, rightLen + 1);
+        }
+    }
+
+    /**
+     * Flushes the left column buffer, printing it and clearing the buffer.
+     * If the buffer is already empty, this does nothing.
+     */
+    private void flushLeft() throws IOException {
+        appendNewlineIfNecessary(leftBuf, leftColumn);
+
+        while (leftBuf.length() != 0) {
+            rightColumn.write('\n');
+            outputFullLines();
+        }
+    }
+
+    /**
+     * Flushes the right column buffer, printing it and clearing the buffer.
+     * If the buffer is already empty, this does nothing.
+     */
+    private void flushRight() throws IOException {
+        appendNewlineIfNecessary(rightBuf, rightColumn);
+
+        while (rightBuf.length() != 0) {
+            leftColumn.write('\n');
+            outputFullLines();
+        }
+    }
+
+    /**
+     * Appends a newline to the given buffer via the given writer, but
+     * only if it isn't empty and doesn't already end with one.
+     *
+     * @param buf {@code non-null;} the buffer in question
+     * @param out {@code non-null;} the writer to use
+     */
+    private static void appendNewlineIfNecessary(StringBuffer buf,
+                                                 Writer out)
+            throws IOException {
+        int len = buf.length();
+
+        if ((len != 0) && (buf.charAt(len - 1) != '\n')) {
+            out.write('\n');
+        }
+    }
+
+    /**
+     * Writes the given number of spaces to the given writer.
+     *
+     * @param out {@code non-null;} where to write
+     * @param amt {@code >= 0;} the number of spaces to write
+     */
+    private static void writeSpaces(Writer out, int amt) throws IOException {
+        while (amt > 0) {
+            out.write(' ');
+            amt--;
+        }
+    }
+}
diff --git a/dx/src/com/android/dx/util/Warning.java b/dx/src/com/android/dx/util/Warning.java
new file mode 100644
index 0000000..3c23c7c
--- /dev/null
+++ b/dx/src/com/android/dx/util/Warning.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Exception which is meant to indicate a non-fatal warning.
+ */
+public class Warning extends RuntimeException {
+    /**
+     * Constructs an instance.
+     *
+     * @param message human-oriented message
+     */
+    public Warning(String message) {
+        super(message);
+    }
+}
diff --git a/dx/src/com/android/dx/util/Writers.java b/dx/src/com/android/dx/util/Writers.java
new file mode 100644
index 0000000..eba845c
--- /dev/null
+++ b/dx/src/com/android/dx/util/Writers.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Utilities for dealing with {@code Writer}s.
+ */
+public final class Writers {
+    /**
+     * This class is uninstantiable.
+     */
+    private Writers() {
+        // This space intentionally left blank.
+    }
+
+    /**
+     * Makes a {@code PrintWriter} for the given {@code Writer},
+     * returning the given writer if it already happens to be the right
+     * class.
+     *
+     * @param writer {@code non-null;} writer to (possibly) wrap
+     * @return {@code non-null;} an appropriate instance
+     */
+    public static PrintWriter printWriterFor(Writer writer) {
+        if (writer instanceof PrintWriter) {
+            return (PrintWriter) writer;
+        }
+
+        return new PrintWriter(writer);
+    }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_BitIntSet.java b/dx/src/com/android/dx/util/_tests/_BitIntSet.java
new file mode 100644
index 0000000..e26d7a4
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_BitIntSet.java
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.IntIterator;
+import com.android.dx.util.ListIntSet;
+
+import junit.framework.TestCase;
+
+import java.util.NoSuchElementException;
+
+public class _BitIntSet extends TestCase {
+    public void test_basic() {
+        BitIntSet set = new BitIntSet(32);
+
+        assertEquals(0, set.elements());
+
+        set.add(0);
+        set.add(1);
+        set.add(31);
+
+        assertTrue(set.has(0));
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+
+        assertEquals(3, set.elements());
+
+        assertFalse(set.has(2));
+        assertFalse(set.has(7));
+        assertFalse(set.has(30));
+    }
+
+    public void test_iterator() {
+        BitIntSet set = new BitIntSet(32);
+
+        set.add(0);
+        set.add(0);
+        set.add(1);
+        set.add(1);
+        set.add(31);
+        set.add(31);
+
+        IntIterator iter = set.iterator();
+
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 0);
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 1);
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 31);
+
+        assertFalse(iter.hasNext());
+
+        try {
+            iter.next();
+            fail();
+        } catch (NoSuchElementException ex) {
+            // exception excepted
+        }
+    }
+
+    public void test_remove() {
+        BitIntSet set = new BitIntSet(32);
+
+        set.add(0);
+        set.add(1);
+        set.add(31);
+
+        assertTrue(set.has(0));
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+
+        assertFalse(set.has(2));
+        assertFalse(set.has(7));
+        assertFalse(set.has(30));
+
+        set.remove(0);
+
+        assertFalse(set.has(0));
+
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+    }
+
+    /**
+     * Tests the auto-expansion of the set
+     */
+    public void test_expand() {
+        BitIntSet set = new BitIntSet(32);
+        int[] values = {0, 1, 31, 32, 128};
+
+        for (int i = 0; i < values.length; i++) {
+            set.add(values[i]);
+        }
+
+        IntIterator iter = set.iterator();
+
+        for (int i = 0; i < values.length; i++) {
+            assertTrue(iter.hasNext());
+            assertEquals(values[i], iter.next());
+        }
+        assertFalse(iter.hasNext());
+    }
+
+    public void test_merge() {
+        BitIntSet setA = new BitIntSet(32);
+        int[] valuesA = {0, 1, 31};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        BitIntSet setB = new BitIntSet(32);
+        int[] valuesB = {0, 5, 6, 8, 31};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+    }
+
+    public void test_mergeWithListIntSet() {
+        BitIntSet setA = new BitIntSet(32);
+        int[] valuesA = {0, 1, 31};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        ListIntSet setB = new ListIntSet();
+        int[] valuesB = {0, 5, 6, 8, 31};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+    }
+
+    public void test_mergeAndExpand() {
+        BitIntSet setA = new BitIntSet(32);
+        int[] valuesA = {0, 1, 31};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        BitIntSet setB = new BitIntSet(32);
+        int[] valuesB = {0, 5, 6, 32, 127};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+    }
+
+    public void test_toString() {
+        BitIntSet set = new BitIntSet(32);
+
+        assertEquals(set.toString(), "{}");
+
+        set.add(1);
+
+        assertEquals(set.toString(), "{1}");
+
+        set.add(2);
+
+        assertEquals(set.toString(), "{1, 2}");
+    }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_Bits.java b/dx/src/com/android/dx/util/_tests/_Bits.java
new file mode 100644
index 0000000..a95fc14
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_Bits.java
@@ -0,0 +1,351 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.Bits;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class {@code com.android.dx.util.Bits}.
+ */
+public class _Bits
+        extends TestCase {
+    public void test_makeBitSet() {
+        assertEquals(label(0), 0, Bits.makeBitSet(0).length);
+
+        for (int i = 1; i <= 32; i++) {
+            assertEquals(label(i), 1, Bits.makeBitSet(i).length);
+        }
+
+        for (int i = 33; i <= 64; i++) {
+            assertEquals(label(i), 2, Bits.makeBitSet(i).length);
+        }
+
+        for (int i = 65; i < 4000; i += 101) {
+            int expect = i >> 5;
+            if ((expect * 32) < i) {
+                expect++;
+            }
+            assertEquals(label(i), expect, Bits.makeBitSet(i).length);
+        }
+    }
+
+    public void test_getMax() {
+        for (int i = 0; i < 4000; i += 59) {
+            int expect = i >> 5;
+            if ((expect * 32) < i) {
+                expect++;
+            }
+            assertEquals(label(i), expect * 32,
+                         Bits.getMax(new int[expect]));
+        }
+    }
+
+    public void test1_get() {
+        int[] bits = Bits.makeBitSet(100);
+
+        for (int i = 0; i < 100; i++) {
+            assertFalse(label(i), Bits.get(bits, i));
+        }
+    }
+
+    public void test2_get() {
+        int[] bits = Bits.makeBitSet(100);
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 0; i < 100; i++) {
+            assertTrue(label(i), Bits.get(bits, i));
+        }
+    }
+
+    public void test3_get() {
+        int[] bits = Bits.makeBitSet(100);
+
+        for (int i = 0; i < 100; i++) {
+            Bits.set(bits, i, (i % 5) == 0);
+        }
+
+        for (int i = 0; i < 100; i++) {
+            boolean expect = (i % 5) == 0;
+            assertTrue(label(i), Bits.get(bits, i) == expect);
+        }
+    }
+
+    public void test1_set1() {
+        int[] bits = Bits.makeBitSet(50);
+        bits[1] = -1;
+
+        Bits.set(bits, 0, true);
+        Bits.set(bits, 3, true);
+        Bits.set(bits, 6, true);
+        Bits.set(bits, 3, false);
+        Bits.set(bits, 35, false);
+        Bits.set(bits, 38, false);
+        Bits.set(bits, 42, false);
+        Bits.set(bits, 38, true);
+
+        assertEquals(label(1), 0x41, bits[0]);
+        assertEquals(label(2), 0xfffffbf7, bits[1]);
+    }
+
+    public void test2_set1() {
+        int[] bits = Bits.makeBitSet(100);
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 3) == 0) {
+                Bits.set(bits, i, true);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 5) == 0) {
+                Bits.set(bits, i, false);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 7) == 0) {
+                Bits.set(bits, i, true);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            boolean expect = ((i % 7) == 0) ||
+                (((i % 3) == 0) && ((i % 5) != 0));
+            assertTrue(label(i), Bits.get(bits, i) == expect);
+        }
+    }
+
+    public void test_set2() {
+        int[] bits = Bits.makeBitSet(100);
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 11) == 0) {
+                Bits.set(bits, i);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            boolean expect = (i % 11) == 0;
+            assertTrue(label(i), Bits.get(bits, i) == expect);
+        }
+    }
+
+    public void test_clear() {
+        int[] bits = Bits.makeBitSet(100);
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 0; i < 100; i++) {
+            if ((i % 5) == 0) {
+                Bits.clear(bits, i);
+            }
+        }
+
+        for (int i = 0; i < 100; i++) {
+            boolean expect = (i % 5) != 0;
+            assertTrue(label(i), Bits.get(bits, i) == expect);
+        }
+    }
+
+    public void test1_isEmpty() {
+        for (int i = 0; i < 10; i++) {
+            assertTrue(label(i), Bits.isEmpty(new int[i]));
+        }
+    }
+
+    public void test2_isEmpty() {
+        for (int i = 1; i < 1000; i += 11) {
+            int[] bits = Bits.makeBitSet(i);
+            for (int j = i % 11; j >= 0; j--) {
+                int x = i - 1 - (j * 13);
+                if (x >= 0) {
+                    Bits.set(bits, x);
+                }
+            }
+            assertFalse(label(i), Bits.isEmpty(bits));
+        }
+    }
+
+    public void test1_bitCount() {
+        for (int i = 0; i < 10; i++) {
+            assertEquals(label(i), 0, Bits.bitCount(new int[i]));
+        }
+    }
+
+    public void test2_bitCount() {
+        for (int i = 1; i < 1000; i += 13) {
+            int[] bits = Bits.makeBitSet(i);
+            int count = 0;
+            for (int j = 0; j < i; j += 20) {
+                Bits.set(bits, j);
+                count++;
+            }
+            for (int j = 7; j < i; j += 11) {
+                if (!Bits.get(bits, j)) {
+                    Bits.set(bits, j);
+                    count++;
+                }
+            }
+            for (int j = 3; j < i; j += 17) {
+                if (!Bits.get(bits, j)) {
+                    Bits.set(bits, j);
+                    count++;
+                }
+            }
+            assertEquals(label(i), count, Bits.bitCount(bits));
+        }
+    }
+
+    public void test1_anyInRange() {
+        int[] bits = new int[100];
+
+        for (int i = 0; i < 100; i += 11) {
+            assertFalse(label(i), Bits.anyInRange(bits, 0, i));
+        }
+    }
+
+    public void test2_anyInRange() {
+        int[] bits = new int[100];
+
+        for (int i = 0; i < 100; i += 11) {
+            assertFalse(label(i), Bits.anyInRange(bits, i, 100));
+        }
+    }
+
+    public void test3_anyInRange() {
+        int[] bits = new int[100];
+
+        for (int i = 0; i < 50; i += 7) {
+            assertFalse(label(i), Bits.anyInRange(bits, i, 100 - i));
+        }
+    }
+
+    public void test4_anyInRange() {
+        int[] bits = new int[100];
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 1; i < 100; i += 11) {
+            assertTrue(label(i), Bits.anyInRange(bits, 0, i));
+        }
+    }
+
+    public void test5_anyInRange() {
+        int[] bits = new int[100];
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 1; i < 100; i += 11) {
+            assertTrue(label(i), Bits.anyInRange(bits, i, 100));
+        }
+    }
+
+    public void test6_anyInRange() {
+        int[] bits = new int[100];
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 0; i < 50; i += 7) {
+            assertTrue(label(i), Bits.anyInRange(bits, i, 100 - i));
+        }
+    }
+
+    public void test1_findFirst1() {
+        int[] bits = new int[100];
+
+        for (int i = 0; i < 100; i++) {
+            assertEquals(label(i), -1, Bits.findFirst(bits, i));
+        }
+    }
+
+    public void test2_findFirst1() {
+        int[] bits = new int[100];
+        for (int i = 0; i < bits.length; i++) {
+            bits[i] = -1;
+        }
+
+        for (int i = 0; i < 100; i++) {
+            assertEquals(label(i), i, Bits.findFirst(bits, i));
+        }
+    }
+
+    public void test3_findFirst1() {
+        int[] bits = new int[100];
+
+        for (int i = 25; i < 80; i++) {
+            for (int j = 0; j < bits.length; j++) {
+                bits[j] = 0;
+            }
+
+            Bits.set(bits, i - 5);
+            Bits.set(bits, i + 5);
+            Bits.set(bits, i + 10);
+            Bits.set(bits, i + 20);
+            assertEquals(label(i), i + 5, Bits.findFirst(bits, i));
+        }
+    }
+
+    public void test1_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            assertEquals(label(i), -1, Bits.findFirst(0, i));
+        }
+    }
+
+    public void test2_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            assertEquals(label(i), i, Bits.findFirst(-1, i));
+        }
+    }
+
+    public void test3_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            assertEquals(label(i), -1, Bits.findFirst((1 << i) >>> 1, i));
+        }
+    }
+
+    public void test4_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            assertEquals(label(i), i, Bits.findFirst(1 << i, i));
+        }
+    }
+
+    public void test5_findFirst2() {
+        for (int i = 0; i < 31; i++) {
+            assertEquals(label(i), i + 1, Bits.findFirst(1 << (i + 1), i));
+        }
+    }
+
+    public void test6_findFirst2() {
+        for (int i = 0; i < 32; i++) {
+            int value = (1 << i);
+            value |= (value >>> 1);
+            assertEquals(label(i), i, Bits.findFirst(value, i));
+        }
+    }
+
+    private static String label(int n) {
+        return "(" + n + ")";
+    }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_IntList.java b/dx/src/com/android/dx/util/_tests/_IntList.java
new file mode 100644
index 0000000..dadbd54
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_IntList.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.IntList;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class {@code com.android.dx.util.IntList}.
+ */
+public class _IntList
+    extends TestCase {
+    // TODO: Add tests for the rest of the methods.
+
+    public void test_contains() {
+        for (int sz = 0; sz < 100; sz++) {
+            IntList list = new IntList(sz);
+            for (int i = 0; i < sz; i++) {
+                list.add(i * 2);
+            }
+            for (int i = (sz * 2) - 1; i >= 0; i--) {
+                boolean contains = list.contains(i);
+                if ((i & 1) == 0) {
+                    assertTrue(label(sz, i), contains);
+                } else {
+                    assertFalse(label(sz, i), contains);
+                }
+            }
+            assertFalse(label(sz, -1), list.contains(-1));
+            assertFalse(label(sz, sz * 2), list.contains(sz * 2));
+        }
+    }
+
+    public void test_addSorted() {
+        IntList list = new IntList(2);
+
+        list.add(9);
+        list.add(12);
+
+        assertTrue(list.contains(9));
+        assertTrue(list.contains(12));
+    }
+
+    public void test_addUnsorted() {
+        IntList list = new IntList(2);
+
+        list.add(12);
+        list.add(9);
+
+        assertTrue(list.contains(12));
+        assertTrue(list.contains(9));
+    }
+
+    private static String label(int n, int m) {
+        return "(" + n + "/" + m + ")";
+    }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_ListIntSet.java b/dx/src/com/android/dx/util/_tests/_ListIntSet.java
new file mode 100644
index 0000000..ccd5991
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_ListIntSet.java
@@ -0,0 +1,203 @@
+/*
+ * 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.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+import com.android.dx.util.IntIterator;
+
+import junit.framework.TestCase;
+
+import java.util.NoSuchElementException;
+
+public class _ListIntSet extends TestCase {
+    public void test_basic() {
+        ListIntSet set = new ListIntSet();
+
+        assertEquals(0, set.elements());
+
+        set.add(31);
+        set.add(0);
+        set.add(1);
+
+        assertTrue(set.has(0));
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+
+        assertEquals(3, set.elements());
+
+        assertFalse(set.has(2));
+        assertFalse(set.has(7));
+        assertFalse(set.has(30));
+    }
+
+    public void test_iterator() {
+        ListIntSet set = new ListIntSet();
+
+        set.add(0);
+        set.add(0);
+        set.add(1);
+        set.add(1);
+        set.add(31);
+        set.add(31);
+
+        IntIterator iter = set.iterator();
+
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 0);
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 1);
+        assertTrue(iter.hasNext());
+        assertEquals(iter.next(), 31);
+
+        assertFalse(iter.hasNext());
+
+        try {
+            iter.next();
+            fail();
+        } catch (NoSuchElementException ex) {
+            // exception excepted
+        }
+    }
+
+    public void test_empty() {
+        ListIntSet set = new ListIntSet();
+
+        IntIterator iter = set.iterator();
+
+        assertFalse(iter.hasNext());
+    }
+
+    public void test_remove() {
+        ListIntSet set = new ListIntSet();
+
+        set.add(0);
+        set.add(1);
+        set.add(31);
+
+        assertTrue(set.has(0));
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+
+        assertFalse(set.has(2));
+        assertFalse(set.has(7));
+        assertFalse(set.has(30));
+
+        set.remove(0);
+
+        assertFalse(set.has(0));
+
+        assertTrue(set.has(1));
+        assertTrue(set.has(31));
+    }
+
+    public void test_mergeA() {
+        ListIntSet setA = new ListIntSet();
+        int[] valuesA = {0, 1, 31};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        ListIntSet setB = new ListIntSet();
+        int[] valuesB = {0, 5, 6, 32, 127, 128};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+
+    }
+
+    public void test_mergeB() {
+        ListIntSet setA = new ListIntSet();
+        int[] valuesA = {0, 1, 31, 129, 130};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        ListIntSet setB = new ListIntSet();
+        int[] valuesB = {0, 5, 6, 32, 127,128};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+
+    }
+
+    public void test_mergeWithBitIntSet() {
+        ListIntSet setA = new ListIntSet();
+        int[] valuesA = {0, 1, 31, 129, 130};
+
+        for (int i = 0; i < valuesA.length; i++) {
+            setA.add(valuesA[i]);
+        }
+
+        BitIntSet setB = new BitIntSet(129);
+        int[] valuesB = {0, 5, 6, 32, 127,128};
+
+        for (int i = 0; i < valuesB.length; i++) {
+            setB.add(valuesB[i]);
+        }
+
+        setA.merge(setB);
+
+        for (int i = 0; i < valuesA.length; i++) {
+            assertTrue(setA.has(valuesA[i]));
+        }
+
+        for (int i = 0; i < valuesB.length; i++) {
+            assertTrue(setA.has(valuesB[i]));
+        }
+
+    }
+
+    public void test_toString() {
+        ListIntSet set = new ListIntSet();
+
+        assertEquals(set.toString(), "{}");
+
+        set.add(1);
+
+        assertEquals(set.toString(), "{1}");
+
+        set.add(2);
+
+        assertEquals(set.toString(), "{1, 2}");
+    }
+
+}
diff --git a/dx/src/com/android/dx/util/package.html b/dx/src/com/android/dx/util/package.html
new file mode 100644
index 0000000..8e81e94
--- /dev/null
+++ b/dx/src/com/android/dx/util/package.html
@@ -0,0 +1,3 @@
+<body>
+<p>Utility classes for class file access/manipulation.</p>
+</body>
diff --git a/dx/src/junit/extensions/ActiveTestSuite.java b/dx/src/junit/extensions/ActiveTestSuite.java
new file mode 100644
index 0000000..1a3f163
--- /dev/null
+++ b/dx/src/junit/extensions/ActiveTestSuite.java
@@ -0,0 +1,64 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A TestSuite for active Tests. It runs each
+ * test in a separate thread and waits until all
+ * threads have terminated.
+ * -- Aarhus Radisson Scandinavian Center 11th floor
+ */ 
+public class ActiveTestSuite extends TestSuite {
+	private volatile int fActiveTestDeathCount;
+
+	public ActiveTestSuite() {
+	}
+		
+	public ActiveTestSuite(Class theClass) {
+		super(theClass);
+	}
+	
+	public ActiveTestSuite(String name) {
+		super (name);
+	}
+	
+	public ActiveTestSuite(Class theClass, String name) {
+		super(theClass, name);
+	}
+	
+	public void run(TestResult result) {
+		fActiveTestDeathCount= 0;
+		super.run(result);
+		waitUntilFinished();
+	}
+	
+	public void runTest(final Test test, final TestResult result) {
+		Thread t= new Thread() {
+			public void run() {
+				try {
+					// inlined due to limitation in VA/Java 
+					//ActiveTestSuite.super.runTest(test, result);
+					test.run(result);
+				} finally {
+					ActiveTestSuite.this.runFinished(test);
+				}
+			}
+		};
+		t.start();
+	}
+
+	synchronized void waitUntilFinished() {
+		while (fActiveTestDeathCount < testCount()) {
+			try {
+				wait();
+			} catch (InterruptedException e) {
+				return; // ignore
+			}
+		}
+	}
+	
+	synchronized public void runFinished(Test test) {
+		fActiveTestDeathCount++;
+		notifyAll();
+	}
+}
diff --git a/dx/src/junit/extensions/ExceptionTestCase.java b/dx/src/junit/extensions/ExceptionTestCase.java
new file mode 100644
index 0000000..fae5746
--- /dev/null
+++ b/dx/src/junit/extensions/ExceptionTestCase.java
@@ -0,0 +1,46 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A TestCase that expects an Exception of class fExpected to be thrown.
+ * The other way to check that an expected exception is thrown is:
+ * <pre>
+ * try {
+ *   shouldThrow();
+ * }
+ * catch (SpecialException e) {
+ *   return;
+ * }
+ * fail("Expected SpecialException");
+ * </pre>
+ *
+ * To use ExceptionTestCase, create a TestCase like:
+ * <pre>
+ * new ExceptionTestCase("testShouldThrow", SpecialException.class);
+ * </pre>
+ */
+public class ExceptionTestCase extends TestCase {
+	Class<?> fExpected;
+
+	public ExceptionTestCase(String name, Class exception) {
+		super(name);
+		fExpected= exception;
+	}
+	/**
+	 * Execute the test method expecting that an Exception of
+	 * class fExpected or one of its subclasses will be thrown
+	 */
+	protected void runTest() throws Throwable {
+		try {
+			super.runTest();
+		}
+		catch (Exception e) {
+			if (fExpected.isAssignableFrom(e.getClass()))
+				return;
+			else
+				throw e;
+		}
+		fail("Expected exception " + fExpected);
+	}
+}
diff --git a/dx/src/junit/extensions/RepeatedTest.java b/dx/src/junit/extensions/RepeatedTest.java
new file mode 100644
index 0000000..ebce775
--- /dev/null
+++ b/dx/src/junit/extensions/RepeatedTest.java
@@ -0,0 +1,31 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator that runs a test repeatedly.
+ *
+ */
+public class RepeatedTest extends  TestDecorator {
+	private int fTimesRepeat;
+
+	public RepeatedTest(Test test, int repeat) {
+		super(test);
+		if (repeat < 0)
+			throw new IllegalArgumentException("Repetition count must be > 0");
+		fTimesRepeat= repeat;
+	}
+	public int countTestCases() {
+		return super.countTestCases()*fTimesRepeat;
+	}
+	public void run(TestResult result) {
+		for (int i= 0; i < fTimesRepeat; i++) {
+			if (result.shouldStop())
+				break;
+			super.run(result);
+		}
+	}
+	public String toString() {
+		return super.toString()+"(repeated)";
+	}
+}
diff --git a/dx/src/junit/extensions/TestDecorator.java b/dx/src/junit/extensions/TestDecorator.java
new file mode 100644
index 0000000..a8e9e7d
--- /dev/null
+++ b/dx/src/junit/extensions/TestDecorator.java
@@ -0,0 +1,38 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator for Tests. Use TestDecorator as the base class
+ * for defining new test decorators. Test decorator subclasses
+ * can be introduced to add behaviour before or after a test
+ * is run.
+ *
+ */
+public class TestDecorator extends Assert implements Test {
+	protected Test fTest;
+
+	public TestDecorator(Test test) {
+		fTest= test;
+	}
+	/**
+	 * The basic run behaviour.
+	 */
+	public void basicRun(TestResult result) {
+		fTest.run(result);
+	}
+	public int countTestCases() {
+		return fTest.countTestCases();
+	}
+	public void run(TestResult result) {
+		basicRun(result);
+	}
+
+	public String toString() {
+		return fTest.toString();
+	}
+
+	public Test getTest() {
+		return fTest;
+	}
+}
diff --git a/dx/src/junit/extensions/TestSetup.java b/dx/src/junit/extensions/TestSetup.java
new file mode 100644
index 0000000..f1c25fa
--- /dev/null
+++ b/dx/src/junit/extensions/TestSetup.java
@@ -0,0 +1,37 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator to set up and tear down additional fixture state.
+ * Subclass TestSetup and insert it into your tests when you want
+ * to set up additional state once before the tests are run.
+ */
+public class TestSetup extends TestDecorator {
+
+	public TestSetup(Test test) {
+		super(test);
+	}
+	public void run(final TestResult result) {
+		Protectable p= new Protectable() {
+			public void protect() throws Exception {
+				setUp();
+				basicRun(result);
+				tearDown();
+			}
+		};
+		result.runProtected(this, p);
+	}
+	/**
+	 * Sets up the fixture. Override to set up additional fixture
+	 * state.
+	 */
+	protected void setUp() throws Exception {
+	}
+	/**
+	 * Tears down the fixture. Override to tear down the additional
+	 * fixture state.
+	 */
+	protected void tearDown() throws Exception {
+	}
+}
diff --git a/dx/src/junit/framework/Assert.java b/dx/src/junit/framework/Assert.java
new file mode 100644
index 0000000..eb5d960
--- /dev/null
+++ b/dx/src/junit/framework/Assert.java
@@ -0,0 +1,291 @@
+package junit.framework;
+
+/**
+ * A set of assert methods.  Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+	/**
+	 * Protect constructor since it is a static only class
+	 */
+	protected Assert() {
+	}
+
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertTrue(String message, boolean condition) {
+		if (!condition)
+			fail(message);
+	}
+	/**
+	 * Asserts that a condition is true. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertTrue(boolean condition) {
+		assertTrue(null, condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError with the given message.
+	 */
+	static public void assertFalse(String message, boolean condition) {
+		assertTrue(message, !condition);
+	}
+	/**
+	 * Asserts that a condition is false. If it isn't it throws
+	 * an AssertionFailedError.
+	 */
+	static public void assertFalse(boolean condition) {
+		assertFalse(null, condition);
+	}
+	/**
+	 * Fails a test with the given message.
+	 */
+	static public void fail(String message) {
+		throw new AssertionFailedError(message);
+	}
+	/**
+	 * Fails a test with no message.
+	 */
+	static public void fail() {
+		fail(null);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, Object expected, Object actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		failNotEquals(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects are equal. If they are not
+	 * an AssertionFailedError is thrown.
+	 */
+	static public void assertEquals(Object expected, Object actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String message, String expected, String actual) {
+		if (expected == null && actual == null)
+			return;
+		if (expected != null && expected.equals(actual))
+			return;
+		throw new ComparisonFailure(message, expected, actual);
+	}
+	/**
+	 * Asserts that two Strings are equal. 
+	 */
+	static public void assertEquals(String expected, String actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta.  If they are not
+	 * an AssertionFailedError is thrown with the given message.  If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, double expected, double actual, double delta) {
+		// handle infinity specially since subtracting to infinite values gives NaN and the
+		// the following test fails
+		if (Double.isInfinite(expected)) {
+			if (!(expected == actual))
+				failNotEquals(message, new Double(expected), new Double(actual));
+		} else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
+			failNotEquals(message, new Double(expected), new Double(actual));
+	}
+	/**
+	 * Asserts that two doubles are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(double expected, double actual, double delta) {
+	    assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two floats are equal concerning a delta. If they are not
+	 * an AssertionFailedError is thrown with the given message.  If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(String message, float expected, float actual, float delta) {
+ 		// handle infinity specially since subtracting to infinite values gives NaN and the
+		// the following test fails
+		if (Float.isInfinite(expected)) {
+			if (!(expected == actual))
+				failNotEquals(message, new Float(expected), new Float(actual));
+		} else if (!(Math.abs(expected-actual) <= delta))
+      		failNotEquals(message, new Float(expected), new Float(actual));
+	}
+	/**
+	 * Asserts that two floats are equal concerning a delta. If the expected
+	 * value is infinity then the delta value is ignored.
+	 */
+	static public void assertEquals(float expected, float actual, float delta) {
+		assertEquals(null, expected, actual, delta);
+	}
+	/**
+	 * Asserts that two longs are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, long expected, long actual) {
+	    assertEquals(message, new Long(expected), new Long(actual));
+	}
+	/**
+	 * Asserts that two longs are equal.
+	 */
+	static public void assertEquals(long expected, long actual) {
+	    assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two booleans are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, boolean expected, boolean actual) {
+    		assertEquals(message, new Boolean(expected), new Boolean(actual));
+  	}
+	/**
+	 * Asserts that two booleans are equal.
+ 	 */
+	static public void assertEquals(boolean expected, boolean actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two bytes are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, byte expected, byte actual) {
+		assertEquals(message, new Byte(expected), new Byte(actual));
+	}
+	/**
+   	 * Asserts that two bytes are equal.
+	 */
+	static public void assertEquals(byte expected, byte actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two chars are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, char expected, char actual) {
+    		assertEquals(message, new Character(expected), new Character(actual));
+  	}
+	/**
+	 * Asserts that two chars are equal.
+	 */
+  	static public void assertEquals(char expected, char actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two shorts are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertEquals(String message, short expected, short actual) {
+    		assertEquals(message, new Short(expected), new Short(actual));
+	}
+  	/**
+	 * Asserts that two shorts are equal.
+	 */
+	static public void assertEquals(short expected, short actual) {
+		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that two ints are equal. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+  	static public void assertEquals(String message, int expected, int actual) {
+		assertEquals(message, new Integer(expected), new Integer(actual));
+  	}
+  	/**
+   	 * Asserts that two ints are equal.
+	 */
+  	static public void assertEquals(int expected, int actual) {
+  		assertEquals(null, expected, actual);
+	}
+	/**
+	 * Asserts that an object isn't null.
+	 */
+	static public void assertNotNull(Object object) {
+		assertNotNull(null, object);
+	}
+	/**
+	 * Asserts that an object isn't null. If it is
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNotNull(String message, Object object) {
+		assertTrue(message, object != null);
+	}
+	/**
+	 * Asserts that an object is null.
+	 */
+	static public void assertNull(Object object) {
+		assertNull(null, object);
+	}
+	/**
+	 * Asserts that an object is null.  If it is not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertNull(String message, Object object) {
+		assertTrue(message, object == null);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * an AssertionFailedError is thrown with the given message.
+	 */
+	static public void assertSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			return;
+		failNotSame(message, expected, actual);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * the same an AssertionFailedError is thrown.
+	 */
+	static public void assertSame(Object expected, Object actual) {
+	    assertSame(null, expected, actual);
+	}
+ 	/**
+ 	 * Asserts that two objects refer to the same object. If they are not
+ 	 * an AssertionFailedError is thrown with the given message.
+ 	 */
+	static public void assertNotSame(String message, Object expected, Object actual) {
+		if (expected == actual)
+			failSame(message);
+	}
+	/**
+	 * Asserts that two objects refer to the same object. If they are not
+	 * the same an AssertionFailedError is thrown.
+	 */
+	static public void assertNotSame(Object expected, Object actual) {
+		assertNotSame(null, expected, actual);
+	}
+
+	static private void failSame(String message) {
+		String formatted= "";
+ 		if (message != null)
+ 			formatted= message+" ";
+ 		fail(formatted+"expected not same");
+	}
+
+	static private void failNotSame(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null)
+			formatted= message+" ";
+		fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+	}
+
+	static private void failNotEquals(String message, Object expected, Object actual) {
+		fail(format(message, expected, actual));
+	}
+
+	static String format(String message, Object expected, Object actual) {
+		String formatted= "";
+		if (message != null)
+			formatted= message+" ";
+		return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+	}
+}
diff --git a/dx/src/junit/framework/AssertionFailedError.java b/dx/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..9aee001
--- /dev/null
+++ b/dx/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,13 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends Error {
+
+	public AssertionFailedError () {
+	}
+	public AssertionFailedError (String message) {
+		super (message);
+	}
+}
diff --git a/dx/src/junit/framework/ComparisonFailure.java b/dx/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..1bfe591
--- /dev/null
+++ b/dx/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,68 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ * 
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+	private String fExpected;
+	private String fActual;
+
+	/**
+	 * Constructs a comparison failure.
+	 * @param message the identifying message or null
+	 * @param expected the expected string value
+	 * @param actual the actual string value
+	 */
+	public ComparisonFailure (String message, String expected, String actual) {
+		super (message);
+		fExpected= expected;
+		fActual= actual;
+	}
+	
+	/**
+	 * Returns "..." in place of common prefix and "..." in
+	 * place of common suffix between expected and actual.
+	 * 
+	 * @see java.lang.Throwable#getMessage()
+	 */
+	public String getMessage() {
+		if (fExpected == null || fActual == null)
+			return Assert.format(super.getMessage(), fExpected, fActual);
+			
+		int end= Math.min(fExpected.length(), fActual.length());
+		
+		int i= 0;
+		for(; i < end; i++) {
+			if (fExpected.charAt(i) != fActual.charAt(i))
+				break;
+		}
+		int j= fExpected.length()-1;
+		int k= fActual.length()-1;
+		for (; k >= i && j >= i; k--,j--) {
+			if (fExpected.charAt(j) != fActual.charAt(k))
+				break;
+		}
+		String actual, expected;
+		
+		// equal strings
+		if (j < i && k < i) {
+			expected= fExpected;
+			actual= fActual;
+		} else {
+			expected= fExpected.substring(i, j+1);
+			actual= fActual.substring(i, k+1);
+			if (i <= end && i > 0) {
+				expected= "..."+expected;
+				actual= "..."+actual;
+			}
+			
+			if (j < fExpected.length()-1)
+				expected= expected+"...";
+			if (k < fActual.length()-1)
+				actual= actual+"...";
+		}	
+		return Assert.format(super.getMessage(), expected, actual);
+	}
+}
diff --git a/dx/src/junit/framework/Protectable.java b/dx/src/junit/framework/Protectable.java
new file mode 100644
index 0000000..8243555
--- /dev/null
+++ b/dx/src/junit/framework/Protectable.java
@@ -0,0 +1,14 @@
+package junit.framework;
+
+/**
+ * A <em>Protectable</em> can be run and can throw a Throwable.
+ *
+ * @see TestResult
+ */
+public interface Protectable {
+
+	/**
+	 * Run the the following method protected.
+	 */
+	public abstract void protect() throws Throwable;
+}
diff --git a/dx/src/junit/framework/Test.java b/dx/src/junit/framework/Test.java
new file mode 100644
index 0000000..1c6d57b
--- /dev/null
+++ b/dx/src/junit/framework/Test.java
@@ -0,0 +1,17 @@
+package junit.framework;
+
+/**
+ * A <em>Test</em> can be run and collect its results.
+ *
+ * @see TestResult
+ */
+public interface Test {
+	/**
+	 * Counts the number of test cases that will be run by this test.
+	 */
+	public abstract int countTestCases();
+	/**
+	 * Runs a test and collects its result in a TestResult instance.
+	 */
+	public abstract void run(TestResult result);
+}
diff --git a/dx/src/junit/framework/TestCase.java b/dx/src/junit/framework/TestCase.java
new file mode 100644
index 0000000..8988c45
--- /dev/null
+++ b/dx/src/junit/framework/TestCase.java
@@ -0,0 +1,197 @@
+package junit.framework;
+
+import java.lang.reflect.*;
+
+/**
+ * A test case defines the fixture to run multiple tests. To define a test case<br>
+ * 1) implement a subclass of TestCase<br>
+ * 2) define instance variables that store the state of the fixture<br>
+ * 3) initialize the fixture state by overriding <code>setUp</code><br>
+ * 4) clean-up after a test by overriding <code>tearDown</code>.<br>
+ * Each test runs in its own fixture so there
+ * can be no side effects among test runs.
+ * Here is an example:
+ * <pre>
+ * public class MathTest extends TestCase {
+ *     protected double fValue1;
+ *     protected double fValue2;
+ *
+ *    protected void setUp() {
+ *         fValue1= 2.0;
+ *         fValue2= 3.0;
+ *     }
+ * }
+ * </pre>
+ *
+ * For each test implement a method which interacts
+ * with the fixture. Verify the expected results with assertions specified
+ * by calling <code>assertTrue</code> with a boolean.
+ * <pre>
+ *    public void testAdd() {
+ *        double result= fValue1 + fValue2;
+ *        assertTrue(result == 5.0);
+ *    }
+ * </pre>
+ * Once the methods are defined you can run them. The framework supports
+ * both a static type safe and more dynamic way to run a test.
+ * In the static way you override the runTest method and define the method to
+ * be invoked. A convenient way to do so is with an anonymous inner class.
+ * <pre>
+ * TestCase test= new MathTest("add") {
+ *        public void runTest() {
+ *            testAdd();
+ *        }
+ * };
+ * test.run();
+ * </pre>
+ * The dynamic way uses reflection to implement <code>runTest</code>. It dynamically finds
+ * and invokes a method.
+ * In this case the name of the test case has to correspond to the test method
+ * to be run.
+ * <pre>
+ * TestCase= new MathTest("testAdd");
+ * test.run();
+ * </pre>
+ * The tests to be run can be collected into a TestSuite. JUnit provides
+ * different <i>test runners</i> which can run a test suite and collect the results.
+ * A test runner either expects a static method <code>suite</code> as the entry
+ * point to get a test to run or it will extract the suite automatically.
+ * <pre>
+ * public static Test suite() {
+ *      suite.addTest(new MathTest("testAdd"));
+ *      suite.addTest(new MathTest("testDivideByZero"));
+ *      return suite;
+ *  }
+ * </pre>
+ * @see TestResult
+ * @see TestSuite
+ */
+
+public abstract class TestCase extends Assert implements Test {
+	/**
+	 * the name of the test case
+	 */
+	private String fName;
+
+	/**
+	 * No-arg constructor to enable serialization. This method
+	 * is not intended to be used by mere mortals without calling setName().
+	 */
+	public TestCase() {
+		fName= null;
+	}
+	/**
+	 * Constructs a test case with the given name.
+	 */
+	public TestCase(String name) {
+		fName= name;
+	}
+	/**
+	 * Counts the number of test cases executed by run(TestResult result).
+	 */
+	public int countTestCases() {
+		return 1;
+	}
+	/**
+	 * Creates a default TestResult object
+	 *
+	 * @see TestResult
+	 */
+	protected TestResult createResult() {
+	    return new TestResult();
+	}
+	/**
+	 * A convenience method to run this test, collecting the results with a
+	 * default TestResult object.
+	 *
+	 * @see TestResult
+	 */
+	public TestResult run() {
+		TestResult result= createResult();
+		run(result);
+		return result;
+	}
+	/**
+	 * Runs the test case and collects the results in TestResult.
+	 */
+	public void run(TestResult result) {
+		result.run(this);
+	}
+	/**
+	 * Runs the bare test sequence.
+	 * @exception Throwable if any exception is thrown
+	 */
+	public void runBare() throws Throwable {
+		setUp();
+		try {
+			runTest();
+		}
+		finally {
+			tearDown();
+		}
+	}
+	/**
+	 * Override to run the test and assert its state.
+	 * @exception Throwable if any exception is thrown
+	 */
+	protected void runTest() throws Throwable {
+		assertNotNull(fName);
+		Method runMethod= null;
+		try {
+			// use getMethod to get all public inherited
+			// methods. getDeclaredMethods returns all
+			// methods of this class but excludes the
+			// inherited ones.
+			runMethod= getClass().getMethod(fName, (Class[]) null);
+		} catch (NoSuchMethodException e) {
+			fail("Method \""+fName+"\" not found");
+		}
+		if (!Modifier.isPublic(runMethod.getModifiers())) {
+			fail("Method \""+fName+"\" should be public");
+		}
+
+		try {
+			runMethod.invoke(this, (Object[]) new Class[0]);
+		}
+		catch (InvocationTargetException e) {
+			e.fillInStackTrace();
+			throw e.getTargetException();
+		}
+		catch (IllegalAccessException e) {
+			e.fillInStackTrace();
+			throw e;
+		}
+	}
+	/**
+	 * Sets up the fixture, for example, open a network connection.
+	 * This method is called before a test is executed.
+	 */
+	protected void setUp() throws Exception {
+	}
+	/**
+	 * Tears down the fixture, for example, close a network connection.
+	 * This method is called after a test is executed.
+	 */
+	protected void tearDown() throws Exception {
+	}
+	/**
+	 * Returns a string representation of the test case
+	 */
+	public String toString() {
+	    return getName() + "(" + getClass().getName() + ")";
+	}
+	/**
+	 * Gets the name of a TestCase
+	 * @return returns a String
+	 */
+	public String getName() {
+		return fName;
+	}
+	/**
+	 * Sets the name of a TestCase
+	 * @param name The name to set
+	 */
+	public void setName(String name) {
+		fName= name;
+	}
+}
diff --git a/dx/src/junit/framework/TestFailure.java b/dx/src/junit/framework/TestFailure.java
new file mode 100644
index 0000000..664747d
--- /dev/null
+++ b/dx/src/junit/framework/TestFailure.java
@@ -0,0 +1,57 @@
+package junit.framework;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * A <code>TestFailure</code> collects a failed test together with
+ * the caught exception.
+ * @see TestResult
+ */
+public class TestFailure extends Object {
+	protected Test fFailedTest;
+	protected Throwable fThrownException;
+	
+
+	/**
+	 * Constructs a TestFailure with the given test and exception.
+	 */
+	public TestFailure(Test failedTest, Throwable thrownException) {
+		fFailedTest= failedTest;
+		fThrownException= thrownException;
+	}
+	/**
+	 * Gets the failed test.
+	 */
+	public Test failedTest() {
+	    return fFailedTest;
+	}
+	/**
+	 * Gets the thrown exception.
+	 */
+	public Throwable thrownException() {
+	    return fThrownException;
+	}
+	/**
+	 * Returns a short description of the failure.
+	 */
+	public String toString() {
+	    StringBuffer buffer= new StringBuffer();
+	    buffer.append(fFailedTest+": "+fThrownException.getMessage());
+	    return buffer.toString();
+	}
+	public String trace() {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		thrownException().printStackTrace(writer);
+		StringBuffer buffer= stringWriter.getBuffer();
+		return buffer.toString();
+	}
+	public String exceptionMessage() {
+		return thrownException().getMessage();
+	}
+	public boolean isFailure() {
+		return thrownException() instanceof AssertionFailedError;
+	}
+}
diff --git a/dx/src/junit/framework/TestListener.java b/dx/src/junit/framework/TestListener.java
new file mode 100644
index 0000000..7558187
--- /dev/null
+++ b/dx/src/junit/framework/TestListener.java
@@ -0,0 +1,23 @@
+package junit.framework;
+
+/**
+ * A Listener for test progress
+ */
+public interface TestListener {
+	/**
+ 	 * An error occurred.
+ 	 */
+	public void addError(Test test, Throwable t);
+	/**
+ 	 * A failure occurred.
+ 	 */
+ 	public void addFailure(Test test, AssertionFailedError t);  
+	/**
+	 * A test ended.
+	 */
+ 	public void endTest(Test test); 
+	/**
+	 * A test started.
+	 */
+	public void startTest(Test test);
+}
diff --git a/dx/src/junit/framework/TestResult.java b/dx/src/junit/framework/TestResult.java
new file mode 100644
index 0000000..4b1a7e2
--- /dev/null
+++ b/dx/src/junit/framework/TestResult.java
@@ -0,0 +1,166 @@
+package junit.framework;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * A <code>TestResult</code> collects the results of executing
+ * a test case. It is an instance of the Collecting Parameter pattern.
+ * The test framework distinguishes between <i>failures</i> and <i>errors</i>.
+ * A failure is anticipated and checked for with assertions. Errors are
+ * unanticipated problems like an <code>ArrayIndexOutOfBoundsException</code>.
+ *
+ * @see Test
+ */
+public class TestResult extends Object {
+	protected Vector<TestFailure> fFailures;
+	protected Vector<TestFailure> fErrors;
+	protected Vector<TestListener> fListeners;
+	protected int fRunTests;
+	private boolean fStop;
+	
+	public TestResult() {
+		fFailures= new Vector<TestFailure>();
+		fErrors= new Vector<TestFailure>();
+		fListeners= new Vector<TestListener>();
+		fRunTests= 0;
+		fStop= false;
+	}
+	/**
+	 * Adds an error to the list of errors. The passed in exception
+	 * caused the error.
+	 */
+	public synchronized void addError(Test test, Throwable t) {
+		fErrors.addElement(new TestFailure(test, t));
+		for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+			((TestListener)e.nextElement()).addError(test, t);
+		}
+	}
+	/**
+	 * Adds a failure to the list of failures. The passed in exception
+	 * caused the failure.
+	 */
+	public synchronized void addFailure(Test test, AssertionFailedError t) {
+		fFailures.addElement(new TestFailure(test, t));
+		for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+			((TestListener)e.nextElement()).addFailure(test, t);
+		}
+	}
+	/**
+	 * Registers a TestListener
+	 */
+	public synchronized void addListener(TestListener listener) {
+		fListeners.addElement(listener);
+	}
+	/**
+	 * Unregisters a TestListener
+	 */
+	public synchronized void removeListener(TestListener listener) {
+		fListeners.removeElement(listener);
+	}
+	/**
+	 * Returns a copy of the listeners.
+	 */
+	private synchronized Vector cloneListeners() {
+		return (Vector)fListeners.clone();
+	}
+	/**
+	 * Informs the result that a test was completed.
+	 */
+	public void endTest(Test test) {
+		for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+			((TestListener)e.nextElement()).endTest(test);
+		}
+	}
+	/**
+	 * Gets the number of detected errors.
+	 */
+	public synchronized int errorCount() {
+		return fErrors.size();
+	}
+	/**
+	 * Returns an Enumeration for the errors
+	 */
+	public synchronized Enumeration errors() {
+		return fErrors.elements();
+	}
+	/**
+	 * Gets the number of detected failures.
+	 */
+	public synchronized int failureCount() {
+		return fFailures.size();
+	}
+	/**
+	 * Returns an Enumeration for the failures
+	 */
+	public synchronized Enumeration failures() {
+		return fFailures.elements();
+	}
+	/**
+	 * Runs a TestCase.
+	 */
+	protected void run(final TestCase test) {
+		startTest(test);
+		Protectable p= new Protectable() {
+			public void protect() throws Throwable {
+				test.runBare();
+			}
+		};
+		runProtected(test, p);
+
+		endTest(test);
+	}
+	/**
+	 * Gets the number of run tests.
+	 */
+	public synchronized int runCount() {
+		return fRunTests;
+	}
+	/**
+	 * Runs a TestCase.
+	 */
+	public void runProtected(final Test test, Protectable p) {
+		try {
+			p.protect();
+		} 
+		catch (AssertionFailedError e) {
+			addFailure(test, e);
+		}
+		catch (ThreadDeath e) { // don't catch ThreadDeath by accident
+			throw e;
+		}
+		catch (Throwable e) {
+			addError(test, e);
+		}
+	}
+	/**
+	 * Checks whether the test run should stop
+	 */
+	public synchronized boolean shouldStop() {
+		return fStop;
+	}
+	/**
+	 * Informs the result that a test will be started.
+	 */
+	public void startTest(Test test) {
+		final int count= test.countTestCases();
+		synchronized(this) {
+			fRunTests+= count;
+		}
+		for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+			((TestListener)e.nextElement()).startTest(test);
+		}
+	}
+	/**
+	 * Marks that the test run should stop.
+	 */
+	public synchronized void stop() {
+		fStop= true;
+	}
+	/**
+	 * Returns whether the entire test was successful or not.
+	 */
+	public synchronized boolean wasSuccessful() {
+		return failureCount() == 0 && errorCount() == 0;
+	}
+}
diff --git a/dx/src/junit/framework/TestSuite.java b/dx/src/junit/framework/TestSuite.java
new file mode 100644
index 0000000..2987ad1
--- /dev/null
+++ b/dx/src/junit/framework/TestSuite.java
@@ -0,0 +1,265 @@
+package junit.framework;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.io.PrintWriter;import java.io.StringWriter;import java.lang.reflect.*;
+import java.lang.reflect.Constructor;
+
+/**
+ * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
+ * It runs a collection of test cases. Here is an example using
+ * the dynamic test definition.
+ * <pre>
+ * TestSuite suite= new TestSuite();
+ * suite.addTest(new MathTest("testAdd"));
+ * suite.addTest(new MathTest("testDivideByZero"));
+ * </pre>
+ * Alternatively, a TestSuite can extract the tests to be run automatically.
+ * To do so you pass the class of your TestCase class to the
+ * TestSuite constructor.
+ * <pre>
+ * TestSuite suite= new TestSuite(MathTest.class);
+ * </pre>
+ * This constructor creates a suite with all the methods
+ * starting with "test" that take no arguments.
+ *
+ * @see Test
+ */
+public class TestSuite implements Test {
+
+	private Vector<Test> fTests= new Vector<Test>(10);
+	private String fName;
+
+    /**
+	 * Constructs an empty TestSuite.
+	 */
+	public TestSuite() {
+	}
+	
+	/**
+	 * Constructs a TestSuite from the given class with the given name.
+	 * @see TestSuite#TestSuite(Class)
+	 */
+	public TestSuite(Class theClass, String name) {
+		this(theClass);
+		setName(name);
+	}
+	
+	/**
+	 * Constructs a TestSuite from the given class. Adds all the methods
+	 * starting with "test" as test cases to the suite.
+	 * Parts of this method was written at 2337 meters in the Huffihutte,
+	 * Kanton Uri
+	 */
+	 public TestSuite(final Class theClass) {
+		fName= theClass.getName();
+		try {
+			getTestConstructor(theClass); // Avoid generating multiple error messages
+		} catch (NoSuchMethodException e) {
+			addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
+			return;
+		}
+
+		if (!Modifier.isPublic(theClass.getModifiers())) {
+			addTest(warning("Class "+theClass.getName()+" is not public"));
+			return;
+		}
+
+		Class superClass= theClass;
+		Vector<String> names= new Vector<String>();
+		while (Test.class.isAssignableFrom(superClass)) {
+			Method[] methods= superClass.getDeclaredMethods();
+			for (int i= 0; i < methods.length; i++) {
+				addTestMethod(methods[i], names, theClass);
+			}
+			superClass= superClass.getSuperclass();
+		}
+		if (fTests.size() == 0)
+			addTest(warning("No tests found in "+theClass.getName()));
+	}
+	
+   	/**
+	 * Constructs an empty TestSuite.
+	 */
+	public TestSuite(String name) {
+		setName(name);
+	}
+	
+	/**
+	 * Adds a test to the suite.
+	 */
+	public void addTest(Test test) {
+		fTests.addElement(test);
+	}
+
+	/**
+	 * Adds the tests from the given class to the suite
+	 */
+	public void addTestSuite(Class testClass) {
+		addTest(new TestSuite(testClass));
+	}
+
+	private void addTestMethod(Method m, Vector<String> names, Class theClass) {
+		String name= m.getName();
+		if (names.contains(name))
+			return;
+		if (! isPublicTestMethod(m)) {
+			if (isTestMethod(m))
+				addTest(warning("Test method isn't public: "+m.getName()));
+			return;
+		}
+		names.addElement(name);
+		addTest(createTest(theClass, name));
+	}
+
+	/**
+	 * ...as the moon sets over the early morning Merlin, Oregon
+	 * mountains, our intrepid adventurers type...
+	 */
+	static public Test createTest(Class theClass, String name) {
+		Constructor constructor;
+		try {
+			constructor= getTestConstructor(theClass);
+		} catch (NoSuchMethodException e) {
+			return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
+		}
+		Object test;
+		try {
+			if (constructor.getParameterTypes().length == 0) {
+				test= constructor.newInstance(new Object[0]);
+				if (test instanceof TestCase)
+					((TestCase) test).setName(name);
+			} else {
+				test= constructor.newInstance(new Object[]{name});
+			}
+		} catch (InstantiationException e) {
+			return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
+		} catch (InvocationTargetException e) {
+			return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
+		} catch (IllegalAccessException e) {
+			return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
+		}
+		return (Test) test;
+	}
+
+	/**
+	 * Converts the stack trace into a string
+	 */
+	private static String exceptionToString(Throwable t) {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		t.printStackTrace(writer);
+		return stringWriter.toString();
+
+	}
+	
+	/**
+	 * Counts the number of test cases that will be run by this test.
+	 */
+	public int countTestCases() {
+		int count= 0;
+		for (Enumeration e= tests(); e.hasMoreElements(); ) {
+			Test test= (Test)e.nextElement();
+			count= count + test.countTestCases();
+		}
+		return count;
+	}
+	
+	/**
+	 * Gets a constructor which takes a single String as
+	 * its argument or a no arg constructor.
+	 */
+	public static Constructor getTestConstructor(Class theClass) throws NoSuchMethodException {
+		Class[] args= { String.class };
+		try {
+			return theClass.getConstructor(args);	
+		} catch (NoSuchMethodException e) {
+			// fall through
+		}
+		return theClass.getConstructor(new Class[0]);
+	}
+
+	private boolean isPublicTestMethod(Method m) {
+		return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
+	 }
+	 
+	private boolean isTestMethod(Method m) {
+		String name= m.getName();
+		Class[] parameters= m.getParameterTypes();
+		Class returnType= m.getReturnType();
+		return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
+	 }
+	 
+	/**
+	 * Runs the tests and collects their result in a TestResult.
+	 */
+	public void run(TestResult result) {
+		for (Enumeration e= tests(); e.hasMoreElements(); ) {
+	  		if (result.shouldStop() )
+	  			break;
+			Test test= (Test)e.nextElement();
+			runTest(test, result);
+		}
+	}
+
+	public void runTest(Test test, TestResult result) {
+		test.run(result);
+	}
+
+	/**
+	 * Returns the test at the given index
+	 */
+	public Test testAt(int index) {
+		return (Test)fTests.elementAt(index);
+	}
+	
+	/**
+	 * Returns the number of tests in this suite
+	 */
+	public int testCount() {
+		return fTests.size();
+	}
+	
+	/**
+	 * Returns the tests as an enumeration
+	 */
+	public Enumeration tests() {
+		return fTests.elements();
+	}
+	
+	/**
+	 */
+	public String toString() {
+		if (getName() != null)
+			return getName();
+		return super.toString();
+	 }
+	 
+	/**
+	 * Sets the name of the suite.
+	 * @param name The name to set
+	 */
+	public void setName(String name) {
+		fName= name;
+	}
+
+	/**
+	 * Returns the name of the suite. Not all
+	 * test suites have a name and this method
+	 * can return null.
+	 */
+	public String getName() {
+		return fName;
+	}
+
+	/**
+	 * Returns a test which will fail and log a warning message.
+	 */
+	private static Test warning(final String message) {
+		return new TestCase("warning") {
+			protected void runTest() {
+				fail(message);
+			}
+		};
+	}
+}
diff --git a/dx/src/junit/runner/BaseTestRunner.java b/dx/src/junit/runner/BaseTestRunner.java
new file mode 100644
index 0000000..39c8c2f
--- /dev/null
+++ b/dx/src/junit/runner/BaseTestRunner.java
@@ -0,0 +1,323 @@
+package junit.runner;
+
+import junit.framework.*;
+import java.lang.reflect.*;
+import java.text.NumberFormat;
+import java.io.*;
+import java.util.*;
+
+/**
+ * Base class for all test runners.
+ * This class was born live on stage in Sardinia during XP2000.
+ */
+public abstract class BaseTestRunner implements TestListener {
+	public static final String SUITE_METHODNAME= "suite";
+
+	private static Properties fPreferences;
+	static int fgMaxMessageLength= 500;
+	static boolean fgFilterStack= true;
+	boolean fLoading= true;
+
+    /*
+    * Implementation of TestListener
+    */
+	public synchronized void startTest(Test test) {
+		testStarted(test.toString());
+	}
+
+	protected static void setPreferences(Properties preferences) {
+		fPreferences= preferences;
+	}
+
+	protected static Properties getPreferences() {
+		if (fPreferences == null) {
+			fPreferences= new Properties();
+	 		fPreferences.put("loading", "true");
+ 			fPreferences.put("filterstack", "true");
+  			readPreferences();
+		}
+		return fPreferences;
+	}
+
+	public static void savePreferences() throws IOException {
+		FileOutputStream fos= new FileOutputStream(getPreferencesFile());
+		try {
+			getPreferences().store(fos, "");
+		} finally {
+			fos.close();
+		}
+	}
+
+	public void setPreference(String key, String value) {
+		getPreferences().setProperty(key, value);
+	}
+
+	public synchronized void endTest(Test test) {
+		testEnded(test.toString());
+	}
+
+	public synchronized void addError(final Test test, final Throwable t) {
+		testFailed(TestRunListener.STATUS_ERROR, test, t);
+	}
+
+	public synchronized void addFailure(final Test test, final AssertionFailedError t) {
+		testFailed(TestRunListener.STATUS_FAILURE, test, t);
+	}
+
+	// TestRunListener implementation
+
+	public abstract void testStarted(String testName);
+
+	public abstract void testEnded(String testName);
+
+	public abstract void testFailed(int status, Test test, Throwable t);
+
+	/**
+	 * Returns the Test corresponding to the given suite. This is
+	 * a template method, subclasses override runFailed(), clearStatus().
+	 */
+	public Test getTest(String suiteClassName) {
+		if (suiteClassName.length() <= 0) {
+			clearStatus();
+			return null;
+		}
+		Class testClass= null;
+		try {
+			testClass= loadSuiteClass(suiteClassName);
+		} catch (ClassNotFoundException e) {
+			String clazz= e.getMessage();
+			if (clazz == null)
+				clazz= suiteClassName;
+			runFailed("Class not found \""+clazz+"\"");
+			return null;
+		} catch(Exception e) {
+			runFailed("Error: "+e.toString());
+			return null;
+		}
+		Method suiteMethod= null;
+		try {
+			suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]);
+	 	} catch(Exception e) {
+	 		// try to extract a test suite automatically
+			clearStatus();
+			return new TestSuite(testClass);
+		}
+		if (! Modifier.isStatic(suiteMethod.getModifiers())) {
+			runFailed("Suite() method must be static");
+			return null;
+		}
+		Test test= null;
+		try {
+			test= (Test)suiteMethod.invoke(null, (Object[]) new Class[0]); // static method
+			if (test == null)
+				return test;
+		}
+		catch (InvocationTargetException e) {
+			runFailed("Failed to invoke suite():" + e.getTargetException().toString());
+			return null;
+		}
+		catch (IllegalAccessException e) {
+			runFailed("Failed to invoke suite():" + e.toString());
+			return null;
+		}
+
+		clearStatus();
+		return test;
+	}
+
+	/**
+	 * Returns the formatted string of the elapsed time.
+	 */
+	public String elapsedTimeAsString(long runTime) {
+		return NumberFormat.getInstance().format((double)runTime/1000);
+	}
+
+	/**
+	 * Processes the command line arguments and
+	 * returns the name of the suite class to run or null
+	 */
+	protected String processArguments(String[] args) {
+		String suiteName= null;
+		for (int i= 0; i < args.length; i++) {
+			if (args[i].equals("-noloading")) {
+				setLoading(false);
+			} else if (args[i].equals("-nofilterstack")) {
+				fgFilterStack= false;
+			} else if (args[i].equals("-c")) {
+				if (args.length > i+1)
+					suiteName= extractClassName(args[i+1]);
+				else
+					System.out.println("Missing Test class name");
+				i++;
+			} else {
+				suiteName= args[i];
+			}
+		}
+		return suiteName;
+	}
+
+	/**
+	 * Sets the loading behaviour of the test runner
+	 */
+	public void setLoading(boolean enable) {
+		fLoading= enable;
+	}
+	/**
+	 * Extract the class name from a String in VA/Java style
+	 */
+	public String extractClassName(String className) {
+		if(className.startsWith("Default package for"))
+			return className.substring(className.lastIndexOf(".")+1);
+		return className;
+	}
+
+	/**
+	 * Truncates a String to the maximum length.
+	 */
+	public static String truncate(String s) {
+		if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength)
+			s= s.substring(0, fgMaxMessageLength)+"...";
+		return s;
+	}
+
+	/**
+	 * Override to define how to handle a failed loading of
+	 * a test suite.
+	 */
+	protected abstract void runFailed(String message);
+
+	/**
+	 * Returns the loaded Class for a suite name.
+	 */
+	protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
+		return getLoader().load(suiteClassName);
+	}
+
+	/**
+	 * Clears the status message.
+	 */
+	protected void clearStatus() { // Belongs in the GUI TestRunner class
+	}
+
+	/**
+	 * Returns the loader to be used.
+	 */
+	public TestSuiteLoader getLoader() {
+		if (useReloadingTestSuiteLoader())
+			return new ReloadingTestSuiteLoader();
+		return new StandardTestSuiteLoader();
+	}
+
+	protected boolean useReloadingTestSuiteLoader() {
+		return getPreference("loading").equals("true") && !inVAJava() && fLoading;
+	}
+
+	private static File getPreferencesFile() {
+	 	String home= System.getProperty("user.home");
+ 		return new File(home, "junit.properties");
+ 	}
+
+ 	private static void readPreferences() {
+ 		InputStream is= null;
+ 		try {
+ 			is= new FileInputStream(getPreferencesFile());
+ 			setPreferences(new Properties(getPreferences()));
+			getPreferences().load(is);
+		} catch (IOException e) {
+			try {
+				if (is != null)
+					is.close();
+			} catch (IOException e1) {
+			}
+		}
+ 	}
+
+ 	public static String getPreference(String key) {
+ 		return getPreferences().getProperty(key);
+ 	}
+
+ 	public static int getPreference(String key, int dflt) {
+ 		String value= getPreference(key);
+ 		int intValue= dflt;
+ 		if (value == null)
+ 			return intValue;
+ 		try {
+ 			intValue= Integer.parseInt(value);
+ 	 	} catch (NumberFormatException ne) {
+ 		}
+ 		return intValue;
+ 	}
+
+ 	public static boolean inVAJava() {
+		try {
+			Class.forName("com.ibm.uvm.tools.DebugSupport");
+		}
+		catch (Exception e) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Returns a filtered stack trace
+	 */
+	public static String getFilteredTrace(Throwable t) {
+		StringWriter stringWriter= new StringWriter();
+		PrintWriter writer= new PrintWriter(stringWriter);
+		t.printStackTrace(writer);
+		StringBuffer buffer= stringWriter.getBuffer();
+		String trace= buffer.toString();
+		return BaseTestRunner.getFilteredTrace(trace);
+	}
+
+	/**
+	 * Filters stack frames from internal JUnit classes
+	 */
+	public static String getFilteredTrace(String stack) {
+		if (showStackRaw())
+			return stack;
+
+		StringWriter sw= new StringWriter();
+		PrintWriter pw= new PrintWriter(sw);
+		StringReader sr= new StringReader(stack);
+		BufferedReader br= new BufferedReader(sr);
+
+		String line;
+		try {
+			while ((line= br.readLine()) != null) {
+				if (!filterLine(line))
+					pw.println(line);
+			}
+		} catch (Exception IOException) {
+			return stack; // return the stack unfiltered
+		}
+		return sw.toString();
+	}
+
+	protected static boolean showStackRaw() {
+		return !getPreference("filterstack").equals("true") || fgFilterStack == false;
+	}
+
+	static boolean filterLine(String line) {
+		String[] patterns= new String[] {
+			"junit.framework.TestCase",
+			"junit.framework.TestResult",
+			"junit.framework.TestSuite",
+			"junit.framework.Assert.", // don't filter AssertionFailure
+			"junit.swingui.TestRunner",
+			"junit.awtui.TestRunner",
+			"junit.textui.TestRunner",
+			"java.lang.reflect.Method.invoke("
+		};
+		for (int i= 0; i < patterns.length; i++) {
+			if (line.indexOf(patterns[i]) > 0)
+				return true;
+		}
+		return false;
+	}
+
+ 	static {
+ 		fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength);
+ 	}
+
+}
diff --git a/dx/src/junit/runner/ClassPathTestCollector.java b/dx/src/junit/runner/ClassPathTestCollector.java
new file mode 100644
index 0000000..24bf1e9
--- /dev/null
+++ b/dx/src/junit/runner/ClassPathTestCollector.java
@@ -0,0 +1,80 @@
+package junit.runner;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * An implementation of a TestCollector that consults the
+ * class path. It considers all classes on the class path
+ * excluding classes in JARs. It leaves it up to subclasses
+ * to decide whether a class is a runnable Test.
+ *
+ * @see TestCollector
+ */
+public abstract class ClassPathTestCollector implements TestCollector {
+	
+	static final int SUFFIX_LENGTH= ".class".length();
+	
+	public ClassPathTestCollector() {
+	}
+	
+	public Enumeration collectTests() {
+		String classPath= System.getProperty("java.class.path");
+		Hashtable result = collectFilesInPath(classPath);
+		return result.elements();
+	}
+
+	public Hashtable collectFilesInPath(String classPath) {
+		Hashtable result= collectFilesInRoots(splitClassPath(classPath));
+		return result;
+	}
+	
+	Hashtable collectFilesInRoots(Vector roots) {
+                Hashtable<String,String> result= new Hashtable<String,String>(100);
+		Enumeration e= roots.elements();
+		while (e.hasMoreElements()) 
+			gatherFiles(new File((String)e.nextElement()), "", result);
+		return result;
+	}
+
+    void gatherFiles(File classRoot, String classFileName, Hashtable<String,String> result) {
+		File thisRoot= new File(classRoot, classFileName);
+		if (thisRoot.isFile()) {
+			if (isTestClass(classFileName)) {
+				String className= classNameFromFile(classFileName);
+				result.put(className, className);
+			}
+			return;
+		}		
+		String[] contents= thisRoot.list();
+		if (contents != null) { 
+			for (int i= 0; i < contents.length; i++) 
+				gatherFiles(classRoot, classFileName+File.separatorChar+contents[i], result);		
+		}
+	}
+	
+	Vector splitClassPath(String classPath) {
+		Vector<String> result= new Vector<String>();
+		String separator= System.getProperty("path.separator");
+		StringTokenizer tokenizer= new StringTokenizer(classPath, separator);
+		while (tokenizer.hasMoreTokens()) 
+			result.addElement(tokenizer.nextToken());
+		return result;
+	}
+	
+	protected boolean isTestClass(String classFileName) {
+		return 
+			classFileName.endsWith(".class") && 
+			classFileName.indexOf('$') < 0 &&
+			classFileName.indexOf("Test") > 0;
+	}
+	
+	protected String classNameFromFile(String classFileName) {
+		// convert /a/b.class to a.b
+		String s= classFileName.substring(0, classFileName.length()-SUFFIX_LENGTH);
+		String s2= s.replace(File.separatorChar, '.');
+		if (s2.startsWith("."))
+			return s2.substring(1);
+		return s2;
+	}	
+}
diff --git a/dx/src/junit/runner/FailureDetailView.java b/dx/src/junit/runner/FailureDetailView.java
new file mode 100644
index 0000000..762326b
--- /dev/null
+++ b/dx/src/junit/runner/FailureDetailView.java
@@ -0,0 +1,23 @@
+package junit.runner;
+
+import java.awt.Component; 
+
+import junit.framework.*;
+
+/**
+ * A view to show a details about a failure
+ */
+public interface FailureDetailView {
+	/**
+	 * Returns the component used to present the TraceView
+	 */
+	public Component getComponent();
+	/**
+	 * Shows details of a TestFailure
+	 */
+	public void showFailure(TestFailure failure);
+	/**
+	 * Clears the view
+	 */
+	public void clear();
+}
diff --git a/dx/src/junit/runner/LoadingTestCollector.java b/dx/src/junit/runner/LoadingTestCollector.java
new file mode 100644
index 0000000..b138359
--- /dev/null
+++ b/dx/src/junit/runner/LoadingTestCollector.java
@@ -0,0 +1,69 @@
+package junit.runner;
+
+import java.lang.reflect.*;
+import junit.runner.*;
+import junit.framework.*;
+
+/**
+ * An implementation of a TestCollector that loads
+ * all classes on the class path and tests whether
+ * it is assignable from Test or provides a static suite method.
+ * @see TestCollector
+ */
+public class LoadingTestCollector extends ClassPathTestCollector {
+	
+	TestCaseClassLoader fLoader;
+	
+	public LoadingTestCollector() {
+		fLoader= new TestCaseClassLoader();
+	}
+	
+	protected boolean isTestClass(String classFileName) {	
+		try {
+			if (classFileName.endsWith(".class")) {
+				Class testClass= classFromFile(classFileName);
+				return (testClass != null) && isTestClass(testClass);
+			}
+		} 
+		catch (ClassNotFoundException expected) {
+		}
+		catch (NoClassDefFoundError notFatal) {
+		} 
+		return false;
+	}
+	
+	Class classFromFile(String classFileName) throws ClassNotFoundException {
+		String className= classNameFromFile(classFileName);
+		if (!fLoader.isExcluded(className))
+			return fLoader.loadClass(className, false);
+		return null;
+	}
+	
+	boolean isTestClass(Class testClass) {
+		if (hasSuiteMethod(testClass))
+			return true;
+		if (Test.class.isAssignableFrom(testClass) &&
+			Modifier.isPublic(testClass.getModifiers()) &&
+			hasPublicConstructor(testClass)) 
+			return true;
+		return false;
+	}
+	
+	boolean hasSuiteMethod(Class testClass) {
+		try {
+			testClass.getMethod(BaseTestRunner.SUITE_METHODNAME, new Class[0]);
+	 	} catch(Exception e) {
+	 		return false;
+		}
+		return true;
+	}
+	
+	boolean hasPublicConstructor(Class testClass) {
+		try {
+			TestSuite.getTestConstructor(testClass);
+		} catch(NoSuchMethodException e) {
+			return false;
+		}
+		return true;
+	}
+}
diff --git a/dx/src/junit/runner/ReloadingTestSuiteLoader.java b/dx/src/junit/runner/ReloadingTestSuiteLoader.java
new file mode 100644
index 0000000..f7ef919
--- /dev/null
+++ b/dx/src/junit/runner/ReloadingTestSuiteLoader.java
@@ -0,0 +1,19 @@
+package junit.runner;
+
+/**
+ * A TestSuite loader that can reload classes.
+ */
+public class ReloadingTestSuiteLoader implements TestSuiteLoader {
+	
+	public Class load(String suiteClassName) throws ClassNotFoundException {
+		return createLoader().loadClass(suiteClassName, true);
+	}
+	
+	public Class reload(Class aClass) throws ClassNotFoundException {
+		return createLoader().loadClass(aClass.getName(), true);
+	}
+	
+	protected TestCaseClassLoader createLoader() {
+		return new TestCaseClassLoader();
+	}
+}
diff --git a/dx/src/junit/runner/SimpleTestCollector.java b/dx/src/junit/runner/SimpleTestCollector.java
new file mode 100644
index 0000000..9d1956a
--- /dev/null
+++ b/dx/src/junit/runner/SimpleTestCollector.java
@@ -0,0 +1,20 @@
+package junit.runner;
+
+/**
+ * An implementation of a TestCollector that considers
+ * a class to be a test class when it contains the
+ * pattern "Test" in its name
+ * @see TestCollector
+ */
+public class SimpleTestCollector extends ClassPathTestCollector {
+	
+	public SimpleTestCollector() {
+	}
+	
+	protected boolean isTestClass(String classFileName) {
+		return 
+			classFileName.endsWith(".class") && 
+			classFileName.indexOf('$') < 0 &&
+			classFileName.indexOf("Test") > 0;
+	}
+}
diff --git a/dx/src/junit/runner/Sorter.java b/dx/src/junit/runner/Sorter.java
new file mode 100644
index 0000000..e614ab4
--- /dev/null
+++ b/dx/src/junit/runner/Sorter.java
@@ -0,0 +1,38 @@
+package junit.runner;
+
+import java.util.*;
+
+import junit.runner.*;
+
+/**
+ * A custom quick sort with support to customize the swap behaviour.
+ * NOTICE: We can't use the the sorting support from the JDK 1.2 collection
+ * classes because of the JDK 1.1.7 compatibility.
+ */
+public class Sorter {
+	public static interface Swapper {
+		public void swap(Vector values, int left, int right);
+	}
+		
+	public static void sortStrings(Vector values , int left, int right, Swapper swapper) { 
+		int oleft= left;
+		int oright= right;
+		String mid= (String)values.elementAt((left + right) / 2); 
+		do { 
+			while (((String)(values.elementAt(left))).compareTo(mid) < 0)  
+				left++; 
+			while (mid.compareTo((String)(values.elementAt(right))) < 0)  
+				right--; 
+			if (left <= right) {
+				swapper.swap(values, left, right); 
+				left++; 
+				right--; 
+			} 
+		} while (left <= right);
+		
+		if (oleft < right) 
+			sortStrings(values, oleft, right, swapper); 
+		if (left < oright) 
+			 sortStrings(values, left, oright, swapper); 
+	}
+}
diff --git a/dx/src/junit/runner/StandardTestSuiteLoader.java b/dx/src/junit/runner/StandardTestSuiteLoader.java
new file mode 100644
index 0000000..e2bd765
--- /dev/null
+++ b/dx/src/junit/runner/StandardTestSuiteLoader.java
@@ -0,0 +1,19 @@
+package junit.runner;
+
+/**
+ * The standard test suite loader. It can only load the same class once.
+ */
+public class StandardTestSuiteLoader implements TestSuiteLoader {
+	/**
+	 * Uses the system class loader to load the test class
+	 */
+	public Class load(String suiteClassName) throws ClassNotFoundException {
+		return Class.forName(suiteClassName);
+	}
+	/**
+	 * Uses the system class loader to load the test class
+	 */
+	public Class reload(Class aClass) throws ClassNotFoundException {
+		return aClass;
+	}
+}
diff --git a/dx/src/junit/runner/TestCaseClassLoader.java b/dx/src/junit/runner/TestCaseClassLoader.java
new file mode 100644
index 0000000..543641a
--- /dev/null
+++ b/dx/src/junit/runner/TestCaseClassLoader.java
@@ -0,0 +1,226 @@
+package junit.runner;
+
+import java.util.*;
+import java.io.*;
+import java.net.URL;
+import java.util.zip.*;
+
+/**
+ * A custom class loader which enables the reloading
+ * of classes for each test run. The class loader
+ * can be configured with a list of package paths that
+ * should be excluded from loading. The loading
+ * of these packages is delegated to the system class
+ * loader. They will be shared across test runs.
+ * <p>
+ * The list of excluded package paths is specified in
+ * a properties file "excluded.properties" that is located in 
+ * the same place as the TestCaseClassLoader class.
+ * <p>
+ * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes
+ * from jar files.
+ */
+
+
+public class TestCaseClassLoader extends ClassLoader {
+	/** scanned class path */
+	private Vector<String> fPathItems;
+	/** default excluded paths */
+	private String[] defaultExclusions= {
+		"junit.framework.", 
+		"junit.extensions.", 
+		"junit.runner."
+	};
+	/** name of excluded properties file */
+	static final String EXCLUDED_FILE= "excluded.properties";
+	/** excluded paths */
+	private Vector<String> fExcluded;
+	 
+	/**
+	 * Constructs a TestCaseLoader. It scans the class path
+	 * and the excluded package paths
+	 */
+	public TestCaseClassLoader() {
+		this(System.getProperty("java.class.path"));
+	}
+	
+	/**
+	 * Constructs a TestCaseLoader. It scans the class path
+	 * and the excluded package paths
+	 */
+	public TestCaseClassLoader(String classPath) {
+		scanPath(classPath);
+		readExcludedPackages();
+	}
+
+	private void scanPath(String classPath) {
+		String separator= System.getProperty("path.separator");
+		fPathItems= new Vector<String>(10);
+		StringTokenizer st= new StringTokenizer(classPath, separator);
+		while (st.hasMoreTokens()) {
+			fPathItems.addElement(st.nextToken());
+		}
+	}
+	
+	public URL getResource(String name) {
+		return ClassLoader.getSystemResource(name);
+	}
+	
+	public InputStream getResourceAsStream(String name) {
+		return ClassLoader.getSystemResourceAsStream(name);
+	} 
+	
+	public boolean isExcluded(String name) {
+		for (int i= 0; i < fExcluded.size(); i++) {
+			if (name.startsWith((String) fExcluded.elementAt(i))) {
+				return true;
+			}
+		}
+		return false;	
+	}
+	
+	public synchronized Class loadClass(String name, boolean resolve)
+		throws ClassNotFoundException {
+			
+		Class c= findLoadedClass(name);
+		if (c != null)
+			return c;
+		//
+		// Delegate the loading of excluded classes to the
+		// standard class loader.
+		//
+		if (isExcluded(name)) {
+			try {
+				c= findSystemClass(name);
+				return c;
+			} catch (ClassNotFoundException e) {
+				// keep searching
+			}
+		}
+		if (c == null) {
+			byte[] data= lookupClassData(name);
+			if (data == null)
+				throw new ClassNotFoundException();
+			c= defineClass(name, data, 0, data.length);
+		}
+		if (resolve) 
+			resolveClass(c);
+		return c;
+	}
+	
+	private byte[] lookupClassData(String className) throws ClassNotFoundException {
+		byte[] data= null;
+		for (int i= 0; i < fPathItems.size(); i++) {
+			String path= (String) fPathItems.elementAt(i);
+			String fileName= className.replace('.', '/')+".class";
+			if (isJar(path)) {
+				data= loadJarData(path, fileName);
+			} else {
+				data= loadFileData(path, fileName);
+			}
+			if (data != null)
+				return data;
+		}
+		throw new ClassNotFoundException(className);
+	}
+		
+	boolean isJar(String pathEntry) {
+		return pathEntry.endsWith(".jar") ||
+                       pathEntry.endsWith(".zip") ||
+                       pathEntry.endsWith(".apk");
+	}
+
+	private byte[] loadFileData(String path, String fileName) {
+		File file= new File(path, fileName);
+		if (file.exists()) { 
+			return getClassData(file);
+		}
+		return null;
+	}
+	
+	private byte[] getClassData(File f) {
+		try {
+			FileInputStream stream= new FileInputStream(f);
+			ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
+			byte[] b= new byte[1000];
+			int n;
+			while ((n= stream.read(b)) != -1) 
+				out.write(b, 0, n);
+			stream.close();
+			out.close();
+			return out.toByteArray();
+
+		} catch (IOException e) {
+		}
+		return null;
+	}
+
+	private byte[] loadJarData(String path, String fileName) {
+		ZipFile zipFile= null;
+		InputStream stream= null;
+		File archive= new File(path);
+		if (!archive.exists())
+			return null;
+		try {
+			zipFile= new ZipFile(archive);
+		} catch(IOException io) {
+			return null;
+		}
+		ZipEntry entry= zipFile.getEntry(fileName);
+		if (entry == null)
+			return null;
+		int size= (int) entry.getSize();
+		try {
+			stream= zipFile.getInputStream(entry);
+			byte[] data= new byte[size];
+			int pos= 0;
+			while (pos < size) {
+				int n= stream.read(data, pos, data.length - pos);
+				pos += n;
+			}
+			zipFile.close();
+			return data;
+		} catch (IOException e) {
+		} finally {
+			try {
+				if (stream != null)
+					stream.close();
+			} catch (IOException e) {
+			}
+		}
+		return null;
+	}
+	
+	private void readExcludedPackages() {		
+		fExcluded= new Vector<String>(10);
+		for (int i= 0; i < defaultExclusions.length; i++)
+			fExcluded.addElement(defaultExclusions[i]);
+			
+		InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
+		if (is == null) 
+			return;
+		Properties p= new Properties();
+		try {
+			p.load(is);
+		}
+		catch (IOException e) {
+			return;
+		} finally {
+			try {
+				is.close();
+			} catch (IOException e) {
+			}
+		}
+		for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
+			String key= (String)e.nextElement();
+			if (key.startsWith("excluded.")) {
+				String path= p.getProperty(key);
+				path= path.trim();
+				if (path.endsWith("*"))
+					path= path.substring(0, path.length()-1);
+				if (path.length() > 0) 
+					fExcluded.addElement(path);				
+			}
+		}
+	}
+}
diff --git a/dx/src/junit/runner/TestCollector.java b/dx/src/junit/runner/TestCollector.java
new file mode 100644
index 0000000..73efb4e
--- /dev/null
+++ b/dx/src/junit/runner/TestCollector.java
@@ -0,0 +1,16 @@
+package junit.runner;
+
+import java.util.*;
+
+
+/**
+ * Collects Test class names to be presented
+ * by the TestSelector. 
+ * @see TestSelector
+ */
+public interface TestCollector {
+	/**
+	 * Returns an enumeration of Strings with qualified class names
+	 */
+	public Enumeration collectTests();
+}
diff --git a/dx/src/junit/runner/TestRunListener.java b/dx/src/junit/runner/TestRunListener.java
new file mode 100644
index 0000000..b11ef07
--- /dev/null
+++ b/dx/src/junit/runner/TestRunListener.java
@@ -0,0 +1,19 @@
+package junit.runner;
+/**
+ * A listener interface for observing the
+ * execution of a test run. Unlike TestListener,
+ * this interface using only primitive objects,
+ * making it suitable for remote test execution.
+ */
+ public interface TestRunListener {
+     /* test status constants*/
+     public static final int STATUS_ERROR= 1;
+     public static final int STATUS_FAILURE= 2;
+
+     public void testRunStarted(String testSuiteName, int testCount);
+     public void testRunEnded(long elapsedTime);
+     public void testRunStopped(long elapsedTime);
+     public void testStarted(String testName);
+     public void testEnded(String testName);
+     public void testFailed(int status, String testName, String trace);
+}
diff --git a/dx/src/junit/runner/TestSuiteLoader.java b/dx/src/junit/runner/TestSuiteLoader.java
new file mode 100644
index 0000000..39a4cf7
--- /dev/null
+++ b/dx/src/junit/runner/TestSuiteLoader.java
@@ -0,0 +1,9 @@
+package junit.runner;
+
+/**
+ * An interface to define how a test suite should be loaded.
+ */
+public interface TestSuiteLoader {
+	abstract public Class load(String suiteClassName) throws ClassNotFoundException;
+	abstract public Class reload(Class aClass) throws ClassNotFoundException;
+}
diff --git a/dx/src/junit/runner/Version.java b/dx/src/junit/runner/Version.java
new file mode 100644
index 0000000..b4541ab
--- /dev/null
+++ b/dx/src/junit/runner/Version.java
@@ -0,0 +1,14 @@
+package junit.runner;
+
+/**
+ * This class defines the current version of JUnit
+ */
+public class Version {
+	private Version() {
+		// don't instantiate
+	}
+
+	public static String id() {
+		return "3.8.1";
+	}
+}
diff --git a/dx/src/junit/runner/excluded.properties b/dx/src/junit/runner/excluded.properties
new file mode 100644
index 0000000..3284628
--- /dev/null
+++ b/dx/src/junit/runner/excluded.properties
@@ -0,0 +1,12 @@
+#
+# The list of excluded package paths for the TestCaseClassLoader
+#
+excluded.0=sun.*
+excluded.1=com.sun.*
+excluded.2=org.omg.*
+excluded.3=javax.*
+excluded.4=sunw.*
+excluded.5=java.*
+excluded.6=org.w3c.dom.*
+excluded.7=org.xml.sax.*
+excluded.8=net.jini.*
diff --git a/dx/src/junit/runner/logo.gif b/dx/src/junit/runner/logo.gif
new file mode 100644
index 0000000..d0e1547
--- /dev/null
+++ b/dx/src/junit/runner/logo.gif
Binary files differ
diff --git a/dx/src/junit/runner/smalllogo.gif b/dx/src/junit/runner/smalllogo.gif
new file mode 100644
index 0000000..7b25eaf
--- /dev/null
+++ b/dx/src/junit/runner/smalllogo.gif
Binary files differ
diff --git a/dx/src/junit/textui/ResultPrinter.java b/dx/src/junit/textui/ResultPrinter.java
new file mode 100644
index 0000000..1ebb7a1
--- /dev/null
+++ b/dx/src/junit/textui/ResultPrinter.java
@@ -0,0 +1,139 @@
+
+package junit.textui;
+
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.Enumeration;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.runner.BaseTestRunner;
+
+public class ResultPrinter implements TestListener {
+	PrintStream fWriter;
+	int fColumn= 0;
+	
+	public ResultPrinter(PrintStream writer) {
+		fWriter= writer;
+	}
+	
+	/* API for use by textui.TestRunner
+	 */
+
+	synchronized void print(TestResult result, long runTime) {
+		printHeader(runTime);
+	    printErrors(result);
+	    printFailures(result);
+	    printFooter(result);
+	}
+
+	void printWaitPrompt() {
+		getWriter().println();
+		getWriter().println("<RETURN> to continue");
+	}
+	
+	/* Internal methods 
+	 */
+
+	protected void printHeader(long runTime) {
+		getWriter().println();
+		getWriter().println("Time: "+elapsedTimeAsString(runTime));
+	}
+	
+	protected void printErrors(TestResult result) {
+		printDefects(result.errors(), result.errorCount(), "error");
+	}
+	
+	protected void printFailures(TestResult result) {
+		printDefects(result.failures(), result.failureCount(), "failure");
+	}
+	
+	protected void printDefects(Enumeration booBoos, int count, String type) {
+		if (count == 0) return;
+		if (count == 1)
+			getWriter().println("There was " + count + " " + type + ":");
+		else
+			getWriter().println("There were " + count + " " + type + "s:");
+		for (int i= 1; booBoos.hasMoreElements(); i++) {
+			printDefect((TestFailure) booBoos.nextElement(), i);
+		}
+	}
+	
+	public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes
+		printDefectHeader(booBoo, count);
+		printDefectTrace(booBoo);
+	}
+
+	protected void printDefectHeader(TestFailure booBoo, int count) {
+		// I feel like making this a println, then adding a line giving the throwable a chance to print something
+		// before we get to the stack trace.
+		getWriter().print(count + ") " + booBoo.failedTest());
+	}
+
+	protected void printDefectTrace(TestFailure booBoo) {
+		getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace()));
+	}
+
+	protected void printFooter(TestResult result) {
+		if (result.wasSuccessful()) {
+			getWriter().println();
+			getWriter().print("OK");
+			getWriter().println (" (" + result.runCount() + " test" + (result.runCount() == 1 ? "": "s") + ")");
+
+		} else {
+			getWriter().println();
+			getWriter().println("FAILURES!!!");
+			getWriter().println("Tests run: "+result.runCount()+ 
+				         ",  Failures: "+result.failureCount()+
+				         ",  Errors: "+result.errorCount());
+		}
+	    getWriter().println();
+	}
+
+
+	/**
+	 * Returns the formatted string of the elapsed time.
+	 * Duplicated from BaseTestRunner. Fix it.
+	 */
+	protected String elapsedTimeAsString(long runTime) {
+		return NumberFormat.getInstance().format((double)runTime/1000);
+	}
+
+	public PrintStream getWriter() {
+		return fWriter;
+	}
+	/**
+	 * @see junit.framework.TestListener#addError(Test, Throwable)
+	 */
+	public void addError(Test test, Throwable t) {
+		getWriter().print("E");
+	}
+
+	/**
+	 * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+	 */
+	public void addFailure(Test test, AssertionFailedError t) {
+		getWriter().print("F");
+	}
+
+	/**
+	 * @see junit.framework.TestListener#endTest(Test)
+	 */
+	public void endTest(Test test) {
+	}
+
+	/**
+	 * @see junit.framework.TestListener#startTest(Test)
+	 */
+	public void startTest(Test test) {
+		getWriter().print(".");
+		if (fColumn++ >= 40) {
+			getWriter().println();
+			fColumn= 0;
+		}
+	}
+
+}
diff --git a/dx/src/junit/textui/TestRunner.java b/dx/src/junit/textui/TestRunner.java
new file mode 100644
index 0000000..8bdc325
--- /dev/null
+++ b/dx/src/junit/textui/TestRunner.java
@@ -0,0 +1,189 @@
+package junit.textui;
+
+
+import java.io.PrintStream;
+
+import junit.framework.*;
+import junit.runner.*;
+
+/**
+ * A command line based tool to run tests.
+ * <pre>
+ * java junit.textui.TestRunner [-wait] TestCaseClass
+ * </pre>
+ * TestRunner expects the name of a TestCase class as argument.
+ * If this class defines a static <code>suite</code> method it 
+ * will be invoked and the returned test is run. Otherwise all 
+ * the methods starting with "test" having no arguments are run.
+ * <p>
+ * When the wait command line argument is given TestRunner
+ * waits until the users types RETURN.
+ * <p>
+ * TestRunner prints a trace as the tests are executed followed by a
+ * summary at the end. 
+ */
+public class TestRunner extends BaseTestRunner {
+	private ResultPrinter fPrinter;
+	
+	public static final int SUCCESS_EXIT= 0;
+	public static final int FAILURE_EXIT= 1;
+	public static final int EXCEPTION_EXIT= 2;
+
+	/**
+	 * Constructs a TestRunner.
+	 */
+	public TestRunner() {
+		this(System.out);
+	}
+
+	/**
+	 * Constructs a TestRunner using the given stream for all the output
+	 */
+	public TestRunner(PrintStream writer) {
+		this(new ResultPrinter(writer));
+	}
+	
+	/**
+	 * Constructs a TestRunner using the given ResultPrinter all the output
+	 */
+	public TestRunner(ResultPrinter printer) {
+		fPrinter= printer;
+	}
+	
+	/**
+	 * Runs a suite extracted from a TestCase subclass.
+	 */
+	static public void run(Class testClass) {
+		run(new TestSuite(testClass));
+	}
+
+	/**
+	 * Runs a single test and collects its results.
+	 * This method can be used to start a test run
+	 * from your program.
+	 * <pre>
+	 * public static void main (String[] args) {
+	 *     test.textui.TestRunner.run(suite());
+	 * }
+	 * </pre>
+	 */
+	static public TestResult run(Test test) {
+		TestRunner runner= new TestRunner();
+		return runner.doRun(test);
+	}
+
+	/**
+	 * Runs a single test and waits until the user
+	 * types RETURN.
+	 */
+	static public void runAndWait(Test suite) {
+		TestRunner aTestRunner= new TestRunner();
+		aTestRunner.doRun(suite, true);
+	}
+
+	/**
+	 * Always use the StandardTestSuiteLoader. Overridden from
+	 * BaseTestRunner.
+	 */
+	public TestSuiteLoader getLoader() {
+		return new StandardTestSuiteLoader();
+	}
+
+	public void testFailed(int status, Test test, Throwable t) {
+	}
+	
+	public void testStarted(String testName) {
+	}
+	
+	public void testEnded(String testName) {
+	}
+
+	/**
+	 * Creates the TestResult to be used for the test run.
+	 */
+	protected TestResult createTestResult() {
+		return new TestResult();
+	}
+	
+	public TestResult doRun(Test test) {
+		return doRun(test, false);
+	}
+	
+	public TestResult doRun(Test suite, boolean wait) {
+		TestResult result= createTestResult();
+		result.addListener(fPrinter);
+		long startTime= System.currentTimeMillis();
+		suite.run(result);
+		long endTime= System.currentTimeMillis();
+		long runTime= endTime-startTime;
+		fPrinter.print(result, runTime);
+
+		pause(wait);
+		return result;
+	}
+
+	protected void pause(boolean wait) {
+		if (!wait) return;
+		fPrinter.printWaitPrompt();
+		try {
+			System.in.read();
+		}
+		catch(Exception e) {
+		}
+	}
+	
+	public static void main(String args[]) {
+		TestRunner aTestRunner= new TestRunner();
+		try {
+			TestResult r= aTestRunner.start(args);
+			if (!r.wasSuccessful()) 
+				System.exit(FAILURE_EXIT);
+			System.exit(SUCCESS_EXIT);
+		} catch(Exception e) {
+			System.err.println(e.getMessage());
+			System.exit(EXCEPTION_EXIT);
+		}
+	}
+
+	/**
+	 * Starts a test run. Analyzes the command line arguments
+	 * and runs the given test suite.
+	 */
+	protected TestResult start(String args[]) throws Exception {
+		String testCase= "";
+		boolean wait= false;
+		
+		for (int i= 0; i < args.length; i++) {
+			if (args[i].equals("-wait"))
+				wait= true;
+			else if (args[i].equals("-c")) 
+				testCase= extractClassName(args[++i]);
+			else if (args[i].equals("-v"))
+				System.err.println("JUnit "+Version.id()+" by Kent Beck and Erich Gamma");
+			else
+				testCase= args[i];
+		}
+		
+		if (testCase.equals("")) 
+			throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+
+		try {
+			Test suite= getTest(testCase);
+			return doRun(suite, wait);
+		}
+		catch(Exception e) {
+			throw new Exception("Could not create and run test suite: "+e);
+		}
+	}
+		
+	protected void runFailed(String message) {
+		System.err.println(message);
+		System.exit(FAILURE_EXIT);
+	}
+	
+	public void setPrinter(ResultPrinter printer) {
+		fPrinter= printer;
+	}
+		
+	
+}
diff --git a/dx/tests/001-nop/expected.txt b/dx/tests/001-nop/expected.txt
new file mode 100644
index 0000000..d4a85ce
--- /dev/null
+++ b/dx/tests/001-nop/expected.txt
@@ -0,0 +1 @@
+I am a jelly donut.
diff --git a/dx/tests/001-nop/info.txt b/dx/tests/001-nop/info.txt
new file mode 100644
index 0000000..9942f10
--- /dev/null
+++ b/dx/tests/001-nop/info.txt
@@ -0,0 +1,2 @@
+This is a sample no-op test, which does at least serve to verify that the
+test harness is working.
diff --git a/dx/tests/001-nop/run b/dx/tests/001-nop/run
new file mode 100644
index 0000000..51637c1
--- /dev/null
+++ b/dx/tests/001-nop/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+echo 'I am a jelly donut.'
diff --git a/dx/tests/002-minimal-valid/expected.txt b/dx/tests/002-minimal-valid/expected.txt
new file mode 100644
index 0000000..3877fb5
--- /dev/null
+++ b/dx/tests/002-minimal-valid/expected.txt
@@ -0,0 +1,46 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000a
+
+constant_pool:
+  0001: method{java.lang.Object.<init>:()V}
+  0002: type{Small}
+  0003: type{java.lang.Object}
+  0004: utf8{"<init>"}
+  0005: utf8{"()V"}
+  0006: utf8{"Code"}
+  0007: nat{<init>:()V}
+  0008: utf8{"Small"}
+  0009: utf8{"java/lang/Object"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: <init>
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 00000011
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 00000005
+    0000: aload_0 // 00
+    0001: invokespecial method{java.lang.Object.<init>:()V}
+    0004: return
+    exception_table_length: 0000
+    attributes_count: 0000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/002-minimal-valid/info.txt b/dx/tests/002-minimal-valid/info.txt
new file mode 100644
index 0000000..f296af8
--- /dev/null
+++ b/dx/tests/002-minimal-valid/info.txt
@@ -0,0 +1 @@
+This is just a dump of a simple but valid class.
diff --git a/dx/tests/002-minimal-valid/run b/dx/tests/002-minimal-valid/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/002-minimal-valid/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/002-minimal-valid/small-class.txt b/dx/tests/002-minimal-valid/small-class.txt
new file mode 100644
index 0000000..25a323f
--- /dev/null
+++ b/dx/tests/002-minimal-valid/small-class.txt
@@ -0,0 +1,49 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000a       # constant_pool_count
+
+#
+# constant_pool
+#
+0a 0003 0007               # 0001: method[0003, 0007]
+07 0008                    # 0002: class[0008]
+07 0009                    # 0003: class[0009]
+01 0006 "<init>"           # 0004: utf8["<init>"]
+01 0003 "()V"              # 0005: utf8["()V"]
+01 0004 "Code"             # 0006: utf8["Code"]
+0c 0004 0005               # 0007: nat[0004, 0005]
+01 0005 "Small"            # 0008: utf8["Small"]
+01 0010 "java/lang/Object" # 0009: utf8["java/lang/Object"]
+
+0021  # access_flags
+0002  # this_class
+0003  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+#
+# methods[0]
+#
+0001  # access_flags
+0004  # name
+0005  # descriptor
+0001  # attributes_count
+# attributes[0]
+0006       # name
+0000 0011  # length
+0001       # max_stack
+0001       # max_locals
+0000 0005  # code_length
+2a         # 0000: aload_0
+b7 0001    # 0001: invokespecial method[java/lang/Object.<init>:()V]
+b1         # 0004: return
+0000       # exception_table_length
+0000       # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-bad-magic.txt b/dx/tests/003-magic-version-access/class-bad-magic.txt
new file mode 100644
index 0000000..f3c64bd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-bad-magic.txt
@@ -0,0 +1,25 @@
+#
+# classfile with a bad magic value
+#
+
+dead babe  # magic
+0000       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-44.0.txt b/dx/tests/003-magic-version-access/class-version-44.0.txt
new file mode 100644
index 0000000..2d9055c
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-44.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with an out-of-range version.
+#
+
+cafe babe  # magic
+0000       # minor_version
+002c       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-44.65535.txt b/dx/tests/003-magic-version-access/class-version-44.65535.txt
new file mode 100644
index 0000000..0f2b582
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-44.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with an out-of-range version.
+#
+
+cafe babe  # magic
+ffff       # minor_version
+002c       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-45.0.txt b/dx/tests/003-magic-version-access/class-version-45.0.txt
new file mode 100644
index 0000000..335079d
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-45.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the lowest valid version, 45.0 (0x2d.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+002d       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-45.65535.txt b/dx/tests/003-magic-version-access/class-version-45.65535.txt
new file mode 100644
index 0000000..2b31404
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-45.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 45.65535 (0x2d.0xffff)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+002d       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-48.0.txt b/dx/tests/003-magic-version-access/class-version-48.0.txt
new file mode 100644
index 0000000..551b221
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-48.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 48.0 (0x30.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0030       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-48.65535.txt b/dx/tests/003-magic-version-access/class-version-48.65535.txt
new file mode 100644
index 0000000..ac95b52
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-48.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 48.65535 (0x30.0xffff)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+0030       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.0.txt b/dx/tests/003-magic-version-access/class-version-49.0.txt
new file mode 100644
index 0000000..0b30fcd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the highest valid version, 49.0 (0x31.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.1.txt b/dx/tests/003-magic-version-access/class-version-49.1.txt
new file mode 100644
index 0000000..9eb477c
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.1.txt
@@ -0,0 +1,26 @@
+#
+# classfile with a minor version 1 higher than the highest valid
+# version.  49.1 (0x31.0x01)
+#
+
+cafe babe  # magic
+0001       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.65535.txt b/dx/tests/003-magic-version-access/class-version-49.65535.txt
new file mode 100644
index 0000000..668631b
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.65535.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with the same major version
+# as the highest valid version.  49.65535 (0x31.0xffff)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.0.txt b/dx/tests/003-magic-version-access/class-version-50.0.txt
new file mode 100644
index 0000000..fa67077
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.0.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  50.0 (0x32.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0032       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.1.txt b/dx/tests/003-magic-version-access/class-version-50.1.txt
new file mode 100644
index 0000000..9543be1
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.1.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  50.0 (0x32.0x00)
+#
+
+cafe babe  # magic
+0001       # minor_version
+0032       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.65535.txt b/dx/tests/003-magic-version-access/class-version-50.65535.txt
new file mode 100644
index 0000000..9db1958
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.65535.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  50.0 (0x32.0x00)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+0032       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-51.0.txt b/dx/tests/003-magic-version-access/class-version-51.0.txt
new file mode 100644
index 0000000..2ffb4cd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-51.0.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  50.0 (0x32.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0033       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/expected.txt b/dx/tests/003-magic-version-access/expected.txt
new file mode 100644
index 0000000..a632922
--- /dev/null
+++ b/dx/tests/003-magic-version-access/expected.txt
@@ -0,0 +1,243 @@
+reading class-bad-magic.txt...
+begin classfile
+magic: deadbabe
+minor_version: 0000
+major_version: 0031
+
+trouble parsing:
+bad class file magic (deadbabe) or version (0031.0000)
+...while parsing class-bad-magic.txt
+reading class-version-44.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002c
+
+trouble parsing:
+bad class file magic (cafebabe) or version (002c.0000)
+...while parsing class-version-44.0.txt
+reading class-version-44.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 002c
+
+trouble parsing:
+bad class file magic (cafebabe) or version (002c.ffff)
+...while parsing class-version-44.65535.txt
+reading class-version-45.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002d
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-45.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 002d
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-48.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0030
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-48.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0030
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0001
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-50.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0032
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-50.1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0001
+major_version: 0032
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0032.0001)
+...while parsing class-version-50.1.txt
+reading class-version-50.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0032
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0032.ffff)
+...while parsing class-version-50.65535.txt
+reading class-version-51.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0033
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0033.0000)
+...while parsing class-version-51.0.txt
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/003-magic-version-access/info.txt b/dx/tests/003-magic-version-access/info.txt
new file mode 100644
index 0000000..4d6a697
--- /dev/null
+++ b/dx/tests/003-magic-version-access/info.txt
@@ -0,0 +1,9 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bits of parsing tested here are:
+
+* magic number
+* major / minor version numbers
+* class access_flags
diff --git a/dx/tests/003-magic-version-access/run b/dx/tests/003-magic-version-access/run
new file mode 100644
index 0000000..24de48e
--- /dev/null
+++ b/dx/tests/003-magic-version-access/run
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# 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.
+
+# The tests that don't specify "--debug" are expected to
+# throw exceptions.  If --debug is on we'll get a stack
+# trace, which is unpredictable and doesn't work well with
+# expected.txt vs. out.txt comparisons.
+
+# Bad magic (throws an expection)
+dx         --dump --strict class-bad-magic.txt
+
+# Too small (throws an exception)
+dx         --dump --strict class-version-44.0.txt
+dx         --dump --strict class-version-44.65535.txt
+
+# Just right
+dx --debug --dump --width=100 class-version-45.0.txt
+dx --debug --dump --width=100 class-version-45.65535.txt
+dx --debug --dump --width=100 class-version-48.0.txt
+dx --debug --dump --width=100 class-version-48.65535.txt
+dx --debug --dump --width=100 class-version-49.0.txt
+dx --debug --dump --width=100 class-version-49.1.txt
+dx --debug --dump --width=100 class-version-49.65535.txt
+dx --debug --dump --width=100 class-version-50.0.txt
+
+# Too big (throws an exception)
+dx         --dump --strict class-version-50.1.txt
+dx         --dump --strict class-version-50.65535.txt
+dx         --dump --strict class-version-51.0.txt
+
+# Show that we can dump the access flags even when they
+# don't make any sense.
+dx --debug --dump --width=100 small-class.txt
diff --git a/dx/tests/003-magic-version-access/small-class.txt b/dx/tests/003-magic-version-access/small-class.txt
new file mode 100644
index 0000000..3eb7402
--- /dev/null
+++ b/dx/tests/003-magic-version-access/small-class.txt
@@ -0,0 +1,25 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+0031       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/004-cp-bottom-up/expected.txt b/dx/tests/004-cp-bottom-up/expected.txt
new file mode 100644
index 0000000..4edbed5
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/expected.txt
@@ -0,0 +1,34 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0014
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"blort"}
+  0006: utf8{"x/y/Zzz"}
+  0007: utf8{"()V"}
+  0008: nat{blort:x/y/Zzz}
+  0009: nat{blort:()V}
+  000a: field{Small.blort:x/y/Zzz}
+  000b: method{Small.blort:()V}
+  000c: ifaceMethod{Small.blort:()V}
+  000d: string{"Small"}
+  000e: int{0x12345678 / 305419896}
+  000f: float{0x42f6e666 / 123.45}
+  0010: long{0x123456789abcdef0 / 1311768467463790320}
+  0012: double{0x411958955f8a0903 / 415269.3433}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/004-cp-bottom-up/info.txt b/dx/tests/004-cp-bottom-up/info.txt
new file mode 100644
index 0000000..f78a626
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the constant pool contains
+at least one valid entry of each possible constant pool type, and that
+entries that are referred to by other entries always occur before the
+referring entries.
diff --git a/dx/tests/004-cp-bottom-up/run b/dx/tests/004-cp-bottom-up/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/004-cp-bottom-up/small-class.txt b/dx/tests/004-cp-bottom-up/small-class.txt
new file mode 100644
index 0000000..8a68cbf
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0014       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0007 "x/y/Zzz"          # 0006: utf8["x/y/Zzz"]
+01 0003 "()V"              # 0007: utf8["()V"]
+0c 0005 0006               # 0008: nat[blort:x/y/Zzz]
+0c 0005 0007               # 0009: nat[blort:()V]
+09 0003 0008               # 000a: field[Small.blort:x/y/Zzz]
+0a 0003 0009               # 000b: method[Small.blort:()V]
+0b 0003 0009               # 000c: ifaceMethod[Small.blort:()V]
+08 0001                    # 000d: string["Small"]
+03 12345678                # 000e: integer[0x12345678]
+04 42f6e666                # 000f: float[123.45]
+05 12345678 9abcdef0       # 0010: long[0x1234567890abcdef0]
+06 41195895 5f8a0903       # 0012: double[415269.3433]
+
+0001  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/005-cp-top-down/expected.txt b/dx/tests/005-cp-top-down/expected.txt
new file mode 100644
index 0000000..791b9da
--- /dev/null
+++ b/dx/tests/005-cp-top-down/expected.txt
@@ -0,0 +1,34 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0014
+
+constant_pool:
+  0001: double{0x411958955f8a0903 / 415269.3433}
+  0003: long{0x123456789abcdef0 / 1311768467463790320}
+  0005: float{0x42f6e666 / 123.45}
+  0006: int{0x12345678 / 305419896}
+  0007: string{"Small"}
+  0008: ifaceMethod{Small.blort:()V}
+  0009: method{Small.blort:()V}
+  000a: field{Small.blort:x/y/Zzz}
+  000b: nat{blort:()V}
+  000c: nat{blort:x/y/Zzz}
+  000d: utf8{"()V"}
+  000e: utf8{"x/y/Zzz"}
+  000f: utf8{"blort"}
+  0010: type{java.lang.Object}
+  0011: type{Small}
+  0012: utf8{"java/lang/Object"}
+  0013: utf8{"Small"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/005-cp-top-down/info.txt b/dx/tests/005-cp-top-down/info.txt
new file mode 100644
index 0000000..5842fb3
--- /dev/null
+++ b/dx/tests/005-cp-top-down/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the constant pool contains
+at least one valid entry of each possible constant pool type, and that
+entries that are referred to by other entries always occur after the
+referring entries.
diff --git a/dx/tests/005-cp-top-down/run b/dx/tests/005-cp-top-down/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/005-cp-top-down/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/005-cp-top-down/small-class.txt b/dx/tests/005-cp-top-down/small-class.txt
new file mode 100644
index 0000000..a681f65
--- /dev/null
+++ b/dx/tests/005-cp-top-down/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0014       # constant_pool_count
+
+#
+# constant_pool
+#
+06 41195895 5f8a0903       # 0001: double[415269.3433]
+05 12345678 9abcdef0       # 0003: long[0x1234567890abcdef0]
+04 42f6e666                # 0005: float[123.45]
+03 12345678                # 0006: integer[0x12345678]
+08 0013                    # 0007: string["Small"]
+0b 0011 000b               # 0008: ifaceMethod[Small.blort:()V]
+0a 0011 000b               # 0009: method[Small.blort:()V]
+09 0011 000c               # 000a: field[Small.blort:x/y/Zzz]
+0c 000f 000d               # 000b: nat[blort:()V]
+0c 000f 000e               # 000c: nat[blort:x/y/Zzz]
+01 0003 "()V"              # 000d: utf8["()V"]
+01 0007 "x/y/Zzz"          # 000e: utf8["x/y/Zzz"]
+01 0005 "blort"            # 000f: utf8["blort"]
+07 0012                    # 0010: class[java/lang/Object]
+07 0013                    # 0011: class[Small]
+01 0010 "java/lang/Object" # 0012: utf8["java/lang/Object"]
+01 0005 "Small"            # 0013: utf8["Small"]
+
+0001  # access_flags
+0011  # this_class
+0010  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/006-interfaces/expected.txt b/dx/tests/006-interfaces/expected.txt
new file mode 100644
index 0000000..09e066b
--- /dev/null
+++ b/dx/tests/006-interfaces/expected.txt
@@ -0,0 +1,31 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000b
+
+constant_pool:
+  0001: utf8{"java/lang/Object"}
+  0002: utf8{"Small"}
+  0003: utf8{"Foo"}
+  0004: utf8{"Bar"}
+  0005: utf8{"Baz"}
+  0006: type{java.lang.Object}
+  0007: type{Small}
+  0008: type{Foo}
+  0009: type{Bar}
+  000a: type{Baz}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0003
+interfaces:
+  type{Foo}
+  type{Bar}
+  type{Baz}
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/006-interfaces/info.txt b/dx/tests/006-interfaces/info.txt
new file mode 100644
index 0000000..0959482
--- /dev/null
+++ b/dx/tests/006-interfaces/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a non-empty
+interfaces list.
diff --git a/dx/tests/006-interfaces/run b/dx/tests/006-interfaces/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/006-interfaces/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/006-interfaces/small-class.txt b/dx/tests/006-interfaces/small-class.txt
new file mode 100644
index 0000000..ea24923
--- /dev/null
+++ b/dx/tests/006-interfaces/small-class.txt
@@ -0,0 +1,33 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000b       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0010 "java/lang/Object" # 0001: utf8["java/lang/Object"]
+01 0005 "Small"            # 0002: utf8["Small"]
+01 0003 "Foo"              # 0003: utf8["Foo"]
+01 0003 "Bar"              # 0004: utf8["Bar"]
+01 0003 "Baz"              # 0005: utf8["Baz"]
+07 0001                    # 0006: class[java/lang/Object]
+07 0002                    # 0007: class[Small]
+07 0003                    # 0008: class[Foo]
+07 0004                    # 0009: class[Bar]
+07 0005                    # 000a: class[Baz]
+
+0001  # access_flags
+0007  # this_class
+0006  # super_class
+0003  # interfaces_count
+0008 0009 000a  # interfaces
+
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/007-no-superclass/expected.txt b/dx/tests/007-no-superclass/expected.txt
new file mode 100644
index 0000000..d635c9a
--- /dev/null
+++ b/dx/tests/007-no-superclass/expected.txt
@@ -0,0 +1,19 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0003
+
+constant_pool:
+  0001: utf8{"java/lang/Object"}
+  0002: type{java.lang.Object}
+end constant_pool
+access_flags: public
+this_class: type{java.lang.Object}
+super_class: (none)
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/007-no-superclass/info.txt b/dx/tests/007-no-superclass/info.txt
new file mode 100644
index 0000000..5f941d0
--- /dev/null
+++ b/dx/tests/007-no-superclass/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class is Object-like and
+has no superclass.
diff --git a/dx/tests/007-no-superclass/run b/dx/tests/007-no-superclass/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/007-no-superclass/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/007-no-superclass/small-class.txt b/dx/tests/007-no-superclass/small-class.txt
new file mode 100644
index 0000000..6fd4408
--- /dev/null
+++ b/dx/tests/007-no-superclass/small-class.txt
@@ -0,0 +1,24 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0003       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0010 "java/lang/Object" # 0001: utf8["java/lang/Object"]
+07 0001                    # 0002: class[java/lang/Object]
+
+0001  # access_flags
+0002  # this_class
+0000  # super_class
+0000  # interfaces_count
+
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/008-field/expected.txt b/dx/tests/008-field/expected.txt
new file mode 100644
index 0000000..9e3bcaf
--- /dev/null
+++ b/dx/tests/008-field/expected.txt
@@ -0,0 +1,30 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"foo"}
+  0006: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+  access_flags: public|private|protected|static|final|volatile|transient|synthetic|enum|af20
+  name: foo
+  descriptor: I
+  attributes_count: 0000
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/008-field/info.txt b/dx/tests/008-field/info.txt
new file mode 100644
index 0000000..0b8e92f
--- /dev/null
+++ b/dx/tests/008-field/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+simple field with no attributes and with every access flag turned on
+(so that the names can be verified in debugging output).
diff --git a/dx/tests/008-field/run b/dx/tests/008-field/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/008-field/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/008-field/small-class.txt b/dx/tests/008-field/small-class.txt
new file mode 100644
index 0000000..81eb164
--- /dev/null
+++ b/dx/tests/008-field/small-class.txt
@@ -0,0 +1,34 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0007       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0003 "foo"              # 0005: utf8["foo"]
+01 0001 "I"                # 0006: utf8["I"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0001  # fields_count
+
+# fields[0]
+ffff  # access_flags
+0005  # name
+0006  # descriptor
+0000  # attributes_count
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/009-method/expected.txt b/dx/tests/009-method/expected.txt
new file mode 100644
index 0000000..3c0d6ad
--- /dev/null
+++ b/dx/tests/009-method/expected.txt
@@ -0,0 +1,30 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"foo"}
+  0006: utf8{"()V"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|private|protected|static|final|synchronized|bridge|varargs|native|abstract|strictfp|synthetic|e200
+  name: foo
+  descriptor: ()V
+  attributes_count: 0000
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/009-method/info.txt b/dx/tests/009-method/info.txt
new file mode 100644
index 0000000..3df2f09
--- /dev/null
+++ b/dx/tests/009-method/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+simple method with no attributes and with every access flag turned on
+(so that the names can be verified in debugging output).
diff --git a/dx/tests/009-method/run b/dx/tests/009-method/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/009-method/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/009-method/small-class.txt b/dx/tests/009-method/small-class.txt
new file mode 100644
index 0000000..54fbc5f
--- /dev/null
+++ b/dx/tests/009-method/small-class.txt
@@ -0,0 +1,34 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0007       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0003 "foo"              # 0005: utf8["foo"]
+01 0003 "()V"              # 0006: utf8["()V"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+
+0001  # methods_count
+
+# methods[0]
+ffff  # access_flags
+0005  # name
+0006  # descriptor
+0000  # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/010-class-attrib-InnerClasses/expected.txt b/dx/tests/010-class-attrib-InnerClasses/expected.txt
new file mode 100644
index 0000000..590ed2e
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/expected.txt
@@ -0,0 +1,46 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"InnerClasses"}
+  0006: utf8{"Zorch"}
+  0007: type{Zorch}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: InnerClasses
+  length: 00000022
+  number_of_classes: 0004
+  inner_class: type{Small}
+    outer_class: (none)
+    name: (none)
+    access_flags: public
+  inner_class: type{Small}
+    outer_class: (none)
+    name: utf8{"Small"}
+    access_flags: private
+  inner_class: type{Small}
+    outer_class: type{Zorch}
+    name: (none)
+    access_flags: protected
+  inner_class: type{Zorch}
+    outer_class: type{Small}
+    name: utf8{"Zorch"}
+    access_flags: public|private|protected|static|final|interface|abstract|synthetic|annotation|enum|89e0
+end attributes[0]
+end classfile
diff --git a/dx/tests/010-class-attrib-InnerClasses/info.txt b/dx/tests/010-class-attrib-InnerClasses/info.txt
new file mode 100644
index 0000000..305b035
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level InnerClasses attribute, which is syntactically valid and contains
+one entry for each of the possible combinations of null-vs-valid cpe.
diff --git a/dx/tests/010-class-attrib-InnerClasses/run b/dx/tests/010-class-attrib-InnerClasses/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/010-class-attrib-InnerClasses/small-class.txt b/dx/tests/010-class-attrib-InnerClasses/small-class.txt
new file mode 100644
index 0000000..54fd19d
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/small-class.txt
@@ -0,0 +1,37 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000c "InnerClasses"     # 0005: utf8["InnerClasses"]
+01 0005 "Zorch"            # 0006: utf8["Zorch"]
+07 0006                    # 0007: class[Zorch]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000022  # length
+0004      # number_of_classes
+0003 0000 0000 0001  # Small / null / null / public
+0003 0000 0001 0002  # Small / null / "Small" / private
+0003 0007 0000 0004  # Small / Zorch / null / protected
+0007 0003 0006 ffff  # Zorch / Small / "Zorch" / all-bits
diff --git a/dx/tests/011-class-attrib-Synthetic/expected.txt b/dx/tests/011-class-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..85e2bff
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/expected.txt
@@ -0,0 +1,27 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Synthetic"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: Synthetic
+  length: 00000000
+end attributes[0]
+end classfile
diff --git a/dx/tests/011-class-attrib-Synthetic/info.txt b/dx/tests/011-class-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..bfd443e
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Synthetic attribute, which is syntactically valid.
diff --git a/dx/tests/011-class-attrib-Synthetic/run b/dx/tests/011-class-attrib-Synthetic/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/011-class-attrib-Synthetic/small-class.txt b/dx/tests/011-class-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..bc3281b
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/small-class.txt
@@ -0,0 +1,30 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0006       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0009 "Synthetic"        # 0005: utf8["Synthetic"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000000  # length
diff --git a/dx/tests/012-class-attrib-SourceFile/expected.txt b/dx/tests/012-class-attrib-SourceFile/expected.txt
new file mode 100644
index 0000000..c795cde
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/expected.txt
@@ -0,0 +1,29 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"SourceFile"}
+  0006: utf8{"Blort.java"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: SourceFile
+  length: 00000002
+  source: utf8{"Blort.java"}
+end attributes[0]
+end classfile
diff --git a/dx/tests/012-class-attrib-SourceFile/info.txt b/dx/tests/012-class-attrib-SourceFile/info.txt
new file mode 100644
index 0000000..f1d8985
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level SourceFile attribute, which is syntactically valid.
diff --git a/dx/tests/012-class-attrib-SourceFile/run b/dx/tests/012-class-attrib-SourceFile/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/012-class-attrib-SourceFile/small-class.txt b/dx/tests/012-class-attrib-SourceFile/small-class.txt
new file mode 100644
index 0000000..3c514be
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/small-class.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0007       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000a "SourceFile"       # 0005: utf8["SourceFile"]
+01 000a "Blort.java"       # 0006: utf8["Blort.java"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000002  # length
+0006      # "Blort.java"
diff --git a/dx/tests/013-class-attrib-Deprecated/expected.txt b/dx/tests/013-class-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..4476c89
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/expected.txt
@@ -0,0 +1,27 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Deprecated"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: Deprecated
+  length: 00000000
+end attributes[0]
+end classfile
diff --git a/dx/tests/013-class-attrib-Deprecated/info.txt b/dx/tests/013-class-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..8164fe1
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Deprecated attribute, which is syntactically valid.
diff --git a/dx/tests/013-class-attrib-Deprecated/run b/dx/tests/013-class-attrib-Deprecated/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/013-class-attrib-Deprecated/small-class.txt b/dx/tests/013-class-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..03d7e24
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/small-class.txt
@@ -0,0 +1,30 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0006       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000a "Deprecated"       # 0005: utf8["Deprecated"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000000  # length
diff --git a/dx/tests/014-field-attrib-ConstantValue/expected.txt b/dx/tests/014-field-attrib-ConstantValue/expected.txt
new file mode 100644
index 0000000..b3d91a5
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/expected.txt
@@ -0,0 +1,162 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 001f
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"ConstantValue"}
+  0006: utf8{"a"}
+  0007: utf8{"b"}
+  0008: utf8{"c"}
+  0009: utf8{"d"}
+  000a: utf8{"e"}
+  000b: utf8{"f"}
+  000c: utf8{"g"}
+  000d: utf8{"h"}
+  000e: utf8{"i"}
+  000f: string{"Small"}
+  0010: int{0x8191a1b1 / -2121162319}
+  0011: float{0xbffeb852 / -1.99}
+  0012: long{0x80818283f0f1f2f3 / -9186918261664386317}
+  0014: double{0xbfffd70a3d70a3d7 / -1.99}
+  0016: utf8{"B"}
+  0017: utf8{"C"}
+  0018: utf8{"D"}
+  0019: utf8{"F"}
+  001a: utf8{"I"}
+  001b: utf8{"J"}
+  001c: utf8{"S"}
+  001d: utf8{"Z"}
+  001e: utf8{"Ljava/lang/String;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0009
+
+fields[0]:
+  access_flags: public
+  name: a
+  descriptor: B
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[0]
+
+fields[1]:
+  access_flags: private
+  name: b
+  descriptor: C
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[1]
+
+fields[2]:
+  access_flags: protected
+  name: c
+  descriptor: D
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: double{0xbfffd70a3d70a3d7 / -1.99}
+  end attributes[0]
+end fields[2]
+
+fields[3]:
+  access_flags: static
+  name: d
+  descriptor: F
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: float{0xbffeb852 / -1.99}
+  end attributes[0]
+end fields[3]
+
+fields[4]:
+  access_flags: final
+  name: e
+  descriptor: I
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[4]
+
+fields[5]:
+  access_flags: volatile
+  name: f
+  descriptor: J
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: long{0x80818283f0f1f2f3 / -9186918261664386317}
+  end attributes[0]
+end fields[5]
+
+fields[6]:
+  access_flags: transient
+  name: g
+  descriptor: S
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[6]
+
+fields[7]:
+  access_flags: public|static|final
+  name: h
+  descriptor: Z
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: int{0x8191a1b1 / -2121162319}
+  end attributes[0]
+end fields[7]
+
+fields[8]:
+  access_flags: public|static|final
+  name: i
+  descriptor: Ljava/lang/String;
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: ConstantValue
+    length: 00000002
+    value: string{"Small"}
+  end attributes[0]
+end fields[8]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/014-field-attrib-ConstantValue/info.txt b/dx/tests/014-field-attrib-ConstantValue/info.txt
new file mode 100644
index 0000000..313159c
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a series of
+fields, each with a single ConstantValue attribute, which points at one
+of the appropriate sorts of cpes.
diff --git a/dx/tests/014-field-attrib-ConstantValue/run b/dx/tests/014-field-attrib-ConstantValue/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/014-field-attrib-ConstantValue/small-class.txt b/dx/tests/014-field-attrib-ConstantValue/small-class.txt
new file mode 100644
index 0000000..8d869de
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/small-class.txt
@@ -0,0 +1,140 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+001f       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"              # 0001: utf8["Small"]
+01 0010 "java/lang/Object"   # 0002: utf8["java/lang/Object"]
+07 0001                      # 0003: class[Small]
+07 0002                      # 0004: class[java/lang/Object]
+01 000d "ConstantValue"      # 0005: utf8["ConstantValue"]
+01 0001 "a"                  # 0006: utf8["a"]
+01 0001 "b"                  # 0007: utf8["b"]
+01 0001 "c"                  # 0008: utf8["c"]
+01 0001 "d"                  # 0009: utf8["d"]
+01 0001 "e"                  # 000a: utf8["e"]
+01 0001 "f"                  # 000b: utf8["f"]
+01 0001 "g"                  # 000c: utf8["g"]
+01 0001 "h"                  # 000d: utf8["h"]
+01 0001 "i"                  # 000e: utf8["i"]
+08 0001                      # 000f: string["Small"]
+03 8191a1b1                  # 0010: integer[0x8191a1b1]
+04 bffeb852                  # 0011: float[-1.99]
+05 80818283 f0f1f2f3         # 0012: long[0x80818283f0f1f2f3]
+06 bfffd70a 3d70a3d7         # 0014: double[-1.99]
+01 0001 "B"                  # 0016: utf8["B"]
+01 0001 "C"                  # 0017: utf8["C"]
+01 0001 "D"                  # 0018: utf8["D"]
+01 0001 "F"                  # 0019: utf8["F"]
+01 0001 "I"                  # 001a: utf8["I"]
+01 0001 "J"                  # 001b: utf8["J"]
+01 0001 "S"                  # 001c: utf8["S"]
+01 0001 "Z"                  # 001d: utf8["Z"]
+01 0012 "Ljava/lang/String;" # 001e: utf8["Ljava/lang/String;"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0009  # fields_count
+
+# fields[0]
+0001  # access_flags
+0006  # "a"
+0016  # "B"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[1]
+0002  # access_flags
+0007  # "b"
+0017  # "C"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[2]
+0004  # access_flags
+0008  # "c"
+0018  # "D"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0014      # value
+
+# fields[3]
+0008  # access_flags
+0009  # "d"
+0019  # "F"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0011      # value
+
+# fields[4]
+0010  # access_flags
+000a  # "e"
+001a  # "I"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[5]
+0040  # access_flags
+000b  # "f"
+001b  # "J"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0012      # value
+
+# fields[6]
+0080  # access_flags
+000c  # "g"
+001c  # "Z"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[7]
+0019  # access_flags
+000d  # "h"
+001d  # "S"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0010      # value
+
+# fields[8]
+0019  # access_flags
+000e  # "i"
+001e  # "Ljava/lang/String;"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+000f      # value
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/015-field-attrib-Synthetic/expected.txt b/dx/tests/015-field-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..bdb4519
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Synthetic"}
+  0006: utf8{"a"}
+  0007: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+  access_flags: public
+  name: a
+  descriptor: I
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Synthetic
+    length: 00000000
+  end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/015-field-attrib-Synthetic/info.txt b/dx/tests/015-field-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..6037cfa
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a valid Synthetic attribute.
diff --git a/dx/tests/015-field-attrib-Synthetic/run b/dx/tests/015-field-attrib-Synthetic/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/015-field-attrib-Synthetic/small-class.txt b/dx/tests/015-field-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..e5f429e
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0009 "Synthetic"        # 0005: utf8["Synthetic"]
+01 0001 "a"                # 0006: utf8["a"]
+01 0001 "I"                # 0007: utf8["I"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0001  # fields_count
+
+# fields[0]
+0001  # access_flags
+0006  # "a"
+0007  # "I"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000000  # length
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/016-field-attrib-Deprecated/expected.txt b/dx/tests/016-field-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..1b5547f
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Deprecated"}
+  0006: utf8{"a"}
+  0007: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+  access_flags: public
+  name: a
+  descriptor: I
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Deprecated
+    length: 00000000
+  end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/016-field-attrib-Deprecated/info.txt b/dx/tests/016-field-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..1185981
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a valid Deprecated attribute.
diff --git a/dx/tests/016-field-attrib-Deprecated/run b/dx/tests/016-field-attrib-Deprecated/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/016-field-attrib-Deprecated/small-class.txt b/dx/tests/016-field-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..2448ce4
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000a "Deprecated"       # 0005: utf8["Deprecated"]
+01 0001 "a"                # 0006: utf8["a"]
+01 0001 "I"                # 0007: utf8["I"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0001  # fields_count
+
+# fields[0]
+0001  # access_flags
+0006  # "a"
+0007  # "I"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000000  # length
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/017-method-attrib-Code/expected.txt b/dx/tests/017-method-attrib-Code/expected.txt
new file mode 100644
index 0000000..c43730c
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/expected.txt
@@ -0,0 +1,42 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 0000000d
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 00000001
+    0000: return
+    exception_table_length: 0000
+    attributes_count: 0000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/017-method-attrib-Code/info.txt b/dx/tests/017-method-attrib-Code/info.txt
new file mode 100644
index 0000000..a3ed24c
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a minimal but syntactically valid Code attribute.
diff --git a/dx/tests/017-method-attrib-Code/run b/dx/tests/017-method-attrib-Code/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/017-method-attrib-Code/small-class.txt b/dx/tests/017-method-attrib-Code/small-class.txt
new file mode 100644
index 0000000..699005b
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/small-class.txt
@@ -0,0 +1,43 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0004 "Code"             # 0007: utf8["Code"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+0000000d  # length
+0001      # max_stack
+0001      # max_locals
+00000001  # code_length
+b1        # 0000: return
+0000      # exception_table_length
+0000      # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/018-method-attrib-Exceptions/expected.txt b/dx/tests/018-method-attrib-Exceptions/expected.txt
new file mode 100644
index 0000000..15f923f
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/expected.txt
@@ -0,0 +1,40 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000a
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Exceptions"}
+  0008: utf8{"java/lang/Error"}
+  0009: type{java.lang.Error}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|abstract
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Exceptions
+    length: 00000004
+    number_of_exceptions: 0001
+      type{java.lang.Error}
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/018-method-attrib-Exceptions/info.txt b/dx/tests/018-method-attrib-Exceptions/info.txt
new file mode 100644
index 0000000..4a3738d
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Exceptions attribute.
diff --git a/dx/tests/018-method-attrib-Exceptions/run b/dx/tests/018-method-attrib-Exceptions/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/018-method-attrib-Exceptions/small-class.txt b/dx/tests/018-method-attrib-Exceptions/small-class.txt
new file mode 100644
index 0000000..5d05b43
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/small-class.txt
@@ -0,0 +1,41 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000a       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 000a "Exceptions"       # 0007: utf8["Exceptions"]
+01 000f "java/lang/Error"  # 0008: utf8["java/lang/Error"]
+07 0008                    # 0009: class[java/lang/Error]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0401  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000004  # length
+0001      # number_of_exceptions
+0009      # class[java/lang/Error]
+
+0000  # attributes_count
diff --git a/dx/tests/019-method-attrib-Synthetic/expected.txt b/dx/tests/019-method-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..5d1d9cb
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Synthetic"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|abstract
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Synthetic
+    length: 00000000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/019-method-attrib-Synthetic/info.txt b/dx/tests/019-method-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..eddf4fb
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Synthetic attribute.
diff --git a/dx/tests/019-method-attrib-Synthetic/run b/dx/tests/019-method-attrib-Synthetic/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/019-method-attrib-Synthetic/small-class.txt b/dx/tests/019-method-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..458dae8
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/small-class.txt
@@ -0,0 +1,37 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0009 "Synthetic"        # 0007: utf8["Synthetic"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0401  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000000  # length
+
+0000  # attributes_count
diff --git a/dx/tests/020-method-attrib-Deprecated/expected.txt b/dx/tests/020-method-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..8da8aa8
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Deprecated"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|abstract
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Deprecated
+    length: 00000000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/020-method-attrib-Deprecated/info.txt b/dx/tests/020-method-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..b7c6266
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Deprecated attribute.
diff --git a/dx/tests/020-method-attrib-Deprecated/run b/dx/tests/020-method-attrib-Deprecated/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/020-method-attrib-Deprecated/small-class.txt b/dx/tests/020-method-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..f906733
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/small-class.txt
@@ -0,0 +1,37 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0008       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 000a "Deprecated"       # 0007: utf8["Deprecated"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0401  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000000  # length
+
+0000  # attributes_count
diff --git a/dx/tests/021-code-attrib-LineNumberTable/expected.txt b/dx/tests/021-code-attrib-LineNumberTable/expected.txt
new file mode 100644
index 0000000..3f29310
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/expected.txt
@@ -0,0 +1,52 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+  0008: utf8{"LineNumberTable"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 0000001e
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 00000002
+    0000: return
+    0001: return
+    exception_table_length: 0000
+    attributes_count: 0001
+    
+    attributes[0]:
+      name: LineNumberTable
+      length: 0000000a
+      line_number_table_length: 0002
+      0000 17
+      0001 34
+    end attributes[0]
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/021-code-attrib-LineNumberTable/info.txt b/dx/tests/021-code-attrib-LineNumberTable/info.txt
new file mode 100644
index 0000000..2ffc798
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a simple Code attribute, which itself has a syntactically
+valid LineNumberTable attribute.
diff --git a/dx/tests/021-code-attrib-LineNumberTable/run b/dx/tests/021-code-attrib-LineNumberTable/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/021-code-attrib-LineNumberTable/small-class.txt b/dx/tests/021-code-attrib-LineNumberTable/small-class.txt
new file mode 100644
index 0000000..c28c39a
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/small-class.txt
@@ -0,0 +1,51 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0009       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0004 "Code"             # 0007: utf8["Code"]
+01 000f "LineNumberTable"  # 0008: utf8["LineNumberTable"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+0000001e  # length
+0001      # max_stack
+0001      # max_locals
+00000002  # code_length
+b1        # 0000: return
+b1        # 0001: return
+0000      # exception_table_length
+0001      # attributes_count
+# attributes[0]
+0008      # name
+0000000a  # length
+0002      # line_number_table_length
+0000 0011 # offset 0000, line #17
+0001 0022 # offset 0001, line #34
+
+0000  # attributes_count
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/expected.txt b/dx/tests/022-code-attrib-LocalVariableTable/expected.txt
new file mode 100644
index 0000000..80091ed
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/expected.txt
@@ -0,0 +1,57 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000d
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+  0008: utf8{"LocalVariableTable"}
+  0009: utf8{"foo"}
+  000a: utf8{"bar"}
+  000b: utf8{"baz"}
+  000c: utf8{"[I"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 00000034
+    max_stack: 0001
+    max_locals: 0002
+    code_length: 00000002
+    0000: return
+    0001: return
+    exception_table_length: 0000
+    attributes_count: 0001
+    
+    attributes[0]:
+      name: LocalVariableTable
+      length: 00000020
+      local_variable_table_length: 0003
+      0000..0002 0000 foo [I
+      0000..0001 0001 bar [I
+      0001..0002 0001 baz [I
+    end attributes[0]
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/info.txt b/dx/tests/022-code-attrib-LocalVariableTable/info.txt
new file mode 100644
index 0000000..5b44286
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a simple Code attribute, which itself has a syntactically
+valid LocalVariableTable attribute.
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/run b/dx/tests/022-code-attrib-LocalVariableTable/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt b/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt
new file mode 100644
index 0000000..abbf8ea
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt
@@ -0,0 +1,56 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000d       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                      # 0001: class[Small]
+07 0004                      # 0002: class[java/lang/Object]
+01 0005 "Small"              # 0003: utf8["Small"]
+01 0010 "java/lang/Object"   # 0004: utf8["java/lang/Object"]
+01 0005 "blort"              # 0005: utf8["blort"]
+01 0003 "()V"                # 0006: utf8["()V"]
+01 0004 "Code"               # 0007: utf8["Code"]
+01 0012 "LocalVariableTable" # 0008: utf8["LocalVariableTable"]
+01 0003 "foo"                # 0009: utf8["foo"]
+01 0003 "bar"                # 000a: utf8["bar"]
+01 0003 "baz"                # 000b: utf8["baz"]
+01 0002 "[I"                 # 000c: utf8["[I"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000034  # length
+0001      # max_stack
+0002      # max_locals
+00000002  # code_length
+b1        # 0000: return
+b1        # 0001: return
+0000      # exception_table_length
+0001      # attributes_count
+# attributes[0]
+0008      # name
+00000020  # length
+0003      # local_variable_table_length
+0000 0002 0009 000c 0000  # 0000..0002 foo:[I #0000
+0000 0001 000a 000c 0001  # 0000..0001 bar:[I #0001
+0001 0001 000b 000c 0001  # 0001..0002 baz:[I #0001
+
+0000  # attributes_count
diff --git a/dx/tests/023-code-exception-table/expected.txt b/dx/tests/023-code-exception-table/expected.txt
new file mode 100644
index 0000000..552e5d3
--- /dev/null
+++ b/dx/tests/023-code-exception-table/expected.txt
@@ -0,0 +1,51 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000c
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+  0008: utf8{"java/lang/Error"}
+  0009: utf8{"java/lang/Exception"}
+  000a: type{java.lang.Error}
+  000b: type{java.lang.Exception}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 00000027
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 00000003
+    0000: return
+    0001: return
+    0002: return
+    exception_table_length: 0003
+      0000..0002 -> 0002 java.lang.Error
+      0000..0001 -> 0001 java.lang.Exception
+      0001..0002 -> 0002 <any>
+    attributes_count: 0000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/023-code-exception-table/info.txt b/dx/tests/023-code-exception-table/info.txt
new file mode 100644
index 0000000..2dbb5da
--- /dev/null
+++ b/dx/tests/023-code-exception-table/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a minimal but syntactically valid Code attribute, which
+sports a non-empty syntactically valid exception table.
diff --git a/dx/tests/023-code-exception-table/run b/dx/tests/023-code-exception-table/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/023-code-exception-table/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/023-code-exception-table/small-class.txt b/dx/tests/023-code-exception-table/small-class.txt
new file mode 100644
index 0000000..f8ff1f3
--- /dev/null
+++ b/dx/tests/023-code-exception-table/small-class.txt
@@ -0,0 +1,52 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+000c       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                       # 0001: class[Small]
+07 0004                       # 0002: class[java/lang/Object]
+01 0005 "Small"               # 0003: utf8["Small"]
+01 0010 "java/lang/Object"    # 0004: utf8["java/lang/Object"]
+01 0005 "blort"               # 0005: utf8["blort"]
+01 0003 "()V"                 # 0006: utf8["()V"]
+01 0004 "Code"                # 0007: utf8["Code"]
+01 000f "java/lang/Error"     # 0008: utf8["java/lang/Error"]
+01 0013 "java/lang/Exception" # 0009: utf8["java/lang/Exception"]
+07 0008                       # 000a: class[java/lang/Error]
+07 0009                       # 000b: class[java/lang/Exception]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000027  # length
+0001      # max_stack
+0001      # max_locals
+00000003  # code_length
+b1        # 0000: return
+b1        # 0001: return
+b1        # 0002: return
+0003      # exception_table_length
+0000 0002 0002 000a  # 0000..0002 -> 0002 java/lang/Error
+0000 0001 0001 000b  # 0000..0001 -> 0001 java/lang/Exception
+0001 0002 0002 0000  # 0001..0002 -> 0002 <any>
+0000      # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/024-code-bytecode/expected.txt b/dx/tests/024-code-bytecode/expected.txt
new file mode 100644
index 0000000..4637474
--- /dev/null
+++ b/dx/tests/024-code-bytecode/expected.txt
@@ -0,0 +1,294 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0017
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Code"}
+  0008: string{"Small"}
+  0009: int{0x12345678 / 305419896}
+  000a: float{0x42f6e666 / 123.45}
+  000b: long{0x123456789abcdef0 / 1311768467463790320}
+  000d: double{0x411958955f8a0903 / 415269.3433}
+  000f: utf8{"blort"}
+  0010: utf8{"x/y/Zzz"}
+  0011: utf8{"()V"}
+  0012: nat{blort:x/y/Zzz}
+  0013: nat{blort:()V}
+  0014: field{Small.blort:x/y/Zzz}
+  0015: method{Small.blort:()V}
+  0016: ifaceMethod{Small.blort:()V}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Code
+    length: 000001dc
+    max_stack: 0001
+    max_locals: 0001
+    code_length: 000001d0
+    0000: nop
+    0001: aconst_null
+    0002: iconst_m1 // #-01
+    0003: iconst_0 // #+00
+    0004: iconst_1 // #+01
+    0005: iconst_2 // #+02
+    0006: iconst_3 // #+03
+    0007: iconst_4 // #+04
+    0008: iconst_5 // #+05
+    0009: lconst_0 // +00
+    000a: lconst_1 // +01
+    000b: fconst_0 // 0.0
+    000c: fconst_1 // 1.0
+    000d: fconst_2 // 2.0
+    000e: dconst_0 // 0.0
+    000f: dconst_1 // 1.0
+    0010: bipush #+45
+    0012: sipush #+5432
+    0015: ldc string{"Small"}
+    0017: ldc #+12345678
+    0019: ldc #42f6e666 // 123.45
+    001b: ldc_w string{"Small"}
+    001e: ldc_w #+12345678
+    0021: ldc_w #42f6e666 // 123.45
+    0024: ldc2_w #+123456789abcdef0
+    0027: ldc2_w #411958955f8a0903 // 415269.3433
+    002a: iload 01
+    002c: lload 02 // category-2
+    002e: fload 03
+    0030: dload 04 // category-2
+    0032: aload 05
+    0034: iload_0 // 00
+    0035: iload_1 // 01
+    0036: iload_2 // 02
+    0037: iload_3 // 03
+    0038: lload_0 // 00, category-2
+    0039: lload_1 // 01, category-2
+    003a: lload_2 // 02, category-2
+    003b: lload_3 // 03, category-2
+    003c: fload_0 // 00
+    003d: fload_1 // 01
+    003e: fload_2 // 02
+    003f: fload_3 // 03
+    0040: dload_0 // 00, category-2
+    0041: dload_1 // 01, category-2
+    0042: dload_2 // 02, category-2
+    0043: dload_3 // 03, category-2
+    0044: aload_0 // 00
+    0045: aload_1 // 01
+    0046: aload_2 // 02
+    0047: aload_3 // 03
+    0048: iaload
+    0049: laload
+    004a: faload
+    004b: daload
+    004c: aaload
+    004d: baload
+    004e: caload
+    004f: saload
+    0050: istore 41
+    0052: lstore 42 // category-2
+    0054: fstore 43
+    0056: dstore 44 // category-2
+    0058: astore 45
+    005a: istore_0 // 00
+    005b: istore_1 // 01
+    005c: istore_2 // 02
+    005d: istore_3 // 03
+    005e: lstore_0 // 00, category-2
+    005f: lstore_1 // 01, category-2
+    0060: lstore_2 // 02, category-2
+    0061: lstore_3 // 03, category-2
+    0062: fstore_0 // 00
+    0063: fstore_1 // 01
+    0064: fstore_2 // 02
+    0065: fstore_3 // 03
+    0066: dstore_0 // 00, category-2
+    0067: dstore_1 // 01, category-2
+    0068: dstore_2 // 02, category-2
+    0069: dstore_3 // 03, category-2
+    006a: astore_0 // 00
+    006b: astore_1 // 01
+    006c: astore_2 // 02
+    006d: astore_3 // 03
+    006e: iastore
+    006f: lastore
+    0070: fastore
+    0071: dastore
+    0072: aastore
+    0073: bastore
+    0074: castore
+    0075: sastore
+    0076: pop
+    0077: pop2
+    0078: dup
+    0079: dup_x1
+    007a: dup_x2
+    007b: dup2
+    007c: dup2_x1
+    007d: dup2_x2
+    007e: swap
+    007f: iadd
+    0080: ladd
+    0081: fadd
+    0082: dadd
+    0083: isub
+    0084: lsub
+    0085: fsub
+    0086: dsub
+    0087: imul
+    0088: lmul
+    0089: fmul
+    008a: dmul
+    008b: idiv
+    008c: ldiv
+    008d: fdiv
+    008e: ddiv
+    008f: irem
+    0090: lrem
+    0091: frem
+    0092: drem
+    0093: ineg
+    0094: lneg
+    0095: fneg
+    0096: dneg
+    0097: ishl
+    0098: lshl
+    0099: ishr
+    009a: lshr
+    009b: iushr
+    009c: lushr
+    009d: iand
+    009e: land
+    009f: ior
+    00a0: lor
+    00a1: ixor
+    00a2: lxor
+    00a3: iinc 05, #-01
+    00a6: i2l
+    00a7: i2f
+    00a8: i2d
+    00a9: l2i
+    00aa: l2f
+    00ab: l2d
+    00ac: f2i
+    00ad: f2l
+    00ae: f2d
+    00af: d2i
+    00b0: d2l
+    00b1: d2f
+    00b2: i2b
+    00b3: i2c
+    00b4: i2s
+    00b5: lcmp
+    00b6: fcmpl
+    00b7: fcmpg
+    00b8: dcmpl
+    00b9: dcmpg
+    00ba: ifeq 00ba
+    00bd: ifne 00ba
+    00c0: iflt 00ba
+    00c3: ifge 00ba
+    00c6: ifgt 00ba
+    00c9: ifle 00ba
+    00cc: if_icmpeq 00db
+    00cf: if_icmpne 00db
+    00d2: if_icmplt 00db
+    00d5: if_icmpge 00db
+    00d8: if_icmpgt 00db
+    00db: if_icmple 00db
+    00de: if_acmpeq 00de
+    00e1: if_acmpne 00e1
+    00e4: goto 0000
+    00e7: jsr 00e7
+    00ea: ret 2f
+    00ec: tableswitch
+      +12340000: 0000
+      +12340001: 0001
+      +12340002: 0002
+      +12340003: 0003
+      +12340004: 0004
+      +12340005: 0005
+      +12340006: 0007
+      +12340007: 0009
+      default: 00ea
+    011c: lookupswitch
+      -7689edcc: 0148
+      +00001000: 0149
+      +03333333: 0149
+      +79787776: 014b
+      default: 00ec
+    0148: ireturn
+    0149: lreturn
+    014a: freturn
+    014b: dreturn
+    014c: areturn
+    014d: return
+    014e: getstatic field{Small.blort:x/y/Zzz}
+    0151: putstatic field{Small.blort:x/y/Zzz}
+    0154: getfield field{Small.blort:x/y/Zzz}
+    0157: putfield field{Small.blort:x/y/Zzz}
+    015a: invokevirtual method{Small.blort:()V}
+    015d: invokespecial method{Small.blort:()V}
+    0160: invokestatic method{Small.blort:()V}
+    0163: invokeinterface ifaceMethod{Small.blort:()V}, 0001
+    0168: unused_ba
+    0169: new type{Small}
+    016c: newarray boolean
+    016e: newarray char
+    0170: newarray float
+    0172: newarray double
+    0174: newarray byte
+    0176: newarray short
+    0178: newarray int
+    017a: newarray long
+    017c: anewarray type{Small}
+    017f: arraylength
+    0180: athrow
+    0181: checkcast type{java.lang.Object}
+    0184: instanceof type{java.lang.Object}
+    0187: monitorenter
+    0188: monitorexit
+    0189: wide iload 0123
+    018d: wide lload 0124 // category-2
+    0191: wide fload 0125
+    0195: wide dload 0126 // category-2
+    0199: wide aload 0127
+    019d: wide istore 20f0
+    01a1: wide lstore 20f1 // category-2
+    01a5: wide fstore 20f2
+    01a9: wide dstore 20f3 // category-2
+    01ad: wide astore 20f4
+    01b1: wide ret ffff
+    01b5: wide iinc 0002, #+1000
+    01bb: multianewarray type{java.lang.Object}, 04
+    01bf: ifnull 0000
+    01c2: ifnonnull 01c2
+    01c5: goto_w 700001c5
+    01ca: jsr_w 000001c5
+    01cf: unused_ca
+    exception_table_length: 0000
+    attributes_count: 0000
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/024-code-bytecode/info.txt b/dx/tests/024-code-bytecode/info.txt
new file mode 100644
index 0000000..d1264a8
--- /dev/null
+++ b/dx/tests/024-code-bytecode/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a Code attribute, and the code[] array of the attribute has
+one instance of each interesting variant of each possible bytecode.
diff --git a/dx/tests/024-code-bytecode/run b/dx/tests/024-code-bytecode/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/024-code-bytecode/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/024-code-bytecode/small-class.txt b/dx/tests/024-code-bytecode/small-class.txt
new file mode 100644
index 0000000..2526cf2
--- /dev/null
+++ b/dx/tests/024-code-bytecode/small-class.txt
@@ -0,0 +1,304 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0017       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0004 "Code"             # 0007: utf8["Code"]
+08 0003                    # 0008: string["Small"]
+03 12345678                # 0009: integer[0x12345678]
+04 42f6e666                # 000a: float[123.45]
+05 12345678 9abcdef0       # 000b: long[0x1234567890abcdef0]
+06 41195895 5f8a0903       # 000d: double[415269.3433]
+01 0005 "blort"            # 000f: utf8["blort"]
+01 0007 "x/y/Zzz"          # 0010: utf8["x/y/Zzz"]
+01 0003 "()V"              # 0011: utf8["()V"]
+0c 000f 0010               # 0012: nat[blort:x/y/Zzz]
+0c 000f 0011               # 0013: nat[blort:()V]
+09 0001 0012               # 0014: field[Small.blort:x/y/Zzz]
+0a 0001 0013               # 0015: method[Small.blort:()V]
+0b 0001 0013               # 0016: ifaceMethod[Small.blort:()V]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0001  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+000001dc  # length (note: == code_length + 0x0c)
+0001      # max_stack
+0001      # max_locals
+000001d0  # code_length
+
+00        # 0000: nop
+01        # 0001: aconst_null
+02        # 0002: aconst_m1
+03        # 0003: iconst_0
+04        # 0004: iconst_1
+05        # 0005: iconst_2
+06        # 0006: iconst_3
+07        # 0007: iconst_4
+08        # 0008: iconst_5
+09        # 0009: lconst_0
+0a        # 000a: lconst_1
+0b        # 000b: fconst_0
+0c        # 000c: fconst_1
+0d        # 000d: fconst_2
+0e        # 000e: dconst_0
+0f        # 000f: dconst_1
+10 45     # 0010: bipush #+45
+11 5432   # 0012: sipush #+5432
+12 08     # 0015: ldc <string>
+12 09     # 0017: ldc <integer>
+12 0a     # 0019: ldc <float>
+13 0008   # 001b: ldc_w <string>
+13 0009   # 001e: ldc_w <integer>
+13 000a   # 0021: ldc_w <float>
+14 000b   # 0024: ldc2_w <long>
+14 000d   # 0027: ldc2_w <double>
+15 01     # 002a: iload 01
+16 02     # 002c: lload 02
+17 03     # 002e: fload 03
+18 04     # 0030: dload 04
+19 05     # 0032: aload 05
+1a        # 0034: iload_0
+1b        # 0035: iload_1
+1c        # 0036: iload_2
+1d        # 0037: iload_3
+1e        # 0038: lload_0
+1f        # 0039: lload_1
+20        # 003a: lload_2
+21        # 003b: lload_3
+22        # 003c: fload_0
+23        # 003d: fload_1
+24        # 003e: fload_2
+25        # 003f: fload_3
+26        # 0040: dload_0
+27        # 0041: dload_1
+28        # 0042: dload_2
+29        # 0043: dload_3
+2a        # 0044: aload_0
+2b        # 0045: aload_1
+2c        # 0046: aload_2
+2d        # 0047: aload_3
+2e        # 0048: iaload
+2f        # 0049: laload
+30        # 004a: faload
+31        # 004b: daload
+32        # 004c: aaload
+33        # 004d: baload
+34        # 004e: caload
+35        # 004f: saload
+36 41     # 0050: istore 41
+37 42     # 0052: lstore 42
+38 43     # 0054: fstore 43
+39 44     # 0056: dstore 44
+3a 45     # 0058: astore 45
+3b        # 005a: istore_0
+3c        # 005b: istore_1
+3d        # 005c: istore_2
+3e        # 005d: istore_3
+3f        # 005e: lstore_0
+40        # 005f: lstore_1
+41        # 0060: lstore_2
+42        # 0061: lstore_3
+43        # 0062: fstore_0
+44        # 0063: fstore_1
+45        # 0064: fstore_2
+46        # 0065: fstore_3
+47        # 0066: dstore_0
+48        # 0067: dstore_1
+49        # 0068: dstore_2
+4a        # 0069: dstore_3
+4b        # 006a: astore_0
+4c        # 006b: astore_1
+4d        # 006c: astore_2
+4e        # 006d: astore_3
+4f        # 006e: iastore
+50        # 006f: lastore
+51        # 0070: fastore
+52        # 0071: dastore
+53        # 0072: aastore
+54        # 0073: bastore
+55        # 0074: castore
+56        # 0075: sastore
+57        # 0076: pop
+58        # 0077: pop2
+59        # 0078: dup
+5a        # 0079: dup_x1
+5b        # 007a: dup_x2
+5c        # 007b: dup2
+5d        # 007c: dup2_x1
+5e        # 007d: dup2_x2
+5f        # 007e: swap
+60        # 007f: iadd
+61        # 0080: ladd
+62        # 0081: fadd
+63        # 0082: dadd
+64        # 0083: isub
+65        # 0084: lsub
+66        # 0085: fsub
+67        # 0086: dsub
+68        # 0087: imul
+69        # 0088: lmul
+6a        # 0089: fmul
+6b        # 008a: dmul
+6c        # 008b: idiv
+6d        # 008c: ldiv
+6e        # 008d: fdiv
+6f        # 008e: ddiv
+70        # 008f: irem
+71        # 0090: lrem
+72        # 0091: frem
+73        # 0092: drem
+74        # 0093: ineg
+75        # 0094: lneg
+76        # 0095: fneg
+77        # 0096: dneg
+78        # 0097: ishl
+79        # 0098: lshl
+7a        # 0099: ishr
+7b        # 009a: lshr
+7c        # 009b: iushr
+7d        # 009c: lushr
+7e        # 009d: iand
+7f        # 009e: land
+80        # 009f: ior
+81        # 00a0: lor
+82        # 00a1: ixor
+83        # 00a2: lxor
+84 05 ff  # 00a3: iinc 05, #-1
+85        # 00a6: i2l
+86        # 00a7: i2f
+87        # 00a8: i2d
+88        # 00a9: l2i
+89        # 00aa: l2f
+8a        # 00ab: l2d
+8b        # 00ac: f2i
+8c        # 00ad: f2l
+8d        # 00ae: f2d
+8e        # 00af: d2i
+8f        # 00b0: d2l
+90        # 00b1: d2f
+91        # 00b2: i2b
+92        # 00b3: i2c
+93        # 00b4: i2s
+94        # 00b5: lcmp
+95        # 00b6: fcmpl
+96        # 00b7: fcmpg
+97        # 00b8: dcmpl
+98        # 00b9: dcmpg
+99 0000   # 00ba: ifeq 00ba
+9a fffd   # 00bd: ifne 00ba
+9b fffa   # 00c0: iflt 00ba
+9c fff7   # 00c3: ifge 00ba
+9d fff4   # 00c6: ifgt 00ba
+9e fff1   # 00c9: ifle 00ba
+9f 000f   # 00cc: if_icmpeq 00db
+a0 000c   # 00cf: if_icmpne 00db
+a1 0009   # 00d2: if_icmplt 00db
+a2 0006   # 00d5: if_icmpge 00db
+a3 0003   # 00d8: if_icmpgt 00db
+a4 0000   # 00db: if_icmple 00db
+a5 0000   # 00de: if_acmpeq 00de
+a6 0000   # 00e1: if_acmpne 00e1
+a7 ff1c   # 00e4: goto 0000
+a8 0000   # 00e7: jsr 00e7
+a9 2f     # 00ea: ret 2f
+aa 000000 # 00ec: tableswitch + padding
+ fffffffe #   default: 000000ea
+ 12340000 #   low: 12340000
+ 12340007 #   high: 12340007
+ ffffff14 #   [0]: 00000000
+ ffffff15 #   [1]: 00000001
+ ffffff16 #   [2]: 00000002
+ ffffff17 #   [3]: 00000003
+ ffffff18 #   [4]: 00000004
+ ffffff19 #   [5]: 00000005
+ ffffff1b #   [6]: 00000007
+ ffffff1d #   [7]: 00000009
+ab 000000 # 011c: lookupswitch + padding
+ ffffffd0 #   default: 000000ec
+ 00000004 #   npairs: 4
+ 89761234 #   match[0]: 89761234
+ 0000002c #   offset[0]: 0148
+ 00001000 #   match[1]: 00001000
+ 0000002d #   offset[1]: 0149
+ 03333333 #   match[2]: 03333333
+ 0000002d #   offset[2]: 0149
+ 79787776 #   match[3]: 79787776
+ 0000002f #   offset[3]: 014b
+ac        # 0148: ireturn
+ad        # 0149: lreturn
+ae        # 014a: freturn
+af        # 014b: dreturn
+b0        # 014c: areturn
+b1        # 014d: return
+b2 0014   # 014e: getstatic 0014
+b3 0014   # 0151: putstatic 0014
+b4 0014   # 0154: getfield 0014
+b5 0014   # 0157: putfield 0014
+b6 0015   # 015a: invokevirtual 0015
+b7 0015   # 015d: invokespecial 0015
+b8 0015   # 0160: invokestatic 0015
+b9 0016 01 00  # 0163: invokeinterface 0016
+ba        # 0168: <unused>
+bb 0001   # 0169: new 0001
+bc 04     # 016c: newarray boolean
+bc 05     # 016e: newarray char
+bc 06     # 0170: newarray float
+bc 07     # 0172: newarray double
+bc 08     # 0174: newarray byte
+bc 09     # 0176: newarray short
+bc 0a     # 0178: newarray int
+bc 0b     # 017a: newarray long
+bd 0001   # 017c: anewarray 0001
+be        # 017f: arraylength
+bf        # 0180: athrow
+c0 0002   # 0181: checkcast 0002
+c1 0002   # 0184: instanceof 0002
+c2        # 0187: monitorenter
+c3        # 0188: monitorexit
+c415 0123 # 0189: wide iload 0123
+c416 0124 # 018d: wide lload 0124
+c417 0125 # 0191: wide fload 0125
+c418 0126 # 0195: wide dload 0126
+c419 0127 # 0199: wide aload 0127
+c436 20f0 # 019d: wide istore 20f0
+c437 20f1 # 01a1: wide lstore 20f1
+c438 20f2 # 01a5: wide fstore 20f2
+c439 20f3 # 01a9: wide dstore 20f3
+c43a 20f4 # 01ad: wide astore 20f4
+c4a9 ffff # 01b1: wide ret ffff
+c484 0002 1000 # 01b5: wide iinc 0002, 1000
+c5 0002 04 # 01bb: multianewarray 0002, #04
+c6 fe41   # 01bf: ifnull 0000
+c7 0000   # 01c2: ifnonnull 01c2
+c8 70000000 # 01c5: goto_w 700001c5
+c9 fffffffb # 01ca: jsr_w 000001c5
+ca        # 01cf: <unused>
+
+0000      # exception_table_length
+0000      # attributes_count
+
+0000  # attributes_count
diff --git a/dx/tests/025-class-attrib-Signature/expected.txt b/dx/tests/025-class-attrib-Signature/expected.txt
new file mode 100644
index 0000000..5ff56ed
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/expected.txt
@@ -0,0 +1,29 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Signature"}
+  0006: utf8{"LYo;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: Signature
+  length: 00000002
+  signature: utf8{"LYo;"}
+end attributes[0]
+end classfile
diff --git a/dx/tests/025-class-attrib-Signature/info.txt b/dx/tests/025-class-attrib-Signature/info.txt
new file mode 100644
index 0000000..3d0ce51
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Signature attribute, which is syntactically valid.
diff --git a/dx/tests/025-class-attrib-Signature/run b/dx/tests/025-class-attrib-Signature/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/025-class-attrib-Signature/small-class.txt b/dx/tests/025-class-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..d332e3a
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/small-class.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0007       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0009 "Signature"        # 0005: utf8["Signature"]
+01 0004 "LYo;"             # 0006: utf8["Yo"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000002  # length
+0006      # signature
\ No newline at end of file
diff --git a/dx/tests/026-field-attrib-Signature/expected.txt b/dx/tests/026-field-attrib-Signature/expected.txt
new file mode 100644
index 0000000..c2e840e
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/expected.txt
@@ -0,0 +1,38 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"Signature"}
+  0006: utf8{"a"}
+  0007: utf8{"I"}
+  0008: utf8{"LYo;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+  access_flags: public
+  name: a
+  descriptor: I
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Signature
+    length: 00000002
+    signature: utf8{"LYo;"}
+  end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/026-field-attrib-Signature/info.txt b/dx/tests/026-field-attrib-Signature/info.txt
new file mode 100644
index 0000000..5b21ccb
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a syntactically valid Signature attribute.
diff --git a/dx/tests/026-field-attrib-Signature/run b/dx/tests/026-field-attrib-Signature/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/026-field-attrib-Signature/small-class.txt b/dx/tests/026-field-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..07d56a0
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/small-class.txt
@@ -0,0 +1,40 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0009       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 0009 "Signature"        # 0005: utf8["Signature"]
+01 0001 "a"                # 0006: utf8["a"]
+01 0001 "I"                # 0007: utf8["I"]
+01 0004 "LYo;"             # 0008: utf8["Yo"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+
+0001  # fields_count
+
+# fields[0]
+0001  # access_flags
+0006  # "a"
+0007  # "I"
+0001  # attributes_count
+# attributes[0]
+0005      # name
+00000002  # length
+0008      # signature
+
+0000  # methods_count
+0000  # attributes_count
diff --git a/dx/tests/027-method-attrib-Signature/expected.txt b/dx/tests/027-method-attrib-Signature/expected.txt
new file mode 100644
index 0000000..abc97c0
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/expected.txt
@@ -0,0 +1,38 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+  0001: type{Small}
+  0002: type{java.lang.Object}
+  0003: utf8{"Small"}
+  0004: utf8{"java/lang/Object"}
+  0005: utf8{"blort"}
+  0006: utf8{"()V"}
+  0007: utf8{"Signature"}
+  0008: utf8{"LYo;"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+  access_flags: public|abstract
+  name: blort
+  descriptor: ()V
+  attributes_count: 0001
+  
+  attributes[0]:
+    name: Signature
+    length: 00000002
+    signature: utf8{"LYo;"}
+  end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/027-method-attrib-Signature/info.txt b/dx/tests/027-method-attrib-Signature/info.txt
new file mode 100644
index 0000000..ea18c03
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Signature attribute.
diff --git a/dx/tests/027-method-attrib-Signature/run b/dx/tests/027-method-attrib-Signature/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/027-method-attrib-Signature/small-class.txt b/dx/tests/027-method-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..c1cd6e3
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/small-class.txt
@@ -0,0 +1,39 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0009       # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003                    # 0001: class[Small]
+07 0004                    # 0002: class[java/lang/Object]
+01 0005 "Small"            # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort"            # 0005: utf8["blort"]
+01 0003 "()V"              # 0006: utf8["()V"]
+01 0009 "Signature"        # 0007: utf8["Signature"]
+01 0004 "LYo;"             # 0008: utf8["Yo"]
+
+0001  # access_flags
+0001  # this_class
+0002  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0001  # methods_count
+
+# methods[0]
+0401  # access_flags
+0005  # name
+0006  # descriptor
+0001  # attributes_count
+# attributes[0]
+0007      # name
+00000002  # length
+0008      # signature
+
+0000  # attributes_count
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/expected.txt b/dx/tests/028-class-attrib-EnclosingMethod/expected.txt
new file mode 100644
index 0000000..15e9524
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/expected.txt
@@ -0,0 +1,61 @@
+reading small-class-1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"EnclosingMethod"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: EnclosingMethod
+  length: 00000004
+  class: type{Small}
+  method: (none)
+end attributes[0]
+end classfile
+reading small-class-2.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+  0005: utf8{"EnclosingMethod"}
+  0006: utf8{"zorp"}
+  0007: utf8{"()V"}
+  0008: nat{zorp:()V}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+  name: EnclosingMethod
+  length: 00000004
+  class: type{Small}
+  method: nat{zorp:()V}
+end attributes[0]
+end classfile
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/info.txt b/dx/tests/028-class-attrib-EnclosingMethod/info.txt
new file mode 100644
index 0000000..9600803
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/info.txt
@@ -0,0 +1,8 @@
+This is a dump of two simple classes which are valid in structure but
+are overall invalid. That being said, the system should still have no
+trouble parsing and dumping them.
+
+The salient bit of parsing tested here is that each class has a single
+class-level EnclosingMethod attribute, which is syntactically valid. There
+are two possible variants (method may be null), and this test contains one
+of each.
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/run b/dx/tests/028-class-attrib-EnclosingMethod/run
new file mode 100644
index 0000000..f33a338
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --width=200 small-class-1.txt small-class-2.txt
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt b/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt
new file mode 100644
index 0000000..aeaf2a3
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0006       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000f "EnclosingMethod"  # 0005: utf8["EnclosingMethod"]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000004  # length
+0003      # class
+0000      # method
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt b/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt
new file mode 100644
index 0000000..8e6148e
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt
@@ -0,0 +1,35 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe  # magic
+0000       # minor_version
+002e       # major_version
+0009       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+01 000f "EnclosingMethod"  # 0005: utf8["EnclosingMethod"]
+01 0004 "zorp"             # 0006: utf8["zorp"]
+01 0003 "()V"              # 0007: utf8["()V"]
+0c 0006 0007               # 0008: nat[zorp:()V]
+
+0021  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0001  # attributes_count
+
+# attribute[0]
+0005      # name
+00000004  # length
+0003      # class
+0008      # method
diff --git a/dx/tests/029-unit-Bits/expected.txt b/dx/tests/029-unit-Bits/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/029-unit-Bits/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/029-unit-Bits/info.txt b/dx/tests/029-unit-Bits/info.txt
new file mode 100644
index 0000000..fa56715
--- /dev/null
+++ b/dx/tests/029-unit-Bits/info.txt
@@ -0,0 +1 @@
+Unit test for com.android.dx.util.Bits.
diff --git a/dx/tests/029-unit-Bits/run b/dx/tests/029-unit-Bits/run
new file mode 100644
index 0000000..9937ce5
--- /dev/null
+++ b/dx/tests/029-unit-Bits/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+dx --junit com.android.dx.util._tests._Bits > unit-out.txt
+
+if [ "$?" = "0" ]; then
+    echo "Yay!"
+else
+    cat unit-out.txt
+fi
diff --git a/dx/tests/030-minimal-jasmin/blort.j b/dx/tests/030-minimal-jasmin/blort.j
new file mode 100644
index 0000000..45722bc
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/blort.j
@@ -0,0 +1,28 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    .limit locals 2
+    .limit stack 3
+
+    aload_0
+    dup
+    dup
+    astore_1
+    pop2
+    return
+.end method
diff --git a/dx/tests/030-minimal-jasmin/expected.txt b/dx/tests/030-minimal-jasmin/expected.txt
new file mode 100644
index 0000000..de52b4d
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/expected.txt
@@ -0,0 +1,7 @@
+Generated: ./blort.class
+    0000: aload_0 // 00
+    0001: dup
+    0002: dup
+    0003: astore_1 // 01
+    0004: pop2
+    0005: return
diff --git a/dx/tests/030-minimal-jasmin/info.txt b/dx/tests/030-minimal-jasmin/info.txt
new file mode 100644
index 0000000..816c244
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/info.txt
@@ -0,0 +1,2 @@
+This test is just a minimal test involving assembling a jasmin source
+file and then dumping it. It doesn't test any features in particular.
diff --git a/dx/tests/030-minimal-jasmin/run b/dx/tests/030-minimal-jasmin/run
new file mode 100644
index 0000000..6a50596
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j
+dx --debug --dump --width=200 blort.class | grep '    000.:'
diff --git a/dx/tests/031-bb-dead-code/blort.j b/dx/tests/031-bb-dead-code/blort.j
new file mode 100644
index 0000000..b6854f2
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/blort.j
@@ -0,0 +1,182 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    .limit locals 1
+
+    aload_0
+    invokespecial java/lang/Object/<init>()V
+    return
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend1()V
+    return
+    aload_0
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend2()V
+    ireturn
+    aload_0
+    aload_0
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend3()V
+    aconst_null
+    athrow
+    sipush 0x1234
+.end method
+
+; make sure an exception handler for a dead range doesn't get enlivened
+.method public test_dead_exception_handler()V
+    return
+    nop
+blort:
+    nop
+    nop
+    return
+handler:
+    nop
+    return
+    .catch all from blort to handler using handler
+.end method
+
+; dead code after goto instruction
+.method public test_dead_goto()V
+    goto blort
+    nop
+blort:
+    return
+.end method
+
+; dead code after ret instruction
+.method public test_dead_ret()V
+    ifeq blort
+    ret 0
+    iconst_m1
+blort:
+    return
+.end method
+
+; dead code after tableswitch instruction
+.method public test_dead_tableswitch()V
+    tableswitch 0x10
+        blort
+        default: blort
+    nop
+    nop
+    nop
+    aload_0
+    aload_1
+    aload_2
+    aload_3
+blort:
+    return
+.end method
+
+; dead code after lookupswitch instruction
+.method public test_dead_lookupswitch()V
+    lookupswitch
+        0x10: blort
+        0x20: blort
+        default: blort
+    ldc "WHYA REYO UREA DING THIS ?"
+blort:
+    return
+.end method
+
+; dead code after ireturn instruction
+.method public test_dead_ireturn()V
+    ifeq blort
+    ireturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after lreturn instruction
+.method public test_dead_lreturn()V
+    ifeq blort
+    lreturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after freturn instruction
+.method public test_dead_freturn()V
+    ifeq blort
+    freturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after dreturn instruction
+.method public test_dead_dreturn()V
+    ifeq blort
+    dreturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after areturn instruction
+.method public test_dead_areturn()V
+    ifeq blort
+    areturn
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after return instruction
+.method public test_dead_return()V
+    ifeq blort
+    return
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after athrow instruction
+.method public test_dead_athrow()V
+    ifeq blort
+    athrow
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after wide ret instruction
+.method public test_dead_wideret()V
+    ifeq blort
+    ret 0x0100
+    iconst_1
+blort:
+    return
+.end method
+
+; dead code after goto_w instruction
+.method public test_dead_goto_w()V
+    goto_w blort
+    iconst_1
+blort:
+    return
+.end method
diff --git a/dx/tests/031-bb-dead-code/expected.txt b/dx/tests/031-bb-dead-code/expected.txt
new file mode 100644
index 0000000..1cd4959
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/expected.txt
@@ -0,0 +1,190 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+block 0000: 0000..0004
+  0000: aload_0 // 00
+  0001: invokespecial method{java.lang.Object.<init>:()V}
+  next 0004
+block 0004: 0004..0005
+  0004: return
+  returns
+
+method test_deadend1 ()V
+block 0000: 0000..0001
+  0000: return
+  returns
+dead code 0001..0002
+
+method test_deadend2 ()V
+block 0000: 0000..0001
+  0000: ireturn
+  returns
+dead code 0001..0003
+
+method test_deadend3 ()V
+block 0000: 0000..0002
+  0000: aconst_null
+  0001: athrow
+  returns
+dead code 0002..0005
+
+method test_dead_exception_handler ()V
+block 0000: 0000..0001
+  0000: return
+  returns
+dead code 0001..0007
+
+method test_dead_goto ()V
+block 0000: 0000..0003
+  0000: goto 0004
+  next 0004
+dead code 0003..0004
+block 0004: 0004..0005
+  0004: return
+  returns
+
+method test_dead_ret ()V
+block 0000: 0000..0003
+  0000: ifeq 0006
+  next 0003
+  next 0006
+block 0003: 0003..0005
+  0003: ret 00
+  returns
+dead code 0005..0006
+block 0006: 0006..0007
+  0006: return
+  returns
+
+method test_dead_tableswitch ()V
+block 0000: 0000..0014
+  0000: tableswitch
+    default: 001b
+  next 001b
+dead code 0014..001b
+block 001b: 001b..001c
+  001b: return
+  returns
+
+method test_dead_lookupswitch ()V
+block 0000: 0000..001c
+  0000: lookupswitch
+    default: 001e
+  next 001e
+dead code 001c..001e
+block 001e: 001e..001f
+  001e: return
+  returns
+
+method test_dead_ireturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: ireturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_lreturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: lreturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_freturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: freturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_dreturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: dreturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_areturn ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: areturn
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_return ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: return
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_athrow ()V
+block 0000: 0000..0003
+  0000: ifeq 0005
+  next 0003
+  next 0005
+block 0003: 0003..0004
+  0003: athrow
+  returns
+dead code 0004..0005
+block 0005: 0005..0006
+  0005: return
+  returns
+
+method test_dead_wideret ()V
+block 0000: 0000..0003
+  0000: ifeq 0008
+  next 0003
+  next 0008
+block 0003: 0003..0007
+  0003: wide ret 0100
+  returns
+dead code 0007..0008
+block 0008: 0008..0009
+  0008: return
+  returns
+
+method test_dead_goto_w ()V
+block 0000: 0000..0005
+  0000: goto_w 00000006
+  next 0006
+dead code 0005..0006
+block 0006: 0006..0007
+  0006: return
+  returns
diff --git a/dx/tests/031-bb-dead-code/info.txt b/dx/tests/031-bb-dead-code/info.txt
new file mode 100644
index 0000000..d5aa398
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/info.txt
@@ -0,0 +1,3 @@
+This test checks to see that the basic block recognizer properly omits
+dead code. There is at least one example of dead code after each instruction 
+that *doesn't* flow to the next instruction.
diff --git a/dx/tests/031-bb-dead-code/run b/dx/tests/031-bb-dead-code/run
new file mode 100644
index 0000000..4f82e15
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j
+dx --debug --dump --basic-blocks --width=200 blort.class
diff --git a/dx/tests/032-bb-live-code/blort.j b/dx/tests/032-bb-live-code/blort.j
new file mode 100644
index 0000000..05790bb
--- /dev/null
+++ b/dx/tests/032-bb-live-code/blort.j
@@ -0,0 +1,342 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    .limit locals 1
+
+    aload_0
+    invokespecial java/lang/Object/<init>()V
+    return
+.end method
+
+; Test that an exception handler for a live range is enlivened.
+.method public test_live_exception([I)V
+    nop
+    nop
+start:
+    aload_0
+    arraylength
+end1:
+    nop
+end2:
+    return
+handler1:
+    return
+handler2:
+    return
+    .catch java/lang/RuntimeException from start to end2 using handler2
+    .catch all from start to end1 using handler1
+.end method
+
+; Test that an exception handler for a live range is dead as long as
+; the covered code can't possibly throw.
+.method public test_dead_exception()V
+    nop
+    nop
+start:
+    nop
+end1:
+    nop
+end2:
+    return
+handler1:
+    return
+handler2:
+    return
+    .catch java/lang/RuntimeException from start to end2 using handler2
+    .catch all from start to end1 using handler1
+.end method
+
+; Test all the if* variants.
+.method public test_ifs()V
+    ifeq x0
+    ifne x1
+    iflt x2
+    ifge x3
+    ifgt x4
+    ifle x5
+    if_icmpeq x6
+    if_icmpne x7
+    if_icmplt x8
+    if_icmpge x9
+    if_icmpgt x10
+    if_icmple x11
+    if_acmpeq x12
+    if_acmpne x13
+    ifnull x14
+    ifnonnull x15
+    return
+x0:
+    return
+x1:
+    return
+x2:
+    return
+x3:
+    return
+x4:
+    return
+x5:
+    return
+x6:
+    return
+x7:
+    return
+x8:
+    return
+x9:
+    return
+x10:
+    return
+x11:
+    return
+x12:
+    return
+x13:
+    return
+x14:
+    return
+x15:
+    return
+.end method
+
+; Test jsr and jsr_w.
+.method public test_jsr()V
+    jsr j1
+    jsr_w j2
+    return
+j1:
+    astore_0
+    ret 0
+j2:
+    astore_0
+    ret_w 0
+.end method
+
+; Test tableswitch.
+.method public test_tableswitch()V
+    tableswitch 0x10
+        t1
+        t2
+        default: t3
+t1:
+    return
+t2:
+    return
+t3:
+    return
+.end method
+
+; Test lookupswitch.
+.method public test_lookupswitch()V
+    lookupswitch
+        0x05: s1
+        0x10: s2
+        default: s3
+s1:
+    return
+s2:
+    return
+s3:
+    return
+.end method
+
+; Test every non-branching op.
+.method public test_nonbranch()V
+    nop
+    aconst_null
+    iconst_m1
+    iconst_0
+    iconst_1
+    iconst_2
+    iconst_3
+    iconst_4
+    iconst_5
+    lconst_0
+    lconst_1
+    fconst_0
+    fconst_1
+    fconst_2
+    dconst_0
+    dconst_1
+    bipush 0x10
+    sipush 0x1000
+    ldc "x"
+    ldc_w "y"
+    ldc2_w 3.0
+    iload 5
+    lload 5
+    fload 5
+    dload 5
+    aload 5
+    iload_0
+    iload_1
+    iload_2
+    iload_3
+    lload_0
+    lload_1
+    lload_2
+    lload_3
+    fload_0
+    fload_1
+    fload_2
+    fload_3
+    dload_0
+    dload_1
+    dload_2
+    dload_3
+    aload_0
+    aload_1
+    aload_2
+    aload_3
+    iaload
+    laload
+    faload
+    daload
+    aaload
+    baload
+    caload
+    saload
+    istore 5
+    lstore 5
+    fstore 5
+    dstore 5
+    astore 5
+    istore_0
+    istore_1
+    istore_2
+    istore_3
+    lstore_0
+    lstore_1
+    lstore_2
+    lstore_3
+    fstore_0
+    fstore_1
+    fstore_2
+    fstore_3
+    dstore_0
+    dstore_1
+    dstore_2
+    dstore_3
+    astore_0
+    astore_1
+    astore_2
+    astore_3
+    iastore
+    lastore
+    fastore
+    dastore
+    aastore
+    bastore
+    castore
+    sastore
+    pop
+    pop2
+    dup
+    dup_x1
+    dup_x2
+    dup2
+    dup2_x1
+    dup2_x2
+    swap
+    iadd
+    ladd
+    fadd
+    dadd
+    isub
+    lsub
+    fsub
+    dsub
+    imul
+    lmul
+    fmul
+    dmul
+    idiv
+    ldiv
+    fdiv
+    ddiv
+    irem
+    lrem
+    frem
+    drem
+    ineg
+    lneg
+    fneg
+    dneg
+    ishl
+    lshl
+    ishr
+    lshr
+    iushr
+    lushr
+    iand
+    land
+    ior
+    lor
+    ixor
+    lxor
+    iinc 5 0x10
+    i2l
+    i2f
+    i2d
+    l2i
+    l2f
+    l2d
+    f2i
+    f2l
+    f2d
+    d2i
+    d2l
+    d2f
+    i2b
+    i2c
+    i2s
+    lcmp
+    fcmpl
+    fcmpg
+    dcmpl
+    dcmpg
+    getstatic blort/x I
+    putstatic blort/x I
+    getfield blort/x I
+    putfield blort/x I
+    invokevirtual blort/x()V
+    invokespecial blort/x()V
+    invokestatic blort/x()V
+    invokeinterface blort/x()V 1
+    new blort
+    newarray int
+    anewarray blort
+    arraylength
+    checkcast blort
+    instanceof blort
+    monitorenter
+    monitorexit
+    iload 0x100
+    lload 0x100
+    fload 0x100
+    dload 0x100
+    aload 0x100
+    istore 0x100
+    lstore 0x100
+    fstore 0x100
+    dstore 0x100
+    astore 0x100
+    iinc 0x123 0x321
+    multianewarray [[[I 2
+    return
+.end method
diff --git a/dx/tests/032-bb-live-code/expected.txt b/dx/tests/032-bb-live-code/expected.txt
new file mode 100644
index 0000000..26444af
--- /dev/null
+++ b/dx/tests/032-bb-live-code/expected.txt
@@ -0,0 +1,497 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+block 0000: 0000..0004
+  0000: aload_0 // 00
+  0001: invokespecial method{java.lang.Object.<init>:()V}
+  next 0004
+block 0004: 0004..0005
+  0004: return
+  returns
+
+method test_live_exception ([I)V
+block 0000: 0000..0002
+  0000: nop
+  0001: nop
+  next 0002
+block 0002: 0002..0004
+  0002: aload_0 // 00
+  0003: arraylength
+  next 0007
+  next 0006
+  next 0004
+  catch java.lang.RuntimeException -> 0007
+  catch <any> -> 0006
+block 0004: 0004..0005
+  0004: nop
+  next 0005
+block 0005: 0005..0006
+  0005: return
+  returns
+block 0006: 0006..0007
+  0006: return
+  returns
+block 0007: 0007..0008
+  0007: return
+  returns
+
+method test_dead_exception ()V
+block 0000: 0000..0002
+  0000: nop
+  0001: nop
+  next 0002
+block 0002: 0002..0003
+  0002: nop
+  next 0003
+block 0003: 0003..0004
+  0003: nop
+  next 0004
+block 0004: 0004..0005
+  0004: return
+  returns
+block 0005: 0005..0006
+  0005: return
+  returns
+block 0006: 0006..0007
+  0006: return
+  returns
+
+method test_ifs ()V
+block 0000: 0000..0003
+  0000: ifeq 0031
+  next 0003
+  next 0031
+block 0003: 0003..0006
+  0003: ifne 0032
+  next 0006
+  next 0032
+block 0006: 0006..0009
+  0006: iflt 0033
+  next 0009
+  next 0033
+block 0009: 0009..000c
+  0009: ifge 0034
+  next 000c
+  next 0034
+block 000c: 000c..000f
+  000c: ifgt 0035
+  next 000f
+  next 0035
+block 000f: 000f..0012
+  000f: ifle 0036
+  next 0012
+  next 0036
+block 0012: 0012..0015
+  0012: if_icmpeq 0037
+  next 0015
+  next 0037
+block 0015: 0015..0018
+  0015: if_icmpne 0038
+  next 0018
+  next 0038
+block 0018: 0018..001b
+  0018: if_icmplt 0039
+  next 001b
+  next 0039
+block 001b: 001b..001e
+  001b: if_icmpge 003a
+  next 001e
+  next 003a
+block 001e: 001e..0021
+  001e: if_icmpgt 003b
+  next 0021
+  next 003b
+block 0021: 0021..0024
+  0021: if_icmple 003c
+  next 0024
+  next 003c
+block 0024: 0024..0027
+  0024: if_acmpeq 003d
+  next 0027
+  next 003d
+block 0027: 0027..002a
+  0027: if_acmpne 003e
+  next 002a
+  next 003e
+block 002a: 002a..002d
+  002a: ifnull 003f
+  next 002d
+  next 003f
+block 002d: 002d..0030
+  002d: ifnonnull 0040
+  next 0030
+  next 0040
+block 0030: 0030..0031
+  0030: return
+  returns
+block 0031: 0031..0032
+  0031: return
+  returns
+block 0032: 0032..0033
+  0032: return
+  returns
+block 0033: 0033..0034
+  0033: return
+  returns
+block 0034: 0034..0035
+  0034: return
+  returns
+block 0035: 0035..0036
+  0035: return
+  returns
+block 0036: 0036..0037
+  0036: return
+  returns
+block 0037: 0037..0038
+  0037: return
+  returns
+block 0038: 0038..0039
+  0038: return
+  returns
+block 0039: 0039..003a
+  0039: return
+  returns
+block 003a: 003a..003b
+  003a: return
+  returns
+block 003b: 003b..003c
+  003b: return
+  returns
+block 003c: 003c..003d
+  003c: return
+  returns
+block 003d: 003d..003e
+  003d: return
+  returns
+block 003e: 003e..003f
+  003e: return
+  returns
+block 003f: 003f..0040
+  003f: return
+  returns
+block 0040: 0040..0041
+  0040: return
+  returns
+
+method test_jsr ()V
+block 0000: 0000..0003
+  0000: jsr 0009
+  next 0003
+  next 0009
+block 0003: 0003..0008
+  0003: jsr_w 0000000c
+  next 0008
+  next 000c
+block 0008: 0008..0009
+  0008: return
+  returns
+block 0009: 0009..000c
+  0009: astore_0 // 00
+  000a: ret 00
+  returns
+block 000c: 000c..0011
+  000c: astore_0 // 00
+  000d: wide ret 0000
+  returns
+
+method test_tableswitch ()V
+block 0000: 0000..0018
+  0000: tableswitch
+    +00000010: 0018
+    +00000011: 0019
+    default: 001a
+  next 0018
+  next 0019
+  next 001a
+block 0018: 0018..0019
+  0018: return
+  returns
+block 0019: 0019..001a
+  0019: return
+  returns
+block 001a: 001a..001b
+  001a: return
+  returns
+
+method test_lookupswitch ()V
+block 0000: 0000..001c
+  0000: lookupswitch
+    +00000005: 001c
+    +00000010: 001d
+    default: 001e
+  next 001c
+  next 001d
+  next 001e
+block 001c: 001c..001d
+  001c: return
+  returns
+block 001d: 001d..001e
+  001d: return
+  returns
+block 001e: 001e..001f
+  001e: return
+  returns
+
+method test_nonbranch ()V
+block 0000: 0000..0017
+  0000: nop
+  0001: aconst_null
+  0002: iconst_m1 // #-01
+  0003: iconst_0 // #+00
+  0004: iconst_1 // #+01
+  0005: iconst_2 // #+02
+  0006: iconst_3 // #+03
+  0007: iconst_4 // #+04
+  0008: iconst_5 // #+05
+  0009: lconst_0 // +00
+  000a: lconst_1 // +01
+  000b: fconst_0 // 0.0
+  000c: fconst_1 // 1.0
+  000d: fconst_2 // 2.0
+  000e: dconst_0 // 0.0
+  000f: dconst_1 // 1.0
+  0010: bipush #+10
+  0012: sipush #+1000
+  0015: ldc string{"x"}
+  next 0017
+block 0017: 0017..001a
+  0017: ldc_w string{"y"}
+  next 001a
+block 001a: 001a..003c
+  001a: ldc2_w #4008000000000000 // 3.0
+  001d: iload 05
+  001f: lload 05 // category-2
+  0021: fload 05
+  0023: dload 05 // category-2
+  0025: aload 05
+  0027: iload_0 // 00
+  0028: iload_1 // 01
+  0029: iload_2 // 02
+  002a: iload_3 // 03
+  002b: lload_0 // 00, category-2
+  002c: lload_1 // 01, category-2
+  002d: lload_2 // 02, category-2
+  002e: lload_3 // 03, category-2
+  002f: fload_0 // 00
+  0030: fload_1 // 01
+  0031: fload_2 // 02
+  0032: fload_3 // 03
+  0033: dload_0 // 00, category-2
+  0034: dload_1 // 01, category-2
+  0035: dload_2 // 02, category-2
+  0036: dload_3 // 03, category-2
+  0037: aload_0 // 00
+  0038: aload_1 // 01
+  0039: aload_2 // 02
+  003a: aload_3 // 03
+  003b: iaload
+  next 003c
+block 003c: 003c..003d
+  003c: laload
+  next 003d
+block 003d: 003d..003e
+  003d: faload
+  next 003e
+block 003e: 003e..003f
+  003e: daload
+  next 003f
+block 003f: 003f..0040
+  003f: aaload
+  next 0040
+block 0040: 0040..0041
+  0040: baload
+  next 0041
+block 0041: 0041..0042
+  0041: caload
+  next 0042
+block 0042: 0042..0043
+  0042: saload
+  next 0043
+block 0043: 0043..0062
+  0043: istore 05
+  0045: lstore 05 // category-2
+  0047: fstore 05
+  0049: dstore 05 // category-2
+  004b: astore 05
+  004d: istore_0 // 00
+  004e: istore_1 // 01
+  004f: istore_2 // 02
+  0050: istore_3 // 03
+  0051: lstore_0 // 00, category-2
+  0052: lstore_1 // 01, category-2
+  0053: lstore_2 // 02, category-2
+  0054: lstore_3 // 03, category-2
+  0055: fstore_0 // 00
+  0056: fstore_1 // 01
+  0057: fstore_2 // 02
+  0058: fstore_3 // 03
+  0059: dstore_0 // 00, category-2
+  005a: dstore_1 // 01, category-2
+  005b: dstore_2 // 02, category-2
+  005c: dstore_3 // 03, category-2
+  005d: astore_0 // 00
+  005e: astore_1 // 01
+  005f: astore_2 // 02
+  0060: astore_3 // 03
+  0061: iastore
+  next 0062
+block 0062: 0062..0063
+  0062: lastore
+  next 0063
+block 0063: 0063..0064
+  0063: fastore
+  next 0064
+block 0064: 0064..0065
+  0064: dastore
+  next 0065
+block 0065: 0065..0066
+  0065: aastore
+  next 0066
+block 0066: 0066..0067
+  0066: bastore
+  next 0067
+block 0067: 0067..0068
+  0067: castore
+  next 0068
+block 0068: 0068..0069
+  0068: sastore
+  next 0069
+block 0069: 0069..007f
+  0069: pop
+  006a: pop2
+  006b: dup
+  006c: dup_x1
+  006d: dup_x2
+  006e: dup2
+  006f: dup2_x1
+  0070: dup2_x2
+  0071: swap
+  0072: iadd
+  0073: ladd
+  0074: fadd
+  0075: dadd
+  0076: isub
+  0077: lsub
+  0078: fsub
+  0079: dsub
+  007a: imul
+  007b: lmul
+  007c: fmul
+  007d: dmul
+  007e: idiv
+  next 007f
+block 007f: 007f..0080
+  007f: ldiv
+  next 0080
+block 0080: 0080..0083
+  0080: fdiv
+  0081: ddiv
+  0082: irem
+  next 0083
+block 0083: 0083..0084
+  0083: lrem
+  next 0084
+block 0084: 0084..00b0
+  0084: frem
+  0085: drem
+  0086: ineg
+  0087: lneg
+  0088: fneg
+  0089: dneg
+  008a: ishl
+  008b: lshl
+  008c: ishr
+  008d: lshr
+  008e: iushr
+  008f: lushr
+  0090: iand
+  0091: land
+  0092: ior
+  0093: lor
+  0094: ixor
+  0095: lxor
+  0096: iinc 05, #+10
+  0099: i2l
+  009a: i2f
+  009b: i2d
+  009c: l2i
+  009d: l2f
+  009e: l2d
+  009f: f2i
+  00a0: f2l
+  00a1: f2d
+  00a2: d2i
+  00a3: d2l
+  00a4: d2f
+  00a5: i2b
+  00a6: i2c
+  00a7: i2s
+  00a8: lcmp
+  00a9: fcmpl
+  00aa: fcmpg
+  00ab: dcmpl
+  00ac: dcmpg
+  00ad: getstatic field{blort.x:I}
+  next 00b0
+block 00b0: 00b0..00b3
+  00b0: putstatic field{blort.x:I}
+  next 00b3
+block 00b3: 00b3..00b6
+  00b3: getfield field{blort.x:I}
+  next 00b6
+block 00b6: 00b6..00b9
+  00b6: putfield field{blort.x:I}
+  next 00b9
+block 00b9: 00b9..00bc
+  00b9: invokevirtual method{blort.x:()V}
+  next 00bc
+block 00bc: 00bc..00bf
+  00bc: invokespecial method{blort.x:()V}
+  next 00bf
+block 00bf: 00bf..00c2
+  00bf: invokestatic method{blort.x:()V}
+  next 00c2
+block 00c2: 00c2..00c7
+  00c2: invokeinterface ifaceMethod{blort.x:()V}, 0001
+  next 00c7
+block 00c7: 00c7..00ca
+  00c7: new type{blort}
+  next 00ca
+block 00ca: 00ca..00cc
+  00ca: newarray int
+  next 00cc
+block 00cc: 00cc..00cf
+  00cc: anewarray type{blort}
+  next 00cf
+block 00cf: 00cf..00d0
+  00cf: arraylength
+  next 00d0
+block 00d0: 00d0..00d3
+  00d0: checkcast type{blort}
+  next 00d3
+block 00d3: 00d3..00d6
+  00d3: instanceof type{blort}
+  next 00d6
+block 00d6: 00d6..00d7
+  00d6: monitorenter
+  next 00d7
+block 00d7: 00d7..00d8
+  00d7: monitorexit
+  next 00d8
+block 00d8: 00d8..010a
+  00d8: wide iload 0100
+  00dc: wide lload 0100 // category-2
+  00e0: wide fload 0100
+  00e4: wide dload 0100 // category-2
+  00e8: wide aload 0100
+  00ec: wide istore 0100
+  00f0: wide lstore 0100 // category-2
+  00f4: wide fstore 0100
+  00f8: wide dstore 0100 // category-2
+  00fc: wide astore 0100
+  0100: wide iinc 0123, #+0321
+  0106: multianewarray type{int[][][]}, 02
+  next 010a
+block 010a: 010a..010b
+  010a: return
+  returns
diff --git a/dx/tests/032-bb-live-code/info.txt b/dx/tests/032-bb-live-code/info.txt
new file mode 100644
index 0000000..f94b435
--- /dev/null
+++ b/dx/tests/032-bb-live-code/info.txt
@@ -0,0 +1,5 @@
+This test checks to see that the basic block recognizer properly
+includes as live code all code which could possibly be flowed
+to. There is at least one example of each instruction which allows
+flow to the subsequent instruction, and all forks of each conditional
+branch are checked for liveness as well.
diff --git a/dx/tests/032-bb-live-code/run b/dx/tests/032-bb-live-code/run
new file mode 100644
index 0000000..4f82e15
--- /dev/null
+++ b/dx/tests/032-bb-live-code/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j
+dx --debug --dump --basic-blocks --width=200 blort.class
diff --git a/dx/tests/033-unit-IntList/expected.txt b/dx/tests/033-unit-IntList/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/033-unit-IntList/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/033-unit-IntList/info.txt b/dx/tests/033-unit-IntList/info.txt
new file mode 100644
index 0000000..7d7e8aa
--- /dev/null
+++ b/dx/tests/033-unit-IntList/info.txt
@@ -0,0 +1 @@
+Unit test for com.android.dx.util.IntList.
diff --git a/dx/tests/033-unit-IntList/run b/dx/tests/033-unit-IntList/run
new file mode 100644
index 0000000..16ca6fb
--- /dev/null
+++ b/dx/tests/033-unit-IntList/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+dx --junit com.android.dx.util._tests._IntList > unit-out.txt
+
+if [ "$?" = "0" ]; then
+    echo "Yay!"
+else
+    cat unit-out.txt
+fi
diff --git a/dx/tests/034-dex-minimal/blort.j b/dx/tests/034-dex-minimal/blort.j
new file mode 100644
index 0000000..b4715b2
--- /dev/null
+++ b/dx/tests/034-dex-minimal/blort.j
@@ -0,0 +1,16 @@
+; 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.
+
+.class blort
+.super java/lang/Object
diff --git a/dx/tests/034-dex-minimal/expected.txt b/dx/tests/034-dex-minimal/expected.txt
new file mode 100644
index 0000000..a8f81c8
--- /dev/null
+++ b/dx/tests/034-dex-minimal/expected.txt
@@ -0,0 +1,70 @@
+000000: 6465 780a 3033|magic: "dex\n035\0"
+000006: 3500          |
+000008: be0b 70d9     |checksum
+00000c: 1d9c 3f88 730d|signature
+000012: 0ed6 caa3 77d4|
+000018: 5204 65e7 322d|
+00001e: 365a          |
+000020: 8c00 0000     |file_size:       0000008c
+000024: 7000 0000     |header_size:     00000070
+000028: 7856 3412     |endian_tag:      12345678
+00002c: 0000 0000     |link_size:       0
+000030: 0000 0000     |link_off:        0
+000034: 7000 0000     |map_off:         00000070
+000038: 0000 0000     |string_ids_size: 00000000
+00003c: 0000 0000     |string_ids_off:  00000000
+000040: 0000 0000     |type_ids_size:   00000000
+000044: 0000 0000     |type_ids_off:    00000000
+000048: 0000 0000     |proto_ids_size:  00000000
+00004c: 0000 0000     |proto_ids_off:   00000000
+000050: 0000 0000     |field_ids_size:  00000000
+000054: 0000 0000     |field_ids_off:   00000000
+000058: 0000 0000     |method_ids_size: 00000000
+00005c: 0000 0000     |method_ids_off:  00000000
+000060: 0000 0000     |class_defs_size: 00000000
+000064: 0000 0000     |class_defs_off:  00000000
+000068: 1c00 0000     |data_size:       0000001c
+00006c: 7000 0000     |data_off:        00000070
+                      |
+                      |string_ids:
+                      |
+                      |type_ids:
+                      |
+                      |proto_ids:
+                      |
+                      |field_ids:
+                      |
+                      |method_ids:
+                      |
+                      |class_defs:
+                      |
+                      |word_data:
+                      |
+                      |
+                      |string_data:
+                      |
+                      |byte_data:
+                      |
+                      |
+                      |map:
+                      |[70] map list
+000070: 0200 0000     |  size: 00000002
+                      |[74] header_item map
+000074: 0000          |  type:   0000 // TYPE_HEADER_ITEM
+000076: 0000          |  unused: 0
+000078: 0100 0000     |  size:   00000001
+00007c: 0000 0000     |  offset: 00000000
+                      |[80] map_list map
+000080: 0010          |  type:   1000 // TYPE_MAP_LIST
+000082: 0000          |  unused: 0
+000084: 0100 0000     |  size:   00000001
+000088: 7000 0000     |  offset: 00000070
+                      |
+                      |statistics:
+                      |  header: 1 item; 112 bytes total
+                      |    112 bytes/item
+                      |  map list: 1 item; 28 bytes total
+                      |    28 bytes/item
+
+processing blort.class...
+Good!
diff --git a/dx/tests/034-dex-minimal/info.txt b/dx/tests/034-dex-minimal/info.txt
new file mode 100644
index 0000000..13bb9ab
--- /dev/null
+++ b/dx/tests/034-dex-minimal/info.txt
@@ -0,0 +1,9 @@
+This is a smoke test of a couple cases of minimal dex creation:
+
+The first test tries to create the truly minimal dex file by using the
+--no-files option. The output dump of this is checked to make sure it
+is exactly correct. (There is arguably only one "right" answer.)
+
+The second test tries to convert a minimal classfile and ensures that
+the conversion runs without failure, though the contents of the
+converted file are not checked for correctness.
diff --git a/dx/tests/034-dex-minimal/run b/dx/tests/034-dex-minimal/run
new file mode 100644
index 0000000..bf90624
--- /dev/null
+++ b/dx/tests/034-dex-minimal/run
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dex --dump-to=- --no-files
+echo ""
+
+jasmin -d . blort.j >/dev/null
+dx --verbose --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/035-dex-instance-var/blort.j b/dx/tests/035-dex-instance-var/blort.j
new file mode 100644
index 0000000..1bcdf8e
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/blort.j
@@ -0,0 +1,18 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.field public x I
diff --git a/dx/tests/035-dex-instance-var/expected.txt b/dx/tests/035-dex-instance-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/035-dex-instance-var/info.txt b/dx/tests/035-dex-instance-var/info.txt
new file mode 100644
index 0000000..294c9eb
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an instance variable.
diff --git a/dx/tests/035-dex-instance-var/run b/dx/tests/035-dex-instance-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/036-dex-static-var/blort.j b/dx/tests/036-dex-static-var/blort.j
new file mode 100644
index 0000000..260f446
--- /dev/null
+++ b/dx/tests/036-dex-static-var/blort.j
@@ -0,0 +1,18 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.field public static x I
diff --git a/dx/tests/036-dex-static-var/expected.txt b/dx/tests/036-dex-static-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/036-dex-static-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/036-dex-static-var/info.txt b/dx/tests/036-dex-static-var/info.txt
new file mode 100644
index 0000000..5c562eb
--- /dev/null
+++ b/dx/tests/036-dex-static-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a static variable.
diff --git a/dx/tests/036-dex-static-var/run b/dx/tests/036-dex-static-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/036-dex-static-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/037-dex-static-final-var/blort.j b/dx/tests/037-dex-static-final-var/blort.j
new file mode 100644
index 0000000..ac0332e
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/blort.j
@@ -0,0 +1,18 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.field public static final x I = 10
diff --git a/dx/tests/037-dex-static-final-var/expected.txt b/dx/tests/037-dex-static-final-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/037-dex-static-final-var/info.txt b/dx/tests/037-dex-static-final-var/info.txt
new file mode 100644
index 0000000..03d798f
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a final static variable.
diff --git a/dx/tests/037-dex-static-final-var/run b/dx/tests/037-dex-static-final-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/038-dex-instance-method/blort.j b/dx/tests/038-dex-instance-method/blort.j
new file mode 100644
index 0000000..7ddd7b5
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/blort.j
@@ -0,0 +1,21 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public blort()V
+    nop
+    return
+.end method
diff --git a/dx/tests/038-dex-instance-method/expected.txt b/dx/tests/038-dex-instance-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/038-dex-instance-method/info.txt b/dx/tests/038-dex-instance-method/info.txt
new file mode 100644
index 0000000..6aa93c3
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an instance method.
diff --git a/dx/tests/038-dex-instance-method/run b/dx/tests/038-dex-instance-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/039-dex-static-method/blort.j b/dx/tests/039-dex-static-method/blort.j
new file mode 100644
index 0000000..5b74c57
--- /dev/null
+++ b/dx/tests/039-dex-static-method/blort.j
@@ -0,0 +1,21 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public static blort()V
+    nop
+    return
+.end method
diff --git a/dx/tests/039-dex-static-method/expected.txt b/dx/tests/039-dex-static-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/039-dex-static-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/039-dex-static-method/info.txt b/dx/tests/039-dex-static-method/info.txt
new file mode 100644
index 0000000..59c8d6e
--- /dev/null
+++ b/dx/tests/039-dex-static-method/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a static method.
diff --git a/dx/tests/039-dex-static-method/run b/dx/tests/039-dex-static-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/039-dex-static-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/040-dex-constructor/blort.j b/dx/tests/040-dex-constructor/blort.j
new file mode 100644
index 0000000..e0ae441
--- /dev/null
+++ b/dx/tests/040-dex-constructor/blort.j
@@ -0,0 +1,21 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    nop
+    return
+.end method
diff --git a/dx/tests/040-dex-constructor/expected.txt b/dx/tests/040-dex-constructor/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/040-dex-constructor/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/040-dex-constructor/info.txt b/dx/tests/040-dex-constructor/info.txt
new file mode 100644
index 0000000..65eef93
--- /dev/null
+++ b/dx/tests/040-dex-constructor/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a constructor.
diff --git a/dx/tests/040-dex-constructor/run b/dx/tests/040-dex-constructor/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/040-dex-constructor/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/041-dex-abstract-method/blort.j b/dx/tests/041-dex-abstract-method/blort.j
new file mode 100644
index 0000000..8074fae
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/blort.j
@@ -0,0 +1,19 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public abstract blort()V
+.end method
diff --git a/dx/tests/041-dex-abstract-method/expected.txt b/dx/tests/041-dex-abstract-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/041-dex-abstract-method/info.txt b/dx/tests/041-dex-abstract-method/info.txt
new file mode 100644
index 0000000..e004388
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an abstract instance method.
diff --git a/dx/tests/041-dex-abstract-method/run b/dx/tests/041-dex-abstract-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/042-dex-ignore-result/Blort.java b/dx/tests/042-dex-ignore-result/Blort.java
new file mode 100644
index 0000000..2df4e66
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/Blort.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    static public int hello() {
+        return 10;
+    }
+
+    static public void ouch() {
+        hello();
+        hello();
+    }
+}
diff --git a/dx/tests/042-dex-ignore-result/expected.txt b/dx/tests/042-dex-ignore-result/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/042-dex-ignore-result/info.txt b/dx/tests/042-dex-ignore-result/info.txt
new file mode 100644
index 0000000..6289664
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/info.txt
@@ -0,0 +1,5 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of the case of a
+method call to a method which returns a value, where that value is
+ignored.
diff --git a/dx/tests/042-dex-ignore-result/run b/dx/tests/042-dex-ignore-result/run
new file mode 100644
index 0000000..d035d09
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --output=blort.dex Blort.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/043-dex-two-classes/Blort.java b/dx/tests/043-dex-two-classes/Blort.java
new file mode 100644
index 0000000..235907d
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/Blort.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/043-dex-two-classes/Zorch.java b/dx/tests/043-dex-two-classes/Zorch.java
new file mode 100644
index 0000000..c9dc4eb
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/Zorch.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+public class Zorch
+{
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/043-dex-two-classes/expected.txt b/dx/tests/043-dex-two-classes/expected.txt
new file mode 100644
index 0000000..e88eb13
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/expected.txt
@@ -0,0 +1,3 @@
+processing Blort.class...
+processing Zorch.class...
+Good!
diff --git a/dx/tests/043-dex-two-classes/info.txt b/dx/tests/043-dex-two-classes/info.txt
new file mode 100644
index 0000000..785fb9b
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test just makes sure that
+an attempt to combine two classes into a .dex file succeeds.
diff --git a/dx/tests/043-dex-two-classes/run b/dx/tests/043-dex-two-classes/run
new file mode 100644
index 0000000..41d3a0b
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java Zorch.java
+dx --debug --verbose --dex --output=blort.dex *.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/044-dex-math-ops/Blort.java b/dx/tests/044-dex-math-ops/Blort.java
new file mode 100644
index 0000000..75095b5
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/Blort.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    private volatile int i;
+    private volatile long l;
+    private volatile float f;
+    private volatile double d;
+
+    public void blort(int i1, int i2) {
+        i = -i1;
+        i = ~i1;
+        i = i1 + i2;
+        i = i1 - i2;
+        i = i1 * i2;
+        i = i1 / i2;
+        i = i1 % i2;
+        i = i1 & i2;
+        i = i1 | i2;
+        i = i1 ^ i2;
+        i = i1 << i2;
+        i = i1 >> i2;
+        i = i1 >>> i2;
+    }
+
+    public void blort(long l1, long l2) {
+        l = -l1;
+        l = ~l1;
+        l = l1 + l2;
+        l = l1 - l2;
+        l = l1 * l2;
+        l = l1 / l2;
+        l = l1 % l2;
+        l = l1 & l2;
+        l = l1 | l2;
+        l = l1 ^ l2;
+        l = l1 << l2;
+        l = l1 >> l2;
+        l = l1 >>> l2;
+    }
+
+    public void blort(float f1, float f2) {
+        f = -f1;
+        f = f1 + f2;
+        f = f1 - f2;
+        f = f1 * f2;
+        f = f1 / f2;
+        f = f1 % f2;
+    }
+
+    public void blort(double d1, double d2) {
+        d = -d1;
+        d = d1 + d2;
+        d = d1 - d2;
+        d = d1 * d2;
+        d = d1 / d2;
+        d = d1 % d2;
+    }
+}
diff --git a/dx/tests/044-dex-math-ops/expected.txt b/dx/tests/044-dex-math-ops/expected.txt
new file mode 100644
index 0000000..f6f8b79
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/expected.txt
@@ -0,0 +1,213 @@
+Blort.blort:(DD)V:
+regs: 000f; ins: 0005; outs: 0000
+  0000: move-object v0, v10
+  0001: move-wide v1, v11
+  0002: move-wide v3, v13
+  0003: move-object v5, v0
+  0004: move-wide v6, v1
+  0005: neg-double v6, v6
+  0006: iput-wide v6, v5, Blort.d:D
+  0008: move-object v5, v0
+  0009: move-wide v6, v1
+  000a: move-wide v8, v3
+  000b: add-double/2addr v6, v8
+  000c: iput-wide v6, v5, Blort.d:D
+  000e: move-object v5, v0
+  000f: move-wide v6, v1
+  0010: move-wide v8, v3
+  0011: sub-double/2addr v6, v8
+  0012: iput-wide v6, v5, Blort.d:D
+  0014: move-object v5, v0
+  0015: move-wide v6, v1
+  0016: move-wide v8, v3
+  0017: mul-double/2addr v6, v8
+  0018: iput-wide v6, v5, Blort.d:D
+  001a: move-object v5, v0
+  001b: move-wide v6, v1
+  001c: move-wide v8, v3
+  001d: div-double/2addr v6, v8
+  001e: iput-wide v6, v5, Blort.d:D
+  0020: move-object v5, v0
+  0021: move-wide v6, v1
+  0022: move-wide v8, v3
+  0023: rem-double/2addr v6, v8
+  0024: iput-wide v6, v5, Blort.d:D
+  0026: return-void
+Blort.blort:(FF)V:
+regs: 0009; ins: 0003; outs: 0000
+  0000: move-object v0, v6
+  0001: move v1, v7
+  0002: move v2, v8
+  0003: move-object v3, v0
+  0004: move v4, v1
+  0005: neg-float v4, v4
+  0006: iput v4, v3, Blort.f:F
+  0008: move-object v3, v0
+  0009: move v4, v1
+  000a: move v5, v2
+  000b: add-float/2addr v4, v5
+  000c: iput v4, v3, Blort.f:F
+  000e: move-object v3, v0
+  000f: move v4, v1
+  0010: move v5, v2
+  0011: sub-float/2addr v4, v5
+  0012: iput v4, v3, Blort.f:F
+  0014: move-object v3, v0
+  0015: move v4, v1
+  0016: move v5, v2
+  0017: mul-float/2addr v4, v5
+  0018: iput v4, v3, Blort.f:F
+  001a: move-object v3, v0
+  001b: move v4, v1
+  001c: move v5, v2
+  001d: div-float/2addr v4, v5
+  001e: iput v4, v3, Blort.f:F
+  0020: move-object v3, v0
+  0021: move v4, v1
+  0022: move v5, v2
+  0023: rem-float/2addr v4, v5
+  0024: iput v4, v3, Blort.f:F
+  0026: return-void
+Blort.blort:(II)V:
+regs: 0009; ins: 0003; outs: 0000
+  0000: move-object v0, v6
+  0001: move v1, v7
+  0002: move v2, v8
+  0003: move-object v3, v0
+  0004: move v4, v1
+  0005: neg-int v4, v4
+  0006: iput v4, v3, Blort.i:I
+  0008: move-object v3, v0
+  0009: move v4, v1
+  000a: const/4 v5, #int -1 // #f
+  000b: xor-int/lit8 v4, v4, #int -1 // #ff
+  000d: iput v4, v3, Blort.i:I
+  000f: move-object v3, v0
+  0010: move v4, v1
+  0011: move v5, v2
+  0012: add-int/2addr v4, v5
+  0013: iput v4, v3, Blort.i:I
+  0015: move-object v3, v0
+  0016: move v4, v1
+  0017: move v5, v2
+  0018: sub-int/2addr v4, v5
+  0019: iput v4, v3, Blort.i:I
+  001b: move-object v3, v0
+  001c: move v4, v1
+  001d: move v5, v2
+  001e: mul-int/2addr v4, v5
+  001f: iput v4, v3, Blort.i:I
+  0021: move-object v3, v0
+  0022: move v4, v1
+  0023: move v5, v2
+  0024: div-int/2addr v4, v5
+  0025: iput v4, v3, Blort.i:I
+  0027: move-object v3, v0
+  0028: move v4, v1
+  0029: move v5, v2
+  002a: rem-int/2addr v4, v5
+  002b: iput v4, v3, Blort.i:I
+  002d: move-object v3, v0
+  002e: move v4, v1
+  002f: move v5, v2
+  0030: and-int/2addr v4, v5
+  0031: iput v4, v3, Blort.i:I
+  0033: move-object v3, v0
+  0034: move v4, v1
+  0035: move v5, v2
+  0036: or-int/2addr v4, v5
+  0037: iput v4, v3, Blort.i:I
+  0039: move-object v3, v0
+  003a: move v4, v1
+  003b: move v5, v2
+  003c: xor-int/2addr v4, v5
+  003d: iput v4, v3, Blort.i:I
+  003f: move-object v3, v0
+  0040: move v4, v1
+  0041: move v5, v2
+  0042: shl-int/2addr v4, v5
+  0043: iput v4, v3, Blort.i:I
+  0045: move-object v3, v0
+  0046: move v4, v1
+  0047: move v5, v2
+  0048: shr-int/2addr v4, v5
+  0049: iput v4, v3, Blort.i:I
+  004b: move-object v3, v0
+  004c: move v4, v1
+  004d: move v5, v2
+  004e: ushr-int/2addr v4, v5
+  004f: iput v4, v3, Blort.i:I
+  0051: return-void
+Blort.blort:(JJ)V:
+regs: 000f; ins: 0005; outs: 0000
+  0000: move-object v0, v10
+  0001: move-wide v1, v11
+  0002: move-wide v3, v13
+  0003: move-object v5, v0
+  0004: move-wide v6, v1
+  0005: neg-long v6, v6
+  0006: iput-wide v6, v5, Blort.l:J
+  0008: move-object v5, v0
+  0009: move-wide v6, v1
+  000a: const-wide/16 v8, #long -1 // #ffff
+  000c: xor-long/2addr v6, v8
+  000d: iput-wide v6, v5, Blort.l:J
+  000f: move-object v5, v0
+  0010: move-wide v6, v1
+  0011: move-wide v8, v3
+  0012: add-long/2addr v6, v8
+  0013: iput-wide v6, v5, Blort.l:J
+  0015: move-object v5, v0
+  0016: move-wide v6, v1
+  0017: move-wide v8, v3
+  0018: sub-long/2addr v6, v8
+  0019: iput-wide v6, v5, Blort.l:J
+  001b: move-object v5, v0
+  001c: move-wide v6, v1
+  001d: move-wide v8, v3
+  001e: mul-long/2addr v6, v8
+  001f: iput-wide v6, v5, Blort.l:J
+  0021: move-object v5, v0
+  0022: move-wide v6, v1
+  0023: move-wide v8, v3
+  0024: div-long/2addr v6, v8
+  0025: iput-wide v6, v5, Blort.l:J
+  0027: move-object v5, v0
+  0028: move-wide v6, v1
+  0029: move-wide v8, v3
+  002a: rem-long/2addr v6, v8
+  002b: iput-wide v6, v5, Blort.l:J
+  002d: move-object v5, v0
+  002e: move-wide v6, v1
+  002f: move-wide v8, v3
+  0030: and-long/2addr v6, v8
+  0031: iput-wide v6, v5, Blort.l:J
+  0033: move-object v5, v0
+  0034: move-wide v6, v1
+  0035: move-wide v8, v3
+  0036: or-long/2addr v6, v8
+  0037: iput-wide v6, v5, Blort.l:J
+  0039: move-object v5, v0
+  003a: move-wide v6, v1
+  003b: move-wide v8, v3
+  003c: xor-long/2addr v6, v8
+  003d: iput-wide v6, v5, Blort.l:J
+  003f: move-object v5, v0
+  0040: move-wide v6, v1
+  0041: move-wide v8, v3
+  0042: long-to-int v8, v8
+  0043: shl-long/2addr v6, v8
+  0044: iput-wide v6, v5, Blort.l:J
+  0046: move-object v5, v0
+  0047: move-wide v6, v1
+  0048: move-wide v8, v3
+  0049: long-to-int v8, v8
+  004a: shr-long/2addr v6, v8
+  004b: iput-wide v6, v5, Blort.l:J
+  004d: move-object v5, v0
+  004e: move-wide v6, v1
+  004f: move-wide v8, v3
+  0050: long-to-int v8, v8
+  0051: ushr-long/2addr v6, v8
+  0052: iput-wide v6, v5, Blort.l:J
+  0054: return-void
diff --git a/dx/tests/044-dex-math-ops/info.txt b/dx/tests/044-dex-math-ops/info.txt
new file mode 100644
index 0000000..733dcb6
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+simple uses of all the math ops end up getting converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/044-dex-math-ops/run b/dx/tests/044-dex-math-ops/run
new file mode 100644
index 0000000..d8d4273
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.blort *.class
diff --git a/dx/tests/045-dex-switch-ops/Blort.java b/dx/tests/045-dex-switch-ops/Blort.java
new file mode 100644
index 0000000..598bd69
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/Blort.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public int switchTest1(int x) {
+        switch (x) {
+            case 1: {
+                return 2;
+            }
+            case 2: {
+                return 3;
+            }
+            case 3: {
+                return 4;
+            }
+            case 4: {
+                return 5;
+            }
+        }
+
+        return 6;
+    }
+
+    public int switchTest2(int x) {
+        switch (x) {
+            case 1: {
+                return 2;
+            }
+            case 10: {
+                return 3;
+            }
+            case 100: {
+                return 4;
+            }
+            case 1000: {
+                return 50;
+            }
+        }
+
+        return 6;
+    }
+}
diff --git a/dx/tests/045-dex-switch-ops/expected.txt b/dx/tests/045-dex-switch-ops/expected.txt
new file mode 100644
index 0000000..46476ea
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/expected.txt
@@ -0,0 +1,53 @@
+Blort.switchTest1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: packed-switch v2, 0016 // +0013
+  0006: const/4 v2, #int 6 // #6
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 2 // #2
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 3 // #3
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: const/4 v2, #int 4 // #4
+  0010: move v0, v2
+  0011: goto 0008 // -0009
+  0012: const/4 v2, #int 5 // #5
+  0013: move v0, v2
+  0014: goto 0008 // -000c
+  0015: nop // spacer
+  0016: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 0000000c // +00000009
+          3: 0000000f // +0000000c
+          4: 00000012 // +0000000f
+Blort.switchTest2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0016 // +0013
+  0006: const/4 v2, #int 6 // #6
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 2 // #2
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 3 // #3
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: const/4 v2, #int 4 // #4
+  0010: move v0, v2
+  0011: goto 0008 // -0009
+  0012: const/16 v2, #int 50 // #0032
+  0014: move v0, v2
+  0015: goto 0008 // -000d
+  0016: sparse-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          10: 0000000c // +00000009
+          100: 0000000f // +0000000c
+          1000: 00000012 // +0000000f
diff --git a/dx/tests/045-dex-switch-ops/info.txt b/dx/tests/045-dex-switch-ops/info.txt
new file mode 100644
index 0000000..492dc0b
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+both kinds of switch op get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/045-dex-switch-ops/run b/dx/tests/045-dex-switch-ops/run
new file mode 100644
index 0000000..8d1b65e
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.switchTest'*' *.class
diff --git a/dx/tests/046-dex-exceptions/Blort.java b/dx/tests/046-dex-exceptions/Blort.java
new file mode 100644
index 0000000..a0b6c0b
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/Blort.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static int maybeThrow(int x) {
+        if (x < 10) {
+            throw new RuntimeException();
+        }
+
+        return x;
+    }
+
+    public static int exTest1(int x) {
+        try {
+            maybeThrow(x);
+            return 1;
+        } catch (RuntimeException ex) {
+            return 2;
+        }
+    }
+
+    public static int exTest2(int x) {
+        try {
+            x++;
+            x = maybeThrow(x);
+        } catch (RuntimeException ex) {
+            return 1;
+        }
+
+        // Since the code in the try block can't possibly throw, there
+        // should not be a catch in the final result.
+        try {
+            x++;
+        } catch (RuntimeException ex) {
+            return 2;
+        }
+
+        try {
+            return maybeThrow(x);
+        } catch (RuntimeException ex) {
+            return 3;
+        }
+    }
+}
diff --git a/dx/tests/046-dex-exceptions/expected.txt b/dx/tests/046-dex-exceptions/expected.txt
new file mode 100644
index 0000000..933a547
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/expected.txt
@@ -0,0 +1,48 @@
+Blort.exTest1:(I)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: invoke-static {v2}, Blort.maybeThrow:(I)I
+  0005: move-result v2
+  0006: const/4 v2, #int 1 // #1
+  0007: move v0, v2
+  0008: return v0
+  0009: move-exception v2
+  000a: move-object v1, v2
+  000b: const/4 v2, #int 2 // #2
+  000c: move v0, v2
+  000d: goto 0008 // -0005
+  catches
+    tries:
+      try 0002..0005
+      catch java.lang.RuntimeException -> 0009
+Blort.exTest2:(I)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: add-int/lit8 v0, v0, #int 1 // #01
+  0003: move v2, v0
+  0004: invoke-static {v2}, Blort.maybeThrow:(I)I
+  0007: move-result v2
+  0008: move v0, v2
+  0009: add-int/lit8 v0, v0, #int 1 // #01
+  000b: move v2, v0
+  000c: invoke-static {v2}, Blort.maybeThrow:(I)I
+  000f: move-result v2
+  0010: move v0, v2
+  0011: return v0
+  0012: move-exception v2
+  0013: move-object v1, v2
+  0014: const/4 v2, #int 1 // #1
+  0015: move v0, v2
+  0016: goto 0011 // -0005
+  0017: move-exception v2
+  0018: move-object v1, v2
+  0019: const/4 v2, #int 3 // #3
+  001a: move v0, v2
+  001b: goto 0011 // -000a
+  catches
+    tries:
+      try 0004..0007
+      catch java.lang.RuntimeException -> 0012
+      try 000c..000f
+      catch java.lang.RuntimeException -> 0017
diff --git a/dx/tests/046-dex-exceptions/info.txt b/dx/tests/046-dex-exceptions/info.txt
new file mode 100644
index 0000000..a4bc065
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple simple cases of exception handling get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/046-dex-exceptions/run b/dx/tests/046-dex-exceptions/run
new file mode 100644
index 0000000..8c821b6
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals --dump-method=Blort.exTest'*' *.class
diff --git a/dx/tests/047-dex-wide-args/Blort.java b/dx/tests/047-dex-wide-args/Blort.java
new file mode 100644
index 0000000..fa0f1d8
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static long test1(int w, long x, int y, long z) {
+        return w + x + y + z;
+    }
+
+    public static long test2(long w, int x, long y, int z) {
+        return w + x + y + z;
+    }
+}
diff --git a/dx/tests/047-dex-wide-args/expected.txt b/dx/tests/047-dex-wide-args/expected.txt
new file mode 100644
index 0000000..cc353fc
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/expected.txt
@@ -0,0 +1,34 @@
+Blort.test1:(IJIJ)J:
+regs: 0010; ins: 0006; outs: 0000
+  0000: move v0, v10
+  0001: move-wide v1, v11
+  0002: move v3, v13
+  0003: move-wide v4, v14
+  0004: move v6, v0
+  0005: int-to-long v6, v6
+  0006: move-wide v8, v1
+  0007: add-long/2addr v6, v8
+  0008: move v8, v3
+  0009: int-to-long v8, v8
+  000a: add-long/2addr v6, v8
+  000b: move-wide v8, v4
+  000c: add-long/2addr v6, v8
+  000d: move-wide v0, v6
+  000e: return-wide v0
+Blort.test2:(JIJI)J:
+regs: 0010; ins: 0006; outs: 0000
+  0000: move-wide v0, v10
+  0001: move v2, v12
+  0002: move-wide v3, v13
+  0003: move v5, v15
+  0004: move-wide v6, v0
+  0005: move v8, v2
+  0006: int-to-long v8, v8
+  0007: add-long/2addr v6, v8
+  0008: move-wide v8, v3
+  0009: add-long/2addr v6, v8
+  000a: move v8, v5
+  000b: int-to-long v8, v8
+  000c: add-long/2addr v6, v8
+  000d: move-wide v0, v6
+  000e: return-wide v0
diff --git a/dx/tests/047-dex-wide-args/info.txt b/dx/tests/047-dex-wide-args/info.txt
new file mode 100644
index 0000000..357204d
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+wide (category-2) arguments get treated reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/047-dex-wide-args/run b/dx/tests/047-dex-wide-args/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/048-dex-new-array/Blort.java b/dx/tests/048-dex-new-array/Blort.java
new file mode 100644
index 0000000..1af0cde
--- /dev/null
+++ b/dx/tests/048-dex-new-array/Blort.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static void sink(Object x) {
+        // Do nothing.
+    }
+
+    public static void test() {
+        sink(new boolean[0]);
+        sink(new byte[1]);
+        sink(new char[2]);
+        sink(new short[3]);
+        sink(new int[4]);
+        sink(new long[5]);
+        sink(new float[6]);
+        sink(new double[7]);
+        sink(new Object[0]);
+    }
+}
diff --git a/dx/tests/048-dex-new-array/expected.txt b/dx/tests/048-dex-new-array/expected.txt
new file mode 100644
index 0000000..15332ca
--- /dev/null
+++ b/dx/tests/048-dex-new-array/expected.txt
@@ -0,0 +1,29 @@
+Blort.test:()V:
+regs: 0002; ins: 0000; outs: 0001
+  0000: const/4 v1, #int 0 // #0
+  0001: new-array v0, v1, boolean[]
+  0003: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0006: const/4 v0, #int 1 // #1
+  0007: new-array v0, v0, byte[]
+  0009: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  000c: const/4 v0, #int 2 // #2
+  000d: new-array v0, v0, char[]
+  000f: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0012: const/4 v0, #int 3 // #3
+  0013: new-array v0, v0, short[]
+  0015: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0018: const/4 v0, #int 4 // #4
+  0019: new-array v0, v0, int[]
+  001b: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  001e: const/4 v0, #int 5 // #5
+  001f: new-array v0, v0, long[]
+  0021: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0024: const/4 v0, #int 6 // #6
+  0025: new-array v0, v0, float[]
+  0027: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  002a: const/4 v0, #int 7 // #7
+  002b: new-array v0, v0, double[]
+  002d: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0030: new-array v0, v1, java.lang.Object[]
+  0032: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+  0035: return-void
diff --git a/dx/tests/048-dex-new-array/info.txt b/dx/tests/048-dex-new-array/info.txt
new file mode 100644
index 0000000..a2ff173
--- /dev/null
+++ b/dx/tests/048-dex-new-array/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+simple array construction expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/048-dex-new-array/run b/dx/tests/048-dex-new-array/run
new file mode 100644
index 0000000..9927f1a
--- /dev/null
+++ b/dx/tests/048-dex-new-array/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test *.class
diff --git a/dx/tests/049-dex-instanceof/Blort.java b/dx/tests/049-dex-instanceof/Blort.java
new file mode 100644
index 0000000..ad41f30
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static boolean test(Object x) {
+        return x instanceof Blort;
+    }
+}
diff --git a/dx/tests/049-dex-instanceof/expected.txt b/dx/tests/049-dex-instanceof/expected.txt
new file mode 100644
index 0000000..77f903c
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/expected.txt
@@ -0,0 +1,7 @@
+Blort.test:(Ljava/lang/Object;)Z:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: move-object v1, v0
+  0002: instance-of v1, v1, Blort
+  0004: move v0, v1
+  0005: return v0
diff --git a/dx/tests/049-dex-instanceof/info.txt b/dx/tests/049-dex-instanceof/info.txt
new file mode 100644
index 0000000..a6c82b2
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+instanceof expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/049-dex-instanceof/run b/dx/tests/049-dex-instanceof/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/050-dex-checkcast/Blort.java b/dx/tests/050-dex-checkcast/Blort.java
new file mode 100644
index 0000000..5441eec
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static Blort test(Object x) {
+        return (Blort) x;
+    }
+}
diff --git a/dx/tests/050-dex-checkcast/expected.txt b/dx/tests/050-dex-checkcast/expected.txt
new file mode 100644
index 0000000..96f7f20
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/expected.txt
@@ -0,0 +1,7 @@
+Blort.test:(Ljava/lang/Object;)LBlort;:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: move-object v1, v0
+  0002: check-cast v1, Blort
+  0004: move-object v0, v1
+  0005: return-object v0
diff --git a/dx/tests/050-dex-checkcast/info.txt b/dx/tests/050-dex-checkcast/info.txt
new file mode 100644
index 0000000..b23daa4
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+checked cast expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/050-dex-checkcast/run b/dx/tests/050-dex-checkcast/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/051-dex-explicit-null/Blort.java b/dx/tests/051-dex-explicit-null/Blort.java
new file mode 100644
index 0000000..9045c50
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static Object test1() {
+        return null;
+    }
+
+    public static void test2(Object x) {
+        x = null;
+    }
+}
diff --git a/dx/tests/051-dex-explicit-null/expected.txt b/dx/tests/051-dex-explicit-null/expected.txt
new file mode 100644
index 0000000..18ce092
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/expected.txt
@@ -0,0 +1,10 @@
+Blort.test1:()Ljava/lang/Object;:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: return-object v0
+Blort.test2:(Ljava/lang/Object;)V:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #null // #0
+  0002: move-object v0, v1
+  0003: return-void
diff --git a/dx/tests/051-dex-explicit-null/info.txt b/dx/tests/051-dex-explicit-null/info.txt
new file mode 100644
index 0000000..8a03dc3
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+assigning a reference variable to null works, as well as explicitly
+returning a null.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/051-dex-explicit-null/run b/dx/tests/051-dex-explicit-null/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/052-dex-static-var-access/Blort.java b/dx/tests/052-dex-static-var-access/Blort.java
new file mode 100644
index 0000000..3dd0e78
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/Blort.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static boolean staticBoolean;
+    public static byte staticByte;
+    public static char staticChar;
+    public static short staticShort;
+    public static int staticInt;
+    public static long staticLong;
+    public static float staticFloat;
+    public static double staticDouble;
+    public static Object staticObject;
+
+    public static Object test1() {
+        int x = staticByte + staticChar + staticShort + staticInt +
+            (int) staticLong + (int) staticFloat + (int) staticDouble;
+
+        if (staticBoolean && (x > 0)) {;
+            return staticObject;
+        } else {
+            return null;
+        }
+    }
+
+    public static void test2(boolean b, int i, Object o) {
+        staticBoolean = b;
+        staticByte = (byte) i;
+        staticChar = (char) i;
+        staticShort = (short) i;
+        staticInt = i;
+        staticLong = i;
+        staticFloat = i;
+        staticDouble = i;
+        staticObject = o;
+    }
+}
diff --git a/dx/tests/052-dex-static-var-access/expected.txt b/dx/tests/052-dex-static-var-access/expected.txt
new file mode 100644
index 0000000..239f984
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/expected.txt
@@ -0,0 +1,59 @@
+Blort.test1:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0000
+  0000: sget-byte v1, Blort.staticByte:B
+  0002: sget-char v2, Blort.staticChar:C
+  0004: add-int/2addr v1, v2
+  0005: sget-short v2, Blort.staticShort:S
+  0007: add-int/2addr v1, v2
+  0008: sget v2, Blort.staticInt:I
+  000a: add-int/2addr v1, v2
+  000b: sget-wide v2, Blort.staticLong:J
+  000d: long-to-int v2, v2
+  000e: add-int/2addr v1, v2
+  000f: sget v2, Blort.staticFloat:F
+  0011: float-to-int v2, v2
+  0012: add-int/2addr v1, v2
+  0013: sget-wide v2, Blort.staticDouble:D
+  0015: double-to-int v2, v2
+  0016: add-int/2addr v1, v2
+  0017: move v0, v1
+  0018: sget-boolean v1, Blort.staticBoolean:Z
+  001a: if-eqz v1, 0023 // +0009
+  001c: move v1, v0
+  001d: if-lez v1, 0023 // +0006
+  001f: sget-object v1, Blort.staticObject:Ljava/lang/Object;
+  0021: move-object v0, v1
+  0022: return-object v0
+  0023: const/4 v1, #null // #0
+  0024: move-object v0, v1
+  0025: goto 0022 // -0003
+Blort.test2:(ZILjava/lang/Object;)V:
+regs: 0008; ins: 0003; outs: 0000
+  0000: move v0, v5
+  0001: move v1, v6
+  0002: move-object v2, v7
+  0003: move v3, v0
+  0004: sput-boolean v3, Blort.staticBoolean:Z
+  0006: move v3, v1
+  0007: int-to-byte v3, v3
+  0008: sput-byte v3, Blort.staticByte:B
+  000a: move v3, v1
+  000b: int-to-char v3, v3
+  000c: sput-char v3, Blort.staticChar:C
+  000e: move v3, v1
+  000f: int-to-short v3, v3
+  0010: sput-short v3, Blort.staticShort:S
+  0012: move v3, v1
+  0013: sput v3, Blort.staticInt:I
+  0015: move v3, v1
+  0016: int-to-long v3, v3
+  0017: sput-wide v3, Blort.staticLong:J
+  0019: move v3, v1
+  001a: int-to-float v3, v3
+  001b: sput v3, Blort.staticFloat:F
+  001d: move v3, v1
+  001e: int-to-double v3, v3
+  001f: sput-wide v3, Blort.staticDouble:D
+  0021: move-object v3, v2
+  0022: sput-object v3, Blort.staticObject:Ljava/lang/Object;
+  0024: return-void
diff --git a/dx/tests/052-dex-static-var-access/info.txt b/dx/tests/052-dex-static-var-access/info.txt
new file mode 100644
index 0000000..c3c3b0b
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+static variable access works.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/052-dex-static-var-access/run b/dx/tests/052-dex-static-var-access/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/053-dex-instance-var-access/Blort.java b/dx/tests/053-dex-instance-var-access/Blort.java
new file mode 100644
index 0000000..eb62d62
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/Blort.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public boolean insBoolean;
+    public byte insByte;
+    public char insChar;
+    public short insShort;
+    public int insInt;
+    public long insLong;
+    public float insFloat;
+    public double insDouble;
+    public Object insObject;
+
+    public Object test1() {
+        int x = insByte + insChar + insShort + insInt +
+            (int) insLong + (int) insFloat + (int) insDouble;
+
+        if (insBoolean && (x > 0)) {;
+            return insObject;
+        } else {
+            return null;
+        }
+    }
+
+    public void test2(boolean b, int i, Object o) {
+        insBoolean = b;
+        insByte = (byte) i;
+        insChar = (char) i;
+        insShort = (short) i;
+        insInt = i;
+        insLong = i;
+        insFloat = i;
+        insDouble = i;
+        insObject = o;
+    }
+}
diff --git a/dx/tests/053-dex-instance-var-access/expected.txt b/dx/tests/053-dex-instance-var-access/expected.txt
new file mode 100644
index 0000000..b73f617
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/expected.txt
@@ -0,0 +1,79 @@
+Blort.test1:()Ljava/lang/Object;:
+regs: 0006; ins: 0001; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v2, v0
+  0002: iget-byte v2, v2, Blort.insByte:B
+  0004: move-object v3, v0
+  0005: iget-char v3, v3, Blort.insChar:C
+  0007: add-int/2addr v2, v3
+  0008: move-object v3, v0
+  0009: iget-short v3, v3, Blort.insShort:S
+  000b: add-int/2addr v2, v3
+  000c: move-object v3, v0
+  000d: iget v3, v3, Blort.insInt:I
+  000f: add-int/2addr v2, v3
+  0010: move-object v3, v0
+  0011: iget-wide v3, v3, Blort.insLong:J
+  0013: long-to-int v3, v3
+  0014: add-int/2addr v2, v3
+  0015: move-object v3, v0
+  0016: iget v3, v3, Blort.insFloat:F
+  0018: float-to-int v3, v3
+  0019: add-int/2addr v2, v3
+  001a: move-object v3, v0
+  001b: iget-wide v3, v3, Blort.insDouble:D
+  001d: double-to-int v3, v3
+  001e: add-int/2addr v2, v3
+  001f: move v1, v2
+  0020: move-object v2, v0
+  0021: iget-boolean v2, v2, Blort.insBoolean:Z
+  0023: if-eqz v2, 002d // +000a
+  0025: move v2, v1
+  0026: if-lez v2, 002d // +0007
+  0028: move-object v2, v0
+  0029: iget-object v2, v2, Blort.insObject:Ljava/lang/Object;
+  002b: move-object v0, v2
+  002c: return-object v0
+  002d: const/4 v2, #null // #0
+  002e: move-object v0, v2
+  002f: goto 002c // -0003
+Blort.test2:(ZILjava/lang/Object;)V:
+regs: 000b; ins: 0004; outs: 0000
+  0000: move-object v0, v7
+  0001: move v1, v8
+  0002: move v2, v9
+  0003: move-object v3, v10
+  0004: move-object v4, v0
+  0005: move v5, v1
+  0006: iput-boolean v5, v4, Blort.insBoolean:Z
+  0008: move-object v4, v0
+  0009: move v5, v2
+  000a: int-to-byte v5, v5
+  000b: iput-byte v5, v4, Blort.insByte:B
+  000d: move-object v4, v0
+  000e: move v5, v2
+  000f: int-to-char v5, v5
+  0010: iput-char v5, v4, Blort.insChar:C
+  0012: move-object v4, v0
+  0013: move v5, v2
+  0014: int-to-short v5, v5
+  0015: iput-short v5, v4, Blort.insShort:S
+  0017: move-object v4, v0
+  0018: move v5, v2
+  0019: iput v5, v4, Blort.insInt:I
+  001b: move-object v4, v0
+  001c: move v5, v2
+  001d: int-to-long v5, v5
+  001e: iput-wide v5, v4, Blort.insLong:J
+  0020: move-object v4, v0
+  0021: move v5, v2
+  0022: int-to-float v5, v5
+  0023: iput v5, v4, Blort.insFloat:F
+  0025: move-object v4, v0
+  0026: move v5, v2
+  0027: int-to-double v5, v5
+  0028: iput-wide v5, v4, Blort.insDouble:D
+  002a: move-object v4, v0
+  002b: move-object v5, v3
+  002c: iput-object v5, v4, Blort.insObject:Ljava/lang/Object;
+  002e: return-void
diff --git a/dx/tests/053-dex-instance-var-access/info.txt b/dx/tests/053-dex-instance-var-access/info.txt
new file mode 100644
index 0000000..4f95797
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+instance variable access works.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/053-dex-instance-var-access/run b/dx/tests/053-dex-instance-var-access/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/054-dex-high16/Blort.java b/dx/tests/054-dex-high16/Blort.java
new file mode 100644
index 0000000..9fba972
--- /dev/null
+++ b/dx/tests/054-dex-high16/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static void sink(int i) {
+        // Do nothing.
+    }
+
+    public static void sink(long l) {
+        // Do nothing.
+    }
+
+    public static void sink(float f) {
+        // Do nothing.
+    }
+
+    public static void sink(double d) {
+        // Do nothing.
+    }
+
+    public static void testInt() {
+        sink(Integer.MIN_VALUE);
+        sink(0x40000000);
+        sink(0x20000000);
+        sink(0x10000000);
+        sink(0x00080000);
+        sink(0x00040000);
+        sink(0x00020000);
+        sink(0x00010000);
+        sink(0x56780000);
+    }
+
+    public static void testLong() {
+        sink(Long.MIN_VALUE);
+        sink(0x4000000000000000L);
+        sink(0x2000000000000000L);
+        sink(0x1000000000000000L);
+        sink(0x0008000000000000L);
+        sink(0x0004000000000000L);
+        sink(0x0002000000000000L);
+        sink(0x0001000000000000L);
+        sink(0x5678000000000000L);
+    }
+
+    public static void testFloat() {
+        sink(Float.NEGATIVE_INFINITY);
+        sink(-0.0f);
+        sink(1.0f);
+        sink(Float.POSITIVE_INFINITY);
+        sink(Float.NaN);
+    }
+
+    public static void testDouble() {
+        sink(Double.NEGATIVE_INFINITY);
+        sink(-0.0);
+        sink(1.0);
+        sink(Double.POSITIVE_INFINITY);
+        sink(Double.NaN);
+    }
+}
diff --git a/dx/tests/054-dex-high16/expected.txt b/dx/tests/054-dex-high16/expected.txt
new file mode 100644
index 0000000..b9d6cf3
--- /dev/null
+++ b/dx/tests/054-dex-high16/expected.txt
@@ -0,0 +1,68 @@
+Blort.testDouble:()V:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const-wide/high16 v0, #double -Infinity // #fff0000000000000
+  0002: invoke-static {v0, v1}, Blort.sink:(D)V
+  0005: const-wide/high16 v0, #double -0.0 // #8000000000000000
+  0007: invoke-static {v0, v1}, Blort.sink:(D)V
+  000a: const-wide/high16 v0, #double 1.0 // #3ff0000000000000
+  000c: invoke-static {v0, v1}, Blort.sink:(D)V
+  000f: const-wide/high16 v0, #double Infinity // #7ff0000000000000
+  0011: invoke-static {v0, v1}, Blort.sink:(D)V
+  0014: const-wide/high16 v0, #double NaN // #7ff8000000000000
+  0016: invoke-static {v0, v1}, Blort.sink:(D)V
+  0019: return-void
+Blort.testFloat:()V:
+regs: 0001; ins: 0000; outs: 0001
+  0000: const/high16 v0, #float -Infinity // #ff800000
+  0002: invoke-static {v0}, Blort.sink:(F)V
+  0005: const/high16 v0, #float -0.0 // #80000000
+  0007: invoke-static {v0}, Blort.sink:(F)V
+  000a: const/high16 v0, #float 1.0 // #3f800000
+  000c: invoke-static {v0}, Blort.sink:(F)V
+  000f: const/high16 v0, #float Infinity // #7f800000
+  0011: invoke-static {v0}, Blort.sink:(F)V
+  0014: const/high16 v0, #float NaN // #7fc00000
+  0016: invoke-static {v0}, Blort.sink:(F)V
+  0019: return-void
+Blort.testInt:()V:
+regs: 0001; ins: 0000; outs: 0001
+  0000: const/high16 v0, #int -2147483648 // #80000000
+  0002: invoke-static {v0}, Blort.sink:(I)V
+  0005: const/high16 v0, #int 1073741824 // #40000000
+  0007: invoke-static {v0}, Blort.sink:(I)V
+  000a: const/high16 v0, #int 536870912 // #20000000
+  000c: invoke-static {v0}, Blort.sink:(I)V
+  000f: const/high16 v0, #int 268435456 // #10000000
+  0011: invoke-static {v0}, Blort.sink:(I)V
+  0014: const/high16 v0, #int 524288 // #00080000
+  0016: invoke-static {v0}, Blort.sink:(I)V
+  0019: const/high16 v0, #int 262144 // #00040000
+  001b: invoke-static {v0}, Blort.sink:(I)V
+  001e: const/high16 v0, #int 131072 // #00020000
+  0020: invoke-static {v0}, Blort.sink:(I)V
+  0023: const/high16 v0, #int 65536 // #00010000
+  0025: invoke-static {v0}, Blort.sink:(I)V
+  0028: const/high16 v0, #int 1450704896 // #56780000
+  002a: invoke-static {v0}, Blort.sink:(I)V
+  002d: return-void
+Blort.testLong:()V:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const-wide/high16 v0, #long -9223372036854775808 // #8000000000000000
+  0002: invoke-static {v0, v1}, Blort.sink:(J)V
+  0005: const-wide/high16 v0, #long 4611686018427387904 // #4000000000000000
+  0007: invoke-static {v0, v1}, Blort.sink:(J)V
+  000a: const-wide/high16 v0, #long 2305843009213693952 // #2000000000000000
+  000c: invoke-static {v0, v1}, Blort.sink:(J)V
+  000f: const-wide/high16 v0, #long 1152921504606846976 // #1000000000000000
+  0011: invoke-static {v0, v1}, Blort.sink:(J)V
+  0014: const-wide/high16 v0, #long 2251799813685248 // #0008000000000000
+  0016: invoke-static {v0, v1}, Blort.sink:(J)V
+  0019: const-wide/high16 v0, #long 1125899906842624 // #0004000000000000
+  001b: invoke-static {v0, v1}, Blort.sink:(J)V
+  001e: const-wide/high16 v0, #long 562949953421312 // #0002000000000000
+  0020: invoke-static {v0, v1}, Blort.sink:(J)V
+  0023: const-wide/high16 v0, #long 281474976710656 // #0001000000000000
+  0025: invoke-static {v0, v1}, Blort.sink:(J)V
+  0028: const-wide/high16 v0, #long 6230730084467081216 // #5678000000000000
+  002a: invoke-static {v0, v1}, Blort.sink:(J)V
+  002d: return-void
diff --git a/dx/tests/054-dex-high16/info.txt b/dx/tests/054-dex-high16/info.txt
new file mode 100644
index 0000000..ef1fac4
--- /dev/null
+++ b/dx/tests/054-dex-high16/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+"high16" constants get converted properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/054-dex-high16/run b/dx/tests/054-dex-high16/run
new file mode 100644
index 0000000..a2c7458
--- /dev/null
+++ b/dx/tests/054-dex-high16/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/055-dex-explicit-throw/Blort.java b/dx/tests/055-dex-explicit-throw/Blort.java
new file mode 100644
index 0000000..e6720d9
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    private static RuntimeException theException = new RuntimeException();
+
+    public static void test1() {
+        throw theException;
+    }
+
+    public static int test2() {
+        try {
+            throw theException;
+        } catch (RuntimeException ex) {
+            return 1;
+        }
+    }
+}
diff --git a/dx/tests/055-dex-explicit-throw/expected.txt b/dx/tests/055-dex-explicit-throw/expected.txt
new file mode 100644
index 0000000..b7e4c33
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/expected.txt
@@ -0,0 +1,17 @@
+Blort.test1:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: sget-object v0, Blort.theException:Ljava/lang/RuntimeException;
+  0002: throw v0
+Blort.test2:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: sget-object v1, Blort.theException:Ljava/lang/RuntimeException;
+  0002: throw v1
+  0003: move-exception v1
+  0004: move-object v0, v1
+  0005: const/4 v1, #int 1 // #1
+  0006: move v0, v1
+  0007: return v0
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 0003
diff --git a/dx/tests/055-dex-explicit-throw/info.txt b/dx/tests/055-dex-explicit-throw/info.txt
new file mode 100644
index 0000000..b3b7808
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+explicit use of "throw" gets converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/055-dex-explicit-throw/run b/dx/tests/055-dex-explicit-throw/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/056-dex-call-interface/Blort.java b/dx/tests/056-dex-call-interface/Blort.java
new file mode 100644
index 0000000..4175f05
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static int test(Zorch z, double d) {
+        z.zorch1();
+        z.zorch2(d);
+        int x = z.zorch3(z);
+        int y = (int) z.zorch4();
+        return x + y;
+    }
+}
diff --git a/dx/tests/056-dex-call-interface/Zorch.java b/dx/tests/056-dex-call-interface/Zorch.java
new file mode 100644
index 0000000..03e3762
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/Zorch.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+public interface Zorch
+{
+    public void zorch1();
+    public void zorch2(double d);
+    public int zorch3(Object o);
+    public long zorch4();
+}
diff --git a/dx/tests/056-dex-call-interface/expected.txt b/dx/tests/056-dex-call-interface/expected.txt
new file mode 100644
index 0000000..faf18c4
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/expected.txt
@@ -0,0 +1,24 @@
+Blort.test:(LZorch;D)I:
+regs: 000b; ins: 0003; outs: 0003
+  0000: move-object v0, v8
+  0001: move-wide v1, v9
+  0002: move-object v5, v0
+  0003: invoke-interface {v5}, Zorch.zorch1:()V
+  0006: move-object v5, v0
+  0007: move-wide v6, v1
+  0008: invoke-interface {v5, v6, v7}, Zorch.zorch2:(D)V
+  000b: move-object v5, v0
+  000c: move-object v6, v0
+  000d: invoke-interface {v5, v6}, Zorch.zorch3:(Ljava/lang/Object;)I
+  0010: move-result v5
+  0011: move v3, v5
+  0012: move-object v5, v0
+  0013: invoke-interface {v5}, Zorch.zorch4:()J
+  0016: move-result-wide v5
+  0017: long-to-int v5, v5
+  0018: move v4, v5
+  0019: move v5, v3
+  001a: move v6, v4
+  001b: add-int/2addr v5, v6
+  001c: move v0, v5
+  001d: return v0
diff --git a/dx/tests/056-dex-call-interface/info.txt b/dx/tests/056-dex-call-interface/info.txt
new file mode 100644
index 0000000..2f964a2
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of interface method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/056-dex-call-interface/run b/dx/tests/056-dex-call-interface/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class
diff --git a/dx/tests/057-dex-call-virtual/Blort.java b/dx/tests/057-dex-call-virtual/Blort.java
new file mode 100644
index 0000000..75ee7d8
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/Blort.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static int test(Zorch z) {
+        z.zorch1();
+        return z.zorch2(5);
+    }
+}
diff --git a/dx/tests/057-dex-call-virtual/Zorch.java b/dx/tests/057-dex-call-virtual/Zorch.java
new file mode 100644
index 0000000..867eaed
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/Zorch.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+public class Zorch
+{
+    public void zorch1() {
+        // This space intentionally left blank.
+    }
+
+    public int zorch2(int x) {
+        return 0;
+    }
+}
diff --git a/dx/tests/057-dex-call-virtual/expected.txt b/dx/tests/057-dex-call-virtual/expected.txt
new file mode 100644
index 0000000..ea50d35
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/expected.txt
@@ -0,0 +1,11 @@
+Blort.test:(LZorch;)I:
+regs: 0004; ins: 0001; outs: 0002
+  0000: move-object v0, v3
+  0001: move-object v1, v0
+  0002: invoke-virtual {v1}, Zorch.zorch1:()V
+  0005: move-object v1, v0
+  0006: const/4 v2, #int 5 // #5
+  0007: invoke-virtual {v1, v2}, Zorch.zorch2:(I)I
+  000a: move-result v1
+  000b: move v0, v1
+  000c: return v0
diff --git a/dx/tests/057-dex-call-virtual/info.txt b/dx/tests/057-dex-call-virtual/info.txt
new file mode 100644
index 0000000..452c9cb
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of regular virtual method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/057-dex-call-virtual/run b/dx/tests/057-dex-call-virtual/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class
diff --git a/dx/tests/058-dex-call-direct/Blort.java b/dx/tests/058-dex-call-direct/Blort.java
new file mode 100644
index 0000000..6d7fa7f
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/Blort.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static int test(Blort b) {
+        b.zorch1();
+        return b.zorch2(5);
+    }
+
+    private void zorch1() {
+        // This space intentionally left blank.
+    }
+
+    private int zorch2(int x) {
+        return 1;
+    }
+}
diff --git a/dx/tests/058-dex-call-direct/expected.txt b/dx/tests/058-dex-call-direct/expected.txt
new file mode 100644
index 0000000..11820a3
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/expected.txt
@@ -0,0 +1,11 @@
+Blort.test:(LBlort;)I:
+regs: 0004; ins: 0001; outs: 0002
+  0000: move-object v0, v3
+  0001: move-object v1, v0
+  0002: invoke-direct {v1}, Blort.zorch1:()V
+  0005: move-object v1, v0
+  0006: const/4 v2, #int 5 // #5
+  0007: invoke-direct {v1, v2}, Blort.zorch2:(I)I
+  000a: move-result v1
+  000b: move v0, v1
+  000c: return v0
diff --git a/dx/tests/058-dex-call-direct/info.txt b/dx/tests/058-dex-call-direct/info.txt
new file mode 100644
index 0000000..a22c479
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of direct instance method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/058-dex-call-direct/run b/dx/tests/058-dex-call-direct/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class
diff --git a/dx/tests/059-dex-call-super/Blort.java b/dx/tests/059-dex-call-super/Blort.java
new file mode 100644
index 0000000..efb451e
--- /dev/null
+++ b/dx/tests/059-dex-call-super/Blort.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+public class Blort
+    extends Zorch
+{
+    public int test1() {
+        super.zorch1();
+        return super.zorch2(5);
+    }
+
+    public void test2() {
+        super.test2();
+    }
+}
diff --git a/dx/tests/059-dex-call-super/Zorch.java b/dx/tests/059-dex-call-super/Zorch.java
new file mode 100644
index 0000000..aa668ab
--- /dev/null
+++ b/dx/tests/059-dex-call-super/Zorch.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+public class Zorch
+{
+    public void zorch1() {
+        // This space intentionally left blank.
+    }
+
+    public int zorch2(int x) {
+        return 0;
+    }
+
+    public void test2() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/059-dex-call-super/expected.txt b/dx/tests/059-dex-call-super/expected.txt
new file mode 100644
index 0000000..8bb3bde
--- /dev/null
+++ b/dx/tests/059-dex-call-super/expected.txt
@@ -0,0 +1,17 @@
+Blort.test1:()I:
+regs: 0004; ins: 0001; outs: 0002
+  0000: move-object v0, v3
+  0001: move-object v1, v0
+  0002: invoke-super {v1}, Zorch.zorch1:()V
+  0005: move-object v1, v0
+  0006: const/4 v2, #int 5 // #5
+  0007: invoke-super {v1, v2}, Zorch.zorch2:(I)I
+  000a: move-result v1
+  000b: move v0, v1
+  000c: return v0
+Blort.test2:()V:
+regs: 0003; ins: 0001; outs: 0001
+  0000: move-object v0, v2
+  0001: move-object v1, v0
+  0002: invoke-super {v1}, Zorch.test2:()V
+  0005: return-void
diff --git a/dx/tests/059-dex-call-super/info.txt b/dx/tests/059-dex-call-super/info.txt
new file mode 100644
index 0000000..ff88814
--- /dev/null
+++ b/dx/tests/059-dex-call-super/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of superclass virtual method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/059-dex-call-super/run b/dx/tests/059-dex-call-super/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/059-dex-call-super/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/060-dex-call-static/Blort.java b/dx/tests/060-dex-call-static/Blort.java
new file mode 100644
index 0000000..8ad2e27
--- /dev/null
+++ b/dx/tests/060-dex-call-static/Blort.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static int test() {
+        Zorch.zorch1();
+        return Zorch.zorch2(5);
+    }
+}
diff --git a/dx/tests/060-dex-call-static/Zorch.java b/dx/tests/060-dex-call-static/Zorch.java
new file mode 100644
index 0000000..8ccc448
--- /dev/null
+++ b/dx/tests/060-dex-call-static/Zorch.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+public class Zorch
+{
+    public static void zorch1() {
+        // This space intentionally left blank.
+    }
+
+    public static int zorch2(int x) {
+        return 1;
+    }
+}
diff --git a/dx/tests/060-dex-call-static/expected.txt b/dx/tests/060-dex-call-static/expected.txt
new file mode 100644
index 0000000..9432989
--- /dev/null
+++ b/dx/tests/060-dex-call-static/expected.txt
@@ -0,0 +1,7 @@
+Blort.test:()I:
+regs: 0001; ins: 0000; outs: 0001
+  0000: invoke-static {}, Zorch.zorch1:()V
+  0003: const/4 v0, #int 5 // #5
+  0004: invoke-static {v0}, Zorch.zorch2:(I)I
+  0007: move-result v0
+  0008: return v0
diff --git a/dx/tests/060-dex-call-static/info.txt b/dx/tests/060-dex-call-static/info.txt
new file mode 100644
index 0000000..1253355
--- /dev/null
+++ b/dx/tests/060-dex-call-static/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of static method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/060-dex-call-static/run b/dx/tests/060-dex-call-static/run
new file mode 100644
index 0000000..346856c
--- /dev/null
+++ b/dx/tests/060-dex-call-static/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --positions=none --no-locals \
+    --dump-method=Blort.test Blort.class
diff --git a/dx/tests/061-dex-try-catch/Blort.java b/dx/tests/061-dex-try-catch/Blort.java
new file mode 100644
index 0000000..903f40e
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/Blort.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static void caught() {
+        // This space intentionally left blank.
+    }
+
+    public static void zorch(int x) {
+        // This space intentionally left blank.
+    }
+
+    public static void test1(int x) {
+        // In this test, the code being try-caught can't possibly throw.
+        try {
+            x = 0;
+        } catch (RuntimeException ex) {
+            caught();
+        }
+    }
+
+    public static void test2(String[] sa) {
+        // In this test, the code being try-caught doesn't contain any
+        // constant pool references.
+        try {
+            int x = sa.length;
+        } catch (RuntimeException ex) {
+            caught();
+        }
+    }
+
+    public static void test3() {
+        // In this test, the code being try-caught contains a constant
+        // pool reference.
+        try {
+            zorch(1);
+        } catch (RuntimeException ex) {
+            caught();
+        }
+    }
+
+    public static void test4(String[] sa) {
+        // In this test, the code being try-caught contains one
+        // throwing instruction that has a constant pool reference and
+        // one that doesn't.
+        try {
+            zorch(sa.length);
+        } catch (RuntimeException ex) {
+            caught();
+        }
+    }
+}
diff --git a/dx/tests/061-dex-try-catch/expected.txt b/dx/tests/061-dex-try-catch/expected.txt
new file mode 100644
index 0000000..d66f404
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/expected.txt
@@ -0,0 +1,49 @@
+Blort.test1:(I)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: const/4 v2, #int 0 // #0
+  0002: move v0, v2
+  0003: return-void
+Blort.test2:([Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: array-length v2, v2
+  0003: move v1, v2
+  0004: return-void
+  0005: move-exception v2
+  0006: move-object v1, v2
+  0007: invoke-static {}, Blort.caught:()V
+  000a: goto 0004 // -0006
+  catches
+    tries:
+      try 0002..0003
+      catch java.lang.RuntimeException -> 0005
+Blort.test3:()V:
+regs: 0002; ins: 0000; outs: 0001
+  0000: const/4 v1, #int 1 // #1
+  0001: invoke-static {v1}, Blort.zorch:(I)V
+  0004: return-void
+  0005: move-exception v1
+  0006: move-object v0, v1
+  0007: invoke-static {}, Blort.caught:()V
+  000a: goto 0004 // -0006
+  catches
+    tries:
+      try 0001..0004
+      catch java.lang.RuntimeException -> 0005
+Blort.test4:([Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: array-length v2, v2
+  0003: invoke-static {v2}, Blort.zorch:(I)V
+  0006: return-void
+  0007: move-exception v2
+  0008: move-object v1, v2
+  0009: invoke-static {}, Blort.caught:()V
+  000c: goto 0006 // -0006
+  catches
+    tries:
+      try 0002..0006
+      catch java.lang.RuntimeException -> 0007
diff --git a/dx/tests/061-dex-try-catch/info.txt b/dx/tests/061-dex-try-catch/info.txt
new file mode 100644
index 0000000..409b251
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of try-catch work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/061-dex-try-catch/run b/dx/tests/061-dex-try-catch/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/062-dex-synch-method/Blort.java b/dx/tests/062-dex-synch-method/Blort.java
new file mode 100644
index 0000000..4aca417
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public synchronized void testInstance1() {
+        // This space intentionally left blank.
+    }
+
+    public synchronized void testInstance2(Object x) {
+        x.hashCode();
+    }
+
+    public synchronized int testInstance3(int x, int y, int z) {
+        if (x == 1) {
+            return 1;
+        } else {
+            return 2;
+        }
+    }
+
+    public synchronized long testInstance4(long x) {
+        if (x == 1) {
+            return 1;
+        } else {
+            return 2;
+        }
+    }
+
+    public synchronized void testInstance5() {
+        testInstance5();
+    }
+
+    public static synchronized void testStatic1() {
+        // This space intentionally left blank.
+    }
+
+    public static synchronized void testStatic2(Object x) {
+        x.hashCode();
+    }
+
+    public static synchronized int testStatic3(int x, int y, int z) {
+        if (x == 1) {
+            return 1;
+        } else {
+            return 2;
+        }
+    }
+
+    public static synchronized long testStatic4(long x) {
+        if (x == 1) {
+            return 1;
+        } else {
+            return 2;
+        }
+    }
+
+    public static synchronized void testStatic5() {
+        testStatic5();
+    }
+}
diff --git a/dx/tests/062-dex-synch-method/expected.txt b/dx/tests/062-dex-synch-method/expected.txt
new file mode 100644
index 0000000..5585038
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/expected.txt
@@ -0,0 +1,146 @@
+Blort.testInstance1:()V:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: move-object v1, v2
+  0002: monitor-enter v1
+  0003: monitor-exit v1
+  0004: return-void
+Blort.testInstance2:(Ljava/lang/Object;)V:
+regs: 0006; ins: 0002; outs: 0001
+  0000: move-object v0, v4
+  0001: move-object v1, v5
+  0002: move-object v3, v4
+  0003: monitor-enter v3
+  0004: move-object v2, v1
+  0005: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  0008: move-result v2
+  0009: monitor-exit v3
+  000a: return-void
+  000b: move-exception v0
+  000c: monitor-exit v3
+  000d: throw v0
+  catches
+    tries:
+      try 0005..0008
+      catch <any> -> 000b
+Blort.testInstance3:(III)I:
+regs: 000b; ins: 0004; outs: 0000
+  0000: move-object v0, v7
+  0001: move v1, v8
+  0002: move v2, v9
+  0003: move v3, v10
+  0004: move-object v6, v7
+  0005: monitor-enter v6
+  0006: move v4, v1
+  0007: const/4 v5, #int 1 // #1
+  0008: if-ne v4, v5, 000e // +0006
+  000a: const/4 v4, #int 1 // #1
+  000b: move v0, v4
+  000c: monitor-exit v6
+  000d: return v0
+  000e: const/4 v4, #int 2 // #2
+  000f: move v0, v4
+  0010: goto 000c // -0004
+Blort.testInstance4:(J)J:
+regs: 000b; ins: 0003; outs: 0000
+  0000: move-object v0, v8
+  0001: move-wide v1, v9
+  0002: move-object v7, v8
+  0003: monitor-enter v7
+  0004: move-wide v3, v1
+  0005: const-wide/16 v5, #long 1 // #0001
+  0007: cmp-long v3, v3, v5
+  0009: if-nez v3, 0010 // +0007
+  000b: const-wide/16 v3, #long 1 // #0001
+  000d: move-wide v0, v3
+  000e: monitor-exit v7
+  000f: return-wide v0
+  0010: const-wide/16 v3, #long 2 // #0002
+  0012: move-wide v0, v3
+  0013: goto 000e // -0005
+Blort.testInstance5:()V:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move-object v0, v3
+  0001: move-object v2, v3
+  0002: monitor-enter v2
+  0003: move-object v1, v0
+  0004: invoke-virtual {v1}, Blort.testInstance5:()V
+  0007: monitor-exit v2
+  0008: return-void
+  0009: move-exception v0
+  000a: monitor-exit v2
+  000b: throw v0
+  catches
+    tries:
+      try 0004..0007
+      catch <any> -> 0009
+Blort.testStatic1:()V:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const-class v1, Blort
+  0002: monitor-enter v1
+  0003: monitor-exit v1
+  0004: return-void
+Blort.testStatic2:(Ljava/lang/Object;)V:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move-object v0, v3
+  0001: const-class v2, Blort
+  0003: monitor-enter v2
+  0004: move-object v1, v0
+  0005: invoke-virtual {v1}, java.lang.Object.hashCode:()I
+  0008: move-result v1
+  0009: monitor-exit v2
+  000a: return-void
+  000b: move-exception v0
+  000c: monitor-exit v2
+  000d: throw v0
+  catches
+    tries:
+      try 0005..0008
+      catch <any> -> 000b
+Blort.testStatic3:(III)I:
+regs: 0009; ins: 0003; outs: 0000
+  0000: move v0, v6
+  0001: move v1, v7
+  0002: move v2, v8
+  0003: const-class v5, Blort
+  0005: monitor-enter v5
+  0006: move v3, v0
+  0007: const/4 v4, #int 1 // #1
+  0008: if-ne v3, v4, 000e // +0006
+  000a: const/4 v3, #int 1 // #1
+  000b: move v0, v3
+  000c: monitor-exit v5
+  000d: return v0
+  000e: const/4 v3, #int 2 // #2
+  000f: move v0, v3
+  0010: goto 000c // -0004
+Blort.testStatic4:(J)J:
+regs: 0009; ins: 0002; outs: 0000
+  0000: move-wide v0, v7
+  0001: const-class v6, Blort
+  0003: monitor-enter v6
+  0004: move-wide v2, v0
+  0005: const-wide/16 v4, #long 1 // #0001
+  0007: cmp-long v2, v2, v4
+  0009: if-nez v2, 0010 // +0007
+  000b: const-wide/16 v2, #long 1 // #0001
+  000d: move-wide v0, v2
+  000e: monitor-exit v6
+  000f: return-wide v0
+  0010: const-wide/16 v2, #long 2 // #0002
+  0012: move-wide v0, v2
+  0013: goto 000e // -0005
+Blort.testStatic5:()V:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const-class v1, Blort
+  0002: monitor-enter v1
+  0003: invoke-static {}, Blort.testStatic5:()V
+  0006: monitor-exit v1
+  0007: return-void
+  0008: move-exception v0
+  0009: monitor-exit v1
+  000a: throw v0
+  catches
+    tries:
+      try 0003..0006
+      catch <any> -> 0008
diff --git a/dx/tests/062-dex-synch-method/info.txt b/dx/tests/062-dex-synch-method/info.txt
new file mode 100644
index 0000000..0e82727
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of synchronized methods get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/062-dex-synch-method/run b/dx/tests/062-dex-synch-method/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/063-dex-empty-switch/Blort.java b/dx/tests/063-dex-empty-switch/Blort.java
new file mode 100644
index 0000000..f538995
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public int test1(int x) {
+        switch (x) {
+            default: return 1;
+        }
+    }
+
+    public int test2(int x) {
+        switch (x) {
+            default: x = 1;
+        }
+
+        return x;
+    }
+}
diff --git a/dx/tests/063-dex-empty-switch/expected.txt b/dx/tests/063-dex-empty-switch/expected.txt
new file mode 100644
index 0000000..e4d1a46
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/expected.txt
@@ -0,0 +1,18 @@
+Blort.test1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: const/4 v2, #int 1 // #1
+  0004: move v0, v2
+  0005: return v0
+Blort.test2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: const/4 v2, #int 1 // #1
+  0004: move v1, v2
+  0005: move v2, v1
+  0006: move v0, v2
+  0007: return v0
diff --git a/dx/tests/063-dex-empty-switch/info.txt b/dx/tests/063-dex-empty-switch/info.txt
new file mode 100644
index 0000000..38f212b
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of empty (that is, default-only) switch statements
+get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/063-dex-empty-switch/run b/dx/tests/063-dex-empty-switch/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/064-dex-array-access/Blort.java b/dx/tests/064-dex-array-access/Blort.java
new file mode 100644
index 0000000..fa03ec0
--- /dev/null
+++ b/dx/tests/064-dex-array-access/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public boolean test01(boolean[] x) {
+        x[0] = true;
+        return x[1];
+    }
+
+    public byte test02(byte[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public short test03(short[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public char test04(char[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public int test05(int[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public long test06(long[] x) {
+        x[0] = 5;
+        return x[1];
+    }
+
+    public float test07(float[] x) {
+        x[0] = 2.0f;
+        return x[1];
+    }
+
+    public double test08(double[] x) {
+        x[0] = 2.0;
+        return x[1];
+    }
+
+    public Object test09(Object[] x) {
+        x[0] = null;
+        return x[1];
+    }
+
+    public static Object test10(Object[][] x) {
+        x[0][0] = null;
+        return x[1][2];
+    }
+
+    public static int test11(Object x) {
+        int[][][] arr = (int[][][]) x;
+        arr[0][0][0] = 123;
+        return arr[1][2][3];
+    }
+}
diff --git a/dx/tests/064-dex-array-access/expected.txt b/dx/tests/064-dex-array-access/expected.txt
new file mode 100644
index 0000000..ae251e7
--- /dev/null
+++ b/dx/tests/064-dex-array-access/expected.txt
@@ -0,0 +1,157 @@
+Blort.test01:([Z)Z:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 1 // #1
+  0005: aput-boolean v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-boolean v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test02:([B)B:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 5 // #5
+  0005: aput-byte v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-byte v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test03:([S)S:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 5 // #5
+  0005: aput-short v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-short v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test04:([C)C:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 5 // #5
+  0005: aput-char v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-char v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test05:([I)I:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #int 5 // #5
+  0005: aput v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget v2, v2, v3
+  000b: move v0, v2
+  000c: return v0
+Blort.test06:([J)J:
+regs: 0008; ins: 0002; outs: 0000
+  0000: move-object v0, v6
+  0001: move-object v1, v7
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const-wide/16 v4, #long 5 // #0005
+  0006: aput-wide v4, v2, v3
+  0008: move-object v2, v1
+  0009: const/4 v3, #int 1 // #1
+  000a: aget-wide v2, v2, v3
+  000c: move-wide v0, v2
+  000d: return-wide v0
+Blort.test07:([F)F:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/high16 v4, #float 2.0 // #40000000
+  0006: aput v4, v2, v3
+  0008: move-object v2, v1
+  0009: const/4 v3, #int 1 // #1
+  000a: aget v2, v2, v3
+  000c: move v0, v2
+  000d: return v0
+Blort.test08:([D)D:
+regs: 0008; ins: 0002; outs: 0000
+  0000: move-object v0, v6
+  0001: move-object v1, v7
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const-wide/high16 v4, #double 2.0 // #4000000000000000
+  0006: aput-wide v4, v2, v3
+  0008: move-object v2, v1
+  0009: const/4 v3, #int 1 // #1
+  000a: aget-wide v2, v2, v3
+  000c: move-wide v0, v2
+  000d: return-wide v0
+Blort.test09:([Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v1, v6
+  0002: move-object v2, v1
+  0003: const/4 v3, #int 0 // #0
+  0004: const/4 v4, #null // #0
+  0005: aput-object v4, v2, v3
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 1 // #1
+  0009: aget-object v2, v2, v3
+  000b: move-object v0, v2
+  000c: return-object v0
+Blort.test10:([[Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0005; ins: 0001; outs: 0000
+  0000: move-object v0, v4
+  0001: move-object v1, v0
+  0002: const/4 v2, #int 0 // #0
+  0003: aget-object v1, v1, v2
+  0005: const/4 v2, #int 0 // #0
+  0006: const/4 v3, #null // #0
+  0007: aput-object v3, v1, v2
+  0009: move-object v1, v0
+  000a: const/4 v2, #int 1 // #1
+  000b: aget-object v1, v1, v2
+  000d: const/4 v2, #int 2 // #2
+  000e: aget-object v1, v1, v2
+  0010: move-object v0, v1
+  0011: return-object v0
+Blort.test11:(Ljava/lang/Object;)I:
+regs: 0006; ins: 0001; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v2, v0
+  0002: check-cast v2, int[][][]
+  0004: check-cast v2, int[][][]
+  0006: move-object v1, v2
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 0 // #0
+  0009: aget-object v2, v2, v3
+  000b: const/4 v3, #int 0 // #0
+  000c: aget-object v2, v2, v3
+  000e: const/4 v3, #int 0 // #0
+  000f: const/16 v4, #int 123 // #007b
+  0011: aput v4, v2, v3
+  0013: move-object v2, v1
+  0014: const/4 v3, #int 1 // #1
+  0015: aget-object v2, v2, v3
+  0017: const/4 v3, #int 2 // #2
+  0018: aget-object v2, v2, v3
+  001a: const/4 v3, #int 3 // #3
+  001b: aget v2, v2, v3
+  001d: move v0, v2
+  001e: return v0
diff --git a/dx/tests/064-dex-array-access/info.txt b/dx/tests/064-dex-array-access/info.txt
new file mode 100644
index 0000000..8f81663
--- /dev/null
+++ b/dx/tests/064-dex-array-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a few cases of array access get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/064-dex-array-access/run b/dx/tests/064-dex-array-access/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/064-dex-array-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/065-dex-new-array/Blort.java b/dx/tests/065-dex-new-array/Blort.java
new file mode 100644
index 0000000..7a7eaa4
--- /dev/null
+++ b/dx/tests/065-dex-new-array/Blort.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public boolean[] test1() {
+        return new boolean[1];
+    }
+
+    public byte[] test2() {
+        return new byte[1];
+    }
+
+    public short[] test3() {
+        return new short[1];
+    }
+
+    public char[] test4() {
+        return new char[1];
+    }
+
+    public int[] test5() {
+        return new int[1];
+    }
+
+    public long[] test6() {
+        return new long[1];
+    }
+
+    public float[] test7() {
+        return new float[1];
+    }
+
+    public double[] test8() {
+        return new double[1];
+    }
+
+    public Object[] test9() {
+        return new Object[1];
+    }
+}
diff --git a/dx/tests/065-dex-new-array/expected.txt b/dx/tests/065-dex-new-array/expected.txt
new file mode 100644
index 0000000..0b26182
--- /dev/null
+++ b/dx/tests/065-dex-new-array/expected.txt
@@ -0,0 +1,63 @@
+Blort.test1:()[Z:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, boolean[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test2:()[B:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, byte[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test3:()[S:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, short[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test4:()[C:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, char[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test5:()[I:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, int[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test6:()[J:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, long[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test7:()[F:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, float[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test8:()[D:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, double[]
+  0004: move-object v0, v1
+  0005: return-object v0
+Blort.test9:()[Ljava/lang/Object;:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: const/4 v1, #int 1 // #1
+  0002: new-array v1, v1, java.lang.Object[]
+  0004: move-object v0, v1
+  0005: return-object v0
diff --git a/dx/tests/065-dex-new-array/info.txt b/dx/tests/065-dex-new-array/info.txt
new file mode 100644
index 0000000..f4d2cc5
--- /dev/null
+++ b/dx/tests/065-dex-new-array/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a few cases of array construction get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/065-dex-new-array/run b/dx/tests/065-dex-new-array/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/065-dex-new-array/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/066-dex-try-catch-rethrow/Blort.java b/dx/tests/066-dex-try-catch-rethrow/Blort.java
new file mode 100644
index 0000000..cdb00ea
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/Blort.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static Object zorch1(String s) {
+        return null;
+    }
+
+    public static void test1() {
+        try {
+            zorch1("x");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static void zorch2(String s) {
+        // This space intentionally left blank.
+    }
+
+    public static void test2() {
+        try {
+            zorch2("x");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static int zorch3(String s) {
+        return 0;
+    }
+
+    public static void test3() {
+        try {
+            zorch3("x");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static Object zorch4(int x) {
+        return null;
+    }
+
+    public static void test4() {
+        try {
+            zorch4(1);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public static Object zorch5(int x) {
+        return null;
+    }
+
+    public static Object test5() {
+        try {
+            return zorch5(1);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/dx/tests/066-dex-try-catch-rethrow/expected.txt b/dx/tests/066-dex-try-catch-rethrow/expected.txt
new file mode 100644
index 0000000..13af56c
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/expected.txt
@@ -0,0 +1,95 @@
+Blort.test1:()V:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const-string v1, "x"
+  0002: invoke-static {v1}, Blort.zorch1:(Ljava/lang/String;)Ljava/lang/Object;
+  0005: move-result-object v1
+  0006: return-void
+  0007: move-exception v1
+  0008: move-object v0, v1
+  0009: new-instance v1, java.lang.RuntimeException
+  000b: move-object v4, v1
+  000c: move-object v1, v4
+  000d: move-object v2, v4
+  000e: move-object v3, v0
+  000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0012: throw v1
+  catches
+    tries:
+      try 0000..0005
+      catch java.lang.Exception -> 0007
+Blort.test2:()V:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const-string v1, "x"
+  0002: invoke-static {v1}, Blort.zorch2:(Ljava/lang/String;)V
+  0005: return-void
+  0006: move-exception v1
+  0007: move-object v0, v1
+  0008: new-instance v1, java.lang.RuntimeException
+  000a: move-object v4, v1
+  000b: move-object v1, v4
+  000c: move-object v2, v4
+  000d: move-object v3, v0
+  000e: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0011: throw v1
+  catches
+    tries:
+      try 0000..0005
+      catch java.lang.Exception -> 0006
+Blort.test3:()V:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const-string v1, "x"
+  0002: invoke-static {v1}, Blort.zorch3:(Ljava/lang/String;)I
+  0005: move-result v1
+  0006: return-void
+  0007: move-exception v1
+  0008: move-object v0, v1
+  0009: new-instance v1, java.lang.RuntimeException
+  000b: move-object v4, v1
+  000c: move-object v1, v4
+  000d: move-object v2, v4
+  000e: move-object v3, v0
+  000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0012: throw v1
+  catches
+    tries:
+      try 0000..0005
+      catch java.lang.Exception -> 0007
+Blort.test4:()V:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 1 // #1
+  0001: invoke-static {v1}, Blort.zorch4:(I)Ljava/lang/Object;
+  0004: move-result-object v1
+  0005: return-void
+  0006: move-exception v1
+  0007: move-object v0, v1
+  0008: new-instance v1, java.lang.RuntimeException
+  000a: move-object v4, v1
+  000b: move-object v1, v4
+  000c: move-object v2, v4
+  000d: move-object v3, v0
+  000e: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0011: throw v1
+  catches
+    tries:
+      try 0001..0004
+      catch java.lang.Exception -> 0006
+Blort.test5:()Ljava/lang/Object;:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 1 // #1
+  0001: invoke-static {v1}, Blort.zorch5:(I)Ljava/lang/Object;
+  0004: move-result-object v1
+  0005: move-object v0, v1
+  0006: return-object v0
+  0007: move-exception v1
+  0008: move-object v0, v1
+  0009: new-instance v1, java.lang.RuntimeException
+  000b: move-object v4, v1
+  000c: move-object v1, v4
+  000d: move-object v2, v4
+  000e: move-object v3, v0
+  000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+  0012: throw v1
+  catches
+    tries:
+      try 0001..0004
+      catch java.lang.Exception -> 0007
diff --git a/dx/tests/066-dex-try-catch-rethrow/info.txt b/dx/tests/066-dex-try-catch-rethrow/info.txt
new file mode 100644
index 0000000..b2696b8
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which looks at a few cases of
+a try-catch where the exception handler rethrows.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/066-dex-try-catch-rethrow/run b/dx/tests/066-dex-try-catch-rethrow/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/067-dex-switch-and-try/Blort.java b/dx/tests/067-dex-switch-and-try/Blort.java
new file mode 100644
index 0000000..e6435e1
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/Blort.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    static public void blort() {
+        // This space intentionally left blank.
+    }
+
+    // This test has a try-catch but the try code can't possibly throw.
+    public int test1(int x) {
+        try {
+            switch (x) {
+                case 1: {
+                    x = 10;
+                    break;
+                }
+                case 2: {
+                    x = 20;
+                    break;
+                }
+            }
+        } catch (RuntimeException ex) {
+            // Ignore it.
+        }
+
+        return x;
+    }
+
+    // This test has a try-catch where the try code can theoretically throw.
+    public int test2(int x) {
+        try {
+            switch (x) {
+                case 1: {
+                    x = 10;
+                    blort();
+                    break;
+                }
+                case 2: {
+                    x = 20;
+                    break;
+                }
+            }
+        } catch (RuntimeException ex) {
+            // Ignore it.
+        }
+
+        return x;
+    }
+
+    // This test has a switch with a case that has a try-catch where
+    // the try code can theoretically throw, but it would be caught
+    // inside the case itself.
+    public int test3(int x) {
+        switch (x) {
+            case 1: {
+                try {
+                    x = 10;
+                    blort();
+                } catch (RuntimeException ex) {
+                    // Ignore it.
+                }
+                break;
+            }
+            case 2: {
+                x = 20;
+                break;
+            }
+        }
+
+        return x;
+    }
+
+    // This test has a try-catch that has a switch with a case that
+    // has a try-catch where the try code can theoretically throw, but
+    // it would be caught inside the case itself, so the outer
+    // exception handler should be considered dead.
+    public int test4(int x) {
+        try {
+            switch (x) {
+                case 1: {
+                    try {
+                        x = 10;
+                        blort();
+                    } catch (RuntimeException ex) {
+                        // Ignore it.
+                    }
+                    break;
+                }
+                case 2: {
+                    x = 20;
+                    break;
+                }
+            }
+        } catch (RuntimeException ex) {
+            return 4;
+        }
+
+        return x;
+    }
+}
diff --git a/dx/tests/067-dex-switch-and-try/expected.txt b/dx/tests/067-dex-switch-and-try/expected.txt
new file mode 100644
index 0000000..5e55bf4
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/expected.txt
@@ -0,0 +1,100 @@
+Blort.test1:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+  0000: move-object v0, v4
+  0001: move v1, v5
+  0002: move v3, v1
+  0003: packed-switch v3, 0012 // +000f
+  0006: move v3, v1
+  0007: move v0, v3
+  0008: return v0
+  0009: const/16 v3, #int 10 // #000a
+  000b: move v1, v3
+  000c: goto 0006 // -0006
+  000d: const/16 v3, #int 20 // #0014
+  000f: move v1, v3
+  0010: goto 0006 // -000a
+  0011: nop // spacer
+  0012: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 0000000d // +0000000a
+Blort.test2:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+  0000: move-object v0, v4
+  0001: move v1, v5
+  0002: move v3, v1
+  0003: packed-switch v3, 0018 // +0015
+  0006: move v3, v1
+  0007: move v0, v3
+  0008: return v0
+  0009: const/16 v3, #int 10 // #000a
+  000b: move v1, v3
+  000c: invoke-static {}, Blort.blort:()V
+  000f: goto 0006 // -0009
+  0010: const/16 v3, #int 20 // #0014
+  0012: move v1, v3
+  0013: goto 0006 // -000d
+  0014: move-exception v3
+  0015: move-object v2, v3
+  0016: goto 0006 // -0010
+  0017: nop // spacer
+  0018: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 00000010 // +0000000d
+  catches
+    tries:
+      try 000c..000f
+      catch java.lang.RuntimeException -> 0014
+Blort.test3:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+  0000: move-object v0, v4
+  0001: move v1, v5
+  0002: move v3, v1
+  0003: packed-switch v3, 0018 // +0015
+  0006: move v3, v1
+  0007: move v0, v3
+  0008: return v0
+  0009: const/16 v3, #int 10 // #000a
+  000b: move v1, v3
+  000c: invoke-static {}, Blort.blort:()V
+  000f: goto 0006 // -0009
+  0010: move-exception v3
+  0011: move-object v2, v3
+  0012: goto 0006 // -000c
+  0013: const/16 v3, #int 20 // #0014
+  0015: move v1, v3
+  0016: goto 0006 // -0010
+  0017: nop // spacer
+  0018: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 00000013 // +00000010
+  catches
+    tries:
+      try 000c..000f
+      catch java.lang.RuntimeException -> 0010
+Blort.test4:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+  0000: move-object v0, v4
+  0001: move v1, v5
+  0002: move v3, v1
+  0003: packed-switch v3, 0018 // +0015
+  0006: move v3, v1
+  0007: move v0, v3
+  0008: return v0
+  0009: const/16 v3, #int 10 // #000a
+  000b: move v1, v3
+  000c: invoke-static {}, Blort.blort:()V
+  000f: goto 0006 // -0009
+  0010: move-exception v3
+  0011: move-object v2, v3
+  0012: goto 0006 // -000c
+  0013: const/16 v3, #int 20 // #0014
+  0015: move v1, v3
+  0016: goto 0006 // -0010
+  0017: nop // spacer
+  0018: packed-switch-data // for switch @ 0003
+          1: 00000009 // +00000006
+          2: 00000013 // +00000010
+  catches
+    tries:
+      try 000c..000f
+      catch java.lang.RuntimeException -> 0010
diff --git a/dx/tests/067-dex-switch-and-try/info.txt b/dx/tests/067-dex-switch-and-try/info.txt
new file mode 100644
index 0000000..68e8117
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which looks at a couple cases of
+embedding a switch statement in a try-catch and vice versa. This test
+was created specifically because of a bug with exactly this situation.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/067-dex-switch-and-try/run b/dx/tests/067-dex-switch-and-try/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/068-dex-infinite-loop/Blort.java b/dx/tests/068-dex-infinite-loop/Blort.java
new file mode 100644
index 0000000..f026407
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/Blort.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static boolean zorch() {
+        return true;
+    }
+
+    public static void test1() {
+        for (;;) {
+            // This space intentionally left blank.
+        }
+    }
+
+    public static void test2() {
+        while (zorch()) {
+            // This space intentionally left blank.
+        }
+    }
+
+    public static void test3() {
+        while (zorch()) {
+            zorch();
+        }
+    }
+
+    public static void test4() {
+        for (;;) {
+            if (zorch()) {
+                break;
+            }
+
+            while (zorch()) {
+                zorch();
+            }
+        }
+    }
+}
diff --git a/dx/tests/068-dex-infinite-loop/expected.txt b/dx/tests/068-dex-infinite-loop/expected.txt
new file mode 100644
index 0000000..1a84bd9
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/expected.txt
@@ -0,0 +1,28 @@
+Blort.test1:()V:
+regs: 0000; ins: 0000; outs: 0000
+  0000: goto/32 0000 // +0000
+Blort.test2:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.zorch:()Z
+  0003: move-result v0
+  0004: if-nez v0, 0000 // -0004
+  0006: return-void
+Blort.test3:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.zorch:()Z
+  0003: move-result v0
+  0004: if-eqz v0, 000a // +0006
+  0006: invoke-static {}, Blort.zorch:()Z
+  0009: goto 0000 // -0009
+  000a: return-void
+Blort.test4:()V:
+regs: 0001; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.zorch:()Z
+  0003: move-result v0
+  0004: if-eqz v0, 0007 // +0003
+  0006: return-void
+  0007: invoke-static {}, Blort.zorch:()Z
+  000a: move-result v0
+  000b: if-eqz v0, 0000 // -000b
+  000d: invoke-static {}, Blort.zorch:()Z
+  0010: goto 0007 // -0009
diff --git a/dx/tests/068-dex-infinite-loop/info.txt b/dx/tests/068-dex-infinite-loop/info.txt
new file mode 100644
index 0000000..358f0a8
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which makes sure that a couple
+cases of (potentially) infinite loops translate reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/068-dex-infinite-loop/run b/dx/tests/068-dex-infinite-loop/run
new file mode 100644
index 0000000..3fe95cc
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/069-dex-source-position/Blort.java b/dx/tests/069-dex-source-position/Blort.java
new file mode 100644
index 0000000..8c0c5c0
--- /dev/null
+++ b/dx/tests/069-dex-source-position/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static int test(int x) {
+        if (x == 0) { // line 6
+            return 1; // line 7
+        } else {
+            try {
+                x = test(x - 1); // line 10
+            } catch (RuntimeException ex) { // line 11
+                return 2; // line 12
+            }
+            x += test(x - 2); // line 14
+            return x; // line 15
+        }
+    }
+}
diff --git a/dx/tests/069-dex-source-position/expected.txt b/dx/tests/069-dex-source-position/expected.txt
new file mode 100644
index 0000000..853ee65
--- /dev/null
+++ b/dx/tests/069-dex-source-position/expected.txt
@@ -0,0 +1,134 @@
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+  0000: move v0, v5
+  0001: move v2, v0
+  0002: if-nez v2, 0007 // +0005
+  0004: const/4 v2, #int 1 // #1
+  0005: move v0, v2
+  0006: return v0
+  0007: move v2, v0
+  0008: const/4 v3, #int 1 // #1
+  0009: sub-int/2addr v2, v3
+  000a: invoke-static {v2}, Blort.test:(I)I
+  000d: move-result v2
+  000e: move v0, v2
+  000f: move v2, v0
+  0010: move v3, v0
+  0011: const/4 v4, #int 2 // #2
+  0012: sub-int/2addr v3, v4
+  0013: invoke-static {v3}, Blort.test:(I)I
+  0016: move-result v3
+  0017: add-int/2addr v2, v3
+  0018: move v0, v2
+  0019: move v2, v0
+  001a: move v0, v2
+  001b: goto 0006 // -0015
+  001c: move-exception v2
+  001d: move-object v1, v2
+  001e: const/4 v2, #int 2 // #2
+  001f: move v0, v2
+  0020: goto 0006 // -001a
+  catches
+    tries:
+      try 000a..000d
+      catch java.lang.RuntimeException -> 001c
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+  0000: move v0, v5
+  0001: move v2, v0
+  0002: if-nez v2, 0007 // +0005
+  0004: const/4 v2, #int 1 // #1
+  0005: move v0, v2
+  0006: return v0
+  0007: move v2, v0
+  0008: const/4 v3, #int 1 // #1
+  0009: sub-int/2addr v2, v3
+  000a: invoke-static {v2}, Blort.test:(I)I
+  000d: move-result v2
+  000e: move v0, v2
+  000f: move v2, v0
+  0010: move v3, v0
+  0011: const/4 v4, #int 2 // #2
+  0012: sub-int/2addr v3, v4
+  0013: invoke-static {v3}, Blort.test:(I)I
+  0016: move-result v3
+  0017: add-int/2addr v2, v3
+  0018: move v0, v2
+  0019: move v2, v0
+  001a: move v0, v2
+  001b: goto 0006 // -0015
+  001c: move-exception v2
+  001d: move-object v1, v2
+  001e: const/4 v2, #int 2 // #2
+  001f: move v0, v2
+  0020: goto 0006 // -001a
+  catches
+    tries:
+      try 000a..000d
+      catch java.lang.RuntimeException -> 001c
+  debug info
+    line_start: 20
+    parameters_size: 0001
+    parameter <unnamed> v5
+    0000: prologue end
+    0000: line 20
+    0004: line 21
+    0006: line 29
+    line = 24
+    0007: line 24
+    000f: line 28
+    0019: line 29
+    001c: line 25
+    001e: line 26
+    end sequence
+  source file: "Blort.java"
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+  0000: move v0, v5
+  0001: move v2, v0
+  0002: if-nez v2, 0007 // +0005
+  0004: const/4 v2, #int 1 // #1
+  0005: move v0, v2
+  0006: return v0
+  0007: move v2, v0
+  0008: const/4 v3, #int 1 // #1
+  0009: sub-int/2addr v2, v3
+  000a: invoke-static {v2}, Blort.test:(I)I
+  000d: move-result v2
+  000e: move v0, v2
+  000f: move v2, v0
+  0010: move v3, v0
+  0011: const/4 v4, #int 2 // #2
+  0012: sub-int/2addr v3, v4
+  0013: invoke-static {v3}, Blort.test:(I)I
+  0016: move-result v3
+  0017: add-int/2addr v2, v3
+  0018: move v0, v2
+  0019: move v2, v0
+  001a: move v0, v2
+  001b: goto 0006 // -0015
+  001c: move-exception v2
+  001d: move-object v1, v2
+  001e: const/4 v2, #int 2 // #2
+  001f: move v0, v2
+  0020: goto 0006 // -001a
+  catches
+    tries:
+      try 000a..000d
+      catch java.lang.RuntimeException -> 001c
+  debug info
+    line_start: 20
+    parameters_size: 0001
+    parameter <unnamed> v5
+    0000: prologue end
+    0000: line 20
+    0004: line 21
+    0006: line 29
+    line = 24
+    0007: line 24
+    000f: line 28
+    0019: line 29
+    001c: line 25
+    001e: line 26
+    end sequence
+  source file: "Blort.java"
diff --git a/dx/tests/069-dex-source-position/info.txt b/dx/tests/069-dex-source-position/info.txt
new file mode 100644
index 0000000..28c8b51
--- /dev/null
+++ b/dx/tests/069-dex-source-position/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which makes sure that source
+position information is faithfully reproduced (or not, as directed).
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/069-dex-source-position/run b/dx/tests/069-dex-source-position/run
new file mode 100644
index 0000000..98c2630
--- /dev/null
+++ b/dx/tests/069-dex-source-position/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
+dx --debug --dex --no-optimize --positions=important --no-locals \
+    --dump-method=Blort.test'*' Blort.class
+dx --debug --dex --no-optimize --positions=lines --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/070-dex-multianewarray/Blort.java b/dx/tests/070-dex-multianewarray/Blort.java
new file mode 100644
index 0000000..ef812cf
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/Blort.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static Object test01() {
+        Object[][] x = new Object[2][5];
+        return x;
+    }
+
+    public static Object test02() {
+        Object[][][] x = new Object[4][1][];
+        return x;
+    }
+
+    public static Object test03() {
+        Object[][][] x = new Object[7][2][4];
+        return x;
+    }
+
+    public static Object test04() {
+        Object[][][] x = new Object[3][0][0];
+        return x;
+    }
+
+    public static Object test05() {
+        Object[][][][] x = new Object[1][3][5][7];
+        return x;
+    }
+
+    public static Object test06() {
+        Object[][][][][] x = new Object[8][7][2][3][4];
+        return x;
+    }
+
+    public static Object test07() {
+        Object[][][][][][] x = new Object[8][7][2][3][4][];
+        return x;
+    }
+
+    public static Object test08() {
+        Object[][][][][][][] x = new Object[8][7][2][3][4][][];
+        return x;
+    }
+
+    public static boolean[][] test09() {
+        return new boolean[1][2];
+    }
+
+    public static byte[][] test10() {
+        return new byte[3][4];
+    }
+
+    public static char[][] test11() {
+        return new char[5][6];
+    }
+
+    public static double[][] test12() {
+        return new double[7][8];
+    }
+
+    public static float[][] test13() {
+        return new float[9][1];
+    }
+
+    public static int[][][] test14() {
+        return new int[5][3][2];
+    }
+
+    public static long[][][] test15() {
+        return new long[3][4][7];
+    }
+
+    public static short[][][][] test16() {
+        return new short[5][4][3][2];
+    }
+
+    public static String[][][][][] test17() {
+        return new String[5][4][3][2][1];
+    }
+
+    public static Runnable[][][][][][] test18() {
+        return new Runnable[5][4][3][2][1][8];
+    }
+}
diff --git a/dx/tests/070-dex-multianewarray/expected.txt b/dx/tests/070-dex-multianewarray/expected.txt
new file mode 100644
index 0000000..9ddbabe
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/expected.txt
@@ -0,0 +1,246 @@
+Blort.test01:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 2 // #2
+  0001: const/4 v2, #int 5 // #5
+  0002: filled-new-array {v1, v2}, int[]
+  0005: move-result-object v2
+  0006: const-class v1, java.lang.Object
+  0008: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v1
+  000c: check-cast v1, java.lang.Object[][]
+  000e: move-object v0, v1
+  000f: move-object v1, v0
+  0010: move-object v0, v1
+  0011: return-object v0
+Blort.test02:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 4 // #4
+  0001: const/4 v2, #int 1 // #1
+  0002: filled-new-array {v1, v2}, int[]
+  0005: move-result-object v2
+  0006: const-class v1, java.lang.Object[]
+  0008: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v1
+  000c: check-cast v1, java.lang.Object[][][]
+  000e: move-object v0, v1
+  000f: move-object v1, v0
+  0010: move-object v0, v1
+  0011: return-object v0
+Blort.test03:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 7 // #7
+  0001: const/4 v2, #int 2 // #2
+  0002: const/4 v3, #int 4 // #4
+  0003: filled-new-array {v1, v2, v3}, int[]
+  0006: move-result-object v2
+  0007: const-class v1, java.lang.Object
+  0009: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v1
+  000d: check-cast v1, java.lang.Object[][][]
+  000f: move-object v0, v1
+  0010: move-object v1, v0
+  0011: move-object v0, v1
+  0012: return-object v0
+Blort.test04:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 3 // #3
+  0001: const/4 v2, #int 0 // #0
+  0002: const/4 v3, #int 0 // #0
+  0003: filled-new-array {v1, v2, v3}, int[]
+  0006: move-result-object v2
+  0007: const-class v1, java.lang.Object
+  0009: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v1
+  000d: check-cast v1, java.lang.Object[][][]
+  000f: move-object v0, v1
+  0010: move-object v1, v0
+  0011: move-object v0, v1
+  0012: return-object v0
+Blort.test05:()Ljava/lang/Object;:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v2, #int 3 // #3
+  0002: const/4 v3, #int 5 // #5
+  0003: const/4 v4, #int 7 // #7
+  0004: filled-new-array {v1, v2, v3, v4}, int[]
+  0007: move-result-object v2
+  0008: const-class v1, java.lang.Object
+  000a: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000d: move-result-object v1
+  000e: check-cast v1, java.lang.Object[][][][]
+  0010: move-object v0, v1
+  0011: move-object v1, v0
+  0012: move-object v0, v1
+  0013: return-object v0
+Blort.test06:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const/16 v1, #int 8 // #0008
+  0002: const/4 v2, #int 7 // #7
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 3 // #3
+  0005: const/4 v5, #int 4 // #4
+  0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+  0009: move-result-object v2
+  000a: const-class v1, java.lang.Object
+  000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000f: move-result-object v1
+  0010: check-cast v1, java.lang.Object[][][][][]
+  0012: move-object v0, v1
+  0013: move-object v1, v0
+  0014: move-object v0, v1
+  0015: return-object v0
+Blort.test07:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const/16 v1, #int 8 // #0008
+  0002: const/4 v2, #int 7 // #7
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 3 // #3
+  0005: const/4 v5, #int 4 // #4
+  0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+  0009: move-result-object v2
+  000a: const-class v1, java.lang.Object[]
+  000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000f: move-result-object v1
+  0010: check-cast v1, java.lang.Object[][][][][][]
+  0012: move-object v0, v1
+  0013: move-object v1, v0
+  0014: move-object v0, v1
+  0015: return-object v0
+Blort.test08:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const/16 v1, #int 8 // #0008
+  0002: const/4 v2, #int 7 // #7
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 3 // #3
+  0005: const/4 v5, #int 4 // #4
+  0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+  0009: move-result-object v2
+  000a: const-class v1, java.lang.Object[][]
+  000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000f: move-result-object v1
+  0010: check-cast v1, java.lang.Object[][][][][][][]
+  0012: move-object v0, v1
+  0013: move-object v1, v0
+  0014: move-object v0, v1
+  0015: return-object v0
+Blort.test09:()[[Z:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 1 // #1
+  0001: const/4 v1, #int 2 // #2
+  0002: filled-new-array {v0, v1}, int[]
+  0005: move-result-object v1
+  0006: sget-object v0, java.lang.Boolean.TYPE:Ljava/lang/Class;
+  0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v0
+  000c: check-cast v0, boolean[][]
+  000e: return-object v0
+Blort.test10:()[[B:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 3 // #3
+  0001: const/4 v1, #int 4 // #4
+  0002: filled-new-array {v0, v1}, int[]
+  0005: move-result-object v1
+  0006: sget-object v0, java.lang.Byte.TYPE:Ljava/lang/Class;
+  0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v0
+  000c: check-cast v0, byte[][]
+  000e: return-object v0
+Blort.test11:()[[C:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 6 // #6
+  0002: filled-new-array {v0, v1}, int[]
+  0005: move-result-object v1
+  0006: sget-object v0, java.lang.Character.TYPE:Ljava/lang/Class;
+  0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000b: move-result-object v0
+  000c: check-cast v0, char[][]
+  000e: return-object v0
+Blort.test12:()[[D:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 7 // #7
+  0001: const/16 v1, #int 8 // #0008
+  0003: filled-new-array {v0, v1}, int[]
+  0006: move-result-object v1
+  0007: sget-object v0, java.lang.Double.TYPE:Ljava/lang/Class;
+  0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v0
+  000d: check-cast v0, double[][]
+  000f: return-object v0
+Blort.test13:()[[F:
+regs: 0002; ins: 0000; outs: 0002
+  0000: const/16 v0, #int 9 // #0009
+  0002: const/4 v1, #int 1 // #1
+  0003: filled-new-array {v0, v1}, int[]
+  0006: move-result-object v1
+  0007: sget-object v0, java.lang.Float.TYPE:Ljava/lang/Class;
+  0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v0
+  000d: check-cast v0, float[][]
+  000f: return-object v0
+Blort.test14:()[[[I:
+regs: 0003; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 3 // #3
+  0002: const/4 v2, #int 2 // #2
+  0003: filled-new-array {v0, v1, v2}, int[]
+  0006: move-result-object v1
+  0007: sget-object v0, java.lang.Integer.TYPE:Ljava/lang/Class;
+  0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v0
+  000d: check-cast v0, int[][][]
+  000f: return-object v0
+Blort.test15:()[[[J:
+regs: 0003; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 3 // #3
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 7 // #7
+  0003: filled-new-array {v0, v1, v2}, int[]
+  0006: move-result-object v1
+  0007: sget-object v0, java.lang.Long.TYPE:Ljava/lang/Class;
+  0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000c: move-result-object v0
+  000d: check-cast v0, long[][][]
+  000f: return-object v0
+Blort.test16:()[[[[S:
+regs: 0004; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 3 // #3
+  0003: const/4 v3, #int 2 // #2
+  0004: filled-new-array {v0, v1, v2, v3}, int[]
+  0007: move-result-object v1
+  0008: sget-object v0, java.lang.Short.TYPE:Ljava/lang/Class;
+  000a: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000d: move-result-object v0
+  000e: check-cast v0, short[][][][]
+  0010: return-object v0
+Blort.test17:()[[[[[Ljava/lang/String;:
+regs: 0005; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 3 // #3
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 1 // #1
+  0005: filled-new-array {v0, v1, v2, v3, v4}, int[]
+  0008: move-result-object v1
+  0009: const-class v0, java.lang.String
+  000b: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  000e: move-result-object v0
+  000f: check-cast v0, java.lang.String[][][][][]
+  0011: return-object v0
+Blort.test18:()[[[[[[Ljava/lang/Runnable;:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 5 // #5
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 3 // #3
+  0003: const/4 v3, #int 2 // #2
+  0004: const/4 v4, #int 1 // #1
+  0005: const/16 v5, #int 8 // #0008
+  0007: filled-new-array/range {v0..v5}, int[]
+  000a: move-result-object v1
+  000b: const-class v0, java.lang.Runnable
+  000d: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+  0010: move-result-object v0
+  0011: check-cast v0, java.lang.Runnable[][][][][][]
+  0013: return-object v0
diff --git a/dx/tests/070-dex-multianewarray/info.txt b/dx/tests/070-dex-multianewarray/info.txt
new file mode 100644
index 0000000..1251f0c
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that a few
+cases of multidimensional array construction get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/070-dex-multianewarray/run b/dx/tests/070-dex-multianewarray/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/071-dex-java-stack-ops/blort.j b/dx/tests/071-dex-java-stack-ops/blort.j
new file mode 100644
index 0000000..848a84e
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/blort.j
@@ -0,0 +1,319 @@
+; 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.
+
+.class Blort
+.super java/lang/Object
+
+; Methods to "consume" an int.
+.method public static consume1(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume2(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume3(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume4(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume5(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+.method public static consume6(I)V
+.limit stack 0
+.limit locals 1
+    nop
+    return
+.end method
+
+; Methods to "consume" a long.
+.method public static consume1(J)V
+.limit stack 0
+.limit locals 2
+    nop
+    return
+.end method
+
+.method public static consume2(J)V
+.limit stack 0
+.limit locals 2
+    nop
+    return
+.end method
+
+.method public static consume3(J)V
+.limit stack 0
+.limit locals 2
+    nop
+    return
+.end method
+
+.method public static consume4(J)V
+.limit stack 0
+.limit locals 2
+    nop
+    return
+.end method
+
+; Test of "pop" opcode. This should end up causing a call to consume1(0).
+.method public static test_pop()V
+.limit stack 2
+.limit locals 0
+    iconst_0
+    iconst_1
+    pop          ; A1 -> (empty)
+    invokestatic Blort/consume1(I)V
+    return
+.end method
+
+; Test of "pop2" opcode, form 1. This should end up causing a call
+; to consume1(0).
+.method public static test_pop2_form1()V
+.limit stack 3
+.limit locals 0
+    iconst_0
+    iconst_1
+    iconst_2
+    pop2         ; A1 B1 -> (empty)
+    invokestatic Blort/consume1(I)V
+    return
+.end method
+
+; Test of "pop2" opcode, form 2. This should end up causing a call
+; to consume1(0).
+.method public static test_pop2_form2()V
+.limit stack 3
+.limit locals 0
+    iconst_0
+    lconst_0
+    pop2         ; A2 -> (empty)
+    invokestatic Blort/consume1(I)V
+    return
+.end method
+
+; Test of "dup" opcode. This should end up causing these calls in order:
+; consume1(0), consume2(0).
+.method public static test_dup()V
+.limit stack 2
+.limit locals 0
+    iconst_0
+    dup          ; A1 -> A1 A1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    return
+.end method
+
+; Test of "dup_x1" opcode. This should end up causing these calls in order:
+; consume1(1), consume2(0), consume3(1).
+.method public static test_dup_x1()V
+.limit stack 3
+.limit locals 0
+    iconst_0
+    iconst_1
+    dup_x1       ; A1 B1 -> B1 A1 B1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    return
+.end method
+
+; Test of "dup_x2" opcode, form 1. This should end up causing these calls
+; in order: consume1(2), consume2(1), consume3(0), consume4(2).
+.method public static test_dup_x2_form1()V
+.limit stack 4
+.limit locals 0
+    iconst_0
+    iconst_1
+    iconst_2
+    dup_x2       ; A1 B1 C1 -> C1 A1 B1 C1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(I)V
+    return
+.end method
+
+; Test of "dup_x2" opcode, form 2. This should end up causing these calls
+; in order: consume1(1), consume2(0L), consume3(1).
+.method public static test_dup_x2_form2()V
+.limit stack 4
+.limit locals 0
+    lconst_0
+    iconst_1
+    dup_x2       ; A2 B1 -> B1 A2 B1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(J)V
+    invokestatic Blort/consume3(I)V
+    return
+.end method
+
+; Test of "dup2" opcode, form 1. This should end up causing these calls
+; in order: consume1(1), consume2(0), consume3(1), consume4(0).
+.method public static test_dup2_form1()V
+.limit stack 4
+.limit locals 0
+    iconst_0
+    iconst_1
+    dup2         ; A1 B1 -> A1 B1 A1 B1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(I)V
+    return
+.end method
+
+; Test of "dup2" opcode, form 2. This should end up causing these calls
+; in order: consume1(0L), consume2(0L).
+.method public static test_dup2_form2()V
+.limit stack 4
+.limit locals 0
+    lconst_0
+    dup2         ; A2 -> A2 A2
+    invokestatic Blort/consume1(J)V
+    invokestatic Blort/consume2(J)V
+    return
+.end method
+
+; Test of "dup2_x1" opcode, form 1. This should end up causing these calls
+; in order: consume1(1), consume2(2), consume3(0), consume4(1), consume5(2).
+.method public static test_dup2_x1_form1()V
+.limit stack 5
+.limit locals 0
+    iconst_0
+    iconst_1
+    iconst_2
+    dup2_x1      ; A1 B1 C1 -> B1 C1 A1 B1 C1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(I)V
+    invokestatic Blort/consume5(I)V
+    return
+.end method
+
+
+; Test of "dup2_x1" opcode, form 2. This should end up causing these calls
+; in order: consume1(1L), consume2(2), consume3(1L).
+.method public static test_dup2_x1_form2()V
+.limit stack 5
+.limit locals 0
+    iconst_0
+    lconst_1
+    dup2_x1      ; A1 B2 -> B2 A1 B2
+    invokestatic Blort/consume1(J)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(J)V
+    return
+.end method
+
+; Test of "dup2_x2" opcode, form 1. This should end up causing these calls
+; in order: consume1(3), consume2(2), consume3(1), consume4(0), consume5(3),
+; consume6(2).
+.method public static test_dup2_x2_form1()V
+.limit stack 6
+.limit locals 0
+    iconst_0
+    iconst_1
+    iconst_2
+    iconst_3
+    dup2_x2      ; A1 B1 C1 D1 -> C1 D1 A1 B1 C1 D1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(I)V
+    invokestatic Blort/consume5(I)V
+    invokestatic Blort/consume6(I)V
+    return
+.end method
+
+; Test of "dup2_x2" opcode, form 2. This should end up causing these calls
+; in order: consume1(2L), consume2(1), consume3(0), consume4(2L).
+.method public static test_dup2_x2_form2()V
+.limit stack 6
+.limit locals 0
+    iconst_0
+    iconst_1
+    ldc2_w 2
+    dup2_x2      ; A1 B1 C2 -> C2 A1 B1 C2
+    invokestatic Blort/consume1(J)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(I)V
+    invokestatic Blort/consume4(J)V
+    return
+.end method
+
+; Test of "dup2_x2" opcode, form 3. This should end up causing these calls
+; in order: consume1(2), consume2(1), consume3(0L), consume4(2), consume5(1).
+.method public static test_dup2_x2_form3()V
+.limit stack 6
+.limit locals 0
+    lconst_0
+    iconst_1
+    iconst_2
+    dup2_x2      ; A2 B1 C1 -> B1 C1 A2 B1 C1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    invokestatic Blort/consume3(J)V
+    invokestatic Blort/consume4(I)V
+    invokestatic Blort/consume5(I)V
+    return
+.end method
+
+; Test of "dup2_x2" opcode, form 4. This should end up causing these calls
+; in order: consume1(1L), consume2(0L), consume3(1L).
+.method public static test_dup2_x2_form4()V
+.limit stack 6
+.limit locals 0
+    lconst_0
+    lconst_1
+    dup2_x2      ; A2 B2 -> B2 A2 B2
+    invokestatic Blort/consume1(J)V
+    invokestatic Blort/consume2(J)V
+    invokestatic Blort/consume3(J)V
+    return
+.end method
+
+; Test of "swap" opcode. This should end up causing these calls
+; in order: consume1(0), consume2(1).
+.method public static test_swap()V
+.limit stack 2
+.limit locals 0
+    iconst_0
+    iconst_1
+    swap         ; A1 B1 -> B1 A1
+    invokestatic Blort/consume1(I)V
+    invokestatic Blort/consume2(I)V
+    return
+.end method
diff --git a/dx/tests/071-dex-java-stack-ops/expected.txt b/dx/tests/071-dex-java-stack-ops/expected.txt
new file mode 100644
index 0000000..3ba8ef3
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/expected.txt
@@ -0,0 +1,210 @@
+Blort.test_dup:()V:
+regs: 0003; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: move v2, v0
+  0002: move v0, v2
+  0003: move v1, v2
+  0004: invoke-static {v1}, Blort.consume1:(I)V
+  0007: invoke-static {v0}, Blort.consume2:(I)V
+  000a: return-void
+Blort.test_dup2_form1:()V:
+regs: 0006; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: move v4, v0
+  0003: move v5, v1
+  0004: move v0, v4
+  0005: move v1, v5
+  0006: move v2, v4
+  0007: move v3, v5
+  0008: invoke-static {v3}, Blort.consume1:(I)V
+  000b: invoke-static {v2}, Blort.consume2:(I)V
+  000e: invoke-static {v1}, Blort.consume3:(I)V
+  0011: invoke-static {v0}, Blort.consume4:(I)V
+  0014: return-void
+Blort.test_dup2_form2:()V:
+regs: 0006; ins: 0000; outs: 0002
+  0000: const-wide/16 v0, #long 0 // #0000
+  0002: move-wide v4, v0
+  0003: move-wide v0, v4
+  0004: move-wide v2, v4
+  0005: invoke-static {v2, v3}, Blort.consume1:(J)V
+  0008: invoke-static {v0, v1}, Blort.consume2:(J)V
+  000b: return-void
+Blort.test_dup2_x1_form1:()V:
+regs: 0008; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const/4 v2, #int 2 // #2
+  0003: move v5, v0
+  0004: move v6, v1
+  0005: move v7, v2
+  0006: move v0, v6
+  0007: move v1, v7
+  0008: move v2, v5
+  0009: move v3, v6
+  000a: move v4, v7
+  000b: invoke-static {v4}, Blort.consume1:(I)V
+  000e: invoke-static {v3}, Blort.consume2:(I)V
+  0011: invoke-static {v2}, Blort.consume3:(I)V
+  0014: invoke-static {v1}, Blort.consume4:(I)V
+  0017: invoke-static {v0}, Blort.consume5:(I)V
+  001a: return-void
+Blort.test_dup2_x1_form2:()V:
+regs: 0008; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 0 // #0
+  0001: const-wide/16 v1, #long 1 // #0001
+  0003: move v5, v0
+  0004: move-wide v6, v1
+  0005: move-wide v0, v6
+  0006: move v2, v5
+  0007: move-wide v3, v6
+  0008: invoke-static {v3, v4}, Blort.consume1:(J)V
+  000b: invoke-static {v2}, Blort.consume2:(I)V
+  000e: invoke-static {v0, v1}, Blort.consume3:(J)V
+  0011: return-void
+Blort.test_dup2_x2_form1:()V:
+regs: 000a; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const/4 v2, #int 2 // #2
+  0003: const/4 v3, #int 3 // #3
+  0004: move v6, v0
+  0005: move v7, v1
+  0006: move v8, v2
+  0007: move v9, v3
+  0008: move v0, v8
+  0009: move v1, v9
+  000a: move v2, v6
+  000b: move v3, v7
+  000c: move v4, v8
+  000d: move v5, v9
+  000e: invoke-static {v5}, Blort.consume1:(I)V
+  0011: invoke-static {v4}, Blort.consume2:(I)V
+  0014: invoke-static {v3}, Blort.consume3:(I)V
+  0017: invoke-static {v2}, Blort.consume4:(I)V
+  001a: invoke-static {v1}, Blort.consume5:(I)V
+  001d: invoke-static {v0}, Blort.consume6:(I)V
+  0020: return-void
+Blort.test_dup2_x2_form2:()V:
+regs: 000a; ins: 0000; outs: 0002
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const-wide/16 v2, #long 2 // #0002
+  0004: move v6, v0
+  0005: move v7, v1
+  0006: move-wide v8, v2
+  0007: move-wide v0, v8
+  0008: move v2, v6
+  0009: move v3, v7
+  000a: move-wide v4, v8
+  000b: invoke-static {v4, v5}, Blort.consume1:(J)V
+  000e: invoke-static {v3}, Blort.consume2:(I)V
+  0011: invoke-static {v2}, Blort.consume3:(I)V
+  0014: invoke-static {v0, v1}, Blort.consume4:(J)V
+  0017: return-void
+Blort.test_dup2_x2_form3:()V:
+regs: 000a; ins: 0000; outs: 0002
+  0000: const-wide/16 v0, #long 0 // #0000
+  0002: const/4 v2, #int 1 // #1
+  0003: const/4 v3, #int 2 // #2
+  0004: move-wide v6, v0
+  0005: move v8, v2
+  0006: move v9, v3
+  0007: move v0, v8
+  0008: move v1, v9
+  0009: move-wide v2, v6
+  000a: move v4, v8
+  000b: move v5, v9
+  000c: invoke-static {v5}, Blort.consume1:(I)V
+  000f: invoke-static {v4}, Blort.consume2:(I)V
+  0012: invoke-static {v2, v3}, Blort.consume3:(J)V
+  0015: invoke-static {v1}, Blort.consume4:(I)V
+  0018: invoke-static {v0}, Blort.consume5:(I)V
+  001b: return-void
+Blort.test_dup2_x2_form4:()V:
+regs: 000a; ins: 0000; outs: 0002
+  0000: const-wide/16 v0, #long 0 // #0000
+  0002: const-wide/16 v2, #long 1 // #0001
+  0004: move-wide v6, v0
+  0005: move-wide v8, v2
+  0006: move-wide v0, v8
+  0007: move-wide v2, v6
+  0008: move-wide v4, v8
+  0009: invoke-static {v4, v5}, Blort.consume1:(J)V
+  000c: invoke-static {v2, v3}, Blort.consume2:(J)V
+  000f: invoke-static {v0, v1}, Blort.consume3:(J)V
+  0012: return-void
+Blort.test_dup_x1:()V:
+regs: 0005; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: move v3, v0
+  0003: move v4, v1
+  0004: move v0, v4
+  0005: move v1, v3
+  0006: move v2, v4
+  0007: invoke-static {v2}, Blort.consume1:(I)V
+  000a: invoke-static {v1}, Blort.consume2:(I)V
+  000d: invoke-static {v0}, Blort.consume3:(I)V
+  0010: return-void
+Blort.test_dup_x2_form1:()V:
+regs: 0007; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const/4 v2, #int 2 // #2
+  0003: move v4, v0
+  0004: move v5, v1
+  0005: move v6, v2
+  0006: move v0, v6
+  0007: move v1, v4
+  0008: move v2, v5
+  0009: move v3, v6
+  000a: invoke-static {v3}, Blort.consume1:(I)V
+  000d: invoke-static {v2}, Blort.consume2:(I)V
+  0010: invoke-static {v1}, Blort.consume3:(I)V
+  0013: invoke-static {v0}, Blort.consume4:(I)V
+  0016: return-void
+Blort.test_dup_x2_form2:()V:
+regs: 0007; ins: 0000; outs: 0002
+  0000: const-wide/16 v0, #long 0 // #0000
+  0002: const/4 v2, #int 1 // #1
+  0003: move-wide v4, v0
+  0004: move v6, v2
+  0005: move v0, v6
+  0006: move-wide v1, v4
+  0007: move v3, v6
+  0008: invoke-static {v3}, Blort.consume1:(I)V
+  000b: invoke-static {v1, v2}, Blort.consume2:(J)V
+  000e: invoke-static {v0}, Blort.consume3:(I)V
+  0011: return-void
+Blort.test_pop:()V:
+regs: 0002; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: invoke-static {v0}, Blort.consume1:(I)V
+  0005: return-void
+Blort.test_pop2_form1:()V:
+regs: 0003; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: const/4 v2, #int 2 // #2
+  0003: invoke-static {v0}, Blort.consume1:(I)V
+  0006: return-void
+Blort.test_pop2_form2:()V:
+regs: 0003; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const-wide/16 v1, #long 0 // #0000
+  0003: invoke-static {v0}, Blort.consume1:(I)V
+  0006: return-void
+Blort.test_swap:()V:
+regs: 0004; ins: 0000; outs: 0001
+  0000: const/4 v0, #int 0 // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: move v2, v0
+  0003: move v3, v1
+  0004: move v0, v3
+  0005: move v1, v2
+  0006: invoke-static {v1}, Blort.consume1:(I)V
+  0009: invoke-static {v0}, Blort.consume2:(I)V
+  000c: return-void
diff --git a/dx/tests/071-dex-java-stack-ops/info.txt b/dx/tests/071-dex-java-stack-ops/info.txt
new file mode 100644
index 0000000..6c5383a
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that at
+least one case of each of the possible forms of Java stack
+manipulation op translate reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/071-dex-java-stack-ops/run b/dx/tests/071-dex-java-stack-ops/run
new file mode 100644
index 0000000..52d8a77
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/072-dex-switch-edge-cases/Blort.java b/dx/tests/072-dex-switch-edge-cases/Blort.java
new file mode 100644
index 0000000..2d4d107
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/Blort.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    // Empty switch statement. (Note: This is the same as a default-only
+    // switch statement, since under the covers every switch statement
+    // has a default of some sort.)
+    public int test1(int x) {
+        switch (x) {
+            // This space intentionally left blank.
+        }
+
+        return 0;
+    }
+
+    // Single element.
+    public int test2(int x) {
+        switch (x) {
+            case 0: return 0;
+        }
+
+        return 1;
+    }
+
+    // Single element: Integer.MIN_VALUE.
+    public int test3(int x) {
+        switch (x) {
+            case Integer.MIN_VALUE: return 0;
+        }
+
+        return 1;
+    }
+
+    // Single element: Integer.MAX_VALUE.
+    public int test4(int x) {
+        switch (x) {
+            case Integer.MAX_VALUE: return 0;
+        }
+
+        return 1;
+    }
+
+    // Two elements: 0 and Integer.MIN_VALUE.
+    public int test5(int x) {
+        switch (x) {
+            case 0: return 0;
+            case Integer.MIN_VALUE: return 1;
+        }
+
+        return 2;
+    }
+
+    // Two elements: 0 and Integer.MAX_VALUE.
+    public int test6(int x) {
+        switch (x) {
+            case 0: return 0;
+            case Integer.MAX_VALUE: return 1;
+        }
+
+        return 2;
+    }
+
+    // Two elements: Integer.MIN_VALUE and Integer.MAX_VALUE.
+    public int test7(int x) {
+        switch (x) {
+            case Integer.MIN_VALUE: return 0;
+            case Integer.MAX_VALUE: return 1;
+        }
+
+        return 2;
+    }
+
+    // Two elements: Large enough to be packed but such that 32 bit
+    // threshold calculations could overflow.
+    public int test8(int x) {
+        switch (x) {
+            case 0: return 0;
+            case 0x4cccccc8: return 1;
+        }
+
+        return 2;
+    }
+}
diff --git a/dx/tests/072-dex-switch-edge-cases/expected.txt b/dx/tests/072-dex-switch-edge-cases/expected.txt
new file mode 100644
index 0000000..6659284
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/expected.txt
@@ -0,0 +1,126 @@
+Blort.test1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: const/4 v2, #int 0 // #0
+  0004: move v0, v2
+  0005: return v0
+Blort.test2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: packed-switch v2, 000c // +0009
+  0006: const/4 v2, #int 1 // #1
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: packed-switch-data // for switch @ 0003
+          0: 00000009 // +00000006
+Blort.test3:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: packed-switch v2, 000c // +0009
+  0006: const/4 v2, #int 1 // #1
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: packed-switch-data // for switch @ 0003
+          -2147483648: 00000009 // +00000006
+Blort.test4:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: packed-switch v2, 000c // +0009
+  0006: const/4 v2, #int 1 // #1
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: packed-switch-data // for switch @ 0003
+          2147483647: 00000009 // +00000006
+Blort.test5:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0010 // +000d
+  0006: const/4 v2, #int 2 // #2
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 1 // #1
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: nop // spacer
+  0010: sparse-switch-data // for switch @ 0003
+          -2147483648: 0000000c // +00000009
+          0: 00000009 // +00000006
+Blort.test6:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0010 // +000d
+  0006: const/4 v2, #int 2 // #2
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 1 // #1
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: nop // spacer
+  0010: sparse-switch-data // for switch @ 0003
+          0: 00000009 // +00000006
+          2147483647: 0000000c // +00000009
+Blort.test7:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0010 // +000d
+  0006: const/4 v2, #int 2 // #2
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 1 // #1
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: nop // spacer
+  0010: sparse-switch-data // for switch @ 0003
+          -2147483648: 00000009 // +00000006
+          2147483647: 0000000c // +00000009
+Blort.test8:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+  0000: move-object v0, v3
+  0001: move v1, v4
+  0002: move v2, v1
+  0003: sparse-switch v2, 0010 // +000d
+  0006: const/4 v2, #int 2 // #2
+  0007: move v0, v2
+  0008: return v0
+  0009: const/4 v2, #int 0 // #0
+  000a: move v0, v2
+  000b: goto 0008 // -0003
+  000c: const/4 v2, #int 1 // #1
+  000d: move v0, v2
+  000e: goto 0008 // -0006
+  000f: nop // spacer
+  0010: sparse-switch-data // for switch @ 0003
+          0: 00000009 // +00000006
+          1288490184: 0000000c // +00000009
diff --git a/dx/tests/072-dex-switch-edge-cases/info.txt b/dx/tests/072-dex-switch-edge-cases/info.txt
new file mode 100644
index 0000000..4c7b42c
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a bunch of switch op edge cases get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/072-dex-switch-edge-cases/run b/dx/tests/072-dex-switch-edge-cases/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/073-dex-null-array-refs/Blort.java b/dx/tests/073-dex-null-array-refs/Blort.java
new file mode 100644
index 0000000..e16e0f4
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/Blort.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static Object test1() {
+        return ((Object[]) null)[0];
+    }
+
+    public static void test2() {
+        ((Object[]) null)[0] = null;
+    }
+
+    public static int test3() {
+        return ((Object[]) null).length;
+    }
+
+    public static Object test4() {
+        Object[] arr = null;
+        return arr[0];
+    }
+
+    public static void test5() {
+        Object[] arr = null;
+        arr[0] = null;
+    }
+
+    public static int test6() {
+        Object[] arr = null;
+        return arr.length;
+    }
+
+    public static Object test7(Object[] arr) {
+        if (check()) {
+            arr = null;
+        }
+
+        return arr[0];
+    }
+
+    public static void test8(Object[] arr) {
+        if (check()) {
+            arr = null;
+        }
+
+        arr[0] = null;
+    }
+
+    public static int test9(Object[] arr) {
+        if (check()) {
+            arr = null;
+        }
+
+        return arr.length;
+    }
+
+    public static boolean check() {
+        return true;
+    }
+}
diff --git a/dx/tests/073-dex-null-array-refs/expected.txt b/dx/tests/073-dex-null-array-refs/expected.txt
new file mode 100644
index 0000000..7f3ee21
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/expected.txt
@@ -0,0 +1,85 @@
+Blort.test1:()Ljava/lang/Object;:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: check-cast v0, java.lang.Object[]
+  0003: const/4 v1, #int 0 // #0
+  0004: aget-object v0, v0, v1
+  0006: return-object v0
+Blort.test2:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: check-cast v0, java.lang.Object[]
+  0003: const/4 v1, #int 0 // #0
+  0004: const/4 v2, #null // #0
+  0005: aput-object v2, v0, v1
+  0007: return-void
+Blort.test3:()I:
+regs: 0001; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: check-cast v0, java.lang.Object[]
+  0003: array-length v0, v0
+  0004: return v0
+Blort.test4:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v1, #null // #0
+  0001: move-object v0, v1
+  0002: move-object v1, v0
+  0003: const/4 v2, #int 0 // #0
+  0004: aget-object v1, v1, v2
+  0006: move-object v0, v1
+  0007: return-object v0
+Blort.test5:()V:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v1, #null // #0
+  0001: move-object v0, v1
+  0002: move-object v1, v0
+  0003: const/4 v2, #int 0 // #0
+  0004: const/4 v3, #null // #0
+  0005: aput-object v3, v1, v2
+  0007: return-void
+Blort.test6:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #null // #0
+  0001: move-object v0, v1
+  0002: move-object v1, v0
+  0003: array-length v1, v1
+  0004: move v0, v1
+  0005: return v0
+Blort.test7:([Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: invoke-static {}, Blort.check:()Z
+  0004: move-result v1
+  0005: if-eqz v1, 0009 // +0004
+  0007: const/4 v1, #null // #0
+  0008: move-object v0, v1
+  0009: move-object v1, v0
+  000a: const/4 v2, #int 0 // #0
+  000b: aget-object v1, v1, v2
+  000d: move-object v0, v1
+  000e: return-object v0
+Blort.test8:([Ljava/lang/Object;)V:
+regs: 0005; ins: 0001; outs: 0000
+  0000: move-object v0, v4
+  0001: invoke-static {}, Blort.check:()Z
+  0004: move-result v1
+  0005: if-eqz v1, 0009 // +0004
+  0007: const/4 v1, #null // #0
+  0008: move-object v0, v1
+  0009: move-object v1, v0
+  000a: const/4 v2, #int 0 // #0
+  000b: const/4 v3, #null // #0
+  000c: aput-object v3, v1, v2
+  000e: return-void
+Blort.test9:([Ljava/lang/Object;)I:
+regs: 0003; ins: 0001; outs: 0000
+  0000: move-object v0, v2
+  0001: invoke-static {}, Blort.check:()Z
+  0004: move-result v1
+  0005: if-eqz v1, 0009 // +0004
+  0007: const/4 v1, #null // #0
+  0008: move-object v0, v1
+  0009: move-object v1, v0
+  000a: array-length v1, v1
+  000b: move v0, v1
+  000c: return v0
diff --git a/dx/tests/073-dex-null-array-refs/info.txt b/dx/tests/073-dex-null-array-refs/info.txt
new file mode 100644
index 0000000..ca3b161
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+a bunch of cases convert reasonably, where necessarily or possibly
+null array references are used.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/073-dex-null-array-refs/run b/dx/tests/073-dex-null-array-refs/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/074-dex-form35c-edge-case/Blort.java b/dx/tests/074-dex-form35c-edge-case/Blort.java
new file mode 100644
index 0000000..979263f
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/Blort.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public void test()
+    {
+        int i1 = 0;
+        int i2 = 0;
+        int i3 = 0;
+        int i4 = 0;
+        int i5 = 0;
+        int i6 = 0;
+        int i7 = 0;
+        int i8 = 0;
+        int i9 = 0;
+        int i10 = 0;
+        int i11 = 0;
+        int i12 = 0;
+        int i13 = 0;
+
+        blort(0);
+    }
+
+    public void blort(long x) {
+        // blank
+    }
+}
diff --git a/dx/tests/074-dex-form35c-edge-case/expected.txt b/dx/tests/074-dex-form35c-edge-case/expected.txt
new file mode 100644
index 0000000..6afdcd7
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/expected.txt
@@ -0,0 +1,33 @@
+Blort.test:()V:
+regs: 0012; ins: 0001; outs: 0003
+  0000: move-object/from16 v0, v17
+  0002: const/4 v14, #int 0 // #0
+  0003: move v1, v14
+  0004: const/4 v14, #int 0 // #0
+  0005: move v2, v14
+  0006: const/4 v14, #int 0 // #0
+  0007: move v3, v14
+  0008: const/4 v14, #int 0 // #0
+  0009: move v4, v14
+  000a: const/4 v14, #int 0 // #0
+  000b: move v5, v14
+  000c: const/4 v14, #int 0 // #0
+  000d: move v6, v14
+  000e: const/4 v14, #int 0 // #0
+  000f: move v7, v14
+  0010: const/4 v14, #int 0 // #0
+  0011: move v8, v14
+  0012: const/4 v14, #int 0 // #0
+  0013: move v9, v14
+  0014: const/4 v14, #int 0 // #0
+  0015: move v10, v14
+  0016: const/4 v14, #int 0 // #0
+  0017: move v11, v14
+  0018: const/4 v14, #int 0 // #0
+  0019: move v12, v14
+  001a: const/4 v14, #int 0 // #0
+  001b: move v13, v14
+  001c: move-object v14, v0
+  001d: const-wide/16 v15, #long 0 // #0000
+  001f: invoke-virtual/range {v14..v16}, Blort.blort:(J)V
+  0022: return-void
diff --git a/dx/tests/074-dex-form35c-edge-case/info.txt b/dx/tests/074-dex-form35c-edge-case/info.txt
new file mode 100644
index 0000000..51d83bd
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of dex conversion, which checks to see that
+an edge case of instruction format 35c works, where a reference
+is made to register 15 as a category-2 value, meaning that
+the instruction has to be rewritten to use a different format.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/074-dex-form35c-edge-case/run b/dx/tests/074-dex-form35c-edge-case/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/075-dex-cat2-value-merge/Blort.java b/dx/tests/075-dex-cat2-value-merge/Blort.java
new file mode 100644
index 0000000..0aa4c5a
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/Blort.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static void test(long[] arr)
+    {
+        long x = 0;
+
+        for (;;) {
+            x += arr[0];
+        }
+    }
+}
diff --git a/dx/tests/075-dex-cat2-value-merge/expected.txt b/dx/tests/075-dex-cat2-value-merge/expected.txt
new file mode 100644
index 0000000..e1b9b1b
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/expected.txt
@@ -0,0 +1,12 @@
+Blort.test:([J)V:
+regs: 0008; ins: 0001; outs: 0000
+  0000: move-object v0, v7
+  0001: const-wide/16 v3, #long 0 // #0000
+  0003: move-wide v1, v3
+  0004: move-wide v3, v1
+  0005: move-object v5, v0
+  0006: const/4 v6, #int 0 // #0
+  0007: aget-wide v5, v5, v6
+  0009: add-long/2addr v3, v5
+  000a: move-wide v1, v3
+  000b: goto 0004 // -0007
diff --git a/dx/tests/075-dex-cat2-value-merge/info.txt b/dx/tests/075-dex-cat2-value-merge/info.txt
new file mode 100644
index 0000000..de411b8
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+when a known value of category-2 gets merged during control
+flow analysis, things don't break.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/075-dex-cat2-value-merge/run b/dx/tests/075-dex-cat2-value-merge/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/076-dex-synch-and-stack/Blort.java b/dx/tests/076-dex-synch-and-stack/Blort.java
new file mode 100644
index 0000000..8a83492
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public synchronized void test() {
+        new Object();
+    }
+}
diff --git a/dx/tests/076-dex-synch-and-stack/expected.txt b/dx/tests/076-dex-synch-and-stack/expected.txt
new file mode 100644
index 0000000..eba839e
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/expected.txt
@@ -0,0 +1,19 @@
+Blort.test:()V:
+regs: 0006; ins: 0001; outs: 0001
+  0000: move-object v0, v5
+  0001: move-object v3, v5
+  0002: monitor-enter v3
+  0003: new-instance v1, java.lang.Object
+  0005: move-object v4, v1
+  0006: move-object v1, v4
+  0007: move-object v2, v4
+  0008: invoke-direct {v2}, java.lang.Object.<init>:()V
+  000b: monitor-exit v3
+  000c: return-void
+  000d: move-exception v0
+  000e: monitor-exit v3
+  000f: throw v0
+  catches
+    tries:
+      try 0003..000b
+      catch <any> -> 000d
diff --git a/dx/tests/076-dex-synch-and-stack/info.txt b/dx/tests/076-dex-synch-and-stack/info.txt
new file mode 100644
index 0000000..ab5206f
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+the synchronized method conversion doesn't interact poorly with stack
+operation unwinding.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/076-dex-synch-and-stack/run b/dx/tests/076-dex-synch-and-stack/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/077-dex-code-alignment/Blort.java b/dx/tests/077-dex-code-alignment/Blort.java
new file mode 100644
index 0000000..3ec041a
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static void justReturn1() {
+        // This space intentionally left blank.
+    }
+
+    public static void justReturn2() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/077-dex-code-alignment/expected.txt b/dx/tests/077-dex-code-alignment/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/expected.txt
diff --git a/dx/tests/077-dex-code-alignment/info.txt b/dx/tests/077-dex-code-alignment/info.txt
new file mode 100644
index 0000000..0dd662b
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+code arrays are 4-byte aligned within a dex file.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/077-dex-code-alignment/run b/dx/tests/077-dex-code-alignment/run
new file mode 100644
index 0000000..f311dbf
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/run
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+
+# The awk script below attempts to filter out everything but the
+# essentials: That methods justReturn1() and justReturn2() contain
+# a single "return-void" code unit, and that there is an empty (0x0000)
+# code unit between the two of them.
+
+dx --debug --dex --positions=none --no-locals --dump-to=- *.class | awk '
+BEGIN { codes = 0; dump = 0; }
+/codes:/ { codes = 1; }
+codes && /justReturn/ { dump = 1; print "method start"; }
+/string_data:/ { codes = 0; dump = 0; }
+dump && /^......: ....     / { print $2; }
+'
diff --git a/dx/tests/078-dex-local-variable-table/Blort.java b/dx/tests/078-dex-local-variable-table/Blort.java
new file mode 100644
index 0000000..7291445
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/Blort.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static void test01(Object x) {
+        x.hashCode();
+    }
+
+    public static Object test02() {
+        Object[] arr = null;
+        return arr[0];
+    }
+
+    public static String test03(int x) {
+        String foo = null;
+        return foo;
+    }
+
+    public static String test04(int x) {
+        String foo = null;
+        if (x < 0) {
+            foo = "bar";
+        }
+        return foo;
+    }
+
+    public static int test05(Object x) {
+        int[] arr = (int[]) x;
+        arr[0] = 123;
+        return arr[0];
+    }
+
+    public static int test06(int x) {
+        if (x < 10) {
+            int y = 1;
+            return y;
+        } else {
+            int y = 2;
+            return y;
+        }
+    }
+
+    // Test for representation of boolean.
+    public static void test07(boolean x) {
+        boolean y = x;
+    }
+
+    // Test for representation of byte.
+    public static void test08(byte x) {
+        byte y = x;
+    }
+
+    // Test for representation of char.
+    public static void test09(char x) {
+        char y = x;
+    }
+
+    // Test for representation of double.
+    public static void test10(double x) {
+        double y = x;
+    }
+
+    // Test for representation of float.
+    public static void test11(float x) {
+        float y = x;
+    }
+
+    // Test for representation of int.
+    public static void test12(int x) {
+        int y = x;
+    }
+
+    // Test for representation of long.
+    public static void test13(long x) {
+        long y = x;
+    }
+
+    // Test for representation of short.
+    public static void test14(short x) {
+        short y = x;
+    }
+
+    // Test for representation of Object.
+    public static void test15(Object x) {
+        Object y = x;
+    }
+
+    // Test for representation of String (as a token example of a non-Object
+    // reference type).
+    public static void test16(String x) {
+        String y = x;
+    }
+
+    // Test for representation of int[] (as a token example of an array class).
+    public static void test17(int[] x) {
+        int[] y = x;
+    }
+}
diff --git a/dx/tests/078-dex-local-variable-table/expected.txt b/dx/tests/078-dex-local-variable-table/expected.txt
new file mode 100644
index 0000000..3e5cd69
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/expected.txt
@@ -0,0 +1,314 @@
+Blort.test01:(Ljava/lang/Object;)V:
+regs: 0003; ins: 0001; outs: 0001
+  0000: move-object v0, v2
+  0001: move-object v1, v0
+  0002: invoke-virtual {v1}, java.lang.Object.hashCode:()I
+  0005: move-result v1
+  0006: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v2
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x java.lang.Object
+    end sequence
+Blort.test02:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v1, #null // #0
+  0001: move-object v0, v1
+  0002: move-object v1, v0
+  0003: const/4 v2, #int 0 // #0
+  0004: aget-object v1, v1, v2
+  0006: move-object v0, v1
+  0007: return-object v0
+  debug info
+    line_start: 1
+    parameters_size: 0000
+    0000: prologue end
+    0002: advance pc
+    0002: +local v0 arr java.lang.Object[]
+    0007: advance pc
+    0007: -local v0 arr java.lang.Object[]
+    end sequence
+Blort.test03:(I)Ljava/lang/String;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: const/4 v2, #null // #0
+  0002: move-object v1, v2
+  0003: move-object v2, v1
+  0004: move-object v0, v2
+  0005: return-object v0
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int
+    0003: advance pc
+    0003: +local v1 foo java.lang.String
+    0005: advance pc
+    0005: -local v0 x int
+    end sequence
+Blort.test04:(I)Ljava/lang/String;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: const/4 v2, #null // #0
+  0002: move-object v1, v2
+  0003: move v2, v0
+  0004: if-gez v2, 0009 // +0005
+  0006: const-string v2, "bar"
+  0008: move-object v1, v2
+  0009: move-object v2, v1
+  000a: move-object v0, v2
+  000b: return-object v0
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int
+    0003: advance pc
+    0003: +local v1 foo java.lang.String
+    000b: advance pc
+    000b: -local v0 x int
+    end sequence
+Blort.test05:(Ljava/lang/Object;)I:
+regs: 0006; ins: 0001; outs: 0000
+  0000: move-object v0, v5
+  0001: move-object v2, v0
+  0002: check-cast v2, int[]
+  0004: check-cast v2, int[]
+  0006: move-object v1, v2
+  0007: move-object v2, v1
+  0008: const/4 v3, #int 0 // #0
+  0009: const/16 v4, #int 123 // #007b
+  000b: aput v4, v2, v3
+  000d: move-object v2, v1
+  000e: const/4 v3, #int 0 // #0
+  000f: aget v2, v2, v3
+  0011: move v0, v2
+  0012: return v0
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v5
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x java.lang.Object
+    0007: advance pc
+    0007: +local v1 arr int[]
+    0012: advance pc
+    0012: -local v0 x java.lang.Object
+    end sequence
+Blort.test06:(I)I:
+regs: 0005; ins: 0001; outs: 0000
+  0000: move v0, v4
+  0001: move v2, v0
+  0002: const/16 v3, #int 10 // #000a
+  0004: if-ge v2, v3, 000b // +0007
+  0006: const/4 v2, #int 1 // #1
+  0007: move v1, v2
+  0008: move v2, v1
+  0009: move v0, v2
+  000a: return v0
+  000b: const/4 v2, #int 2 // #2
+  000c: move v1, v2
+  000d: move v2, v1
+  000e: move v0, v2
+  000f: goto 000a // -0005
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v4
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int
+    0008: advance pc
+    0008: +local v1 y int
+    000a: advance pc
+    000a: -local v0 x int
+    000b: advance pc
+    000b: -local v1 y int
+    000b: +local restart v0 x int
+    000d: advance pc
+    000d: +local restart v1 y int
+    end sequence
+Blort.test07:(Z)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x boolean
+    0003: advance pc
+    0003: +local v1 y boolean
+    end sequence
+Blort.test08:(B)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x byte
+    0003: advance pc
+    0003: +local v1 y byte
+    end sequence
+Blort.test09:(C)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x char
+    0003: advance pc
+    0003: +local v1 y char
+    end sequence
+Blort.test10:(D)V:
+regs: 0008; ins: 0002; outs: 0000
+  0000: move-wide v0, v6
+  0001: move-wide v4, v0
+  0002: move-wide v2, v4
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v6
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x double
+    0003: advance pc
+    0003: +local v2 y double
+    end sequence
+Blort.test11:(F)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x float
+    0003: advance pc
+    0003: +local v1 y float
+    end sequence
+Blort.test12:(I)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int
+    0003: advance pc
+    0003: +local v1 y int
+    end sequence
+Blort.test13:(J)V:
+regs: 0008; ins: 0002; outs: 0000
+  0000: move-wide v0, v6
+  0001: move-wide v4, v0
+  0002: move-wide v2, v4
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v6
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x long
+    0003: advance pc
+    0003: +local v2 y long
+    end sequence
+Blort.test14:(S)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: move v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x short
+    0003: advance pc
+    0003: +local v1 y short
+    end sequence
+Blort.test15:(Ljava/lang/Object;)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: move-object v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x java.lang.Object
+    0003: advance pc
+    0003: +local v1 y java.lang.Object
+    end sequence
+Blort.test16:(Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: move-object v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x java.lang.String
+    0003: advance pc
+    0003: +local v1 y java.lang.String
+    end sequence
+Blort.test17:([I)V:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move-object v0, v3
+  0001: move-object v2, v0
+  0002: move-object v1, v2
+  0003: return-void
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v3
+    0000: prologue end
+    0001: advance pc
+    0001: +local v0 x int[]
+    0003: advance pc
+    0003: +local v1 y int[]
+    end sequence
diff --git a/dx/tests/078-dex-local-variable-table/info.txt b/dx/tests/078-dex-local-variable-table/info.txt
new file mode 100644
index 0000000..7834271
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+local variable tables get emitted properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/078-dex-local-variable-table/run b/dx/tests/078-dex-local-variable-table/run
new file mode 100644
index 0000000..426f1e6
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --dump-method=Blort.test'*' \
+    *.class
diff --git a/dx/tests/079-dex-local-variable-renumbering/Blort.java b/dx/tests/079-dex-local-variable-renumbering/Blort.java
new file mode 100644
index 0000000..c8453a2
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/Blort.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static int test1(int x) {
+        float f0 = 0.0f;
+        float f1 = 0.0f;
+        float f2 = 0.0f;
+        float f3 = 0.0f;
+        float f4 = 0.0f;
+        float f5 = 0.0f;
+        float f6 = 0.0f;
+        float f7 = 0.0f;
+        float f8 = 0.0f;
+        float f9 = 0.0f;
+        float f10 = 0.0f;
+        float f11 = 0.0f;
+        float f12 = 0.0f;
+        float f13 = 0.0f;
+        float f14 = 0.0f;
+        float f15 = 0.0f;
+        int x16 = x;
+        return -x16;
+    }
+}
diff --git a/dx/tests/079-dex-local-variable-renumbering/expected.txt b/dx/tests/079-dex-local-variable-renumbering/expected.txt
new file mode 100644
index 0000000..94ba113
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/expected.txt
@@ -0,0 +1,87 @@
+Blort.test1:(I)I:
+regs: 0015; ins: 0001; outs: 0000
+  0000: move/from16 v1, v20
+  0002: const/16 v19, #float 0.0 // #0000
+  0004: move/from16 v2, v19
+  0006: const/16 v19, #float 0.0 // #0000
+  0008: move/from16 v3, v19
+  000a: const/16 v19, #float 0.0 // #0000
+  000c: move/from16 v4, v19
+  000e: const/16 v19, #float 0.0 // #0000
+  0010: move/from16 v5, v19
+  0012: const/16 v19, #float 0.0 // #0000
+  0014: move/from16 v6, v19
+  0016: const/16 v19, #float 0.0 // #0000
+  0018: move/from16 v7, v19
+  001a: const/16 v19, #float 0.0 // #0000
+  001c: move/from16 v8, v19
+  001e: const/16 v19, #float 0.0 // #0000
+  0020: move/from16 v9, v19
+  0022: const/16 v19, #float 0.0 // #0000
+  0024: move/from16 v10, v19
+  0026: const/16 v19, #float 0.0 // #0000
+  0028: move/from16 v11, v19
+  002a: const/16 v19, #float 0.0 // #0000
+  002c: move/from16 v12, v19
+  002e: const/16 v19, #float 0.0 // #0000
+  0030: move/from16 v13, v19
+  0032: const/16 v19, #float 0.0 // #0000
+  0034: move/from16 v14, v19
+  0036: const/16 v19, #float 0.0 // #0000
+  0038: move/from16 v15, v19
+  003a: const/16 v19, #float 0.0 // #0000
+  003c: move/from16 v16, v19
+  003e: const/16 v19, #float 0.0 // #0000
+  0040: move/from16 v17, v19
+  0042: move/from16 v19, v1
+  0044: move/from16 v18, v19
+  0046: move/from16 v19, v18
+  0048: move/from16 v0, v19
+  004a: neg-int v0, v0
+  004b: move/from16 v19, v0
+  004d: move/from16 v1, v19
+  004f: return v1
+  debug info
+    line_start: 1
+    parameters_size: 0001
+    parameter <unnamed> v20
+    0000: prologue end
+    0002: advance pc
+    0002: +local v1 x int
+    0006: advance pc
+    0006: +local v2 f0 float
+    000a: advance pc
+    000a: +local v3 f1 float
+    000e: advance pc
+    000e: +local v4 f2 float
+    0012: advance pc
+    0012: +local v5 f3 float
+    0016: advance pc
+    0016: +local v6 f4 float
+    001a: advance pc
+    001a: +local v7 f5 float
+    001e: advance pc
+    001e: +local v8 f6 float
+    0022: advance pc
+    0022: +local v9 f7 float
+    0026: advance pc
+    0026: +local v10 f8 float
+    002a: advance pc
+    002a: +local v11 f9 float
+    002e: advance pc
+    002e: +local v12 f10 float
+    0032: advance pc
+    0032: +local v13 f11 float
+    0036: advance pc
+    0036: +local v14 f12 float
+    003a: advance pc
+    003a: +local v15 f13 float
+    003e: advance pc
+    003e: +local v16 f14 float
+    0042: advance pc
+    0042: +local v17 f15 float
+    0046: advance pc
+    0046: +local v18 x16 int
+    004f: advance pc
+    004f: -local v1 x int
+    end sequence
diff --git a/dx/tests/079-dex-local-variable-renumbering/info.txt b/dx/tests/079-dex-local-variable-renumbering/info.txt
new file mode 100644
index 0000000..249b23f
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+local variable tables stay in sync when the register set gets renumbered
+to make room for low scratch registers.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/079-dex-local-variable-renumbering/run b/dx/tests/079-dex-local-variable-renumbering/run
new file mode 100644
index 0000000..426f1e6
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --dump-method=Blort.test'*' \
+    *.class
diff --git a/dx/tests/080-dex-exception-tables/Blort.java b/dx/tests/080-dex-exception-tables/Blort.java
new file mode 100644
index 0000000..2143f7f
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/Blort.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public static void call1() { }
+    public static void call2() { }
+    public static void call3() { }
+    public static void call4() { }
+    public static void call5() { }
+
+    public static int test1() {
+        try {
+            call1();
+            call2();
+        } catch (IndexOutOfBoundsException ex) {
+            return 10;
+        } catch (RuntimeException ex) {
+            return 11;
+        }
+
+        call3();
+        return 12;
+    }
+
+    public static int test2() {
+        try {
+            call1();
+            try {
+                call2();
+            } catch (IndexOutOfBoundsException ex) {
+                return 10;
+            }
+            call3();
+        } catch (RuntimeException ex) {
+            return 11;
+        }
+
+        return 12;
+    }
+
+    public static int test3() {
+        try {
+            call1();
+            try {
+                call2();
+                try {
+                    call3();
+                } catch (NullPointerException ex) {
+                    return 10;
+                }
+                call4();
+            } catch (IndexOutOfBoundsException ex) {
+                return 11;
+            }
+            call5();
+        } catch (RuntimeException ex) {
+            return 12;
+        }
+
+        return 13;
+    }
+
+    public static int test4() {
+        try {
+            call1();
+            try {
+                call2();
+                try {
+                    call3();
+                } catch (NullPointerException ex) {
+                    return 10;
+                }
+            } catch (IndexOutOfBoundsException ex) {
+                return 11;
+            }
+            call5();
+        } catch (RuntimeException ex) {
+            return 12;
+        }
+
+        return 13;
+    }
+
+    public static int test5() {
+        try {
+            call1();
+            try {
+                call2();
+                try {
+                    call3();
+                } catch (NullPointerException ex) {
+                    return 10;
+                }
+            } catch (IndexOutOfBoundsException ex) {
+                return 11;
+            }
+        } catch (RuntimeException ex) {
+            return 12;
+        }
+
+        return 13;
+    }
+
+    public static int test6() {
+        try {
+            try {
+                try {
+                    call1();
+                } catch (NullPointerException ex) {
+                    return 10;
+                }
+                call2();
+            } catch (IndexOutOfBoundsException ex) {
+                return 11;
+            }
+            call3();
+        } catch (RuntimeException ex) {
+            return 12;
+        }
+
+        call4();
+        return 13;
+    }
+
+    public static int test7() {
+        try {
+            call1();
+        } catch (RuntimeException ex) {
+            return 10;
+        }
+
+        try {
+            call2();
+        } catch (RuntimeException ex) {
+            return 11;
+        }
+
+        return 12;
+    }
+
+    public static int test8() {
+        try {
+            call1();
+            call2();
+        } catch (RuntimeException ex) {
+            return 10;
+        }
+
+        try {
+            call3();
+            call4();
+        } catch (RuntimeException ex) {
+            return 11;
+        }
+
+        return 12;
+    }
+
+    public static int test9() {
+        try {
+            call1();
+            try {
+                call2();
+            } catch (IllegalArgumentException ex) {
+                return 10;
+            }
+        } catch (RuntimeException ex) {
+            return 11;
+        }
+
+        try {
+            call3();
+            try {
+                call4();
+            } catch (IllegalArgumentException ex) {
+                return 12;
+            }
+        } catch (RuntimeException ex) {
+            return 13;
+        }
+
+        return 14;
+    }
+
+}
diff --git a/dx/tests/080-dex-exception-tables/expected.txt b/dx/tests/080-dex-exception-tables/expected.txt
new file mode 100644
index 0000000..4cf43f1
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/expected.txt
@@ -0,0 +1,286 @@
+Blort.test1:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: const/16 v1, #int 12 // #000c
+  000b: move v0, v1
+  000c: return v0
+  000d: move-exception v1
+  000e: move-object v0, v1
+  000f: const/16 v1, #int 10 // #000a
+  0011: move v0, v1
+  0012: goto 000c // -0006
+  0013: move-exception v1
+  0014: move-object v0, v1
+  0015: const/16 v1, #int 11 // #000b
+  0017: move v0, v1
+  0018: goto 000c // -000c
+  catches
+    tries:
+      try 0000..0006
+      catch java.lang.IndexOutOfBoundsException -> 000d,
+        java.lang.RuntimeException -> 0013
+Blort.test2:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: const/16 v1, #int 12 // #000c
+  000b: move v0, v1
+  000c: return v0
+  000d: move-exception v1
+  000e: move-object v0, v1
+  000f: const/16 v1, #int 10 // #000a
+  0011: move v0, v1
+  0012: goto 000c // -0006
+  0013: move-exception v1
+  0014: move-object v0, v1
+  0015: const/16 v1, #int 11 // #000b
+  0017: move v0, v1
+  0018: goto 000c // -000c
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 0013
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 000d,
+        java.lang.RuntimeException -> 0013
+      try 0006..0009
+      catch java.lang.RuntimeException -> 0013
+Blort.test3:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call4:()V
+  000c: invoke-static {}, Blort.call5:()V
+  000f: const/16 v1, #int 13 // #000d
+  0011: move v0, v1
+  0012: return v0
+  0013: move-exception v1
+  0014: move-object v0, v1
+  0015: const/16 v1, #int 10 // #000a
+  0017: move v0, v1
+  0018: goto 0012 // -0006
+  0019: move-exception v1
+  001a: move-object v0, v1
+  001b: const/16 v1, #int 11 // #000b
+  001d: move v0, v1
+  001e: goto 0012 // -000c
+  001f: move-exception v1
+  0020: move-object v0, v1
+  0021: const/16 v1, #int 12 // #000c
+  0023: move v0, v1
+  0024: goto 0012 // -0012
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 001f
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 0019,
+        java.lang.RuntimeException -> 001f
+      try 0006..0009
+      catch java.lang.NullPointerException -> 0013,
+        java.lang.IndexOutOfBoundsException -> 0019,
+        java.lang.RuntimeException -> 001f
+      try 0009..000c
+      catch java.lang.IndexOutOfBoundsException -> 0019,
+        java.lang.RuntimeException -> 001f
+      try 000c..000f
+      catch java.lang.RuntimeException -> 001f
+Blort.test4:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call5:()V
+  000c: const/16 v1, #int 13 // #000d
+  000e: move v0, v1
+  000f: return v0
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 10 // #000a
+  0014: move v0, v1
+  0015: goto 000f // -0006
+  0016: move-exception v1
+  0017: move-object v0, v1
+  0018: const/16 v1, #int 11 // #000b
+  001a: move v0, v1
+  001b: goto 000f // -000c
+  001c: move-exception v1
+  001d: move-object v0, v1
+  001e: const/16 v1, #int 12 // #000c
+  0020: move v0, v1
+  0021: goto 000f // -0012
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 001c
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 0016,
+        java.lang.RuntimeException -> 001c
+      try 0006..0009
+      catch java.lang.NullPointerException -> 0010,
+        java.lang.IndexOutOfBoundsException -> 0016,
+        java.lang.RuntimeException -> 001c
+      try 0009..000c
+      catch java.lang.RuntimeException -> 001c
+Blort.test5:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: const/16 v1, #int 13 // #000d
+  000b: move v0, v1
+  000c: return v0
+  000d: move-exception v1
+  000e: move-object v0, v1
+  000f: const/16 v1, #int 10 // #000a
+  0011: move v0, v1
+  0012: goto 000c // -0006
+  0013: move-exception v1
+  0014: move-object v0, v1
+  0015: const/16 v1, #int 11 // #000b
+  0017: move v0, v1
+  0018: goto 000c // -000c
+  0019: move-exception v1
+  001a: move-object v0, v1
+  001b: const/16 v1, #int 12 // #000c
+  001d: move v0, v1
+  001e: goto 000c // -0012
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 0019
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 0013,
+        java.lang.RuntimeException -> 0019
+      try 0006..0009
+      catch java.lang.NullPointerException -> 000d,
+        java.lang.IndexOutOfBoundsException -> 0013,
+        java.lang.RuntimeException -> 0019
+Blort.test6:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call4:()V
+  000c: const/16 v1, #int 13 // #000d
+  000e: move v0, v1
+  000f: return v0
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 10 // #000a
+  0014: move v0, v1
+  0015: goto 000f // -0006
+  0016: move-exception v1
+  0017: move-object v0, v1
+  0018: const/16 v1, #int 11 // #000b
+  001a: move v0, v1
+  001b: goto 000f // -000c
+  001c: move-exception v1
+  001d: move-object v0, v1
+  001e: const/16 v1, #int 12 // #000c
+  0020: move v0, v1
+  0021: goto 000f // -0012
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.NullPointerException -> 0010,
+        java.lang.IndexOutOfBoundsException -> 0016,
+        java.lang.RuntimeException -> 001c
+      try 0003..0006
+      catch java.lang.IndexOutOfBoundsException -> 0016,
+        java.lang.RuntimeException -> 001c
+      try 0006..0009
+      catch java.lang.RuntimeException -> 001c
+Blort.test7:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: const/16 v1, #int 12 // #000c
+  0008: move v0, v1
+  0009: return v0
+  000a: move-exception v1
+  000b: move-object v0, v1
+  000c: const/16 v1, #int 10 // #000a
+  000e: move v0, v1
+  000f: goto 0009 // -0006
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 11 // #000b
+  0014: move v0, v1
+  0015: goto 0009 // -000c
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 000a
+      try 0003..0006
+      catch java.lang.RuntimeException -> 0010
+Blort.test8:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call4:()V
+  000c: const/16 v1, #int 12 // #000c
+  000e: move v0, v1
+  000f: return v0
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 10 // #000a
+  0014: move v0, v1
+  0015: goto 000f // -0006
+  0016: move-exception v1
+  0017: move-object v0, v1
+  0018: const/16 v1, #int 11 // #000b
+  001a: move v0, v1
+  001b: goto 000f // -000c
+  catches
+    tries:
+      try 0000..0006
+      catch java.lang.RuntimeException -> 0010
+      try 0006..000c
+      catch java.lang.RuntimeException -> 0016
+Blort.test9:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: invoke-static {}, Blort.call1:()V
+  0003: invoke-static {}, Blort.call2:()V
+  0006: invoke-static {}, Blort.call3:()V
+  0009: invoke-static {}, Blort.call4:()V
+  000c: const/16 v1, #int 14 // #000e
+  000e: move v0, v1
+  000f: return v0
+  0010: move-exception v1
+  0011: move-object v0, v1
+  0012: const/16 v1, #int 10 // #000a
+  0014: move v0, v1
+  0015: goto 000f // -0006
+  0016: move-exception v1
+  0017: move-object v0, v1
+  0018: const/16 v1, #int 11 // #000b
+  001a: move v0, v1
+  001b: goto 000f // -000c
+  001c: move-exception v1
+  001d: move-object v0, v1
+  001e: const/16 v1, #int 12 // #000c
+  0020: move v0, v1
+  0021: goto 000f // -0012
+  0022: move-exception v1
+  0023: move-object v0, v1
+  0024: const/16 v1, #int 13 // #000d
+  0026: move v0, v1
+  0027: goto 000f // -0018
+  catches
+    tries:
+      try 0000..0003
+      catch java.lang.RuntimeException -> 0016
+      try 0003..0006
+      catch java.lang.IllegalArgumentException -> 0010,
+        java.lang.RuntimeException -> 0016
+      try 0006..0009
+      catch java.lang.RuntimeException -> 0022
+      try 0009..000c
+      catch java.lang.IllegalArgumentException -> 001c,
+        java.lang.RuntimeException -> 0022
diff --git a/dx/tests/080-dex-exception-tables/info.txt b/dx/tests/080-dex-exception-tables/info.txt
new file mode 100644
index 0000000..99f2cbc
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of dex conversion, which checks to make sure that
+exception handler tables get built reasonably (combining entries that
+ought to be combined, listing entries in a correct and sensible order,
+etc.).
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/080-dex-exception-tables/run b/dx/tests/080-dex-exception-tables/run
new file mode 100644
index 0000000..3acfcfd
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/081-dex-throws-list/Blort.java b/dx/tests/081-dex-throws-list/Blort.java
new file mode 100644
index 0000000..6011c9c
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/Blort.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public int test1()
+            throws RuntimeException {
+        throw new RuntimeException();
+    }
+
+    public int test2()
+            throws Throwable, IllegalArgumentException {
+        throw new IllegalArgumentException();
+    }
+}
diff --git a/dx/tests/081-dex-throws-list/expected.txt b/dx/tests/081-dex-throws-list/expected.txt
new file mode 100644
index 0000000..1350edf
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/expected.txt
@@ -0,0 +1,4 @@
+Blort.test1:()I:
+    system-annotation dalvik.annotation.Throws {value: {java.lang.RuntimeException}}
+Blort.test2:()I:
+    system-annotation dalvik.annotation.Throws {value: {java.lang.Throwable, java.lang.IllegalArgumentException}}
diff --git a/dx/tests/081-dex-throws-list/info.txt b/dx/tests/081-dex-throws-list/info.txt
new file mode 100644
index 0000000..eb4bdd7
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+throws lists (that is, list of declared exceptions on methods) get
+represented reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/081-dex-throws-list/run b/dx/tests/081-dex-throws-list/run
new file mode 100644
index 0000000..2236cf2
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+    *.class | grep 'Blort\|Throws'
diff --git a/dx/tests/082-dex-throws-list-sharing/Blort.java b/dx/tests/082-dex-throws-list-sharing/Blort.java
new file mode 100644
index 0000000..31591d0
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/Blort.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public int test1()
+            throws RuntimeException {
+        throw new RuntimeException();
+    }
+
+    public int test2()
+            throws RuntimeException {
+        throw new RuntimeException();
+    }
+
+    public int test3()
+            throws Error, UnsupportedOperationException {
+        throw new RuntimeException();
+    }
+
+    public int test4()
+            throws Error, UnsupportedOperationException {
+        throw new RuntimeException();
+    }
+}
diff --git a/dx/tests/082-dex-throws-list-sharing/expected.txt b/dx/tests/082-dex-throws-list-sharing/expected.txt
new file mode 100644
index 0000000..0f33924
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/expected.txt
@@ -0,0 +1,2 @@
+java.lang.RuntimeException
+java.lang.Error, java.lang.UnsupportedOperationException
diff --git a/dx/tests/082-dex-throws-list-sharing/info.txt b/dx/tests/082-dex-throws-list-sharing/info.txt
new file mode 100644
index 0000000..3b7dca1
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+identical throws lists in different methods get collapsed into a single
+dex file structure.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/082-dex-throws-list-sharing/run b/dx/tests/082-dex-throws-list-sharing/run
new file mode 100644
index 0000000..6eed9bd
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-to=- --dump-width=200 \
+    *.class | grep '^[0-9a-f].*value.*Exception' \
+    | sed -e 's/^[^{]*{//g' -e 's/}//g'
diff --git a/dx/tests/083-ssa-phi-placement/Blort.java b/dx/tests/083-ssa-phi-placement/Blort.java
new file mode 100644
index 0000000..ed3264d
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/Blort.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+
+    public int phiTest() {
+        int i = 1;
+        int j = 1;
+        int k = 0;
+
+        while (k < 100) {
+            if (j < 20) {
+                j = i;
+                k++;
+            } else {
+                j = k;
+                k += 2;
+            }
+        }
+
+        return j;
+    }
+
+    /**
+     * This method uses no registers.
+     */
+    public static void noVars() {
+    }
+
+    /**
+     * This method requires an ordered successor list with
+     * multiple identically-valued entries.
+     */
+    Object fd;
+    public Object getOption(int optID) throws RuntimeException
+    {
+        if (fd == null) {
+            throw new RuntimeException("socket not created");
+        }
+
+        int value = 0;
+        switch (optID)
+        {
+            case 1:
+            case 2:
+                return new Integer(value);
+            case 3:
+            default:
+                return Boolean.valueOf(value != 0);
+        }
+    }
+}
diff --git a/dx/tests/083-ssa-phi-placement/expected.txt b/dx/tests/083-ssa-phi-placement/expected.txt
new file mode 100644
index 0000000..9a0cb0b
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/expected.txt
@@ -0,0 +1,345 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  live in:{}
+  Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+  Blort.java:17@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 000a
+  live in:{}
+  Blort.java:17@0000: move-object v1:NffffLBlort; <- v0:NffffLBlort;
+  Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <an
+  y>}(java.lang.Object.<init>:()V catch) . <- v1:NffffLBlort;
+  next 0004
+  live out:{}
+block 0004
+  pred 0000
+  live in:{}
+  Blort.java:17@0004: goto . <- .
+  next 000b
+  live out:{}
+block 000b
+  pred 0004
+  live in:{}
+  Blort.java:17@0004: return-void . <- .
+  returns
+  live out:{}
+block 000c
+  live in:{}
+  @????: goto . <- .
+  next 000a
+  live out:{}
+
+method phiTest ()I
+first 0048
+block 0046
+  pred 0048
+  live in:{}
+  Blort.java:21@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:21@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0046
+  live in:{}
+  Blort.java:21@0000: const-int(1) v4:I=1 <- .
+  Blort.java:21@0001: move-int v1:I <- v4:I=1
+  Blort.java:22@0002: const-int(1) v4:I=1 <- .
+  Blort.java:22@0003: move-int v2:I <- v4:I=1
+  Blort.java:23@0004: const-int(0) v4:I=0 <- .
+  Blort.java:23@0005: move-int v3:I <- v4:I=0
+  Blort.java:23@0005: goto . <- .
+  next 0049
+  live out:{}
+block 0006
+  pred 0049
+  live in:{}
+  Blort.java:25@0006: move-int v4:I <- v3:I
+  Blort.java:25@0007: const-int(100) v5:I=100 <- .
+  Blort.java:25@0009: if-ge-int . <- v4:I v5:I=100
+  next 000c *
+  next 0022
+  live out:{}
+block 000c
+  pred 0006
+  live in:{}
+  Blort.java:26@000c: move-int v4:I <- v2:I
+  Blort.java:26@000d: const-int(20) v5:I=20 <- .
+  Blort.java:26@000f: if-ge-int . <- v4:I v5:I=20
+  next 0012 *
+  next 001a
+  live out:{}
+block 0012
+  pred 000c
+  live in:{}
+  Blort.java:27@0012: move-int v4:I <- v1:I
+  Blort.java:27@0013: move-int v2:I <- v4:I
+  Blort.java:28@0014: add-const-int(1) v3:I <- v3:I
+  Blort.java:28@0017: goto . <- .
+  next 0049
+  live out:{}
+block 001a
+  pred 000c
+  live in:{}
+  Blort.java:30@001a: move-int v4:I <- v3:I
+  Blort.java:30@001b: move-int v2:I <- v4:I
+  Blort.java:31@001c: add-const-int(2) v3:I <- v3:I
+  Blort.java:31@001f: goto . <- .
+  next 0049
+  live out:{}
+block 0022
+  pred 0006
+  live in:{}
+  Blort.java:35@0022: move-int v4:I <- v2:I
+  Blort.java:35@0023: move-int v0:I <- v4:I
+  Blort.java:35@0023: goto . <- .
+  next 0047
+  live out:{}
+block 0047
+  pred 0022
+  live in:{}
+  Blort.java:35@0023: return-int . <- v0:I
+  returns
+  live out:{}
+block 0048
+  live in:{}
+  @????: goto . <- .
+  next 0046
+  live out:{}
+block 0049
+  pred 0000
+  pred 0012
+  pred 001a
+  live in:{}
+  @????: phi v5:V <- .
+  @????: phi v4:V <- .
+  @????: phi v3:V <- .
+  @????: phi v2:V <- .
+  @????: goto . <- .
+  next 0006
+  live out:{}
+
+method noVars ()V
+first 0004
+block 0002
+  pred 0004
+  live in:{}
+  Blort.java:42@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0002
+  live in:{}
+  Blort.java:42@0000: goto . <- .
+  next 0003
+  live out:{}
+block 0003
+  pred 0000
+  live in:{}
+  Blort.java:42@0000: return-void . <- .
+  returns
+  live out:{}
+block 0004
+  live in:{}
+  @????: goto . <- .
+  next 0002
+  live out:{}
+
+method getOption (I)Ljava/lang/Object;
+first 0098
+block 008c
+  pred 0098
+  live in:{}
+  Blort.java:51@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:51@0000: move-param-int(1) v1:I <- .
+  Blort.java:51@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0093
+  pred 0000
+  live in:{}
+  Blort.java:51@0001: Rop{move-result-pseudo Ljava/lang/Object; <- . flows} v3:
+  Ljava/lang/Object; <- .
+  Blort.java:51@0001: goto . <- .
+  next 0004
+  live out:{}
+block 0000
+  pred 008c
+  live in:{}
+  Blort.java:51@0000: move-object v3:LBlort; <- v0:LBlort;
+  Blort.java:51@0001: get-field-object(Blort.fd:Ljava/lang/Object; catch) . <- 
+  v3:LBlort;
+  next 0093
+  live out:{}
+block 0004
+  pred 0093
+  live in:{}
+  Blort.java:51@0004: if-nez-object . <- v3:Ljava/lang/Object;
+  next 0007 *
+  next 0011
+  live out:{}
+block 0094
+  pred 0007
+  live in:{}
+  Blort.java:52@0007: Rop{move-result-pseudo N0007Ljava/lang/RuntimeException; 
+  <- . flows} v3:N0007Ljava/lang/RuntimeException; <- .
+  Blort.java:52@0007: goto . <- .
+  next 000a
+  live out:{}
+block 0007
+  pred 0004
+  live in:{}
+  Blort.java:52@0007: new-instance(java.lang.RuntimeException catch) . <- .
+  next 0094
+  live out:{}
+block 0095
+  pred 000a
+  live in:{}
+  Blort.java:52@000b: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v5:
+  Ljava/lang/String;="socket not created" <- .
+  Blort.java:52@000b: goto . <- .
+  next 000d
+  live out:{}
+block 000a
+  pred 0094
+  live in:{}
+  Blort.java:52@000a: move-object v6:N0007Ljava/lang/RuntimeException; <- v3:N0
+  007Ljava/lang/RuntimeException;
+  Blort.java:52@000a: move-object v3:N0007Ljava/lang/RuntimeException; <- v6:N0
+  007Ljava/lang/RuntimeException;
+  Blort.java:52@000a: move-object v4:N0007Ljava/lang/RuntimeException; <- v6:N0
+  007Ljava/lang/RuntimeException;
+  Blort.java:52@000b: const-object("socket not created" catch) . <- .
+  next 0095
+  live out:{}
+block 000d
+  pred 0095
+  live in:{}
+  Blort.java:52@000d: Rop{invoke-direct . <- Ljava/lang/RuntimeException; Ljava
+  /lang/String; call throws <any>}(java.lang.RuntimeException.<init>:(Ljava/lan
+  g/String;)V catch) . <- v4:N0007Ljava/lang/RuntimeException; v5:Ljava/lang/St
+  ring;="socket not created"
+  next 0010
+  live out:{}
+block 0010
+  pred 000d
+  live in:{}
+  Blort.java:52@0010: throw(catch) . <- v3:Ljava/lang/RuntimeException;
+  returns
+  live out:{}
+block 0011
+  pred 0004
+  live in:{}
+  Blort.java:55@0011: const-int(0) v3:I=0 <- .
+  Blort.java:55@0012: move-int v2:I <- v3:I=0
+  Blort.java:56@0013: move-int v3:I <- v1:I
+  Blort.java:56@0014: switch({1, 2}) . <- v3:I
+  next 0030
+  next 0030
+  next 0039 *
+  live out:{}
+block 0096
+  pred 0030
+  live in:{}
+  Blort.java:60@0030: Rop{move-result-pseudo N0030Ljava/lang/Integer; <- . flow
+  s} v3:N0030Ljava/lang/Integer; <- .
+  Blort.java:60@0030: goto . <- .
+  next 0033
+  live out:{}
+block 0030
+  pred 0011
+  live in:{}
+  Blort.java:60@0030: new-instance(java.lang.Integer catch) . <- .
+  next 0096
+  live out:{}
+block 0033
+  pred 0096
+  live in:{}
+  Blort.java:60@0033: move-object v6:N0030Ljava/lang/Integer; <- v3:N0030Ljava/
+  lang/Integer;
+  Blort.java:60@0033: move-object v3:N0030Ljava/lang/Integer; <- v6:N0030Ljava/
+  lang/Integer;
+  Blort.java:60@0033: move-object v4:N0030Ljava/lang/Integer; <- v6:N0030Ljava/
+  lang/Integer;
+  Blort.java:60@0034: move-int v5:I <- v2:I
+  Blort.java:60@0035: Rop{invoke-direct . <- Ljava/lang/Integer; I call throws 
+  <any>}(java.lang.Integer.<init>:(I)V catch) . <- v4:N0030Ljava/lang/Integer; 
+  v5:I
+  next 0038
+  live out:{}
+block 0038
+  pred 0033
+  live in:{}
+  Blort.java:60@0038: move-object v0:Ljava/lang/Integer; <- v3:Ljava/lang/Integ
+  er;
+  Blort.java:60@0038: goto . <- .
+  next 008d
+  live out:{}
+block 0039
+  pred 0011
+  live in:{}
+  Blort.java:63@0039: move-int v3:I <- v2:I
+  Blort.java:63@003a: if-eqz-int . <- v3:I
+  next 003d *
+  next 0041
+  live out:{}
+block 003d
+  pred 0039
+  live in:{}
+  Blort.java:63@003d: const-int(1) v3:I=1 <- .
+  Blort.java:63@003e: goto . <- .
+  next 0042
+  live out:{}
+block 0041
+  pred 0039
+  live in:{}
+  Blort.java:63@0041: const-int(0) v3:I=0 <- .
+  Blort.java:63@0041: goto . <- .
+  next 0042
+  live out:{}
+block 0097
+  pred 0042
+  live in:{}
+  Blort.java:63@0042: Rop{move-result Ljava/lang/Boolean; <- . flows} v3:Ljava/
+  lang/Boolean; <- .
+  Blort.java:63@0042: goto . <- .
+  next 0045
+  live out:{}
+block 0042
+  pred 003d
+  pred 0041
+  live in:{}
+  @????: phi v3:V <- .
+  Blort.java:63@0042: Rop{invoke-static . <- I call throws <any>}(java.lang.Boo
+  lean.valueOf:(Z)Ljava/lang/Boolean; catch) . <- v3:I
+  next 0097
+  live out:{}
+block 0045
+  pred 0097
+  live in:{}
+  Blort.java:63@0045: move-object v0:Ljava/lang/Boolean; <- v3:Ljava/lang/Boole
+  an;
+  Blort.java:63@0045: goto . <- .
+  next 008d
+  live out:{}
+block 008d
+  pred 0038
+  pred 0045
+  live in:{}
+  @????: phi v6:V <- .
+  @????: phi v5:V <- .
+  @????: phi v4:V <- .
+  @????: phi v3:V <- .
+  @????: phi v0:V <- .
+  Blort.java:63@0045: return-object . <- v0:Ljava/lang/Object;
+  returns
+  live out:{}
+block 0098
+  live in:{}
+  @????: goto . <- .
+  next 008c
+  live out:{}
diff --git a/dx/tests/083-ssa-phi-placement/info.txt b/dx/tests/083-ssa-phi-placement/info.txt
new file mode 100644
index 0000000..8d4eebf
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the phi placement algorthim used in the conversion to SSA form.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/083-ssa-phi-placement/run b/dx/tests/083-ssa-phi-placement/run
new file mode 100644
index 0000000..ddcb597
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --dump --ssa-blocks --ssa-step=phi-placement Blort.class
diff --git a/dx/tests/084-dex-high-register-moves/Blort.java b/dx/tests/084-dex-high-register-moves/Blort.java
new file mode 100644
index 0000000..e68ebd8
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/Blort.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    private static int i;
+    private static long l;
+    private static Object o;
+
+    public static void test() {
+        int i0 = 0;
+        int i1 = 0;
+        int i2 = 0;
+        int i3 = 0;
+        int i4 = 0;
+        int i5 = 0;
+        int i6 = 0;
+        int i7 = 0;
+        int i8 = 0;
+        int i9 = 0;
+        int i10 = 0;
+        int i11 = 0;
+        int i12 = 0;
+        int i13 = 0;
+        int i14 = 0;
+        int i15 = 0;
+
+        int ix = i;
+        long lx = l;
+        Object ox = o;
+
+        i = -ix;
+        l = -lx;
+        i = (ox instanceof String) ? 0 : 1;
+    }
+}
diff --git a/dx/tests/084-dex-high-register-moves/expected.txt b/dx/tests/084-dex-high-register-moves/expected.txt
new file mode 100644
index 0000000..33466c4
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/expected.txt
@@ -0,0 +1,60 @@
+Blort.test:()V:
+regs: 0018; ins: 0000; outs: 0000
+  0000: const/16 v22, #int 0 // #0000
+  0002: move/from16 v2, v22
+  0004: const/16 v22, #int 0 // #0000
+  0006: move/from16 v3, v22
+  0008: const/16 v22, #int 0 // #0000
+  000a: move/from16 v4, v22
+  000c: const/16 v22, #int 0 // #0000
+  000e: move/from16 v5, v22
+  0010: const/16 v22, #int 0 // #0000
+  0012: move/from16 v6, v22
+  0014: const/16 v22, #int 0 // #0000
+  0016: move/from16 v7, v22
+  0018: const/16 v22, #int 0 // #0000
+  001a: move/from16 v8, v22
+  001c: const/16 v22, #int 0 // #0000
+  001e: move/from16 v9, v22
+  0020: const/16 v22, #int 0 // #0000
+  0022: move/from16 v10, v22
+  0024: const/16 v22, #int 0 // #0000
+  0026: move/from16 v11, v22
+  0028: const/16 v22, #int 0 // #0000
+  002a: move/from16 v12, v22
+  002c: const/16 v22, #int 0 // #0000
+  002e: move/from16 v13, v22
+  0030: const/16 v22, #int 0 // #0000
+  0032: move/from16 v14, v22
+  0034: const/16 v22, #int 0 // #0000
+  0036: move/from16 v15, v22
+  0038: const/16 v22, #int 0 // #0000
+  003a: move/from16 v16, v22
+  003c: const/16 v22, #int 0 // #0000
+  003e: move/from16 v17, v22
+  0040: sget v22, Blort.i:I
+  0042: move/from16 v18, v22
+  0044: sget-wide v22, Blort.l:J
+  0046: move-wide/from16 v19, v22
+  0048: sget-object v22, Blort.o:Ljava/lang/Object;
+  004a: move-object/from16 v21, v22
+  004c: move/from16 v22, v18
+  004e: move/from16 v0, v22
+  0050: neg-int v0, v0
+  0051: move/from16 v22, v0
+  0053: sput v22, Blort.i:I
+  0055: move-wide/from16 v22, v19
+  0057: move-wide/from16 v0, v22
+  0059: neg-long v0, v0
+  005a: move-wide/from16 v22, v0
+  005c: sput-wide v22, Blort.l:J
+  005e: move-object/from16 v22, v21
+  0060: move-object/from16 v0, v22
+  0062: instance-of v0, v0, java.lang.String
+  0064: move/from16 v22, v0
+  0066: if-eqz v22, 006d // +0007
+  0068: const/16 v22, #int 0 // #0000
+  006a: sput v22, Blort.i:I
+  006c: return-void
+  006d: const/16 v22, #int 1 // #0001
+  006f: goto 006a // -0005
diff --git a/dx/tests/084-dex-high-register-moves/info.txt b/dx/tests/084-dex-high-register-moves/info.txt
new file mode 100644
index 0000000..77f0945
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+high registers are moved to and from low registers with
+type-appropriate instructions.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/084-dex-high-register-moves/run b/dx/tests/084-dex-high-register-moves/run
new file mode 100644
index 0000000..3acfcfd
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/085-dex-jsr-ret/blort.j b/dx/tests/085-dex-jsr-ret/blort.j
new file mode 100644
index 0000000..2616723
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/blort.j
@@ -0,0 +1,69 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+    .limit locals 2
+    .limit stack 3
+
+    aload_0
+    dup
+    dup
+    astore_1
+    pop2
+    return
+.end method
+
+; Test jsr and jsr_w.
+.method public test_jsr()Ljava/lang/Object;
+    .limit locals 3
+    .limit stack 4
+    aload_0
+    jsr j1
+    aload_0
+    pop
+    ; Call j1 with different locals
+    ldc 10
+    astore_0
+    jsr j1
+    aload_0
+    pop
+    jsr j3
+    areturn
+j1:
+    astore_2
+    jsr_w j2
+    ret 2
+j2:
+    ; a subroutine with two returns and a catch block
+    astore_1
+    dup
+    dup
+    ; Just something that could throw an exception...
+    invokevirtual blort.test_jsr()V
+    ifnonnull j2a
+    ret_w 1
+j2a:
+    ret_w 1
+j3:
+    ; a subroutine that does not return
+    pop
+    areturn
+catchBlock:
+    areturn
+
+.catch java/lang/Throwable from j2 to j2a using catchBlock
+.end method
diff --git a/dx/tests/085-dex-jsr-ret/expected.txt b/dx/tests/085-dex-jsr-ret/expected.txt
new file mode 100644
index 0000000..ba61996
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/expected.txt
@@ -0,0 +1,171 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+first 0002
+block 0002
+  blort.j:@0000: move-param-object(0) v0:NffffLblort; <- .
+  blort.j:@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0002
+  blort.j:@0000: move-object v2:NffffLblort; <- v0:NffffLblort;
+  blort.j:@0001: move-object v5:NffffLblort; <- v2:NffffLblort;
+  blort.j:@0001: move-object v2:NffffLblort; <- v5:NffffLblort;
+  blort.j:@0001: move-object v3:NffffLblort; <- v5:NffffLblort;
+  blort.j:@0002: move-object v5:NffffLblort; <- v3:NffffLblort;
+  blort.j:@0002: move-object v3:NffffLblort; <- v5:NffffLblort;
+  blort.j:@0002: move-object v4:NffffLblort; <- v5:NffffLblort;
+  blort.j:@0003: move-object v1:NffffLblort; <- v4:NffffLblort;
+  blort.j:@0005: goto . <- .
+  next 0003
+block 0003
+  pred 0000
+  blort.j:@0005: return-void . <- .
+  returns
+
+method test_jsr ()Ljava/lang/Object;
+first 005c
+block 005c
+  blort.j:@0000: move-param-object(0) v0:Lblort; <- .
+  blort.j:@0000: goto . <- .
+  next 0000
+block 0000
+  pred 005c
+  blort.j:@0000: move-object v3:Lblort; <- v0:Lblort;
+  blort.j:@0000: goto . <- .
+  next 0001
+block 0004
+  pred 0065
+  blort.j:@0004: move-object v4:Lblort; <- v0:Lblort;
+  blort.j:@0006: const-int(10) v4:I=10 <- .
+  blort.j:@0008: move-int v0:I=10 <- v4:I=10
+  blort.j:@0008: goto . <- .
+  next 0009
+block 000c
+  pred 006e
+  blort.j:@000c: move-int v4:I=10 <- v0:I=10
+  blort.j:@000c: goto . <- .
+  next 000e
+block 005d
+  pred 006b
+  pred 0074
+  pred 0075
+  blort.j:@002c: return-object . <- v0:Ljava/lang/Object;
+  returns
+block 0063
+  pred 0001
+  blort.j:@0012: goto . <- .
+  next 0064
+block 0066
+  pred 0064
+  blort.j:@001b: move-object v7:Lblort; <- v3:Lblort;
+  blort.j:@001b: move-object v3:Lblort; <- v7:Lblort;
+  blort.j:@001b: move-object v4:Lblort; <- v7:Lblort;
+  blort.j:@001c: move-object v7:Lblort; <- v4:Lblort;
+  blort.j:@001c: move-object v4:Lblort; <- v7:Lblort;
+  blort.j:@001c: move-object v5:Lblort; <- v7:Lblort;
+  blort.j:@001d: Rop{invoke-virtual . <- Lblort; call throws <any>}(blort.test_
+  jsr:()V catch java.lang.Throwable) . <- v5:Lblort;
+  next 0067
+  next 0068 *
+block 0068
+  pred 0066
+  blort.j:@0020: if-nez-object . <- v4:Lblort;
+  next 0069 *
+  next 006a
+block 0069
+  pred 0068
+  @????: goto . <- .
+  next 0065
+block 006a
+  pred 0068
+  @????: goto . <- .
+  next 0065
+block 0067
+  pred 0066
+  blort.j:@002d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v3:Ljava/
+  lang/Throwable; <- .
+  blort.j:@002d: goto . <- .
+  next 006b
+block 006b
+  pred 0067
+  blort.j:@002d: move-object v0:Ljava/lang/Class;=java.lang.Throwable <- v3:Lja
+  va/lang/Class;=java.lang.Throwable
+  blort.j:@002d: goto . <- .
+  next 005d
+block 0064
+  pred 0063
+  @????: goto . <- .
+  next 0066
+block 0065
+  pred 0069
+  pred 006a
+  @????: goto . <- .
+  next 0004
+block 0001
+  pred 0000
+  @????: goto . <- .
+  next 0063
+block 006c
+  pred 0009
+  blort.j:@0012: goto . <- .
+  next 006d
+block 006f
+  pred 006d
+  blort.j:@001b: move-object v7:Lblort; <- v3:Lblort;
+  blort.j:@001b: move-object v3:Lblort; <- v7:Lblort;
+  blort.j:@001b: move-object v4:Lblort; <- v7:Lblort;
+  blort.j:@001c: move-object v7:Lblort; <- v4:Lblort;
+  blort.j:@001c: move-object v4:Lblort; <- v7:Lblort;
+  blort.j:@001c: move-object v5:Lblort; <- v7:Lblort;
+  blort.j:@001d: Rop{invoke-virtual . <- Lblort; call throws <any>}(blort.test_
+  jsr:()V catch java.lang.Throwable) . <- v5:Lblort;
+  next 0070
+  next 0071 *
+block 0071
+  pred 006f
+  blort.j:@0020: if-nez-object . <- v4:Lblort;
+  next 0072 *
+  next 0073
+block 0072
+  pred 0071
+  @????: goto . <- .
+  next 006e
+block 0073
+  pred 0071
+  @????: goto . <- .
+  next 006e
+block 0070
+  pred 006f
+  blort.j:@002d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v3:Ljava/
+  lang/Throwable; <- .
+  blort.j:@002d: goto . <- .
+  next 0074
+block 0074
+  pred 0070
+  blort.j:@002d: move-object v0:Ljava/lang/Class;=java.lang.Throwable <- v3:Lja
+  va/lang/Class;=java.lang.Throwable
+  blort.j:@002d: goto . <- .
+  next 005d
+block 006d
+  pred 006c
+  @????: goto . <- .
+  next 006f
+block 006e
+  pred 0072
+  pred 0073
+  @????: goto . <- .
+  next 000c
+block 0009
+  pred 0004
+  @????: goto . <- .
+  next 006c
+block 0075
+  pred 000e
+  blort.j:@002c: move-object v0:Lblort; <- v3:Lblort;
+  blort.j:@002c: goto . <- .
+  next 005d
+block 000e
+  pred 000c
+  @????: goto . <- .
+  next 0075
diff --git a/dx/tests/085-dex-jsr-ret/info.txt b/dx/tests/085-dex-jsr-ret/info.txt
new file mode 100644
index 0000000..8e164d6
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/info.txt
@@ -0,0 +1 @@
+Tests handling of the Java jsr/jsr_w/ret bytecodes.
diff --git a/dx/tests/085-dex-jsr-ret/run b/dx/tests/085-dex-jsr-ret/run
new file mode 100644
index 0000000..00a7404
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j
+dx --dump --rop-blocks blort.class
diff --git a/dx/tests/086-ssa-edge-split/Blort.java b/dx/tests/086-ssa-edge-split/Blort.java
new file mode 100644
index 0000000..d12b3f9
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    /**
+     * This method requires the edge-splitter to add a node
+     * to get to the finally block, since there are
+     * two exception sources.
+     *
+     */
+    public int edgeSplitPredTest(int x) {
+        int y = 1;
+
+        try {
+            Integer.toString(x);
+            Integer.toString(x);
+            y++;
+        } finally {
+            return y;
+        }
+    }
+
+    /**
+     * just because this should do nothing
+     */
+    void voidFunction() {
+    }
+
+    /**
+     * Current SSA form requires each move-exception block to have
+     * a unique predecessor
+     */
+    void edgeSplitMoveException() {
+        try {
+            hashCode();
+            hashCode();
+        } catch (Throwable tr) {
+        }
+    }
+
+    /**
+     * Presently, any basic block ending in an instruction with
+     * a result needs to have a unique successor. This appies
+     * only to the block between the switch instruction and the return
+     * in this case.
+     */
+    int edgeSplitSuccessor(int x) {
+        int y = 0;
+
+        switch(x) {
+            case 1: y++;
+            break;
+            case 2: y++;
+            break;
+            case 3: y++;
+            break;
+        }
+        return y;
+    }
+}
diff --git a/dx/tests/086-ssa-edge-split/expected.txt b/dx/tests/086-ssa-edge-split/expected.txt
new file mode 100644
index 0000000..e59af31
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/expected.txt
@@ -0,0 +1,343 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  live in:{}
+  Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+  Blort.java:17@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 000a
+  live in:{}
+  Blort.java:17@0000: move-object v1:NffffLBlort; <- v0:NffffLBlort;
+  Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.<init>:()V catch) . <- v1:NffffLBlort;
+  next 0004
+  live out:{}
+block 0004
+  pred 0000
+  live in:{}
+  Blort.java:17@0004: goto . <- .
+  next 000b
+  live out:{}
+block 000b
+  pred 0004
+  live in:{}
+  Blort.java:17@0004: return-void . <- .
+  returns
+  live out:{}
+block 000c
+  live in:{}
+  @????: goto . <- .
+  next 000a
+  live out:{}
+
+method edgeSplitPredTest (I)I
+first 002f
+block 0026
+  pred 002f
+  live in:{}
+  Blort.java:26@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:26@0000: move-param-int(1) v1:I <- .
+  Blort.java:26@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0026
+  live in:{}
+  Blort.java:26@0000: const-int(1) v4:I=1 <- .
+  Blort.java:26@0001: move-int v2:I <- v4:I=1
+  Blort.java:26@0001: goto . <- .
+  next 0002
+  live out:{}
+block 002d
+  pred 0002
+  live in:{}
+  Blort.java:29@0003: Rop{move-result Ljava/lang/String; <- . flows} v4:Ljava/lang/String; <- .
+  Blort.java:29@0003: goto . <- .
+  next 0006
+  live out:{}
+block 0002
+  pred 0000
+  live in:{}
+  Blort.java:29@0002: move-int v4:I <- v1:I
+  Blort.java:29@0003: Rop{invoke-static . <- I call throws <any>}(java.lang.Integer.toString:(I)Ljava/lang/String; catch java.lang.Object) . <- v4:I
+  next 0030
+  next 002d *
+  live out:{}
+block 002e
+  pred 0006
+  live in:{}
+  Blort.java:30@0008: Rop{move-result Ljava/lang/String; <- . flows} v4:Ljava/lang/String; <- .
+  Blort.java:30@0008: goto . <- .
+  next 000b
+  live out:{}
+block 0006
+  pred 002d
+  live in:{}
+  Blort.java:30@0007: move-int v4:I <- v1:I
+  Blort.java:30@0008: Rop{invoke-static . <- I call throws <any>}(java.lang.Integer.toString:(I)Ljava/lang/String; catch java.lang.Object) . <- v4:I
+  next 0031
+  next 002e *
+  live out:{}
+block 000b
+  pred 002e
+  live in:{}
+  Blort.java:31@000c: add-const-int(1) v2:I <- v2:I
+  Blort.java:31@000c: goto . <- .
+  next 000f
+  live out:{}
+block 000f
+  pred 000b
+  live in:{}
+  Blort.java:33@000f: move-int v4:I <- v2:I
+  Blort.java:33@0010: move-int v0:I <- v4:I
+  Blort.java:33@0010: goto . <- .
+  next 0027
+  live out:{}
+block 0011
+  pred 0024
+  live in:{}
+  Blort.java:33@0011: move-object v3:Ljava/lang/Class;=java.lang.Object <- v4:Ljava/lang/Class;=java.lang.Object
+  Blort.java:33@0011: goto . <- .
+  next 0012
+  live out:{}
+block 0012
+  pred 0011
+  live in:{}
+  Blort.java:33@0012: move-int v4:I <- v2:I
+  Blort.java:33@0013: move-int v0:I <- v4:I
+  Blort.java:33@0013: goto . <- .
+  next 0027
+  live out:{}
+block 0027
+  pred 000f
+  pred 0012
+  live in:{}
+  Blort.java:33@0010: return-int . <- v0:I
+  returns
+  live out:{}
+block 0024
+  pred 0030
+  pred 0031
+  live in:{}
+  Blort.java:33@0011: goto . <- .
+  next 0011
+  live out:{}
+block 002f
+  live in:{}
+  @????: goto . <- .
+  next 0026
+  live out:{}
+block 0030
+  pred 0002
+  live in:{}
+  Blort.java:33@0011: Rop{move-exception Ljava/lang/Object; <- . flows} v4:Ljava/lang/Object; <- .
+  @????: goto . <- .
+  next 0024
+  live out:{}
+block 0031
+  pred 0006
+  live in:{}
+  Blort.java:33@0011: Rop{move-exception Ljava/lang/Object; <- . flows} v4:Ljava/lang/Object; <- .
+  @????: goto . <- .
+  next 0024
+  live out:{}
+
+method voidFunction ()V
+first 0004
+block 0002
+  pred 0004
+  live in:{}
+  Blort.java:41@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:41@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0002
+  live in:{}
+  Blort.java:41@0000: goto . <- .
+  next 0003
+  live out:{}
+block 0003
+  pred 0000
+  live in:{}
+  Blort.java:41@0000: return-void . <- .
+  returns
+  live out:{}
+block 0004
+  live in:{}
+  @????: goto . <- .
+  next 0002
+  live out:{}
+
+method edgeSplitMoveException ()V
+first 0027
+block 001e
+  pred 0027
+  live in:{}
+  Blort.java:49@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:49@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0025
+  pred 0000
+  live in:{}
+  Blort.java:49@0001: Rop{move-result I <- . flows} v2:I <- .
+  Blort.java:49@0001: goto . <- .
+  next 0004
+  live out:{}
+block 0000
+  pred 001e
+  live in:{}
+  Blort.java:49@0000: move-object v2:LBlort; <- v0:LBlort;
+  Blort.java:49@0001: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v2:LBlort;
+  next 0028
+  next 0025 *
+  live out:{}
+block 0026
+  pred 0004
+  live in:{}
+  Blort.java:50@0006: Rop{move-result I <- . flows} v2:I <- .
+  Blort.java:50@0006: goto . <- .
+  next 0009
+  live out:{}
+block 0004
+  pred 0025
+  live in:{}
+  Blort.java:50@0005: move-object v2:LBlort; <- v0:LBlort;
+  Blort.java:50@0006: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v2:LBlort;
+  next 0029
+  next 0026 *
+  live out:{}
+block 0009
+  pred 0026
+  live in:{}
+  @????: goto . <- .
+  next 000a
+  live out:{}
+block 000a
+  pred 0009
+  live in:{}
+  Blort.java:52@000a: goto . <- .
+  next 000e
+  live out:{}
+block 000d
+  pred 001c
+  live in:{}
+  Blort.java:51@000d: move-object v1:Ljava/lang/Class;=java.lang.Throwable <- v2:Ljava/lang/Class;=java.lang.Throwable
+  Blort.java:51@000d: goto . <- .
+  next 000e
+  live out:{}
+block 000e
+  pred 000a
+  pred 000d
+  live in:{}
+  Blort.java:53@000e: goto . <- .
+  next 001f
+  live out:{}
+block 001f
+  pred 000e
+  live in:{}
+  Blort.java:53@000e: return-void . <- .
+  returns
+  live out:{}
+block 001c
+  pred 0028
+  pred 0029
+  live in:{}
+  Blort.java:51@000d: goto . <- .
+  next 000d
+  live out:{}
+block 0027
+  live in:{}
+  @????: goto . <- .
+  next 001e
+  live out:{}
+block 0028
+  pred 0000
+  live in:{}
+  Blort.java:51@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:Ljava/lang/Throwable; <- .
+  @????: goto . <- .
+  next 001c
+  live out:{}
+block 0029
+  pred 0004
+  live in:{}
+  Blort.java:51@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:Ljava/lang/Throwable; <- .
+  @????: goto . <- .
+  next 001c
+  live out:{}
+
+method edgeSplitSuccessor (I)I
+first 005a
+block 0058
+  pred 005a
+  live in:{}
+  Blort.java:62@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:62@0000: move-param-int(1) v1:I <- .
+  Blort.java:62@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 0058
+  live in:{}
+  Blort.java:62@0000: const-int(0) v3:I=0 <- .
+  Blort.java:62@0001: move-int v2:I <- v3:I=0
+  Blort.java:64@0002: move-int v3:I <- v1:I
+  Blort.java:64@0003: switch({1, 2, 3}) . <- v3:I
+  next 001c
+  next 0022
+  next 0028
+  next 005b *
+  live out:{}
+block 001c
+  pred 0000
+  live in:{}
+  Blort.java:65@001c: add-const-int(1) v2:I <- v2:I
+  Blort.java:66@001f: goto . <- .
+  next 002b
+  live out:{}
+block 0022
+  pred 0000
+  live in:{}
+  Blort.java:67@0022: add-const-int(1) v2:I <- v2:I
+  Blort.java:68@0025: goto . <- .
+  next 002b
+  live out:{}
+block 0028
+  pred 0000
+  live in:{}
+  Blort.java:69@0028: add-const-int(1) v2:I <- v2:I
+  Blort.java:69@0028: goto . <- .
+  next 002b
+  live out:{}
+block 002b
+  pred 001c
+  pred 0022
+  pred 0028
+  pred 005b
+  live in:{}
+  Blort.java:72@002b: move-int v3:I <- v2:I
+  Blort.java:72@002c: move-int v0:I <- v3:I
+  Blort.java:72@002c: goto . <- .
+  next 0059
+  live out:{}
+block 0059
+  pred 002b
+  live in:{}
+  Blort.java:72@002c: return-int . <- v0:I
+  returns
+  live out:{}
+block 005a
+  live in:{}
+  @????: goto . <- .
+  next 0058
+  live out:{}
+block 005b
+  pred 0000
+  live in:{}
+  @????: goto . <- .
+  next 002b
+  live out:{}
diff --git a/dx/tests/086-ssa-edge-split/info.txt b/dx/tests/086-ssa-edge-split/info.txt
new file mode 100644
index 0000000..ff873e8
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the edge-splitting algorthim used in the conversion to SSA form.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/086-ssa-edge-split/run b/dx/tests/086-ssa-edge-split/run
new file mode 100644
index 0000000..914deb1
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --dump --width=1000 --ssa-blocks --ssa-step=edge-split Blort.class
diff --git a/dx/tests/087-ssa-local-vars/Blort.java b/dx/tests/087-ssa-local-vars/Blort.java
new file mode 100644
index 0000000..7ce8a91
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/Blort.java
@@ -0,0 +1,94 @@
+import java.io.IOException;
+class Blort {
+    private static void arrayCopyTest(int k) {
+        // A local variable assigned from an argument
+        int j = k;
+        // These two locals are defined once and used multiple times
+        String[] stringArray = new String[8];
+        Object[] objectArray = new Object[8];
+        // Should cause another move to be inserted
+        Object anotherOne = objectArray;
+
+        if (anotherOne != null) {
+            System.out.println("foo");
+        }
+
+        // "i" is used in a loop
+        for (int i = 0; i < stringArray.length; i++)
+            stringArray[i] = new String(Integer.toString(i));
+
+        System.out.println("string -> object");
+        System.arraycopy(stringArray, 0, objectArray, 0, stringArray.length);
+        System.out.println("object -> string");
+        System.arraycopy(objectArray, 0, stringArray, 0, stringArray.length);
+        System.out.println("object -> string (modified)");
+        objectArray[4] = new Object();
+        try {
+            System.arraycopy(objectArray, 0, stringArray, 0,stringArray.length);
+        } catch (ArrayStoreException ase) {
+            // "ase" is an unused local which still must be preserved
+            System.out.println("caught ArrayStoreException (expected)");
+        }
+    }
+
+    private void testConstructor() {
+        Blort foo = null;
+        try {
+            foo = new Blort();
+        } catch (Exception ex) {
+        }
+        System.err.println(foo);
+    }
+    /**
+     * Stolen from
+     * java/android/org/apache/http/impl/io/AbstractMessageParser.java
+     * Simplified.
+     *
+     * Checks to see that local variable assignment is preserved through
+     * phi's. The key component here is the assignment of previous = current.
+     */
+    public static void parseHeaderGroup(
+            final Object headGroup,
+            final Object inbuffer,
+            int maxHeaderCount,
+            int maxLineLen)
+        throws  IOException {
+
+        StringBuilder current = null;
+        StringBuilder previous = null;
+        for (;;) {
+            if (current == null) {
+                current = new StringBuilder(64);
+            } else {
+                current.length();
+            }
+            int l = inbuffer.hashCode();
+            if (l == -1 || current.length() < 1) {
+                break;
+            }
+
+            if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
+                int i = 0;
+                while (i < current.length()) {
+                    char ch = current.charAt(i);
+                    if (ch != ' ' && ch != '\t') {
+                        break;
+                    }
+                    i++;
+                }
+                if (maxLineLen > 0
+                        && previous.length() + 1 + current.length() - i > maxLineLen) {
+                    throw new IOException("Maximum line length limit exceeded");
+                }
+                previous.append(' ');
+                previous.append(current, i, current.length() - i);
+            } else {
+                previous = current;
+                current = null;
+            }
+            if (maxHeaderCount > 0) {
+                throw new IOException("Maximum header count exceeded");
+            }
+        }
+    }
+}
diff --git a/dx/tests/087-ssa-local-vars/expected.txt b/dx/tests/087-ssa-local-vars/expected.txt
new file mode 100644
index 0000000..6c0a6f7
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/expected.txt
@@ -0,0 +1,1266 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  live in:{}
+  Blort.java:2@0000: move-param-object(0) v2:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+  live out:{2}
+block 0000
+  pred 000a
+  live in:{2}
+  Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+  >}(java.lang.Object.<init>:()V catch) . <- v2:NffffLBlort;
+  next 0004
+  live out:{}
+block 0004
+  pred 0000
+  live in:{}
+  Blort.java:2@0004: goto . <- .
+  next 000b
+  live out:{}
+block 000b
+  pred 0004
+  live in:{}
+  Blort.java:2@0004: return-void . <- .
+  next 000d
+  live out:{}
+block 000c
+  live in:{}
+  @????: goto . <- .
+  next 000a
+  live out:{}
+block 000d
+  pred 000b
+  live in:{}
+  returns
+  live out:{}
+
+method arrayCopyTest (I)V
+first 012c
+block 0112
+  pred 012c
+  live in:{62, 63}
+  Blort.java:5@0000: move-param-int(0) v12:"k"I <- .
+  Blort.java:5@0000: goto . <- .
+  next 0000
+  live out:{12, 62, 63}
+block 0119
+  pred 0000
+  live in:{62, 63}
+  Blort.java:7@0004: Rop{move-result-pseudo [Ljava/lang/String; <- . flows} v15
+  :[Ljava/lang/String; <- .
+  Blort.java:7@0004: goto . <- .
+  next 0007
+  live out:{15, 62, 63}
+block 0000
+  pred 0112
+  live in:{12, 62, 63}
+  Blort.java:5@0001: move-int v13:"j"I <- v12:I
+  Blort.java:7@0004: new-array-object(java.lang.String[] catch) . <- v63:I=8
+  next 0119
+  live out:{62, 63}
+block 011a
+  pred 0007
+  live in:{15, 62}
+  Blort.java:8@000a: Rop{move-result-pseudo [Ljava/lang/Object; <- . flows} v17
+  :[Ljava/lang/Object; <- .
+  Blort.java:8@000a: goto . <- .
+  next 000d
+  live out:{15, 17, 62}
+block 0007
+  pred 0119
+  live in:{15, 62, 63}
+  @????: mark-local-object . <- v15:"stringArray"[Ljava/lang/String;
+  Blort.java:8@000a: new-array-object(java.lang.Object[] catch) . <- v63:I=8
+  next 011a
+  live out:{15, 62}
+block 000d
+  pred 011a
+  live in:{15, 17, 62}
+  @????: mark-local-object . <- v17:"objectArray"[Ljava/lang/Object;
+  Blort.java:10@000f: move-object v18:"anotherOne"[Ljava/lang/Object; <- v17:[L
+  java/lang/Object;
+  Blort.java:12@0013: if-eqz-object . <- v18:[Ljava/lang/Object;
+  next 0016 *
+  next 0131
+  live out:{15, 17, 62}
+block 011b
+  pred 0016
+  live in:{15, 17, 62}
+  Blort.java:13@0016: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v19:Ljava/io/PrintStream; <- .
+  Blort.java:13@0016: goto . <- .
+  next 0019
+  live out:{15, 17, 19, 62}
+block 0016
+  pred 000d
+  live in:{15, 17, 62}
+  Blort.java:13@0016: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 011b
+  live out:{15, 17, 62}
+block 011c
+  pred 0019
+  live in:{15, 17, 19, 62}
+  Blort.java:13@0019: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v20
+  :Ljava/lang/String;="foo" <- .
+  Blort.java:13@0019: goto . <- .
+  next 001b
+  live out:{15, 17, 19, 20, 62}
+block 0019
+  pred 011b
+  live in:{15, 17, 19, 62}
+  Blort.java:13@0019: const-object("foo" catch) . <- .
+  next 011c
+  live out:{15, 17, 19, 62}
+block 001b
+  pred 011c
+  live in:{15, 17, 19, 20, 62}
+  Blort.java:13@001b: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v19:Ljava/io/PrintStream; v20:Ljava/lang/String;="foo"
+  next 0130
+  live out:{15, 17, 62}
+block 001e
+  pred 0130
+  pred 0131
+  live in:{15, 17, 62}
+  Blort.java:17@001e: const-int(0) v23:I=0 <- .
+  @????: mark-local-int . <- v23:"i"I
+  Blort.java:17@001f: goto . <- .
+  next 0021
+  live out:{15, 17, 23, 62}
+block 011d
+  pred 0021
+  live in:{15, 17, 30, 62}
+  Blort.java:17@0024: Rop{move-result-pseudo I <- . flows} v31:I <- .
+  Blort.java:17@0024: goto . <- .
+  next 0025
+  live out:{15, 17, 30, 31, 62}
+block 0021
+  pred 001e
+  pred 0038
+  live in:{15, 17, 62}
+  @????: phi v30:"i"I <- v23:"i"I[b=001e] v34:"i"I[b=0038]
+  Blort.java:17@0024: array-length(catch) . <- v15:[Ljava/lang/String;
+  next 011d
+  live out:{15, 17, 30, 62}
+block 0025
+  pred 011d
+  live in:{15, 17, 30, 31, 62}
+  Blort.java:17@0025: if-ge-int . <- v30:I v31:I
+  next 0028 *
+  next 003e
+  live out:{15, 17, 30, 62}
+block 011e
+  pred 0028
+  live in:{15, 17, 30, 62}
+  Blort.java:18@002b: Rop{move-result-pseudo N002bLjava/lang/String; <- . flows
+  } v32:N002bLjava/lang/String; <- .
+  Blort.java:18@002b: goto . <- .
+  next 002e
+  live out:{15, 17, 30, 32, 62}
+block 0028
+  pred 0025
+  live in:{15, 17, 30, 62}
+  Blort.java:18@002b: new-instance(java.lang.String catch) . <- .
+  next 011e
+  live out:{15, 17, 30, 62}
+block 011f
+  pred 002e
+  live in:{15, 17, 30, 32, 62}
+  Blort.java:18@0031: Rop{move-result Ljava/lang/String; <- . flows} v33:Ljava/
+  lang/String; <- .
+  Blort.java:18@0031: goto . <- .
+  next 0034
+  live out:{15, 17, 30, 32, 33, 62}
+block 002e
+  pred 011e
+  live in:{15, 17, 30, 32, 62}
+  Blort.java:18@0031: Rop{invoke-static . <- I call throws <any>}(java.lang.Int
+  eger.toString:(I)Ljava/lang/String; catch) . <- v30:I
+  next 011f
+  live out:{15, 17, 30, 32, 62}
+block 0034
+  pred 011f
+  live in:{15, 17, 30, 32, 33, 62}
+  Blort.java:18@0034: Rop{invoke-direct . <- Ljava/lang/String; Ljava/lang/Stri
+  ng; call throws <any>}(java.lang.String.<init>:(Ljava/lang/String;)V catch) .
+   <- v32:N002bLjava/lang/String; v33:Ljava/lang/String;
+  next 0037
+  live out:{15, 17, 30, 32, 62}
+block 0037
+  pred 0034
+  live in:{15, 17, 30, 32, 62}
+  Blort.java:18@0037: aput-object(catch) . <- v32:Ljava/lang/String; v15:[Ljava
+  /lang/String; v30:I
+  next 0038
+  live out:{15, 17, 30, 62}
+block 0038
+  pred 0037
+  live in:{15, 17, 30, 62}
+  Blort.java:17@0038: add-const-int(1) v34:"i"I <- v30:I
+  Blort.java:17@003b: goto . <- .
+  next 0021
+  live out:{15, 17, 34, 62}
+block 0120
+  pred 003e
+  live in:{15, 17, 62}
+  Blort.java:20@003e: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v35:Ljava/io/PrintStream; <- .
+  Blort.java:20@003e: goto . <- .
+  next 0041
+  live out:{15, 17, 35, 62}
+block 003e
+  pred 0025
+  live in:{15, 17, 62}
+  Blort.java:20@003e: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0120
+  live out:{15, 17, 62}
+block 0121
+  pred 0041
+  live in:{15, 17, 35, 62}
+  Blort.java:20@0041: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v36
+  :Ljava/lang/String;="string -> object" <- .
+  Blort.java:20@0041: goto . <- .
+  next 0043
+  live out:{15, 17, 35, 36, 62}
+block 0041
+  pred 0120
+  live in:{15, 17, 35, 62}
+  Blort.java:20@0041: const-object("string -> object" catch) . <- .
+  next 0121
+  live out:{15, 17, 35, 62}
+block 0043
+  pred 0121
+  live in:{15, 17, 35, 36, 62}
+  Blort.java:20@0043: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v35:Ljava/io/PrintStream; v36:Ljava/lang/String;="string -> objec
+  t"
+  next 0046
+  live out:{15, 17, 62}
+block 0122
+  pred 0046
+  live in:{15, 17, 62}
+  Blort.java:21@004b: Rop{move-result-pseudo I <- . flows} v39:I <- .
+  Blort.java:21@004b: goto . <- .
+  next 004c
+  live out:{15, 17, 39, 62}
+block 0046
+  pred 0043
+  live in:{15, 17, 62}
+  Blort.java:21@004b: array-length(catch) . <- v15:[Ljava/lang/String;
+  next 0122
+  live out:{15, 17, 62}
+block 004c
+  pred 0122
+  live in:{15, 17, 39, 62}
+  Blort.java:21@004c: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+  ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+  java/lang/Object;II)V catch) . <- v15:[Ljava/lang/String; v62:I=0 v17:[Ljava/
+  lang/Object; v62:I=0 v39:I
+  next 004f
+  live out:{15, 17, 62}
+block 0123
+  pred 004f
+  live in:{15, 17, 62}
+  Blort.java:22@004f: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v40:Ljava/io/PrintStream; <- .
+  Blort.java:22@004f: goto . <- .
+  next 0052
+  live out:{15, 17, 40, 62}
+block 004f
+  pred 004c
+  live in:{15, 17, 62}
+  Blort.java:22@004f: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0123
+  live out:{15, 17, 62}
+block 0124
+  pred 0052
+  live in:{15, 17, 40, 62}
+  Blort.java:22@0052: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v41
+  :Ljava/lang/String;="object -> string" <- .
+  Blort.java:22@0052: goto . <- .
+  next 0054
+  live out:{15, 17, 40, 41, 62}
+block 0052
+  pred 0123
+  live in:{15, 17, 40, 62}
+  Blort.java:22@0052: const-object("object -> string" catch) . <- .
+  next 0124
+  live out:{15, 17, 40, 62}
+block 0054
+  pred 0124
+  live in:{15, 17, 40, 41, 62}
+  Blort.java:22@0054: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v40:Ljava/io/PrintStream; v41:Ljava/lang/String;="object -> strin
+  g"
+  next 0057
+  live out:{15, 17, 62}
+block 0125
+  pred 0057
+  live in:{15, 17, 62}
+  Blort.java:23@005c: Rop{move-result-pseudo I <- . flows} v44:I <- .
+  Blort.java:23@005c: goto . <- .
+  next 005d
+  live out:{15, 17, 44, 62}
+block 0057
+  pred 0054
+  live in:{15, 17, 62}
+  Blort.java:23@005c: array-length(catch) . <- v15:[Ljava/lang/String;
+  next 0125
+  live out:{15, 17, 62}
+block 005d
+  pred 0125
+  live in:{15, 17, 44, 62}
+  Blort.java:23@005d: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+  ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+  java/lang/Object;II)V catch) . <- v17:[Ljava/lang/Object; v62:I=0 v15:[Ljava/
+  lang/String; v62:I=0 v44:I
+  next 0060
+  live out:{15, 17}
+block 0126
+  pred 0060
+  live in:{15, 17}
+  Blort.java:24@0060: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v45:Ljava/io/PrintStream; <- .
+  Blort.java:24@0060: goto . <- .
+  next 0063
+  live out:{15, 17, 45}
+block 0060
+  pred 005d
+  live in:{15, 17}
+  Blort.java:24@0060: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0126
+  live out:{15, 17}
+block 0127
+  pred 0063
+  live in:{15, 17, 45}
+  Blort.java:24@0063: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v46
+  :Ljava/lang/String;="object -> string (modified)" <- .
+  Blort.java:24@0063: goto . <- .
+  next 0065
+  live out:{15, 17, 45, 46}
+block 0063
+  pred 0126
+  live in:{15, 17, 45}
+  Blort.java:24@0063: const-object("object -> string (modified)" catch) . <- .
+  next 0127
+  live out:{15, 17, 45}
+block 0065
+  pred 0127
+  live in:{15, 17, 45, 46}
+  Blort.java:24@0065: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v45:Ljava/io/PrintStream; v46:Ljava/lang/String;="object -> strin
+  g (modified)"
+  next 0068
+  live out:{15, 17}
+block 0128
+  pred 0068
+  live in:{15, 17, 47}
+  Blort.java:25@006a: Rop{move-result-pseudo N006aLjava/lang/Object; <- . flows
+  } v48:N006aLjava/lang/Object; <- .
+  Blort.java:25@006a: goto . <- .
+  next 006d
+  live out:{15, 17, 47, 48}
+block 0068
+  pred 0065
+  live in:{15, 17}
+  Blort.java:25@0069: const-int(4) v47:I=4 <- .
+  Blort.java:25@006a: new-instance(java.lang.Object catch) . <- .
+  next 0128
+  live out:{15, 17, 47}
+block 006d
+  pred 0128
+  live in:{15, 17, 47, 48}
+  Blort.java:25@006e: Rop{invoke-direct . <- Ljava/lang/Object; call throws <an
+  y>}(java.lang.Object.<init>:()V catch) . <- v48:N006aLjava/lang/Object;
+  next 0071
+  live out:{15, 17, 47, 48}
+block 0071
+  pred 006d
+  live in:{15, 17, 47, 48}
+  Blort.java:25@0071: aput-object(catch) . <- v48:Ljava/lang/Object; v17:[Ljava
+  /lang/Object; v47:I=4
+  next 0072
+  live out:{15, 17}
+block 0129
+  pred 0072
+  live in:{15, 17, 49, 50}
+  Blort.java:27@0077: Rop{move-result-pseudo I <- . flows} v51:I <- .
+  Blort.java:27@0077: goto . <- .
+  next 0078
+  live out:{15, 17, 49, 50, 51}
+block 0072
+  pred 0071
+  live in:{15, 17}
+  Blort.java:27@0073: const-int(0) v49:I=0 <- .
+  Blort.java:27@0075: const-int(0) v50:I=0 <- .
+  Blort.java:27@0077: array-length(catch java.lang.ArrayStoreException) . <- v1
+  5:[Ljava/lang/String;
+  next 012d
+  next 0129 *
+  live out:{15, 17, 49, 50}
+block 0078
+  pred 0129
+  live in:{15, 17, 49, 50, 51}
+  Blort.java:27@0078: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+  ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+  java/lang/Object;II)V catch java.lang.ArrayStoreException) . <- v17:[Ljava/la
+  ng/Object; v49:I=0 v15:[Ljava/lang/String; v50:I=0 v51:I
+  next 012e
+  next 007b *
+  live out:{}
+block 007b
+  pred 0078
+  live in:{}
+  Blort.java:31@007b: goto . <- .
+  next 0088
+  live out:{}
+block 012a
+  pred 007e
+  live in:{}
+  Blort.java:30@0080: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v59:Ljava/io/PrintStream; <- .
+  Blort.java:30@0080: goto . <- .
+  next 0083
+  live out:{59}
+block 007e
+  pred 0107
+  live in:{58}
+  @????: mark-local-object . <- v58:"ase"Ljava/lang/ArrayStoreException;
+  Blort.java:30@0080: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 012a
+  live out:{}
+block 012b
+  pred 0083
+  live in:{59}
+  Blort.java:30@0083: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v60
+  :Ljava/lang/String;="caught ArrayStoreException (expected)" <- .
+  Blort.java:30@0083: goto . <- .
+  next 0085
+  live out:{59, 60}
+block 0083
+  pred 012a
+  live in:{59}
+  Blort.java:30@0083: const-object("caught ArrayStoreException (expected)" catc
+  h) . <- .
+  next 012b
+  live out:{59}
+block 0085
+  pred 012b
+  live in:{59, 60}
+  Blort.java:30@0085: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v59:Ljava/io/PrintStream; v60:Ljava/lang/String;="caught ArraySto
+  reException (expected)"
+  next 012f
+  live out:{}
+block 0088
+  pred 007b
+  pred 012f
+  live in:{}
+  Blort.java:32@0088: goto . <- .
+  next 0113
+  live out:{}
+block 0113
+  pred 0088
+  live in:{}
+  Blort.java:32@0088: return-void . <- .
+  next 0132
+  live out:{}
+block 0107
+  pred 012d
+  pred 012e
+  live in:{}
+  @????: phi v58:Ljava/lang/ArrayStoreException; <- v52:Ljava/lang/ArrayStoreEx
+  ception;[b=012e] v61:Ljava/lang/ArrayStoreException;[b=012d]
+  Blort.java:28@007e: goto . <- .
+  next 007e
+  live out:{58}
+block 012c
+  live in:{}
+  @????: const-int(8) v63:I=8 <- .
+  @????: const-int(0) v62:I=0 <- .
+  @????: goto . <- .
+  next 0112
+  live out:{62, 63}
+block 012d
+  pred 0072
+  live in:{}
+  Blort.java:28@007e: Rop{move-exception Ljava/lang/ArrayStoreException; <- . f
+  lows} v61:Ljava/lang/ArrayStoreException; <- .
+  @????: goto . <- .
+  next 0107
+  live out:{61}
+block 012e
+  pred 0078
+  live in:{}
+  Blort.java:28@007e: Rop{move-exception Ljava/lang/ArrayStoreException; <- . f
+  lows} v52:Ljava/lang/ArrayStoreException; <- .
+  @????: goto . <- .
+  next 0107
+  live out:{52}
+block 012f
+  pred 0085
+  live in:{}
+  @????: goto . <- .
+  next 0088
+  live out:{}
+block 0130
+  pred 001b
+  live in:{15, 17, 62}
+  @????: goto . <- .
+  next 001e
+  live out:{15, 17, 62}
+block 0131
+  pred 000d
+  live in:{15, 17, 62}
+  @????: goto . <- .
+  next 001e
+  live out:{15, 17, 62}
+block 0132
+  pred 0113
+  live in:{}
+  returns
+  live out:{}
+
+method testConstructor ()V
+first 0035
+block 002c
+  pred 0035
+  live in:{}
+  Blort.java:35@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:35@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0000
+  pred 002c
+  live in:{}
+  Blort.java:35@0000: const-object-nothrow(null) v7:<null>=null <- .
+  @????: mark-local-object . <- v7:"foo"LBlort;
+  Blort.java:35@0001: goto . <- .
+  next 0002
+  live out:{7}
+block 0033
+  pred 0002
+  live in:{7}
+  Blort.java:37@0002: Rop{move-result-pseudo N0002LBlort; <- . flows} v8:N0002L
+  Blort; <- .
+  Blort.java:37@0002: goto . <- .
+  next 0005
+  live out:{7, 8}
+block 0002
+  pred 0000
+  live in:{7}
+  Blort.java:37@0002: new-instance(Blort catch java.lang.Exception) . <- .
+  next 0036
+  next 0033 *
+  live out:{7}
+block 0005
+  pred 0033
+  live in:{7, 8}
+  Blort.java:37@0006: Rop{invoke-direct . <- LBlort; call throws <any>}(Blort.<
+  init>:()V catch java.lang.Exception) . <- v8:N0002LBlort;
+  next 0037
+  next 0009 *
+  live out:{7, 8}
+block 0009
+  pred 0005
+  live in:{8}
+  @????: mark-local-object . <- v8:"foo"LBlort;
+  Blort.java:37@0009: goto . <- .
+  next 000a
+  live out:{8}
+block 000a
+  pred 0009
+  live in:{8}
+  Blort.java:39@000a: goto . <- .
+  next 000e
+  live out:{8}
+block 000d
+  pred 0023
+  live in:{7}
+  Blort.java:38@000d: goto . <- .
+  next 000e
+  live out:{7}
+block 0034
+  pred 000e
+  live in:{14}
+  Blort.java:40@000e: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v15:Ljava/io/PrintStream; <- .
+  Blort.java:40@000e: goto . <- .
+  next 0011
+  live out:{14, 15}
+block 000e
+  pred 000a
+  pred 000d
+  live in:{}
+  @????: phi v14:"foo"LBlort; <- v8:"foo"LBlort;[b=000a] v7:"foo"LBlort;[b=000d
+  ]
+  Blort.java:40@000e: get-static-object(java.lang.System.err:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0034
+  live out:{14}
+block 0011
+  pred 0034
+  live in:{14, 15}
+  Blort.java:40@0012: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  Object; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/Object;)V 
+  catch) . <- v15:Ljava/io/PrintStream; v14:LBlort;
+  next 0015
+  live out:{}
+block 0015
+  pred 0011
+  live in:{}
+  Blort.java:41@0015: goto . <- .
+  next 002d
+  live out:{}
+block 002d
+  pred 0015
+  live in:{}
+  Blort.java:41@0015: return-void . <- .
+  next 0038
+  live out:{}
+block 0023
+  pred 0036
+  pred 0037
+  live in:{7}
+  Blort.java:38@000d: goto . <- .
+  next 000d
+  live out:{7}
+block 0035
+  live in:{}
+  @????: goto . <- .
+  next 002c
+  live out:{}
+block 0036
+  pred 0002
+  live in:{7}
+  Blort.java:38@000d: Rop{move-exception Ljava/lang/Exception; <- . flows} v19:
+  Ljava/lang/Exception; <- .
+  @????: goto . <- .
+  next 0023
+  live out:{7}
+block 0037
+  pred 0005
+  live in:{7}
+  Blort.java:38@000d: Rop{move-exception Ljava/lang/Exception; <- . flows} v9:L
+  java/lang/Exception; <- .
+  @????: goto . <- .
+  next 0023
+  live out:{7}
+block 0038
+  pred 002d
+  live in:{}
+  returns
+  live out:{}
+
+method parseHeaderGroup (Ljava/lang/Object;Ljava/lang/Object;II)V
+first 01c6
+block 01ae
+  pred 01c6
+  live in:{99, 100, 101}
+  Blort.java:57@0000: move-param-object(0) v15:"headGroup"Ljava/lang/Object; <-
+   .
+  Blort.java:57@0000: move-param-object(1) v16:"inbuffer"Ljava/lang/Object; <- 
+  .
+  Blort.java:57@0000: move-param-int(2) v17:"maxHeaderCount"I <- .
+  Blort.java:57@0000: move-param-int(3) v18:"maxLineLen"I <- .
+  Blort.java:57@0000: goto . <- .
+  next 0000
+  live out:{16, 17, 18, 99, 100, 101}
+block 0000
+  pred 01ae
+  live in:{16, 17, 18, 99, 100, 101}
+  Blort.java:57@0000: const-object-nothrow(null) v19:<null>=null <- .
+  @????: mark-local-object . <- v19:"current"Ljava/lang/StringBuilder;
+  Blort.java:58@0003: const-object-nothrow(null) v20:<null>=null <- .
+  @????: mark-local-object . <- v20:"previous"Ljava/lang/StringBuilder;
+  Blort.java:58@0004: goto . <- .
+  next 01ca
+  live out:{16, 17, 18, 19, 20, 99, 100, 101}
+block 0006
+  pred 01ca
+  live in:{16, 17, 18, 30, 31, 99, 100, 101}
+  Blort.java:60@0008: if-nez-object . <- v31:Ljava/lang/StringBuilder;
+  next 000b *
+  next 0019
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01b5
+  pred 000b
+  live in:{16, 17, 18, 30, 99, 100, 101}
+  Blort.java:61@000b: Rop{move-result-pseudo N000bLjava/lang/StringBuilder; <- 
+  . flows} v32:N000bLjava/lang/StringBuilder; <- .
+  Blort.java:61@000b: goto . <- .
+  next 000e
+  live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 000b
+  pred 0006
+  live in:{16, 17, 18, 30, 99, 100, 101}
+  Blort.java:61@000b: new-instance(java.lang.StringBuilder catch) . <- .
+  next 01b5
+  live out:{16, 17, 18, 30, 99, 100, 101}
+block 000e
+  pred 01b5
+  live in:{16, 17, 18, 30, 32, 99, 100, 101}
+  Blort.java:61@000f: const-int(64) v33:I=64 <- .
+  Blort.java:61@0011: Rop{invoke-direct . <- Ljava/lang/StringBuilder; I call t
+  hrows <any>}(java.lang.StringBuilder.<init>:(I)V catch) . <- v32:N000bLjava/l
+  ang/StringBuilder; v33:I=64
+  next 0014
+  live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 0014
+  pred 000e
+  live in:{16, 17, 18, 30, 32, 99, 100, 101}
+  @????: mark-local-object . <- v32:"current"Ljava/lang/StringBuilder;
+  Blort.java:61@0016: goto . <- .
+  next 001f
+  live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 01b6
+  pred 0019
+  live in:{16, 17, 18, 30, 31, 99, 100, 101}
+  Blort.java:63@001b: goto . <- .
+  next 001e
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 0019
+  pred 0006
+  live in:{16, 17, 18, 30, 31, 99, 100, 101}
+  Blort.java:63@001b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v31:Ljava/lang/Str
+  ingBuilder;
+  next 01b6
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 001e
+  pred 01b6
+  live in:{16, 17, 18, 30, 31, 99, 100, 101}
+  @????: goto . <- .
+  next 001f
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01b7
+  pred 001f
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:65@0020: Rop{move-result I <- . flows} v40:I <- .
+  Blort.java:65@0020: goto . <- .
+  next 0023
+  live out:{16, 17, 18, 30, 39, 40, 99, 100, 101}
+block 001f
+  pred 0014
+  pred 001e
+  live in:{16, 17, 18, 30, 99, 100, 101}
+  @????: phi v39:"current"Ljava/lang/StringBuilder; <- v32:"current"Ljava/lang/
+  StringBuilder;[b=0014] v31:"current"Ljava/lang/StringBuilder;[b=001e]
+  Blort.java:65@0020: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <a
+  ny>}(java.lang.Object.hashCode:()I catch) . <- v16:Ljava/lang/Object;
+  next 01b7
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0023
+  pred 01b7
+  live in:{16, 17, 18, 30, 39, 40, 99, 100, 101}
+  @????: mark-local-int . <- v40:"l"I
+  Blort.java:66@0027: const-int(-1) v41:I=-1 <- .
+  Blort.java:66@0028: if-eq-int . <- v40:I v41:I=-1
+  next 002b *
+  next 01d4
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01b8
+  pred 002b
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:66@002d: Rop{move-result I <- . flows} v42:I <- .
+  Blort.java:66@002d: goto . <- .
+  next 0030
+  live out:{16, 17, 18, 30, 39, 42, 99, 100, 101}
+block 002b
+  pred 0023
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:66@002d: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+  ingBuilder;
+  next 01b8
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0030
+  pred 01b8
+  live in:{16, 17, 18, 30, 39, 42, 99, 100, 101}
+  Blort.java:66@0030: const-int(1) v43:I=1 <- .
+  Blort.java:66@0031: if-ge-int . <- v42:I v43:I=1
+  next 0034 *
+  next 0037
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0034
+  pred 0030
+  live in:{}
+  Blort.java:67@0034: goto . <- .
+  next 00d6
+  live out:{}
+block 01b9
+  pred 0037
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@003a: Rop{move-result C <- . flows} v45:C <- .
+  Blort.java:70@003a: goto . <- .
+  next 003d
+  live out:{16, 17, 18, 30, 39, 45, 99, 100, 101}
+block 0037
+  pred 0030
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@003a: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call 
+  throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+  StringBuilder; v100:I=0
+  next 01b9
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 003d
+  pred 01b9
+  live in:{16, 17, 18, 30, 39, 45, 99, 100, 101}
+  Blort.java:70@003f: if-eq-int . <- v45:I v99:I=32
+  next 0042 *
+  next 01d3
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01ba
+  pred 0042
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@0045: Rop{move-result C <- . flows} v48:C <- .
+  Blort.java:70@0045: goto . <- .
+  next 0048
+  live out:{16, 17, 18, 30, 39, 48, 99, 100, 101}
+block 0042
+  pred 003d
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@0045: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call 
+  throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+  StringBuilder; v100:I=0
+  next 01ba
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0048
+  pred 01ba
+  live in:{16, 17, 18, 30, 39, 48, 99, 100, 101}
+  Blort.java:70@004a: if-ne-int . <- v48:I v101:I=9
+  next 01d2 *
+  next 01d1
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 004d
+  pred 01c9
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:70@004f: if-eqz-object . <- v30:Ljava/lang/StringBuilder;
+  next 0052 *
+  next 01d0
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0052
+  pred 004d
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:71@0052: const-int(0) v67:I=0 <- .
+  @????: mark-local-int . <- v67:"i"I
+  Blort.java:71@0053: goto . <- .
+  next 0055
+  live out:{16, 17, 18, 30, 39, 67, 99, 100, 101}
+block 01bb
+  pred 0055
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:72@0059: Rop{move-result I <- . flows} v72:I <- .
+  Blort.java:72@0059: goto . <- .
+  next 005c
+  live out:{16, 17, 18, 30, 39, 71, 72, 99, 100, 101}
+block 0055
+  pred 0052
+  pred 0079
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  @????: phi v71:"i"I <- v67:"i"I[b=0052] v78:"i"I[b=0079]
+  Blort.java:72@0059: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+  ingBuilder;
+  next 01bb
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 005c
+  pred 01bb
+  live in:{16, 17, 18, 30, 39, 71, 72, 99, 100, 101}
+  Blort.java:72@005c: if-ge-int . <- v71:I v72:I
+  next 005f *
+  next 01cf
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bc
+  pred 005f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:73@0063: Rop{move-result C <- . flows} v73:C <- .
+  Blort.java:73@0063: goto . <- .
+  next 0066
+  live out:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+block 005f
+  pred 005c
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:73@0063: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call 
+  throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+  StringBuilder; v71:I
+  next 01bc
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0066
+  pred 01bc
+  live in:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+  @????: mark-local-int . <- v73:"ch"C
+  Blort.java:74@006c: if-eq-int . <- v73:I v99:I=32
+  next 006f *
+  next 01ce
+  live out:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+block 006f
+  pred 0066
+  live in:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+  Blort.java:74@0073: if-eq-int . <- v73:I v101:I=9
+  next 0076 *
+  next 01cd
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0076
+  pred 006f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:75@0076: goto . <- .
+  next 01c8
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0079
+  pred 01cd
+  pred 01ce
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:77@0079: add-const-int(1) v78:"i"I <- v71:I
+  Blort.java:78@007c: goto . <- .
+  next 0055
+  live out:{16, 17, 18, 30, 39, 78, 99, 100, 101}
+block 007f
+  pred 01c8
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:79@0080: if-lez-int . <- v18:I
+  next 0083 *
+  next 01cc
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bd
+  pred 0083
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:79@0085: Rop{move-result I <- . flows} v82:I <- .
+  Blort.java:79@0085: goto . <- .
+  next 0088
+  live out:{16, 17, 18, 30, 39, 71, 82, 99, 100, 101}
+block 0083
+  pred 007f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:79@0085: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v30:Ljava/lang/Str
+  ingBuilder;
+  next 01bd
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01be
+  pred 0088
+  live in:{16, 17, 18, 30, 39, 71, 84, 99, 100, 101}
+  Blort.java:79@008c: Rop{move-result I <- . flows} v85:I <- .
+  Blort.java:79@008c: goto . <- .
+  next 008f
+  live out:{16, 17, 18, 30, 39, 71, 84, 85, 99, 100, 101}
+block 0088
+  pred 01bd
+  live in:{16, 17, 18, 30, 39, 71, 82, 99, 100, 101}
+  Blort.java:79@0089: add-const-int(1) v84:I <- v82:I
+  Blort.java:79@008c: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+  ingBuilder;
+  next 01be
+  live out:{16, 17, 18, 30, 39, 71, 84, 99, 100, 101}
+block 008f
+  pred 01be
+  live in:{16, 17, 18, 30, 39, 71, 84, 85, 99, 100, 101}
+  Blort.java:79@008f: add-int v86:I <- v84:I v85:I
+  Blort.java:79@0092: sub-int v87:I <- v86:I v71:I
+  Blort.java:79@0094: if-le-int . <- v87:I v18:I
+  next 0097 *
+  next 01cb
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bf
+  pred 0097
+  live in:{}
+  Blort.java:81@0097: Rop{move-result-pseudo N0097Ljava/io/IOException; <- . fl
+  ows} v88:N0097Ljava/io/IOException; <- .
+  Blort.java:81@0097: goto . <- .
+  next 009a
+  live out:{88}
+block 0097
+  pred 008f
+  live in:{}
+  Blort.java:81@0097: new-instance(java.io.IOException catch) . <- .
+  next 01bf
+  live out:{}
+block 01c0
+  pred 009a
+  live in:{88}
+  Blort.java:81@009b: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v89
+  :Ljava/lang/String;="Maximum line length limit exceeded" <- .
+  Blort.java:81@009b: goto . <- .
+  next 009d
+  live out:{88, 89}
+block 009a
+  pred 01bf
+  live in:{88}
+  Blort.java:81@009b: const-object("Maximum line length limit exceeded" catch) 
+  . <- .
+  next 01c0
+  live out:{88}
+block 009d
+  pred 01c0
+  live in:{88, 89}
+  Blort.java:81@009d: Rop{invoke-direct . <- Ljava/io/IOException; Ljava/lang/S
+  tring; call throws <any>}(java.io.IOException.<init>:(Ljava/lang/String;)V ca
+  tch) . <- v88:N0097Ljava/io/IOException; v89:Ljava/lang/String;="Maximum line
+   length limit exceeded"
+  next 00a0
+  live out:{88}
+block 00a0
+  pred 009d
+  live in:{88}
+  Blort.java:81@00a0: throw(catch) . <- v88:Ljava/io/IOException;
+  next 01d5
+  live out:{}
+block 01c1
+  pred 00a1
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:83@00a5: goto . <- .
+  next 00a8
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 00a1
+  pred 01cb
+  pred 01cc
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:83@00a5: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call 
+  throws <any>}(java.lang.StringBuilder.append:(C)Ljava/lang/StringBuilder; cat
+  ch) . <- v30:Ljava/lang/StringBuilder; v99:I=32
+  next 01c1
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c2
+  pred 00a8
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:84@00b1: Rop{move-result I <- . flows} v94:I <- .
+  Blort.java:84@00b1: goto . <- .
+  next 00b4
+  live out:{16, 17, 18, 30, 39, 71, 94, 99, 100, 101}
+block 00a8
+  pred 01c1
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  Blort.java:84@00b1: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+  rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+  ingBuilder;
+  next 01c2
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c3
+  pred 00b4
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:84@00b7: goto . <- .
+  next 00ba
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00b4
+  pred 01c2
+  live in:{16, 17, 18, 30, 39, 71, 94, 99, 100, 101}
+  Blort.java:84@00b6: sub-int v95:I <- v94:I v71:I
+  Blort.java:84@00b7: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/CharSequence; I I call throws <any>}(java.lang.StringBuilder.append:(Ljav
+  a/lang/CharSequence;II)Ljava/lang/StringBuilder; catch) . <- v30:Ljava/lang/S
+  tringBuilder; v39:Ljava/lang/StringBuilder; v71:I v95:I
+  next 01c3
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00ba
+  pred 01c3
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  Blort.java:85@00bb: goto . <- .
+  next 01c7
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00be
+  pred 01d0
+  pred 01d1
+  live in:{16, 17, 18, 39, 99, 100, 101}
+  Blort.java:86@00c0: move-object v52:"previous"Ljava/lang/StringBuilder; <- v3
+  9:Ljava/lang/StringBuilder;
+  Blort.java:87@00c2: const-object-nothrow(null) v53:<null>=null <- .
+  @????: mark-local-object . <- v53:"current"Ljava/lang/StringBuilder;
+  Blort.java:87@00c3: goto . <- .
+  next 01c7
+  live out:{16, 17, 18, 52, 53, 99, 100, 101}
+block 00c5
+  pred 01c7
+  live in:{16, 17, 18, 61, 62, 99, 100, 101}
+  Blort.java:89@00c6: if-lez-int . <- v17:I
+  next 00c9 *
+  next 00d3
+  live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 01c4
+  pred 00c9
+  live in:{}
+  Blort.java:90@00c9: Rop{move-result-pseudo N00c9Ljava/io/IOException; <- . fl
+  ows} v63:N00c9Ljava/io/IOException; <- .
+  Blort.java:90@00c9: goto . <- .
+  next 00cc
+  live out:{63}
+block 00c9
+  pred 00c5
+  live in:{}
+  Blort.java:90@00c9: new-instance(java.io.IOException catch) . <- .
+  next 01c4
+  live out:{}
+block 01c5
+  pred 00cc
+  live in:{63}
+  Blort.java:90@00cd: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v64
+  :Ljava/lang/String;="Maximum header count exceeded" <- .
+  Blort.java:90@00cd: goto . <- .
+  next 00cf
+  live out:{63, 64}
+block 00cc
+  pred 01c4
+  live in:{63}
+  Blort.java:90@00cd: const-object("Maximum header count exceeded" catch) . <- 
+  .
+  next 01c5
+  live out:{63}
+block 00cf
+  pred 01c5
+  live in:{63, 64}
+  Blort.java:90@00cf: Rop{invoke-direct . <- Ljava/io/IOException; Ljava/lang/S
+  tring; call throws <any>}(java.io.IOException.<init>:(Ljava/lang/String;)V ca
+  tch) . <- v63:N00c9Ljava/io/IOException; v64:Ljava/lang/String;="Maximum head
+  er count exceeded"
+  next 00d2
+  live out:{63}
+block 00d2
+  pred 00cf
+  live in:{63}
+  Blort.java:90@00d2: throw(catch) . <- v63:Ljava/io/IOException;
+  next 01d5
+  live out:{}
+block 00d3
+  pred 00c5
+  live in:{16, 17, 18, 61, 62, 99, 100, 101}
+  Blort.java:92@00d3: goto . <- .
+  next 01ca
+  live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 00d6
+  pred 0034
+  pred 01d4
+  live in:{}
+  Blort.java:93@00d6: goto . <- .
+  next 01af
+  live out:{}
+block 01af
+  pred 00d6
+  live in:{}
+  Blort.java:93@00d6: return-void . <- .
+  next 01d5
+  live out:{}
+block 01c6
+  live in:{}
+  @????: const-int(9) v101:I=9 <- .
+  @????: const-int(0) v100:I=0 <- .
+  @????: const-int(32) v99:I=32 <- .
+  @????: goto . <- .
+  next 01ae
+  live out:{99, 100, 101}
+block 01c7
+  pred 00ba
+  pred 00be
+  live in:{16, 17, 18, 99, 100, 101}
+  @????: phi v61:"previous"Ljava/lang/StringBuilder; <- v52:"previous"Ljava/lan
+  g/StringBuilder;[b=00be] v30:"previous"Ljava/lang/StringBuilder;[b=00ba]
+  @????: phi v62:"current"Ljava/lang/StringBuilder; <- v53:"current"Ljava/lang/
+  StringBuilder;[b=00be] v39:"current"Ljava/lang/StringBuilder;[b=00ba]
+  @????: goto . <- .
+  next 00c5
+  live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 01c8
+  pred 0076
+  pred 01cf
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 007f
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c9
+  pred 01d2
+  pred 01d3
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 004d
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01ca
+  pred 0000
+  pred 00d3
+  live in:{16, 17, 18, 99, 100, 101}
+  @????: phi v30:"previous"Ljava/lang/StringBuilder; <- v20:"previous"Ljava/lan
+  g/StringBuilder;[b=0000] v61:"previous"Ljava/lang/StringBuilder;[b=00d3]
+  @????: phi v31:"current"Ljava/lang/StringBuilder; <- v19:"current"Ljava/lang/
+  StringBuilder;[b=0000] v62:"current"Ljava/lang/StringBuilder;[b=00d3]
+  @????: goto . <- .
+  next 0006
+  live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01cb
+  pred 008f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 00a1
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cc
+  pred 007f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 00a1
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cd
+  pred 006f
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 0079
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01ce
+  pred 0066
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 0079
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cf
+  pred 005c
+  live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+  @????: goto . <- .
+  next 01c8
+  live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01d0
+  pred 004d
+  live in:{16, 17, 18, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 00be
+  live out:{16, 17, 18, 39, 99, 100, 101}
+block 01d1
+  pred 0048
+  live in:{16, 17, 18, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 00be
+  live out:{16, 17, 18, 39, 99, 100, 101}
+block 01d2
+  pred 0048
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 01c9
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01d3
+  pred 003d
+  live in:{16, 17, 18, 30, 39, 99, 100, 101}
+  @????: goto . <- .
+  next 01c9
+  live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01d4
+  pred 0023
+  live in:{}
+  @????: goto . <- .
+  next 00d6
+  live out:{}
+block 01d5
+  pred 00a0
+  pred 00d2
+  pred 01af
+  live in:{}
+  returns
+  live out:{}
diff --git a/dx/tests/087-ssa-local-vars/info.txt b/dx/tests/087-ssa-local-vars/info.txt
new file mode 100644
index 0000000..6e9d675
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/info.txt
@@ -0,0 +1,5 @@
+This is a test case to ensure proper preservation of local variable information through the register renamer and dead code remover at the beginning of the SSA conversion.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/087-ssa-local-vars/run b/dx/tests/087-ssa-local-vars/run
new file mode 100644
index 0000000..ae97c15
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --dump --ssa-blocks Blort.class
diff --git a/dx/tests/088-ssa-combine-blocks/Blort.java b/dx/tests/088-ssa-combine-blocks/Blort.java
new file mode 100644
index 0000000..bf49dbe
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/Blort.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    /**
+     * just because this should do nothing
+     */
+    void voidFunction() {
+    }
+
+    /**
+     * Current SSA form requires each move-exception block to have
+     * a unique predecessor
+     */
+    void edgeSplitMoveException() {
+        try {
+            hashCode();
+            hashCode();
+        } catch (Throwable tr) {
+        }
+    }
+
+    /**
+     * An empty infinite loop for the empty goto optimizer
+     */
+    void infiniteLoop() {
+        while (true) {
+        }
+    }
+}
diff --git a/dx/tests/088-ssa-combine-blocks/expected.txt b/dx/tests/088-ssa-combine-blocks/expected.txt
new file mode 100644
index 0000000..7bf445d
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/expected.txt
@@ -0,0 +1,82 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+  Blort.java:17@0000: goto . <- .
+  next 0000
+block 0000
+  pred 000a
+  Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+  next 000b
+block 000b
+  pred 0000
+  Blort.java:17@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method voidFunction ()V
+first 0004
+block 0002
+  pred 0004
+  Blort.java:23@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:23@0000: goto . <- .
+  next 0003
+block 0003
+  pred 0002
+  Blort.java:23@0000: return-void . <- .
+  returns
+block 0004
+  @????: goto . <- .
+  next 0002
+
+method edgeSplitMoveException ()V
+first 0027
+block 001e
+  pred 0027
+  Blort.java:31@0000: move-param-object(0) v1:LBlort; <- .
+  Blort.java:31@0000: goto . <- .
+  next 0000
+block 0000
+  pred 001e
+  Blort.java:31@0001: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v1:LBlort;
+  next 0028
+  next 0004 *
+block 0004
+  pred 0000
+  Blort.java:32@0006: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v1:LBlort;
+  next 0028
+  next 001f *
+block 001f
+  pred 0004
+  pred 0028
+  Blort.java:35@000e: return-void . <- .
+  returns
+block 0027
+  @????: goto . <- .
+  next 001e
+block 0028
+  pred 0000
+  pred 0004
+  Blort.java:33@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v0:Ljava/lang/Throwable; <- .
+  @????: goto . <- .
+  next 001f
+
+method infiniteLoop ()V
+first 0003
+block 0002
+  pred 0003
+  Blort.java:41@0000: move-param-object(0) v0:LBlort; <- .
+  Blort.java:41@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0000
+  pred 0002
+  Blort.java:41@0000: goto . <- .
+  next 0000
+block 0003
+  @????: goto . <- .
+  next 0002
diff --git a/dx/tests/088-ssa-combine-blocks/info.txt b/dx/tests/088-ssa-combine-blocks/info.txt
new file mode 100644
index 0000000..603c1c0
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the identical-block combining algorithm, which runs after the SSA optimizer to recombine identical blocks (usually exception handlers) created during edge-splitting.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/088-ssa-combine-blocks/run b/dx/tests/088-ssa-combine-blocks/run
new file mode 100644
index 0000000..29fe559
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java
+dx --dump --width=1000 --optimize --rop-blocks Blort.class
diff --git a/dx/tests/089-dex-define-object/Class.java b/dx/tests/089-dex-define-object/Class.java
new file mode 100644
index 0000000..4de5e70
--- /dev/null
+++ b/dx/tests/089-dex-define-object/Class.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package java.lang;
+
+public class Class<T> {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/089-dex-define-object/Object.java b/dx/tests/089-dex-define-object/Object.java
new file mode 100644
index 0000000..575c736
--- /dev/null
+++ b/dx/tests/089-dex-define-object/Object.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package java.lang;
+
+public class Object {
+    public Object() {
+        // This space intentionally left blank.
+    }
+
+    public boolean equals(Object o) {
+        return true;
+    }
+
+    protected void finalize() {
+        // This space intentionally left blank.
+    }
+
+    public final native Class<? extends Object> getClass();
+    public native int hashCode();
+    public final native void notify();
+    public final native void notifyAll();
+
+    public String toString() {
+        return "blort";
+    }
+
+    public final void wait() {
+        wait(0, 0);
+    }
+
+    public final void wait(long time) {
+        wait(time, 0);
+    }
+
+    public final native void wait(long time, int frac);
+}
diff --git a/dx/tests/089-dex-define-object/String.java b/dx/tests/089-dex-define-object/String.java
new file mode 100644
index 0000000..26e7370
--- /dev/null
+++ b/dx/tests/089-dex-define-object/String.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package java.lang;
+
+public final class String {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/089-dex-define-object/expected.txt b/dx/tests/089-dex-define-object/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/089-dex-define-object/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/089-dex-define-object/info.txt b/dx/tests/089-dex-define-object/info.txt
new file mode 100644
index 0000000..e035834
--- /dev/null
+++ b/dx/tests/089-dex-define-object/info.txt
@@ -0,0 +1,4 @@
+This tests that a stripped down definition of the class Object can in
+fact be converted to a dex file. This test ensures that the conversion
+runs without failure, though the contents of the converted file are
+not checked for correctness.
diff --git a/dx/tests/089-dex-define-object/run b/dx/tests/089-dex-define-object/run
new file mode 100644
index 0000000..e53e147
--- /dev/null
+++ b/dx/tests/089-dex-define-object/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --core-library --output=blort.dex */*/*.class
+if [ -r blort.dex ]; then
+    echo Good!
+fi
diff --git a/dx/tests/090-dex-unify-arrays/Blort.java b/dx/tests/090-dex-unify-arrays/Blort.java
new file mode 100644
index 0000000..507153e
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/Blort.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    /*
+     * Note: The use of the casts after the "?" in the following are
+     * to avoid a bug in some (source code level) compilers.
+     */
+
+    static public Object test1(boolean b) {
+        return (b ? new String[1] : new Integer[1])[0];
+    }
+
+    static public int test2(boolean b) {
+        Object o = b ? (Object) new int[1] : new float[1];
+        return o.hashCode();
+    }
+
+    static public int test3(boolean b) {
+        Object o = b ? (Object) new char[1] : new double[1];
+        return o.hashCode();
+    }
+
+    static public int test4(boolean b) {
+        Object o = b ? (Object) new long[1] : new boolean[1];
+        return o.hashCode();
+    }
+
+    static public int test5(boolean b) {
+        Object o = b ? (Object) new short[1] : new Object[1];
+        return o.hashCode();
+    }
+
+    static public int test6(boolean b) {
+        Object o = b ? (Object) new byte[1] : new boolean[1];
+        return o.hashCode();
+    }
+
+    static public Object test7(boolean b) {
+        return (b ? new String[1] : new int[1][])[0];
+    }
+
+    static public Object[] test8(boolean b) {
+        return (b ? new String[1][] : new int[1][][])[0];
+    }
+}
diff --git a/dx/tests/090-dex-unify-arrays/expected.txt b/dx/tests/090-dex-unify-arrays/expected.txt
new file mode 100644
index 0000000..7a4125f
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/expected.txt
@@ -0,0 +1,122 @@
+Blort.test1:(Z)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v1, v0
+  0002: if-eqz v1, 000c // +000a
+  0004: const/4 v1, #int 1 // #1
+  0005: new-array v1, v1, java.lang.String[]
+  0007: const/4 v2, #int 0 // #0
+  0008: aget-object v1, v1, v2
+  000a: move-object v0, v1
+  000b: return-object v0
+  000c: const/4 v1, #int 1 // #1
+  000d: new-array v1, v1, java.lang.Integer[]
+  000f: goto 0007 // -0008
+Blort.test2:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, int[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, float[]
+  0012: goto 0007 // -000b
+Blort.test3:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, char[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, double[]
+  0012: goto 0007 // -000b
+Blort.test4:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, long[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, boolean[]
+  0012: goto 0007 // -000b
+Blort.test5:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, short[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, java.lang.Object[]
+  0012: goto 0007 // -000b
+Blort.test6:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+  0000: move v0, v3
+  0001: move v2, v0
+  0002: if-eqz v2, 000f // +000d
+  0004: const/4 v2, #int 1 // #1
+  0005: new-array v2, v2, byte[]
+  0007: move-object v1, v2
+  0008: move-object v2, v1
+  0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+  000c: move-result v2
+  000d: move v0, v2
+  000e: return v0
+  000f: const/4 v2, #int 1 // #1
+  0010: new-array v2, v2, boolean[]
+  0012: goto 0007 // -000b
+Blort.test7:(Z)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v1, v0
+  0002: if-eqz v1, 000c // +000a
+  0004: const/4 v1, #int 1 // #1
+  0005: new-array v1, v1, java.lang.String[]
+  0007: const/4 v2, #int 0 // #0
+  0008: aget-object v1, v1, v2
+  000a: move-object v0, v1
+  000b: return-object v0
+  000c: const/4 v1, #int 1 // #1
+  000d: new-array v1, v1, int[][]
+  000f: goto 0007 // -0008
+Blort.test8:(Z)[Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+  0000: move v0, v3
+  0001: move v1, v0
+  0002: if-eqz v1, 000c // +000a
+  0004: const/4 v1, #int 1 // #1
+  0005: new-array v1, v1, java.lang.String[][]
+  0007: const/4 v2, #int 0 // #0
+  0008: aget-object v1, v1, v2
+  000a: move-object v0, v1
+  000b: return-object v0
+  000c: const/4 v1, #int 1 // #1
+  000d: new-array v1, v1, int[][][]
+  000f: goto 0007 // -0008
diff --git a/dx/tests/090-dex-unify-arrays/info.txt b/dx/tests/090-dex-unify-arrays/info.txt
new file mode 100644
index 0000000..018fd25
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+array type unification works properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/090-dex-unify-arrays/run b/dx/tests/090-dex-unify-arrays/run
new file mode 100644
index 0000000..47b709c
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/091-ssa-const-collector/Blort.java b/dx/tests/091-ssa-const-collector/Blort.java
new file mode 100644
index 0000000..ea99a0d
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/Blort.java
@@ -0,0 +1,64 @@
+
+class Blort {
+    /** Class constructors for enums use a lot of const's */
+    enum Foo {
+        ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT
+    }
+
+    /** all uses of 10 should be combined except the local assignment */
+    void testNumeric() {
+        int foo = 10;
+
+        for (int i = 0; i < 10; i++){
+            foo += i * 10;
+        }
+
+        for (int i = 0; i < 10; i++){
+            foo += i + 10;
+        }
+    }
+
+    void testStrings() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+    }
+
+    void testCaughtStrings() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("foo");
+        sb.append("foo");
+        sb.append("foo");
+        try {
+            sb.append("foo");
+            sb.append("foo");
+            sb.append("foo");
+        } catch (Throwable tr) {
+            System.out.println("foo");
+        }
+    }
+
+    /** local variables cannot be intermingled */
+    void testLocalVars() {
+        int i = 10;
+        int j = 10;
+        int k = 10;
+        int a = 10;
+        int b = 10;
+        int c = 10;
+
+        i *= 10;
+    }
+
+    void testNull(Object a) {
+        a.equals(null);
+        a.equals(null);
+
+    }
+}
diff --git a/dx/tests/091-ssa-const-collector/expected.txt b/dx/tests/091-ssa-const-collector/expected.txt
new file mode 100644
index 0000000..0edb6e8
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/expected.txt
@@ -0,0 +1,475 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+block 0000
+  pred 000a
+  Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+  >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+  next 000b
+block 000b
+  pred 0000
+  Blort.java:4@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method testNumeric ()V
+first 005e
+block 005c
+  pred 005e
+  Blort.java:10@0000: move-param-object(0) v4:"this"LBlort; <- .
+  Blort.java:10@0000: goto . <- .
+  next 0000
+block 0000
+  pred 005c
+  Blort.java:10@0000: const-int(10) v0:I=10 <- .
+  @????: mark-local-int . <- v0:"foo"I
+  Blort.java:12@0003: const-int(0) v1:I=0 <- .
+  @????: mark-local-int . <- v1:"i"I
+  Blort.java:12@0004: goto . <- .
+  next 0005
+block 0005
+  pred 0000
+  pred 000b
+  Blort.java:12@0008: if-ge-int . <- v1:I v3:I=10
+  next 000b *
+  next 0018
+block 000b
+  pred 0005
+  Blort.java:13@000f: mul-const-int(10) v2:I <- v1:I
+  Blort.java:13@0010: add-int v0:I <- v0:I v2:I
+  @????: mark-local-int . <- v0:"foo"I
+  Blort.java:12@0012: add-const-int(1) v1:"i"I <- v1:I
+  Blort.java:12@0015: goto . <- .
+  next 0005
+block 0018
+  pred 0005
+  Blort.java:16@0018: const-int(0) v1:I=0 <- .
+  @????: mark-local-int . <- v1:"i"I
+  Blort.java:16@0019: goto . <- .
+  next 001a
+block 001a
+  pred 0018
+  pred 0020
+  Blort.java:16@001d: if-ge-int . <- v1:I v3:I=10
+  next 0020 *
+  next 005d
+block 0020
+  pred 001a
+  Blort.java:17@0024: add-const-int(10) v2:I <- v1:I
+  Blort.java:17@0025: add-int v0:I <- v0:I v2:I
+  @????: mark-local-int . <- v0:"foo"I
+  Blort.java:16@0027: add-const-int(1) v1:"i"I <- v1:I
+  Blort.java:16@002a: goto . <- .
+  next 001a
+block 005d
+  pred 001a
+  Blort.java:19@002d: return-void . <- .
+  returns
+block 005e
+  @????: const-int(10) v3:I=10 <- .
+  @????: goto . <- .
+  next 005c
+
+method testStrings ()V
+first 0078
+block 0064
+  pred 007b
+  Blort.java:22@0000: move-param-object(0) v3:"this"LBlort; <- .
+  Blort.java:22@0000: goto . <- .
+  next 0000
+block 006b
+  pred 0000
+  Blort.java:22@0000: Rop{move-result-pseudo N0000Ljava/lang/StringBuilder; <- 
+  . flows} v0:N0000Ljava/lang/StringBuilder; <- .
+  Blort.java:22@0000: goto . <- .
+  next 0003
+block 0000
+  pred 0064
+  Blort.java:22@0000: new-instance(java.lang.StringBuilder catch) . <- .
+  next 006b
+block 0003
+  pred 006b
+  Blort.java:22@0004: Rop{invoke-direct . <- Ljava/lang/StringBuilder; call thr
+  ows <any>}(java.lang.StringBuilder.<init>:()V catch) . <- v0:N0000Ljava/lang/
+  StringBuilder;
+  next 0007
+block 006c
+  pred 0007
+  Blort.java:24@0009: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:24@0009: goto . <- .
+  next 000b
+block 0007
+  pred 0003
+  @????: mark-local-object . <- v0:"sb"Ljava/lang/StringBuilder;
+  Blort.java:24@0009: const-object("foo" catch) . <- .
+  next 006c
+block 000b
+  pred 006c
+  Blort.java:24@000b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 000e
+block 006e
+  pred 000e
+  Blort.java:25@0010: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:25@0010: goto . <- .
+  next 0012
+block 000e
+  pred 000b
+  Blort.java:25@0010: const-object("foo" catch) . <- .
+  next 006e
+block 0012
+  pred 006e
+  Blort.java:25@0012: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 0015
+block 0070
+  pred 0015
+  Blort.java:26@0017: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:26@0017: goto . <- .
+  next 0019
+block 0015
+  pred 0012
+  Blort.java:26@0017: const-object("foo" catch) . <- .
+  next 0070
+block 0019
+  pred 0070
+  Blort.java:26@0019: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 001c
+block 0072
+  pred 001c
+  Blort.java:27@001e: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:27@001e: goto . <- .
+  next 0020
+block 001c
+  pred 0019
+  Blort.java:27@001e: const-object("foo" catch) . <- .
+  next 0072
+block 0020
+  pred 0072
+  Blort.java:27@0020: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 0023
+block 0074
+  pred 0023
+  Blort.java:28@0025: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:28@0025: goto . <- .
+  next 0027
+block 0023
+  pred 0020
+  Blort.java:28@0025: const-object("foo" catch) . <- .
+  next 0074
+block 0027
+  pred 0074
+  Blort.java:28@0027: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 002a
+block 0076
+  pred 002a
+  Blort.java:29@002c: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:29@002c: goto . <- .
+  next 002e
+block 002a
+  pred 0027
+  Blort.java:29@002c: const-object("foo" catch) . <- .
+  next 0076
+block 002e
+  pred 0076
+  Blort.java:29@002e: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+  ava/lang/String;="foo"
+  next 0065
+block 0065
+  pred 002e
+  Blort.java:30@0032: return-void . <- .
+  returns
+block 0078
+  @????: goto . <- .
+  next 007a
+block 007a
+  pred 0078
+  @????: const-object("foo" catch) . <- .
+  next 007b
+block 007b
+  pred 007a
+  @????: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:Ljava/lang/St
+  ring;="foo" <- .
+  @????: goto . <- .
+  next 0064
+
+method testCaughtStrings ()V
+first 0094
+block 007e
+  pred 009e
+  Blort.java:33@0000: move-param-object(0) v5:"this"LBlort; <- .
+  Blort.java:33@0000: goto . <- .
+  next 0000
+block 0085
+  pred 0000
+  Blort.java:33@0000: Rop{move-result-pseudo N0000Ljava/lang/StringBuilder; <- 
+  . flows} v0:N0000Ljava/lang/StringBuilder; <- .
+  Blort.java:33@0000: goto . <- .
+  next 0003
+block 0000
+  pred 007e
+  Blort.java:33@0000: new-instance(java.lang.StringBuilder catch) . <- .
+  next 0085
+block 0003
+  pred 0085
+  Blort.java:33@0004: Rop{invoke-direct . <- Ljava/lang/StringBuilder; call thr
+  ows <any>}(java.lang.StringBuilder.<init>:()V catch) . <- v0:N0000Ljava/lang/
+  StringBuilder;
+  next 0007
+block 0086
+  pred 0007
+  Blort.java:35@0009: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:35@0009: goto . <- .
+  next 000b
+block 0007
+  pred 0003
+  @????: mark-local-object . <- v0:"sb"Ljava/lang/StringBuilder;
+  Blort.java:35@0009: const-object("foo" catch) . <- .
+  next 0086
+block 000b
+  pred 0086
+  Blort.java:35@000b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+  ava/lang/String;="foo"
+  next 000e
+block 0088
+  pred 000e
+  Blort.java:36@0010: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:36@0010: goto . <- .
+  next 0012
+block 000e
+  pred 000b
+  Blort.java:36@0010: const-object("foo" catch) . <- .
+  next 0088
+block 0012
+  pred 0088
+  Blort.java:36@0012: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+  ava/lang/String;="foo"
+  next 0015
+block 008a
+  pred 0015
+  Blort.java:37@0017: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:37@0017: goto . <- .
+  next 0019
+block 0015
+  pred 0012
+  Blort.java:37@0017: const-object("foo" catch) . <- .
+  next 008a
+block 0019
+  pred 008a
+  Blort.java:37@0019: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+  ava/lang/String;="foo"
+  next 001d
+block 008c
+  pred 001d
+  Blort.java:39@001e: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:39@001e: goto . <- .
+  next 0020
+block 001d
+  pred 0019
+  Blort.java:39@001e: const-object("foo" catch java.lang.Throwable) . <- .
+  next 0095
+  next 008c *
+block 0020
+  pred 008c
+  Blort.java:39@0020: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+  StringBuilder; v2:Ljava/lang/String;="foo"
+  next 0095
+  next 0023 *
+block 008e
+  pred 0023
+  Blort.java:40@0025: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:40@0025: goto . <- .
+  next 0027
+block 0023
+  pred 0020
+  Blort.java:40@0025: const-object("foo" catch java.lang.Throwable) . <- .
+  next 0095
+  next 008e *
+block 0027
+  pred 008e
+  Blort.java:40@0027: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+  StringBuilder; v2:Ljava/lang/String;="foo"
+  next 0095
+  next 002a *
+block 0090
+  pred 002a
+  Blort.java:41@002c: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:41@002c: goto . <- .
+  next 002e
+block 002a
+  pred 0027
+  Blort.java:41@002c: const-object("foo" catch java.lang.Throwable) . <- .
+  next 0095
+  next 0090 *
+block 002e
+  pred 0090
+  Blort.java:41@002e: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+  ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+  ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+  StringBuilder; v2:Ljava/lang/String;="foo"
+  next 0095
+  next 007f *
+block 0092
+  pred 0035
+  Blort.java:43@0036: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} 
+  v2:Ljava/io/PrintStream; <- .
+  Blort.java:43@0036: goto . <- .
+  next 0039
+block 0035
+  pred 0095
+  @????: mark-local-object . <- v1:"tr"Ljava/lang/Throwable;
+  Blort.java:43@0036: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+  am; catch) . <- .
+  next 0092
+block 0093
+  pred 0039
+  Blort.java:43@0039: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v3:
+  Ljava/lang/String;="foo" <- .
+  Blort.java:43@0039: goto . <- .
+  next 003b
+block 0039
+  pred 0092
+  Blort.java:43@0039: const-object("foo" catch) . <- .
+  next 0093
+block 003b
+  pred 0093
+  Blort.java:43@003b: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+  String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V 
+  catch) . <- v2:Ljava/io/PrintStream; v4:Ljava/lang/String;="foo"
+  next 007f
+block 007f
+  pred 002e
+  pred 003b
+  Blort.java:45@003e: return-void . <- .
+  returns
+block 0094
+  @????: goto . <- .
+  next 009d
+block 0095
+  pred 001d
+  pred 0020
+  pred 0023
+  pred 0027
+  pred 002a
+  pred 002e
+  Blort.java:42@0035: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:L
+  java/lang/Throwable; <- .
+  @????: move-object v1:Ljava/lang/Throwable; <- v2:Ljava/lang/Throwable;
+  @????: goto . <- .
+  next 0035
+block 009d
+  pred 0094
+  @????: const-object("foo" catch) . <- .
+  next 009e
+block 009e
+  pred 009d
+  @????: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v4:Ljava/lang/St
+  ring;="foo" <- .
+  @????: goto . <- .
+  next 007e
+
+method testLocalVars ()V
+first 0004
+block 0002
+  pred 0004
+  Blort.java:49@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:49@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0002
+  Blort.java:49@0000: const-int(10) v3:I=10 <- .
+  @????: mark-local-int . <- v3:"i"I
+  Blort.java:50@0003: const-int(10) v4:I=10 <- .
+  @????: mark-local-int . <- v4:"j"I
+  Blort.java:51@0006: const-int(10) v5:I=10 <- .
+  @????: mark-local-int . <- v5:"k"I
+  Blort.java:52@0009: const-int(10) v0:I=10 <- .
+  @????: mark-local-int . <- v0:"a"I
+  Blort.java:53@000d: const-int(10) v1:I=10 <- .
+  @????: mark-local-int . <- v1:"b"I
+  Blort.java:54@0011: const-int(10) v2:I=10 <- .
+  @????: mark-local-int . <- v2:"c"I
+  Blort.java:56@0018: mul-const-int(10) v3:I <- v3:I
+  @????: mark-local-int . <- v3:"i"I=100
+  Blort.java:57@001a: goto . <- .
+  next 0003
+block 0003
+  pred 0000
+  Blort.java:57@001a: return-void . <- .
+  returns
+block 0004
+  @????: goto . <- .
+  next 0002
+
+method testNull (Ljava/lang/Object;)V
+first 0021
+block 0018
+  pred 0021
+  Blort.java:60@0000: move-param-object(0) v1:"this"LBlort; <- .
+  Blort.java:60@0000: move-param-object(1) v2:"a"Ljava/lang/Object; <- .
+  Blort.java:60@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0018
+  Blort.java:60@0002: Rop{invoke-virtual . <- Ljava/lang/Object; Ljava/lang/Obj
+  ect; call throws <any>}(java.lang.Object.equals:(Ljava/lang/Object;)Z catch) 
+  . <- v2:Ljava/lang/Object; v0:<null>=null
+  next 0005
+block 0005
+  pred 0000
+  Blort.java:61@0008: Rop{invoke-virtual . <- Ljava/lang/Object; Ljava/lang/Obj
+  ect; call throws <any>}(java.lang.Object.equals:(Ljava/lang/Object;)Z catch) 
+  . <- v2:Ljava/lang/Object; v0:<null>=null
+  next 0019
+block 0019
+  pred 0005
+  Blort.java:63@000c: return-void . <- .
+  returns
+block 0021
+  @????: const-object-nothrow(null) v0:<null>=null <- .
+  @????: goto . <- .
+  next 0018
diff --git a/dx/tests/091-ssa-const-collector/info.txt b/dx/tests/091-ssa-const-collector/info.txt
new file mode 100644
index 0000000..020d3b3
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/info.txt
@@ -0,0 +1,5 @@
+This test case tests the "const collector" optimization step.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/091-ssa-const-collector/run b/dx/tests/091-ssa-const-collector/run
new file mode 100644
index 0000000..aa37784
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/092-ssa-cfg-edge-cases/Blort.java b/dx/tests/092-ssa-cfg-edge-cases/Blort.java
new file mode 100644
index 0000000..8b3602f
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/Blort.java
@@ -0,0 +1,20 @@
+
+class Blort {
+
+    void testMultipleIdenticalSuccessors(int foo) {
+        switch(foo) {
+            case 1:
+            case 2:
+            case 3:
+                System.out.println("foo");
+            break;
+        }
+    }
+
+    void testNoPrimarySuccessor() {
+        try {
+            throw new RuntimeException();
+        } catch (RuntimeException ex){
+        }
+    }
+}
diff --git a/dx/tests/092-ssa-cfg-edge-cases/expected.txt b/dx/tests/092-ssa-cfg-edge-cases/expected.txt
new file mode 100644
index 0000000..41fefc4
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/expected.txt
@@ -0,0 +1,120 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+block 0000
+  pred 000a
+  Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+  >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+  next 000b
+block 000b
+  pred 0000
+  Blort.java:2@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method testMultipleIdenticalSuccessors (I)V
+first 0053
+block 004a
+  pred 0053
+  Blort.java:5@0000: move-param-object(0) v2:"this"LBlort; <- .
+  Blort.java:5@0000: move-param-int(1) v3:"foo"I <- .
+  Blort.java:5@0000: goto . <- .
+  next 0000
+block 0000
+  pred 004a
+  Blort.java:5@0001: switch({1, 2, 3}) . <- v3:I
+  next 001c
+  next 001c
+  next 001c
+  next 004b *
+block 0051
+  pred 001c
+  Blort.java:9@001c: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} v
+  0:Ljava/io/PrintStream; <- .
+  Blort.java:9@001c: goto . <- .
+  next 001f
+block 001c
+  pred 0000
+  pred 0000
+  pred 0000
+  Blort.java:9@001c: get-static-object(java.lang.System.out:Ljava/io/PrintStrea
+  m; catch) . <- .
+  next 0051
+block 0052
+  pred 001f
+  Blort.java:9@001f: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:L
+  java/lang/String;="foo" <- .
+  Blort.java:9@001f: goto . <- .
+  next 0021
+block 001f
+  pred 0051
+  Blort.java:9@001f: const-object("foo" catch) . <- .
+  next 0052
+block 0021
+  pred 0052
+  Blort.java:9@0021: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/S
+  tring; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V c
+  atch) . <- v0:Ljava/io/PrintStream; v1:Ljava/lang/String;="foo"
+  next 004b
+block 004b
+  pred 0000
+  pred 0021
+  Blort.java:12@0024: return-void . <- .
+  returns
+block 0053
+  @????: goto . <- .
+  next 004a
+
+method testNoPrimarySuccessor ()V
+first 001a
+block 0012
+  pred 001a
+  Blort.java:16@0000: move-param-object(0) v1:"this"LBlort; <- .
+  Blort.java:16@0000: goto . <- .
+  next 0000
+block 0019
+  pred 0000
+  Blort.java:16@0000: Rop{move-result-pseudo N0000Ljava/lang/RuntimeException; 
+  <- . flows} v0:N0000Ljava/lang/RuntimeException; <- .
+  Blort.java:16@0000: goto . <- .
+  next 0003
+block 0000
+  pred 0012
+  Blort.java:16@0000: new-instance(java.lang.RuntimeException catch java.lang.R
+  untimeException) . <- .
+  next 001b
+  next 0019 *
+block 0003
+  pred 0019
+  Blort.java:16@0004: Rop{invoke-direct . <- Ljava/lang/RuntimeException; call 
+  throws <any>}(java.lang.RuntimeException.<init>:()V catch java.lang.RuntimeEx
+  ception) . <- v0:N0000Ljava/lang/RuntimeException;
+  next 001b
+  next 0007 *
+block 0007
+  pred 0003
+  Blort.java:16@0007: throw(catch java.lang.RuntimeException) . <- v0:Ljava/lan
+  g/RuntimeException;
+  next 001b
+block 0013
+  pred 001b
+  Blort.java:19@0009: return-void . <- .
+  returns
+block 001a
+  @????: goto . <- .
+  next 0012
+block 001b
+  pred 0000
+  pred 0003
+  pred 0007
+  Blort.java:17@0008: Rop{move-exception Ljava/lang/RuntimeException; <- . flow
+  s} v0:Ljava/lang/RuntimeException; <- .
+  @????: goto . <- .
+  next 0013
diff --git a/dx/tests/092-ssa-cfg-edge-cases/info.txt b/dx/tests/092-ssa-cfg-edge-cases/info.txt
new file mode 100644
index 0000000..7c56302
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/info.txt
@@ -0,0 +1,5 @@
+This test case runs a few odd control flow graphs through the optimizer.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/092-ssa-cfg-edge-cases/run b/dx/tests/092-ssa-cfg-edge-cases/run
new file mode 100644
index 0000000..090a4d7
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/093-ssa-invoke-range/Blort.java b/dx/tests/093-ssa-invoke-range/Blort.java
new file mode 100644
index 0000000..f2bb66c
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/Blort.java
@@ -0,0 +1,69 @@
+
+class Blort {
+
+    static void methodThatNeedsInvokeRange
+        (int a, int b, int c, int d, int e, int f) {
+    }
+
+    void testNoLocals() {
+        methodThatNeedsInvokeRange(5, 0, 5, 0, 5, 0);
+    }
+
+    void testMixedLocals() {
+        int src = 6;
+        int dest = 7;
+
+        methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+        methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+    }
+
+    // here the current algorithm partial-overlapping will stumble a bit
+    // The register containing "zero" will be marked as "reserved for locals"
+    // Then the subsequent arraycopy will need a whole new set of 5 registers
+    void testMixedWorseCase() {
+        int src = 6;
+        int dest = 7;
+        int zero = 0;
+
+        methodThatNeedsInvokeRange(src, zero, dest, 1, 5, 0);
+        methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+    }
+
+    void testAllParams(int a, int b, int c, int d, int e, int f) {
+        methodThatNeedsInvokeRange(a, b, c, d, e, f);
+    }
+
+    // this could try to make use of param positions, but doesn't
+    static void testTailParams(int destPos, int length) {
+        int src = 6;
+        int dest = 7;
+
+        methodThatNeedsInvokeRange(src, 0, dest, 0, destPos, length);
+    }
+
+
+    // This presently requires a whole N new registers
+    void testFlip() {
+        int src = 6;
+        int dest = 7;
+
+        methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+        methodThatNeedsInvokeRange(dest, 0, src, 1, 5, 0);
+    }
+
+    // ensure that an attempt to combine registers for a local
+    // with a differing category doesn't mess us up.
+    long testMixedCategory(boolean foo) {
+        if (foo) {
+            int offset = 1;
+            int src = 6;
+            int dest = 7;
+
+            methodThatNeedsInvokeRange(src, 0, dest, offset, 5, 0);
+            return offset;
+        } else {
+            long offset = System.currentTimeMillis();;
+            return offset;
+        }
+    }
+}
diff --git a/dx/tests/093-ssa-invoke-range/expected.txt b/dx/tests/093-ssa-invoke-range/expected.txt
new file mode 100644
index 0000000..bb383c0
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/expected.txt
@@ -0,0 +1,301 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+  pred 000c
+  Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+  Blort.java:2@0000: goto . <- .
+  next 0000
+block 0000
+  pred 000a
+  Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+  >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+  next 000b
+block 000b
+  pred 0000
+  Blort.java:2@0004: return-void . <- .
+  returns
+block 000c
+  @????: goto . <- .
+  next 000a
+
+method methodThatNeedsInvokeRange (IIIIII)V
+first 0004
+block 0002
+  pred 0004
+  Blort.java:6@0000: move-param-int(0) v0:"a"I <- .
+  Blort.java:6@0000: move-param-int(1) v1:"b"I <- .
+  Blort.java:6@0000: move-param-int(2) v2:"c"I <- .
+  Blort.java:6@0000: move-param-int(3) v3:"d"I <- .
+  Blort.java:6@0000: move-param-int(4) v4:"e"I <- .
+  Blort.java:6@0000: move-param-int(5) v5:"f"I <- .
+  Blort.java:6@0000: goto . <- .
+  next 0003
+block 0003
+  pred 0002
+  Blort.java:6@0000: return-void . <- .
+  returns
+block 0004
+  @????: goto . <- .
+  next 0002
+
+method testNoLocals ()V
+first 0016
+block 0014
+  pred 0016
+  Blort.java:9@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:9@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0014
+  @????: move-int v2:I=5 <- v0:I=5
+  @????: move-int v3:I=0 <- v1:I=0
+  @????: move-int v4:I=5 <- v0:I=5
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:9@0006: Rop{invoke-static . <- I I I I I I call throws <any>}(Blor
+  t.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I=5 v1:I=0 v2:I=5 v3:I=
+  0 v4:I=5 v5:I=0
+  next 0015
+block 0015
+  pred 0000
+  Blort.java:10@0009: return-void . <- .
+  returns
+block 0016
+  @????: const-int(5) v0:I=5 <- .
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 0014
+
+method testMixedLocals ()V
+first 0034
+block 0032
+  pred 0034
+  Blort.java:13@0000: move-param-object(0) v6:"this"LBlort; <- .
+  Blort.java:13@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0032
+  Blort.java:13@0000: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:14@0003: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:16@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+  4:I=5 v5:I=0
+  next 000f
+block 000f
+  pred 0000
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:17@0015: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+  4:I=5 v5:I=0
+  next 0033
+block 0033
+  pred 000f
+  Blort.java:18@0018: return-void . <- .
+  returns
+block 0034
+  @????: const-int(5) v4:I=5 <- .
+  @????: const-int(1) v3:I=1 <- .
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 0032
+
+method testMixedWorseCase ()V
+first 0038
+block 0036
+  pred 0038
+  Blort.java:24@0000: move-param-object(0) v12:"this"LBlort; <- .
+  Blort.java:24@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0036
+  Blort.java:24@0000: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:25@0003: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  Blort.java:26@0006: const-int(0) v1:I=0 <- .
+  @????: mark-local-int . <- v1:"zero"I
+  Blort.java:28@000e: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I v2:I v3:I=1 v4:
+  I=5 v5:I=0
+  next 0011
+block 0011
+  pred 0000
+  @????: move-int v6:I <- v0:I
+  @????: move-int v7:I=0 <- v5:I=0
+  @????: move-int v8:I <- v2:I
+  @????: move-int v9:I=1 <- v3:I=1
+  @????: move-int v10:I=5 <- v4:I=5
+  @????: move-int v11:I=0 <- v5:I=0
+  Blort.java:29@0017: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v6:I v7:I=0 v8:I v9:I=1 v
+  10:I=5 v11:I=0
+  next 0037
+block 0037
+  pred 0011
+  Blort.java:30@001a: return-void . <- .
+  returns
+block 0038
+  @????: const-int(5) v4:I=5 <- .
+  @????: const-int(1) v3:I=1 <- .
+  @????: const-int(0) v5:I=0 <- .
+  @????: goto . <- .
+  next 0036
+
+method testAllParams (IIIIII)V
+first 001c
+block 001a
+  pred 001c
+  Blort.java:33@0000: move-param-object(0) v0:"this"LBlort; <- .
+  Blort.java:33@0000: move-param-int(1) v1:"a"I <- .
+  Blort.java:33@0000: move-param-int(2) v2:"b"I <- .
+  Blort.java:33@0000: move-param-int(3) v3:"c"I <- .
+  Blort.java:33@0000: move-param-int(4) v4:"d"I <- .
+  Blort.java:33@0000: move-param-int(5) v5:"e"I <- .
+  Blort.java:33@0000: move-param-int(6) v6:"f"I <- .
+  Blort.java:33@0000: goto . <- .
+  next 0000
+block 0000
+  pred 001a
+  Blort.java:33@0009: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v1:I v2:I v3:I v4:I v5:I 
+  v6:I
+  next 001b
+block 001b
+  pred 0000
+  Blort.java:34@000c: return-void . <- .
+  returns
+block 001c
+  @????: goto . <- .
+  next 001a
+
+method testTailParams (II)V
+first 0022
+block 0020
+  pred 0022
+  Blort.java:38@0000: move-param-int(0) v6:"destPos"I <- .
+  Blort.java:38@0000: move-param-int(1) v7:"length"I <- .
+  Blort.java:38@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0020
+  Blort.java:38@0000: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:39@0003: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  @????: move-int v3:I=0 <- v1:I=0
+  @????: move-int v4:I <- v6:I
+  @????: move-int v5:I <- v7:I
+  Blort.java:41@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=0 v
+  4:I v5:I
+  next 0021
+block 0021
+  pred 0000
+  Blort.java:42@000f: return-void . <- .
+  returns
+block 0022
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 0020
+
+method testFlip ()V
+first 0034
+block 0032
+  pred 0034
+  Blort.java:47@0000: move-param-object(0) v11:"this"LBlort; <- .
+  Blort.java:47@0000: goto . <- .
+  next 0000
+block 0000
+  pred 0032
+  Blort.java:47@0000: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:48@0003: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:50@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+  4:I=5 v5:I=0
+  next 000f
+block 000f
+  pred 0000
+  @????: move-int v5:I <- v2:I
+  @????: move-int v6:I=0 <- v1:I=0
+  @????: move-int v7:I <- v0:I
+  @????: move-int v8:I=1 <- v3:I=1
+  @????: move-int v9:I=5 <- v4:I=5
+  @????: move-int v10:I=0 <- v1:I=0
+  Blort.java:51@0015: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v5:I v6:I=0 v7:I v8:I=1 v
+  9:I=5 v10:I=0
+  next 0033
+block 0033
+  pred 000f
+  Blort.java:52@0018: return-void . <- .
+  returns
+block 0034
+  @????: const-int(5) v4:I=5 <- .
+  @????: const-int(1) v3:I=1 <- .
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 0032
+
+method testMixedCategory (Z)J
+first 0044
+block 003c
+  pred 0044
+  Blort.java:57@0000: move-param-object(0) v8:"this"LBlort; <- .
+  Blort.java:57@0000: move-param-int(1) v9:"foo"Z <- .
+  Blort.java:57@0000: goto . <- .
+  next 0000
+block 0000
+  pred 003c
+  Blort.java:57@0001: if-eqz-int . <- v9:I
+  next 0004 *
+  next 001a
+block 0004
+  pred 0000
+  Blort.java:58@0004: const-int(1) v3:I=1 <- .
+  @????: mark-local-int . <- v3:"offset"I
+  Blort.java:59@0006: const-int(6) v0:I=6 <- .
+  @????: mark-local-int . <- v0:"src"I
+  Blort.java:60@0009: const-int(7) v2:I=7 <- .
+  @????: mark-local-int . <- v2:"dest"I
+  Blort.java:62@0012: const-int(5) v4:I=5 <- .
+  @????: move-int v5:I=0 <- v1:I=0
+  Blort.java:62@0014: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+  rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I v4:
+  I=5 v5:I=0
+  next 0017
+block 0017
+  pred 0004
+  Blort.java:63@0018: conv-i2l v4:J <- v3:I
+  Blort.java:63@0019: goto . <- .
+  next 003d
+block 0043
+  pred 001a
+  Blort.java:65@001a: Rop{move-result J <- . flows} v6:J <- .
+  Blort.java:65@001a: goto . <- .
+  next 001d
+block 001a
+  pred 0000
+  Blort.java:65@001a: Rop{invoke-static . <- . call throws <any>}(java.lang.Sys
+  tem.currentTimeMillis:()J catch) . <- .
+  next 0043
+block 001d
+  pred 0043
+  @????: mark-local-long . <- v6:"offset"J
+  @????: move-long v4:J <- v6:"offset"J
+  Blort.java:66@001f: goto . <- .
+  next 003d
+block 003d
+  pred 0017
+  pred 001d
+  Blort.java:66@001f: return-long . <- v4:J
+  returns
+block 0044
+  @????: const-int(0) v1:I=0 <- .
+  @????: goto . <- .
+  next 003c
diff --git a/dx/tests/093-ssa-invoke-range/info.txt b/dx/tests/093-ssa-invoke-range/info.txt
new file mode 100644
index 0000000..372bed7
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/info.txt
@@ -0,0 +1,6 @@
+This test case checks the ability of the register allocator to plan
+for dex's invoke-range instruction.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/093-ssa-invoke-range/run b/dx/tests/093-ssa-invoke-range/run
new file mode 100644
index 0000000..aa37784
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/094-scala-locals/blort.j b/dx/tests/094-scala-locals/blort.j
new file mode 100644
index 0000000..7c711ef
--- /dev/null
+++ b/dx/tests/094-scala-locals/blort.j
@@ -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.
+
+.class Blort
+.super java/lang/Object
+
+.method public static returnint()I
+    .limit stack 1
+    ldc 10
+    ireturn
+.end method
+
+.method public static scalalocals()V
+    .limit locals 5
+    .limit stack 5
+    .var 4 is x I from start to end
+start:
+    invokestatic blort/returnint()I
+    invokestatic blort/returnint()I
+    invokestatic blort/returnint()I
+    invokestatic blort/returnint()I
+    dup
+    istore 4
+    istore 2
+    istore 3
+    istore 1
+    istore 0
+    iload_2
+    istore 4
+    iload_3
+end:
+    return
+.end method
diff --git a/dx/tests/094-scala-locals/expected.txt b/dx/tests/094-scala-locals/expected.txt
new file mode 100644
index 0000000..c74db70
--- /dev/null
+++ b/dx/tests/094-scala-locals/expected.txt
@@ -0,0 +1,85 @@
+reading Blort.class...
+method scalalocals ()V
+first 0025
+block 001a
+  pred 0025
+  live in:{}
+  blort.j:@0000: goto . <- .
+  next 0000
+  live out:{}
+block 0021
+  pred 0000
+  live in:{}
+  blort.j:@0000: goto . <- .
+  next 0003
+  live out:{}
+block 0000
+  pred 001a
+  live in:{}
+  blort.j:@0000: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0021
+  live out:{}
+block 0022
+  pred 0003
+  live in:{}
+  blort.j:@0003: goto . <- .
+  next 0006
+  live out:{}
+block 0003
+  pred 0021
+  live in:{}
+  blort.j:@0003: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0022
+  live out:{}
+block 0023
+  pred 0006
+  live in:{}
+  blort.j:@0006: goto . <- .
+  next 0009
+  live out:{}
+block 0006
+  pred 0022
+  live in:{}
+  blort.j:@0006: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0023
+  live out:{}
+block 0024
+  pred 0009
+  live in:{}
+  blort.j:@0009: Rop{move-result I <- . flows} v14:I <- .
+  blort.j:@0009: goto . <- .
+  next 000c
+  live out:{14}
+block 0009
+  pred 0023
+  live in:{}
+  blort.j:@0009: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+  I catch) . <- .
+  next 0024
+  live out:{}
+block 000c
+  pred 0024
+  live in:{14}
+  @????: mark-local-int . <- v14:"x"I
+  blort.j:@001b: goto . <- .
+  next 001b
+  live out:{}
+block 001b
+  pred 000c
+  live in:{}
+  blort.j:@001b: return-void . <- .
+  next 0026
+  live out:{}
+block 0025
+  live in:{}
+  @????: goto . <- .
+  next 001a
+  live out:{}
+block 0026
+  pred 001b
+  live in:{}
+  returns
+  live out:{}
diff --git a/dx/tests/094-scala-locals/info.txt b/dx/tests/094-scala-locals/info.txt
new file mode 100644
index 0000000..cb1a42e
--- /dev/null
+++ b/dx/tests/094-scala-locals/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of the SSA renamer's local variable preserver.
+It tests a case observed from Scala, wherein a local variable is assigned
+an identical value twice. The correct result should be only a single
+mark-local, with the second assignment eaten by copy-propogation.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/094-scala-locals/run b/dx/tests/094-scala-locals/run
new file mode 100644
index 0000000..4bbfa79
--- /dev/null
+++ b/dx/tests/094-scala-locals/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dump --ssa-blocks  --method=scalalocals Blort.class
diff --git a/dx/tests/095-dex-const-string-jumbo/Blort.java b/dx/tests/095-dex-const-string-jumbo/Blort.java
new file mode 100644
index 0000000..a0271b5
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/Blort.java
@@ -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.
+ */
+
+public class Blort {
+    static public void consume(String s) {
+        // This space intentionally left blank.
+    }
+
+    public void test() {
+        consume("zorch");
+    }
+}
diff --git a/dx/tests/095-dex-const-string-jumbo/expected.txt b/dx/tests/095-dex-const-string-jumbo/expected.txt
new file mode 100644
index 0000000..a4014d9
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/expected.txt
@@ -0,0 +1,6 @@
+Blort.test:()V:
+regs: 0003; ins: 0001; outs: 0001
+  0000: move-object v0, v2
+  0001: const-string/jumbo v1, "zorch"
+  0004: invoke-static {v1}, Blort.consume:(Ljava/lang/String;)V
+  0007: return-void
diff --git a/dx/tests/095-dex-const-string-jumbo/info.txt b/dx/tests/095-dex-const-string-jumbo/info.txt
new file mode 100644
index 0000000..c14fd8e
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+const-string/jumbo gets emitted appropriately.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/095-dex-const-string-jumbo/run b/dx/tests/095-dex-const-string-jumbo/run
new file mode 100644
index 0000000..a1c7365
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/run
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# 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.
+
+# Write out files with 32768 total static string declarations, so that
+# the reference to "zorch" in the real test file will be guaranteed to
+# need a jumbo string reference (it sorts last after all the others).
+# Note: Each string reference is stored in a separate static variable,
+# and that variable's name is also represented in the strings, which
+# is why we can just have 32768 and not 65536 declarations.
+
+awk '
+BEGIN {
+    writeFile("Zorch1", 0, 16383);
+    writeFile("Zorch2", 16384, 32767);
+}
+function writeFile(name, start, end) {
+    fileName = name ".java";
+    printf("public class %s {\n", name) > fileName;
+    for (i = start; i <= end; i++) {
+        printf("    static public final String s%d = \"%d\";\n",
+            i, i) > fileName;
+    }
+    printf("}\n") > fileName;
+}'
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Blort.test *.class
diff --git a/dx/tests/096-dex-giant-catch/Blort.java b/dx/tests/096-dex-giant-catch/Blort.java
new file mode 100644
index 0000000..f5f6e8d
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+public class Blort {
+    static public void blort(long v1, long v2, long v3, long v4,
+            long v5, long v6, long v7, long v8) {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/096-dex-giant-catch/expected.txt b/dx/tests/096-dex-giant-catch/expected.txt
new file mode 100644
index 0000000..e71992e
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/expected.txt
@@ -0,0 +1,5 @@
+  catches
+      try 0024..00010017
+      catch java.lang.RuntimeException -> 00011260
+      try 0001003b..0001125f
+      catch java.lang.RuntimeException -> 00011260
diff --git a/dx/tests/096-dex-giant-catch/info.txt b/dx/tests/096-dex-giant-catch/info.txt
new file mode 100644
index 0000000..b81ce94
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+very long catch ranges (that cover >= 65536 code units) get emitted
+appropriately.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/096-dex-giant-catch/run b/dx/tests/096-dex-giant-catch/run
new file mode 100644
index 0000000..c81d04c
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/run
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# 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.
+
+# Write out a file with a really huge catch range.
+
+awk '
+BEGIN {
+    fileName = "Zorch.java";
+    printf("public class Zorch {\n") > fileName;
+    printf("    static public void test() {\n") > fileName;
+    printf("        try {\n") > fileName;
+    for (i = 0; i <= 1800; i++) {
+        d = i + 1000000;
+        printf("    Blort.blort(100%dL, 200%dL, 300%dL, 400%dL, 500%dL, " \
+            "600%dL, 700%dL, 800%dL);\n",
+            d, d + 1, d + 2, d + 3, d + 4, d + 5, d + 6, d + 7) > fileName;
+    }
+    printf("        } catch (RuntimeException ex) {\n") > fileName;
+    printf("            throw ex;\n") > fileName;
+    printf("        }\n") > fileName;
+    printf("    }\n") > fileName;
+    printf("}\n") > fileName;
+}'
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-method=Zorch.test Zorch.class | grep 'try\|catch'
diff --git a/dx/tests/097-dex-branch-offset-zero/Blort.java b/dx/tests/097-dex-branch-offset-zero/Blort.java
new file mode 100644
index 0000000..5033c8f
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/Blort.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+public class Blort
+{
+    public void test1() {
+        for (;;) /*empty*/ ;
+    }
+
+    public void test2(int x) {
+        while (x > 0) /*empty*/ ;
+    }
+
+    public void test3(int x, int y) {
+        while (x < y) /*empty*/ ;
+    }
+}
diff --git a/dx/tests/097-dex-branch-offset-zero/expected.txt b/dx/tests/097-dex-branch-offset-zero/expected.txt
new file mode 100644
index 0000000..2b021a5
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/expected.txt
@@ -0,0 +1 @@
+No bad branches found.
diff --git a/dx/tests/097-dex-branch-offset-zero/info.txt b/dx/tests/097-dex-branch-offset-zero/info.txt
new file mode 100644
index 0000000..4bf9502
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+the only non-switch branches to offset 0 happen using the goto/32 opcode.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/097-dex-branch-offset-zero/run b/dx/tests/097-dex-branch-offset-zero/run
new file mode 100644
index 0000000..34539eb
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/run
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+    *.class | grep '[-+][0-9]' | grep -v 'goto/32.*+00*$' | grep '// +00*$'
+
+if [ "$?" = "1" ]; then
+    echo "No bad branches found."
+else
+    # Redo the dx command without filters, to aid with debugging.
+    dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+    *.class
+fi
diff --git a/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class b/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class
new file mode 100644
index 0000000..ff992ca
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class
Binary files differ
diff --git a/dx/tests/098-dex-jsr-ret-throw/expected.txt b/dx/tests/098-dex-jsr-ret-throw/expected.txt
new file mode 100644
index 0000000..b5b1b93
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/expected.txt
@@ -0,0 +1,652 @@
+reading ViewDebug$ViewServer.class...
+method run ()V
+first 0162
+block 0162
+  ViewDebug.java:564@0000: move-param-object(0) v0:"this"Landroid/view/ViewDebu
+  g$ViewServer; <- .
+  ViewDebug.java:564@0000: goto . <- .
+  next 0000
+block 0169
+  pred 0000
+  ViewDebug.java:564@0001: Rop{move-result-pseudo Ljava/net/ServerSocket; <- . 
+  flows} v11:Ljava/net/ServerSocket; <- .
+  ViewDebug.java:564@0001: goto . <- .
+  next 0004
+block 0000
+  pred 0162
+  ViewDebug.java:564@0000: move-object v11:Landroid/view/ViewDebug$ViewServer; 
+  <- v0:Landroid/view/ViewDebug$ViewServer;
+  ViewDebug.java:564@0001: get-field-object(android.view.ViewDebug$ViewServer.m
+  ViewServerSocket:Ljava/net/ServerSocket; catch) . <- v11:Landroid/view/ViewDe
+  bug$ViewServer;
+  next 0169
+block 0004
+  pred 0169
+  ViewDebug.java:564@0004: move-object v1:"server"Ljava/net/ServerSocket; <- v1
+  1:Ljava/net/ServerSocket;
+  ViewDebug.java:564@0004: goto . <- .
+  next 0005
+block 016a
+  pred 0005
+  ViewDebug.java:566@0005: Rop{move-result Ljava/lang/Thread; <- . flows} v11:L
+  java/lang/Thread; <- .
+  ViewDebug.java:566@0005: goto . <- .
+  next 0008
+block 0005
+  pred 0004
+  pred 00ad
+  ViewDebug.java:566@0005: Rop{invoke-static . <- . call throws <any>}(java.lan
+  g.Thread.currentThread:()Ljava/lang/Thread; catch) . <- .
+  next 016a
+block 016b
+  pred 0008
+  ViewDebug.java:566@0009: Rop{move-result-pseudo Ljava/lang/Thread; <- . flows
+  } v12:Ljava/lang/Thread; <- .
+  ViewDebug.java:566@0009: goto . <- .
+  next 000c
+block 0008
+  pred 016a
+  ViewDebug.java:566@0008: move-object v12:Landroid/view/ViewDebug$ViewServer; 
+  <- v0:Landroid/view/ViewDebug$ViewServer;
+  ViewDebug.java:566@0009: get-field-object(android.view.ViewDebug$ViewServer.m
+  Thread:Ljava/lang/Thread; catch) . <- v12:Landroid/view/ViewDebug$ViewServer;
+  next 016b
+block 000c
+  pred 016b
+  ViewDebug.java:566@000c: if-ne-object . <- v11:Ljava/lang/Thread; v12:Ljava/l
+  ang/Thread;
+  next 000f *
+  next 00b0
+block 000f
+  pred 000c
+  ViewDebug.java:567@000f: const-object-nothrow(null) v11:<null>=null <- .
+  ViewDebug.java:567@0010: move-object v2:"client"Ljava/net/Socket; <- v11:<nul
+  l>=null
+  ViewDebug.java:567@0010: goto . <- .
+  next 0011
+block 016c
+  pred 0011
+  ViewDebug.java:569@0012: Rop{move-result Ljava/net/Socket; <- . flows} v11:Lj
+  ava/net/Socket; <- .
+  ViewDebug.java:569@0012: goto . <- .
+  next 0015
+block 0011
+  pred 000f
+  ViewDebug.java:569@0011: move-object v11:Ljava/net/ServerSocket; <- v1:Ljava/
+  net/ServerSocket;
+  ViewDebug.java:569@0012: Rop{invoke-virtual . <- Ljava/net/ServerSocket; call
+   throws <any>}(java.net.ServerSocket.accept:()Ljava/net/Socket; catch java.io
+  .IOException java.lang.Object) . <- v11:Ljava/net/ServerSocket;
+  next 0130
+  next 0140
+  next 016c *
+block 0015
+  pred 016c
+  ViewDebug.java:569@0015: move-object v2:"client"Ljava/net/Socket; <- v11:Ljav
+  a/net/Socket;
+  ViewDebug.java:571@0016: const-object-nothrow(null) v11:<null>=null <- .
+  ViewDebug.java:571@0017: move-object v3:"in"Ljava/io/BufferedReader; <- v11:<
+  null>=null
+  ViewDebug.java:571@0017: goto . <- .
+  next 0018
+block 016d
+  pred 0018
+  ViewDebug.java:573@0018: Rop{move-result-pseudo N0018Ljava/io/BufferedReader;
+   <- . flows} v11:N0018Ljava/io/BufferedReader; <- .
+  ViewDebug.java:573@0018: goto . <- .
+  next 001b
+block 0018
+  pred 0015
+  ViewDebug.java:573@0018: new-instance(java.io.BufferedReader catch java.lang.
+  Object) . <- .
+  next 0116
+  next 016d *
+block 016e
+  pred 001b
+  ViewDebug.java:573@001c: Rop{move-result-pseudo N001cLjava/io/InputStreamRead
+  er; <- . flows} v13:N001cLjava/io/InputStreamReader; <- .
+  ViewDebug.java:573@001c: goto . <- .
+  next 001f
+block 001b
+  pred 016d
+  ViewDebug.java:573@001b: move-object v16:N0018Ljava/io/BufferedReader; <- v11
+  :N0018Ljava/io/BufferedReader;
+  ViewDebug.java:573@001b: move-object v11:N0018Ljava/io/BufferedReader; <- v16
+  :N0018Ljava/io/BufferedReader;
+  ViewDebug.java:573@001b: move-object v12:N0018Ljava/io/BufferedReader; <- v16
+  :N0018Ljava/io/BufferedReader;
+  ViewDebug.java:573@001c: new-instance(java.io.InputStreamReader catch java.la
+  ng.Object) . <- .
+  next 0116
+  next 016e *
+block 016f
+  pred 001f
+  ViewDebug.java:573@0021: Rop{move-result Ljava/io/InputStream; <- . flows} v1
+  5:Ljava/io/InputStream; <- .
+  ViewDebug.java:573@0021: goto . <- .
+  next 0024
+block 001f
+  pred 016e
+  ViewDebug.java:573@001f: move-object v16:N001cLjava/io/InputStreamReader; <- 
+  v13:N001cLjava/io/InputStreamReader;
+  ViewDebug.java:573@001f: move-object v13:N001cLjava/io/InputStreamReader; <- 
+  v16:N001cLjava/io/InputStreamReader;
+  ViewDebug.java:573@001f: move-object v14:N001cLjava/io/InputStreamReader; <- 
+  v16:N001cLjava/io/InputStreamReader;
+  ViewDebug.java:573@0020: move-object v15:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:573@0021: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+  s <any>}(java.net.Socket.getInputStream:()Ljava/io/InputStream; catch java.la
+  ng.Object) . <- v15:Ljava/net/Socket;
+  next 0116
+  next 016f *
+block 0024
+  pred 016f
+  ViewDebug.java:573@0024: Rop{invoke-direct . <- Ljava/io/InputStreamReader; L
+  java/io/InputStream; call throws <any>}(java.io.InputStreamReader.<init>:(Lja
+  va/io/InputStream;)V catch java.lang.Object) . <- v14:N001cLjava/io/InputStre
+  amReader; v15:Ljava/io/InputStream;
+  next 0116
+  next 0027 *
+block 0027
+  pred 0024
+  ViewDebug.java:573@0027: Rop{invoke-direct . <- Ljava/io/BufferedReader; Ljav
+  a/io/Reader; call throws <any>}(java.io.BufferedReader.<init>:(Ljava/io/Reade
+  r;)V catch java.lang.Object) . <- v12:N0018Ljava/io/BufferedReader; v13:Ljava
+  /io/InputStreamReader;
+  next 0116
+  next 002a *
+block 0170
+  pred 002a
+  ViewDebug.java:574@002c: Rop{move-result Ljava/lang/String; <- . flows} v11:L
+  java/lang/String; <- .
+  ViewDebug.java:574@002c: goto . <- .
+  next 002f
+block 002a
+  pred 0027
+  ViewDebug.java:573@002a: move-object v3:"in"Ljava/io/BufferedReader; <- v11:L
+  java/io/BufferedReader;
+  ViewDebug.java:574@002b: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:574@002c: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+  l throws <any>}(java.io.BufferedReader.readLine:()Ljava/lang/String; catch ja
+  va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+  next 0116
+  next 0170 *
+block 0171
+  pred 002f
+  ViewDebug.java:576@0031: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v11:Ljava/lang/String;="DUMP" <- .
+  ViewDebug.java:576@0031: goto . <- .
+  next 0033
+block 002f
+  pred 0170
+  ViewDebug.java:574@002f: move-object v4:"command"Ljava/lang/String; <- v11:Lj
+  ava/lang/String;
+  ViewDebug.java:576@0031: const-object("DUMP" catch java.lang.Object) . <- .
+  next 0116
+  next 0171 *
+block 0172
+  pred 0033
+  ViewDebug.java:576@0035: Rop{move-result Z <- . flows} v11:Z <- .
+  ViewDebug.java:576@0035: goto . <- .
+  next 0038
+block 0033
+  pred 0171
+  ViewDebug.java:576@0033: move-object v12:Ljava/lang/String; <- v4:Ljava/lang/
+  String;
+  ViewDebug.java:576@0035: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+  g/String; call throws <any>}(java.lang.String.equalsIgnoreCase:(Ljava/lang/St
+  ring;)Z catch java.lang.Object) . <- v11:Ljava/lang/String;="DUMP" v12:Ljava/
+  lang/String;
+  next 0116
+  next 0172 *
+block 0038
+  pred 0172
+  ViewDebug.java:576@0038: if-eqz-int . <- v11:I
+  next 003b *
+  next 0042
+block 003b
+  pred 0038
+  ViewDebug.java:577@003b: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:577@003c: Rop{invoke-static . <- Ljava/net/Socket; call throws
+   <any>}(android.view.ViewDebug$ViewServer.dump:(Ljava/net/Socket;)V catch jav
+  a.lang.Object) . <- v11:Ljava/net/Socket;
+  next 0116
+  next 003f *
+block 003f
+  pred 003b
+  ViewDebug.java:577@003f: goto . <- .
+  next 005f
+block 0173
+  pred 0042
+  ViewDebug.java:579@0044: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v12:Ljava/lang/String;=" " <- .
+  ViewDebug.java:579@0044: goto . <- .
+  next 0046
+block 0042
+  pred 0038
+  ViewDebug.java:579@0042: move-object v11:Ljava/lang/String; <- v4:Ljava/lang/
+  String;
+  ViewDebug.java:579@0044: const-object(" " catch java.lang.Object) . <- .
+  next 0116
+  next 0173 *
+block 0174
+  pred 0046
+  ViewDebug.java:579@0046: Rop{move-result [Ljava/lang/String; <- . flows} v11:
+  [Ljava/lang/String; <- .
+  ViewDebug.java:579@0046: goto . <- .
+  next 0049
+block 0046
+  pred 0173
+  ViewDebug.java:579@0046: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+  g/String; call throws <any>}(java.lang.String.split:(Ljava/lang/String;)[Ljav
+  a/lang/String; catch java.lang.Object) . <- v11:Ljava/lang/String; v12:Ljava/
+  lang/String;=" "
+  next 0116
+  next 0174 *
+block 0175
+  pred 0049
+  ViewDebug.java:580@004b: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v11:Ljava/lang/String;="CAPTURE" <- .
+  ViewDebug.java:580@004b: goto . <- .
+  next 004d
+block 0049
+  pred 0174
+  ViewDebug.java:579@0049: move-object v5:"params"[Ljava/lang/String; <- v11:[L
+  java/lang/String;
+  ViewDebug.java:580@004b: const-object("CAPTURE" catch java.lang.Object) . <- 
+  .
+  next 0116
+  next 0175 *
+block 0176
+  pred 004d
+  ViewDebug.java:580@0050: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v12:Ljava/lang/String; <- .
+  ViewDebug.java:580@0050: goto . <- .
+  next 0051
+block 004d
+  pred 0175
+  ViewDebug.java:580@004d: move-object v12:[Ljava/lang/String; <- v5:[Ljava/lan
+  g/String;
+  ViewDebug.java:580@004f: const-int(0) v13:I=0 <- .
+  ViewDebug.java:580@0050: aget-object(catch java.lang.Object) . <- v12:[Ljava/
+  lang/String; v13:I=0
+  next 0116
+  next 0176 *
+block 0177
+  pred 0051
+  ViewDebug.java:580@0051: Rop{move-result Z <- . flows} v11:Z <- .
+  ViewDebug.java:580@0051: goto . <- .
+  next 0054
+block 0051
+  pred 0176
+  ViewDebug.java:580@0051: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+  g/String; call throws <any>}(java.lang.String.equalsIgnoreCase:(Ljava/lang/St
+  ring;)Z catch java.lang.Object) . <- v11:Ljava/lang/String;="CAPTURE" v12:Lja
+  va/lang/String;
+  next 0116
+  next 0177 *
+block 0054
+  pred 0177
+  ViewDebug.java:580@0054: if-eqz-int . <- v11:I
+  next 0057 *
+  next 005f
+block 0178
+  pred 0057
+  ViewDebug.java:581@005b: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v12:Ljava/lang/String; <- .
+  ViewDebug.java:581@005b: goto . <- .
+  next 005c
+block 0057
+  pred 0054
+  ViewDebug.java:581@0057: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:581@0058: move-object v12:[Ljava/lang/String; <- v5:[Ljava/lan
+  g/String;
+  ViewDebug.java:581@005a: const-int(1) v13:I=1 <- .
+  ViewDebug.java:581@005b: aget-object(catch java.lang.Object) . <- v12:[Ljava/
+  lang/String; v13:I=1
+  next 0116
+  next 0178 *
+block 005c
+  pred 0178
+  ViewDebug.java:581@005c: Rop{invoke-static . <- Ljava/net/Socket; Ljava/lang/
+  String; call throws <any>}(android.view.ViewDebug$ViewServer.capture:(Ljava/n
+  et/Socket;Ljava/lang/String;)V catch java.lang.Object) . <- v11:Ljava/net/Soc
+  ket; v12:Ljava/lang/String;
+  next 0116
+  next 005f *
+block 0065
+  pred 0116
+  ViewDebug.java:586@0065: move-object v6:Ljava/lang/Class;=java.lang.Object <-
+   v11:Ljava/lang/Class;=java.lang.Object
+  ViewDebug.java:586@0065: goto . <- .
+  next 0067
+block 0062
+  pred 018d
+  ViewDebug.java:589@0062: goto . <- .
+  next 0079
+block 006a
+  pred 018a
+  ViewDebug.java:586@006a: move-object v11:Ljava/lang/Class;=java.lang.Object <
+  - v6:Ljava/lang/Class;=java.lang.Object
+  ViewDebug.java:586@006c: throw(catch java.io.IOException java.lang.Object) . 
+  <- v11:Ljava/lang/Class;=java.lang.Object
+  next 0130
+  next 0140
+block 0179
+  pred 007f
+  ViewDebug.java:591@0080: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v11:Ljava/lang/String;="ViewServer" <- .
+  ViewDebug.java:591@0080: goto . <- .
+  next 0082
+block 007f
+  pred 0130
+  ViewDebug.java:590@007f: move-object v3:"e"Ljava/io/IOException; <- v11:Ljava
+  /lang/Class;=java.io.IOException
+  ViewDebug.java:591@0080: const-object("ViewServer" catch java.lang.Object) . 
+  <- .
+  next 0140
+  next 0179 *
+block 017a
+  pred 0082
+  ViewDebug.java:591@0082: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+  } v12:Ljava/lang/String;="Connection error: " <- .
+  ViewDebug.java:591@0082: goto . <- .
+  next 0084
+block 0082
+  pred 0179
+  ViewDebug.java:591@0082: const-object("Connection error: " catch java.lang.Ob
+  ject) . <- .
+  next 0140
+  next 017a *
+block 017b
+  pred 0084
+  ViewDebug.java:591@0085: Rop{move-result I <- . flows} v11:I <- .
+  ViewDebug.java:591@0085: goto . <- .
+  next 0088
+block 0084
+  pred 017a
+  ViewDebug.java:591@0084: move-object v13:Ljava/io/IOException; <- v3:Ljava/io
+  /IOException;
+  ViewDebug.java:591@0085: Rop{invoke-static . <- Ljava/lang/String; Ljava/lang
+  /String; Ljava/lang/Throwable; call throws <any>}(android.util.Log.w:(Ljava/l
+  ang/String;Ljava/lang/String;Ljava/lang/Throwable;)I catch java.lang.Object) 
+  . <- v11:Ljava/lang/String;="ViewServer" v12:Ljava/lang/String;="Connection e
+  rror: " v13:Ljava/io/IOException;
+  next 0140
+  next 017b *
+block 0088
+  pred 017b
+  @????: goto . <- .
+  next 0089
+block 008f
+  pred 0140
+  ViewDebug.java:593@008f: move-object v8:Ljava/lang/Class;=java.lang.Object <-
+   v11:Ljava/lang/Class;=java.lang.Object
+  ViewDebug.java:593@008f: goto . <- .
+  next 0091
+block 007c
+  pred 0190
+  ViewDebug.java:600@007c: goto . <- .
+  next 00ad
+block 008c
+  pred 0184
+  ViewDebug.java:600@008c: goto . <- .
+  next 00ad
+block 0094
+  pred 017e
+  ViewDebug.java:593@0094: move-object v11:Ljava/lang/Class;=java.lang.Object <
+  - v8:Ljava/lang/Class;=java.lang.Object
+  ViewDebug.java:593@0096: throw(catch) . <- v11:Ljava/lang/Class;=java.lang.Ob
+  ject
+  returns
+block 00ad
+  pred 007c
+  pred 008c
+  ViewDebug.java:601@00ad: goto . <- .
+  next 0005
+block 00b0
+  pred 000c
+  ViewDebug.java:602@00b0: goto . <- .
+  next 0163
+block 0163
+  pred 00b0
+  ViewDebug.java:602@00b0: return-void . <- .
+  returns
+block 0116
+  pred 0018
+  pred 001b
+  pred 001f
+  pred 0024
+  pred 0027
+  pred 002a
+  pred 002f
+  pred 0033
+  pred 003b
+  pred 0042
+  pred 0046
+  pred 0049
+  pred 004d
+  pred 0051
+  pred 0057
+  pred 005c
+  ViewDebug.java:586@0065: Rop{move-exception Ljava/lang/Object; <- . flows} v1
+  1:Ljava/lang/Object; <- .
+  ViewDebug.java:586@0065: goto . <- .
+  next 0065
+block 0130
+  pred 0011
+  pred 006a
+  pred 0189
+  pred 018c
+  ViewDebug.java:590@007f: Rop{move-exception Ljava/io/IOException; <- . flows}
+   v11:Ljava/io/IOException; <- .
+  ViewDebug.java:590@007f: goto . <- .
+  next 007f
+block 0140
+  pred 0011
+  pred 006a
+  pred 007f
+  pred 0082
+  pred 0084
+  pred 0189
+  pred 018c
+  ViewDebug.java:593@008f: Rop{move-exception Ljava/lang/Object; <- . flows} v1
+  1:Ljava/lang/Object; <- .
+  ViewDebug.java:593@008f: goto . <- .
+  next 008f
+block 017c
+  pred 0091
+  ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+  next 017d *
+  next 017e
+block 017d
+  pred 017c
+  ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+  s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+  net/Socket;
+  next 017f
+  next 0180 *
+block 0180
+  pred 017d
+  ViewDebug.java:598@00a1: goto . <- .
+  next 017e
+block 017e
+  pred 017c
+  pred 0180
+  pred 0181
+  @????: goto . <- .
+  next 0094
+block 017f
+  pred 017d
+  ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+   v11:Ljava/io/IOException; <- .
+  ViewDebug.java:596@00a4: goto . <- .
+  next 0181
+block 0181
+  pred 017f
+  ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+  a/lang/Class;=java.io.IOException
+  ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+  o/IOException;
+  ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+  hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+  /IOException;
+  next 017e
+block 0091
+  pred 008f
+  @????: goto . <- .
+  next 017c
+block 0182
+  pred 0089
+  ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+  next 0183 *
+  next 0184
+block 0183
+  pred 0182
+  ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+  s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+  net/Socket;
+  next 0185
+  next 0186 *
+block 0186
+  pred 0183
+  ViewDebug.java:598@00a1: goto . <- .
+  next 0184
+block 0184
+  pred 0182
+  pred 0186
+  pred 0187
+  @????: goto . <- .
+  next 008c
+block 0185
+  pred 0183
+  ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+   v11:Ljava/io/IOException; <- .
+  ViewDebug.java:596@00a4: goto . <- .
+  next 0187
+block 0187
+  pred 0185
+  ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+  a/lang/Class;=java.io.IOException
+  ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+  o/IOException;
+  ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+  hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+  /IOException;
+  next 0184
+block 0089
+  pred 0088
+  @????: goto . <- .
+  next 0182
+block 0188
+  pred 0067
+  ViewDebug.java:586@006f: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:586@0070: if-eqz-object . <- v11:Ljava/io/BufferedReader;
+  next 0189 *
+  next 018a
+block 0189
+  pred 0188
+  ViewDebug.java:587@0073: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:587@0074: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+  l throws <any>}(java.io.BufferedReader.close:()V catch java.io.IOException ja
+  va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+  next 0130
+  next 0140
+  next 018a *
+block 018a
+  pred 0188
+  pred 0189
+  @????: goto . <- .
+  next 006a
+block 0067
+  pred 0065
+  @????: goto . <- .
+  next 0188
+block 018b
+  pred 005f
+  ViewDebug.java:586@006f: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:586@0070: if-eqz-object . <- v11:Ljava/io/BufferedReader;
+  next 018c *
+  next 018d
+block 018c
+  pred 018b
+  ViewDebug.java:587@0073: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+  /io/BufferedReader;
+  ViewDebug.java:587@0074: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+  l throws <any>}(java.io.BufferedReader.close:()V catch java.io.IOException ja
+  va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+  next 0130
+  next 0140
+  next 018d *
+block 018d
+  pred 018b
+  pred 018c
+  @????: goto . <- .
+  next 0062
+block 005f
+  pred 003f
+  pred 0054
+  pred 005c
+  @????: goto . <- .
+  next 018b
+block 018e
+  pred 0079
+  ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+  next 018f *
+  next 0190
+block 018f
+  pred 018e
+  ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+  cket;
+  ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+  s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+  net/Socket;
+  next 0191
+  next 0192 *
+block 0192
+  pred 018f
+  ViewDebug.java:598@00a1: goto . <- .
+  next 0190
+block 0190
+  pred 018e
+  pred 0192
+  pred 0193
+  @????: goto . <- .
+  next 007c
+block 0191
+  pred 018f
+  ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+   v11:Ljava/io/IOException; <- .
+  ViewDebug.java:596@00a4: goto . <- .
+  next 0193
+block 0193
+  pred 0191
+  ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+  a/lang/Class;=java.io.IOException
+  ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+  o/IOException;
+  ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+  hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+  /IOException;
+  next 0190
+block 0079
+  pred 0062
+  @????: goto . <- .
+  next 018e
diff --git a/dx/tests/098-dex-jsr-ret-throw/info.txt b/dx/tests/098-dex-jsr-ret-throw/info.txt
new file mode 100644
index 0000000..9dcd39d
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/info.txt
@@ -0,0 +1,4 @@
+The enclosed class file was generated with javac version 1.5.0_13-b05. 
+It contains an example of a subroutine being exited by a "throw" instruction in 
+such a way that it caused the frame merge and subroutine inliner
+algorithms to not converge. This was bug #1137450.
diff --git a/dx/tests/098-dex-jsr-ret-throw/run b/dx/tests/098-dex-jsr-ret-throw/run
new file mode 100755
index 0000000..dfc7b89
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+dx --debug --dump --method=run --rop-blocks 'ViewDebug$ViewServer.class'
diff --git a/dx/tests/099-dex-core-library-error/Blort.java b/dx/tests/099-dex-core-library-error/Blort.java
new file mode 100644
index 0000000..6f619d7
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Blort.java
@@ -0,0 +1,5 @@
+package java.blort;
+
+public class Blort {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/Muffins.java b/dx/tests/099-dex-core-library-error/Muffins.java
new file mode 100644
index 0000000..7ee4c4c
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Muffins.java
@@ -0,0 +1,5 @@
+package javax.net;
+
+public class Muffins {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/Zorch.java b/dx/tests/099-dex-core-library-error/Zorch.java
new file mode 100644
index 0000000..57c311f
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Zorch.java
@@ -0,0 +1,5 @@
+package javax.zorch;
+
+public class Zorch {
+    // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/expected.txt b/dx/tests/099-dex-core-library-error/expected.txt
new file mode 100644
index 0000000..d9c405b
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/expected.txt
@@ -0,0 +1,5 @@
+exit code: 1
+exit code: 1
+exit code: 0
+Found zorch.dex
+Done
diff --git a/dx/tests/099-dex-core-library-error/info.txt b/dx/tests/099-dex-core-library-error/info.txt
new file mode 100644
index 0000000..3a62267
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/info.txt
@@ -0,0 +1,3 @@
+This tests that attempts to define core classes fail and that
+an attempt to define a legal javax.* class succeeds. (Only *some*
+javax packages are considered to be off-limits.)
diff --git a/dx/tests/099-dex-core-library-error/run b/dx/tests/099-dex-core-library-error/run
new file mode 100644
index 0000000..f063266
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/run
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . *.java
+
+dx --debug --dex --output=blort.dex java/blort/Blort.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r blort.dex ]; then
+    echo Found blort.dex
+fi
+
+dx --debug --dex --output=muffins.dex javax/net/Muffins.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r muffins.dex ]; then
+    echo Found muffins.dex
+fi
+
+dx --debug --dex --output=zorch.dex javax/zorch/Zorch.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r zorch.dex ]; then
+    echo Found zorch.dex
+fi
+
+echo Done
diff --git a/dx/tests/100-local-mismatch/blort1.j b/dx/tests/100-local-mismatch/blort1.j
new file mode 100644
index 0000000..327557e
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort1.j
@@ -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.
+
+.class Blort1
+.super java/lang/Object
+
+.method public static basicTypeMismatch1()V
+    .limit locals 1
+    .limit stack 1
+    .var 0 is x Ljava/lang/Object; from start to end
+    bipush 1
+    istore_0
+start:
+    nop
+end:
+    return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort2.j b/dx/tests/100-local-mismatch/blort2.j
new file mode 100644
index 0000000..6fc79cc
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort2.j
@@ -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.
+
+.class Blort2
+.super java/lang/Object
+
+.method public static basicTypeMismatch2()V
+    .limit locals 1
+    .limit stack 1
+    .var 0 is x I from start to end
+    aconst_null
+    astore_0
+start:
+    nop
+end:
+    return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort3.j b/dx/tests/100-local-mismatch/blort3.j
new file mode 100644
index 0000000..0fdcb89
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort3.j
@@ -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.
+
+.class Blort3
+.super java/lang/Object
+
+.method public static arrayMismatch1()V
+    .limit locals 1
+    .limit stack 1
+    .var 0 is x [B from start to end
+    bipush 1
+    istore_0
+start:
+    nop
+end:
+    return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort4.j b/dx/tests/100-local-mismatch/blort4.j
new file mode 100644
index 0000000..1ef207d
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort4.j
@@ -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.
+
+.class Blort4
+.super java/lang/Object
+
+.method public static arrayMismatch2()V
+    .limit locals 1
+    .limit stack 1
+    .var 0 is x [Ljava/lang/Object; from start to end
+    ldc "hello"
+    astore_0
+start:
+    nop
+end:
+    return
+.end method
diff --git a/dx/tests/100-local-mismatch/expected.txt b/dx/tests/100-local-mismatch/expected.txt
new file mode 100644
index 0000000..235b206
--- /dev/null
+++ b/dx/tests/100-local-mismatch/expected.txt
@@ -0,0 +1,9 @@
+TEST 1
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type int using a local variable of type java.lang.Object. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 2
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.lang.Object using a local variable of type int. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 3
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type int using a local variable of type byte[]. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 4
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.lang.String using a local variable of type java.lang.Object[]. This is symptomatic of .class transformation tools that ignore local variable information.
+DONE
diff --git a/dx/tests/100-local-mismatch/info.txt b/dx/tests/100-local-mismatch/info.txt
new file mode 100644
index 0000000..89b6e10
--- /dev/null
+++ b/dx/tests/100-local-mismatch/info.txt
@@ -0,0 +1,3 @@
+This is a smoke test that makes sure that dx complains when a local
+variable table entry fundamentally disagrees with an instruction that
+accesses that local.
diff --git a/dx/tests/100-local-mismatch/run b/dx/tests/100-local-mismatch/run
new file mode 100644
index 0000000..fbcf1ed
--- /dev/null
+++ b/dx/tests/100-local-mismatch/run
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort1.j >/dev/null
+jasmin -d . blort2.j >/dev/null
+jasmin -d . blort3.j >/dev/null
+jasmin -d . blort4.j >/dev/null
+
+echo "TEST 1"
+dx --dex Blort1.class 2>&1 | grep mismatch
+
+echo "TEST 2"
+dx --dex Blort2.class 2>&1 | grep mismatch
+
+echo "TEST 3"
+dx --dex Blort3.class 2>&1 | grep mismatch
+
+echo "TEST 4"
+dx --dex Blort4.class 2>&1 | grep mismatch
+
+echo "DONE"
diff --git a/dx/tests/101-verify-wide-math/expected.txt b/dx/tests/101-verify-wide-math/expected.txt
new file mode 100644
index 0000000..4bd352d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/expected.txt
@@ -0,0 +1,54 @@
+Generated: ./op_d2f.class
+d2f: expected failure occurred
+Generated: ./op_d2i.class
+d2i: expected failure occurred
+Generated: ./op_d2l.class
+d2l: expected failure occurred
+Generated: ./op_dadd.class
+dadd: expected failure occurred
+Generated: ./op_dcmpg.class
+dcmpg: expected failure occurred
+Generated: ./op_dcmpl.class
+dcmpl: expected failure occurred
+Generated: ./op_ddiv.class
+ddiv: expected failure occurred
+Generated: ./op_dmul.class
+dmul: expected failure occurred
+Generated: ./op_dneg.class
+dneg: expected failure occurred
+Generated: ./op_drem.class
+drem: expected failure occurred
+Generated: ./op_dsub.class
+dsub: expected failure occurred
+Generated: ./op_l2d.class
+l2d: expected failure occurred
+Generated: ./op_l2f.class
+l2f: expected failure occurred
+Generated: ./op_l2i.class
+l2i: expected failure occurred
+Generated: ./op_ladd.class
+ladd: expected failure occurred
+Generated: ./op_land.class
+land: expected failure occurred
+Generated: ./op_lcmp.class
+lcmp: expected failure occurred
+Generated: ./op_ldiv.class
+ldiv: expected failure occurred
+Generated: ./op_lmul.class
+lmul: expected failure occurred
+Generated: ./op_lneg.class
+lneg: expected failure occurred
+Generated: ./op_lor.class
+lor: expected failure occurred
+Generated: ./op_lrem.class
+lrem: expected failure occurred
+Generated: ./op_lshl.class
+lshl: expected failure occurred
+Generated: ./op_lshr.class
+lshr: expected failure occurred
+Generated: ./op_lsub.class
+lsub: expected failure occurred
+Generated: ./op_lushr.class
+lushr: expected failure occurred
+Generated: ./op_lxor.class
+lxor: expected failure occurred
diff --git a/dx/tests/101-verify-wide-math/info.txt b/dx/tests/101-verify-wide-math/info.txt
new file mode 100644
index 0000000..6ec551d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/info.txt
@@ -0,0 +1,3 @@
+This tests that wide-taking (category-2) "calculation" opcodes (math
+ops, comparisons, etc.) verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/101-verify-wide-math/op_d2f.j b/dx/tests/101-verify-wide-math/op_d2f.j
new file mode 100644
index 0000000..65a3c9d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2f.j
@@ -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.
+
+.class op_d2f
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    d2f
+    freturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_d2i.j b/dx/tests/101-verify-wide-math/op_d2i.j
new file mode 100644
index 0000000..6e8976c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2i.j
@@ -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.
+
+.class op_d2i
+.super java/lang/Object
+
+.method public static test(II)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    d2i
+    ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_d2l.j b/dx/tests/101-verify-wide-math/op_d2l.j
new file mode 100644
index 0000000..f8e24c9
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2l.j
@@ -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.
+
+.class op_d2l
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    d2l
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dadd.j b/dx/tests/101-verify-wide-math/op_dadd.j
new file mode 100644
index 0000000..232c541
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dadd.j
@@ -0,0 +1,26 @@
+; 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 op_dadd
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dadd
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dcmpg.j b/dx/tests/101-verify-wide-math/op_dcmpg.j
new file mode 100644
index 0000000..cd1b151
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dcmpg.j
@@ -0,0 +1,26 @@
+; 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 op_dcmpg
+.super java/lang/Object
+
+.method public static test(II)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dcmpg
+    ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dcmpl.j b/dx/tests/101-verify-wide-math/op_dcmpl.j
new file mode 100644
index 0000000..dd54c52
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dcmpl.j
@@ -0,0 +1,26 @@
+; 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 op_dcmpl
+.super java/lang/Object
+
+.method public static test(II)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dcmpl
+    ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ddiv.j b/dx/tests/101-verify-wide-math/op_ddiv.j
new file mode 100644
index 0000000..b9ee329
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ddiv.j
@@ -0,0 +1,26 @@
+; 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 op_ddiv
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    ddiv
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dmul.j b/dx/tests/101-verify-wide-math/op_dmul.j
new file mode 100644
index 0000000..f915e79
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dmul.j
@@ -0,0 +1,26 @@
+; 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 op_dmul
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dmul
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dneg.j b/dx/tests/101-verify-wide-math/op_dneg.j
new file mode 100644
index 0000000..98fd9df
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dneg.j
@@ -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.
+
+.class op_dneg
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    dneg
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_drem.j b/dx/tests/101-verify-wide-math/op_drem.j
new file mode 100644
index 0000000..c0fca65
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_drem.j
@@ -0,0 +1,26 @@
+; 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 op_drem
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    drem
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dsub.j b/dx/tests/101-verify-wide-math/op_dsub.j
new file mode 100644
index 0000000..e04f505
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dsub.j
@@ -0,0 +1,26 @@
+; 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 op_dsub
+.super java/lang/Object
+
+.method public static test(II)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    dsub
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2d.j b/dx/tests/101-verify-wide-math/op_l2d.j
new file mode 100644
index 0000000..d4ac0a8
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2d.j
@@ -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.
+
+.class op_l2d
+.super java/lang/Object
+
+.method public static test(I)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    l2d
+    dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2f.j b/dx/tests/101-verify-wide-math/op_l2f.j
new file mode 100644
index 0000000..2dbe9d2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2f.j
@@ -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.
+
+.class op_l2f
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    l2f
+    freturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2i.j b/dx/tests/101-verify-wide-math/op_l2i.j
new file mode 100644
index 0000000..1b4e68a
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2i.j
@@ -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.
+
+.class op_l2i
+.super java/lang/Object
+
+.method public static test(I)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    l2i
+    ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ladd.j b/dx/tests/101-verify-wide-math/op_ladd.j
new file mode 100644
index 0000000..1dbb6f8
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ladd.j
@@ -0,0 +1,26 @@
+; 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 op_ladd
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    ladd
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_land.j b/dx/tests/101-verify-wide-math/op_land.j
new file mode 100644
index 0000000..e8a55bb
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_land.j
@@ -0,0 +1,26 @@
+; 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 op_land
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    land
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lcmp.j b/dx/tests/101-verify-wide-math/op_lcmp.j
new file mode 100644
index 0000000..b651c9c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lcmp.j
@@ -0,0 +1,26 @@
+; 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 op_lcmp
+.super java/lang/Object
+
+.method public static test(II)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lcmp
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ldiv.j b/dx/tests/101-verify-wide-math/op_ldiv.j
new file mode 100644
index 0000000..677daa2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ldiv.j
@@ -0,0 +1,26 @@
+; 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 op_ldiv
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    ldiv
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lmul.j b/dx/tests/101-verify-wide-math/op_lmul.j
new file mode 100644
index 0000000..074d67c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lmul.j
@@ -0,0 +1,26 @@
+; 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 op_lmul
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lmul
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lneg.j b/dx/tests/101-verify-wide-math/op_lneg.j
new file mode 100644
index 0000000..18d5780
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lneg.j
@@ -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.
+
+.class op_lneg
+.super java/lang/Object
+
+.method public static test(I)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    lneg
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lor.j b/dx/tests/101-verify-wide-math/op_lor.j
new file mode 100644
index 0000000..267ff1f
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lor.j
@@ -0,0 +1,26 @@
+; 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 op_lor
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lor
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lrem.j b/dx/tests/101-verify-wide-math/op_lrem.j
new file mode 100644
index 0000000..5e0df6e
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lrem.j
@@ -0,0 +1,26 @@
+; 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 op_lrem
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lrem
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lshl.j b/dx/tests/101-verify-wide-math/op_lshl.j
new file mode 100644
index 0000000..bc16ea5
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lshl.j
@@ -0,0 +1,26 @@
+; 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 op_lshl
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lshl
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lshr.j b/dx/tests/101-verify-wide-math/op_lshr.j
new file mode 100644
index 0000000..b93fb2f
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lshr.j
@@ -0,0 +1,26 @@
+; 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 op_lshr
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lshr
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lsub.j b/dx/tests/101-verify-wide-math/op_lsub.j
new file mode 100644
index 0000000..823d899
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lsub.j
@@ -0,0 +1,26 @@
+; 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 op_lsub
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lsub
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lushr.j b/dx/tests/101-verify-wide-math/op_lushr.j
new file mode 100644
index 0000000..aa9feb2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lushr.j
@@ -0,0 +1,26 @@
+; 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 op_lushr
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lushr
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lxor.j b/dx/tests/101-verify-wide-math/op_lxor.j
new file mode 100644
index 0000000..3897c96
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lxor.j
@@ -0,0 +1,26 @@
+; 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 op_lxor
+.super java/lang/Object
+
+.method public static test(II)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    lxor
+    lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/run b/dx/tests/101-verify-wide-math/run
new file mode 100644
index 0000000..a5ecd58
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/run
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# 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.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop d2f
+oneop d2i
+oneop d2l
+oneop dadd
+oneop dcmpg
+oneop dcmpl
+oneop ddiv
+oneop dmul
+oneop dneg
+oneop drem
+oneop dsub
+oneop l2d
+oneop l2f
+oneop l2i
+oneop ladd
+oneop land
+oneop lcmp
+oneop ldiv
+oneop lmul
+oneop lneg
+oneop lor
+oneop lrem
+oneop lshl
+oneop lshr
+oneop lsub
+oneop lushr
+oneop lxor
diff --git a/dx/tests/102-verify-nonwide-math/expected.txt b/dx/tests/102-verify-nonwide-math/expected.txt
new file mode 100644
index 0000000..3f857b1
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/expected.txt
@@ -0,0 +1,48 @@
+Generated: ./op_f2d.class
+f2d: expected failure occurred
+Generated: ./op_f2i.class
+f2i: expected failure occurred
+Generated: ./op_f2l.class
+f2l: expected failure occurred
+Generated: ./op_fadd.class
+fadd: expected failure occurred
+Generated: ./op_fdiv.class
+fdiv: expected failure occurred
+Generated: ./op_fmul.class
+fmul: expected failure occurred
+Generated: ./op_fneg.class
+fneg: expected failure occurred
+Generated: ./op_frem.class
+frem: expected failure occurred
+Generated: ./op_fsub.class
+fsub: expected failure occurred
+Generated: ./op_i2d.class
+i2d: expected failure occurred
+Generated: ./op_i2f.class
+i2f: expected failure occurred
+Generated: ./op_i2l.class
+i2l: expected failure occurred
+Generated: ./op_iadd.class
+iadd: expected failure occurred
+Generated: ./op_iand.class
+iand: expected failure occurred
+Generated: ./op_idiv.class
+idiv: expected failure occurred
+Generated: ./op_imul.class
+imul: expected failure occurred
+Generated: ./op_ineg.class
+ineg: expected failure occurred
+Generated: ./op_ior.class
+ior: expected failure occurred
+Generated: ./op_irem.class
+irem: expected failure occurred
+Generated: ./op_ishl.class
+ishl: expected failure occurred
+Generated: ./op_ishr.class
+ishr: expected failure occurred
+Generated: ./op_isub.class
+isub: expected failure occurred
+Generated: ./op_iushr.class
+iushr: expected failure occurred
+Generated: ./op_ixor.class
+ixor: expected failure occurred
diff --git a/dx/tests/102-verify-nonwide-math/info.txt b/dx/tests/102-verify-nonwide-math/info.txt
new file mode 100644
index 0000000..10e52ba
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/info.txt
@@ -0,0 +1,3 @@
+This tests that non-wide-taking (category-1) "calculation" opcodes (math
+ops, comparisons, etc.) to verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/102-verify-nonwide-math/op_f2d.j b/dx/tests/102-verify-nonwide-math/op_f2d.j
new file mode 100644
index 0000000..75e8917
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2d.j
@@ -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.
+
+.class op_f2d
+.super java/lang/Object
+
+.method public static test(I)D
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    f2d
+    dreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_f2i.j b/dx/tests/102-verify-nonwide-math/op_f2i.j
new file mode 100644
index 0000000..2d36af7
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2i.j
@@ -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.
+
+.class op_f2i
+.super java/lang/Object
+
+.method public static test(I)I
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    f2i
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_f2l.j b/dx/tests/102-verify-nonwide-math/op_f2l.j
new file mode 100644
index 0000000..fae9e21
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2l.j
@@ -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.
+
+.class op_f2l
+.super java/lang/Object
+
+.method public static test(I)J
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    f2l
+    lreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fadd.j b/dx/tests/102-verify-nonwide-math/op_fadd.j
new file mode 100644
index 0000000..dc3743f
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fadd.j
@@ -0,0 +1,26 @@
+; 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 op_fadd
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    fadd
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fdiv.j b/dx/tests/102-verify-nonwide-math/op_fdiv.j
new file mode 100644
index 0000000..8609be2
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fdiv.j
@@ -0,0 +1,26 @@
+; 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 op_fdiv
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    fdiv
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fmul.j b/dx/tests/102-verify-nonwide-math/op_fmul.j
new file mode 100644
index 0000000..fe4661c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fmul.j
@@ -0,0 +1,26 @@
+; 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 op_fmul
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    fmul
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fneg.j b/dx/tests/102-verify-nonwide-math/op_fneg.j
new file mode 100644
index 0000000..34898bd
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fneg.j
@@ -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.
+
+.class op_fneg
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    fneg
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_frem.j b/dx/tests/102-verify-nonwide-math/op_frem.j
new file mode 100644
index 0000000..17f4602
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_frem.j
@@ -0,0 +1,26 @@
+; 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 op_frem
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    frem
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fsub.j b/dx/tests/102-verify-nonwide-math/op_fsub.j
new file mode 100644
index 0000000..692f4f8
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fsub.j
@@ -0,0 +1,26 @@
+; 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 op_fsub
+.super java/lang/Object
+
+.method public static test(II)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    iload_1
+    fsub
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2d.j b/dx/tests/102-verify-nonwide-math/op_i2d.j
new file mode 100644
index 0000000..6c73dbe
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2d.j
@@ -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.
+
+.class op_i2d
+.super java/lang/Object
+
+.method public static test(FF)D
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    i2d
+    dreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2f.j b/dx/tests/102-verify-nonwide-math/op_i2f.j
new file mode 100644
index 0000000..cee0b84
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2f.j
@@ -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.
+
+.class op_i2f
+.super java/lang/Object
+
+.method public static test(FF)F
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    i2f
+    freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2l.j b/dx/tests/102-verify-nonwide-math/op_i2l.j
new file mode 100644
index 0000000..d6f2daa
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2l.j
@@ -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.
+
+.class op_i2l
+.super java/lang/Object
+
+.method public static test(FF)J
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    i2l
+    lreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iadd.j b/dx/tests/102-verify-nonwide-math/op_iadd.j
new file mode 100644
index 0000000..e3d92e3
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iadd.j
@@ -0,0 +1,26 @@
+; 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 op_iadd
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    iadd
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iand.j b/dx/tests/102-verify-nonwide-math/op_iand.j
new file mode 100644
index 0000000..063738c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iand.j
@@ -0,0 +1,26 @@
+; 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 op_iand
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    iand
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_idiv.j b/dx/tests/102-verify-nonwide-math/op_idiv.j
new file mode 100644
index 0000000..2e3e3a3
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_idiv.j
@@ -0,0 +1,26 @@
+; 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 op_idiv
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    idiv
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_imul.j b/dx/tests/102-verify-nonwide-math/op_imul.j
new file mode 100644
index 0000000..7f4f612
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_imul.j
@@ -0,0 +1,26 @@
+; 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 op_imul
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    imul
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ineg.j b/dx/tests/102-verify-nonwide-math/op_ineg.j
new file mode 100644
index 0000000..68fcb8c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ineg.j
@@ -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.
+
+.class op_ineg
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ineg
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ior.j b/dx/tests/102-verify-nonwide-math/op_ior.j
new file mode 100644
index 0000000..c8c3a4b
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ior.j
@@ -0,0 +1,26 @@
+; 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 op_ior
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    ior
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_irem.j b/dx/tests/102-verify-nonwide-math/op_irem.j
new file mode 100644
index 0000000..b22f1fd
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_irem.j
@@ -0,0 +1,26 @@
+; 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 op_irem
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    irem
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ishl.j b/dx/tests/102-verify-nonwide-math/op_ishl.j
new file mode 100644
index 0000000..e617182
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ishl.j
@@ -0,0 +1,26 @@
+; 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 op_ishl
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    ishl
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ishr.j b/dx/tests/102-verify-nonwide-math/op_ishr.j
new file mode 100644
index 0000000..64eba11
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ishr.j
@@ -0,0 +1,26 @@
+; 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 op_ishr
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    ishr
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_isub.j b/dx/tests/102-verify-nonwide-math/op_isub.j
new file mode 100644
index 0000000..ee789b0
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_isub.j
@@ -0,0 +1,26 @@
+; 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 op_isub
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    isub
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iushr.j b/dx/tests/102-verify-nonwide-math/op_iushr.j
new file mode 100644
index 0000000..73c34f1
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iushr.j
@@ -0,0 +1,26 @@
+; 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 op_iushr
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    iushr
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ixor.j b/dx/tests/102-verify-nonwide-math/op_ixor.j
new file mode 100644
index 0000000..68f095b
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ixor.j
@@ -0,0 +1,26 @@
+; 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 op_ixor
+.super java/lang/Object
+
+.method public static test(FF)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    ixor
+    ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/run b/dx/tests/102-verify-nonwide-math/run
new file mode 100644
index 0000000..eb4a294
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/run
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# 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.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop f2d
+oneop f2i
+oneop f2l
+oneop fadd
+oneop fdiv
+oneop fmul
+oneop fneg
+oneop frem
+oneop fsub
+oneop i2d
+oneop i2f
+oneop i2l
+oneop iadd
+oneop iand
+oneop idiv
+oneop imul
+oneop ineg
+oneop ior
+oneop irem
+oneop ishl
+oneop ishr
+oneop isub
+oneop iushr
+oneop ixor
diff --git a/dx/tests/103-verify-branch-ops/expected.txt b/dx/tests/103-verify-branch-ops/expected.txt
new file mode 100644
index 0000000..2c96704
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/expected.txt
@@ -0,0 +1,36 @@
+Generated: ./op_if_acmpeq.class
+if_acmpeq: expected failure occurred
+Generated: ./op_if_acmpne.class
+if_acmpne: expected failure occurred
+Generated: ./op_if_icmpeq.class
+if_icmpeq: expected failure occurred
+Generated: ./op_if_icmpge.class
+if_icmpge: expected failure occurred
+Generated: ./op_if_icmpgt.class
+if_icmpgt: expected failure occurred
+Generated: ./op_if_icmple.class
+if_icmple: expected failure occurred
+Generated: ./op_if_icmplt.class
+if_icmplt: expected failure occurred
+Generated: ./op_if_icmpne.class
+if_icmpne: expected failure occurred
+Generated: ./op_ifeq.class
+ifeq: expected failure occurred
+Generated: ./op_ifge.class
+ifge: expected failure occurred
+Generated: ./op_ifgt.class
+ifgt: expected failure occurred
+Generated: ./op_ifle.class
+ifle: expected failure occurred
+Generated: ./op_iflt.class
+iflt: expected failure occurred
+Generated: ./op_ifne.class
+ifne: expected failure occurred
+Generated: ./op_ifnonnull.class
+ifnonnull: expected failure occurred
+Generated: ./op_ifnull.class
+ifnull: expected failure occurred
+Generated: ./op_lookupswitch.class
+lookupswitch: expected failure occurred
+Generated: ./op_tableswitch.class
+tableswitch: expected failure occurred
diff --git a/dx/tests/103-verify-branch-ops/info.txt b/dx/tests/103-verify-branch-ops/info.txt
new file mode 100644
index 0000000..8705e83
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/info.txt
@@ -0,0 +1,2 @@
+This tests branch opcodes to verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/103-verify-branch-ops/op_if_acmpeq.j b/dx/tests/103-verify-branch-ops/op_if_acmpeq.j
new file mode 100644
index 0000000..4339200
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_acmpeq.j
@@ -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.
+
+.class op_if_acmpeq
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_acmpeq blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_acmpne.j b/dx/tests/103-verify-branch-ops/op_if_acmpne.j
new file mode 100644
index 0000000..57a317b
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_acmpne.j
@@ -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.
+
+.class op_if_acmpne
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_acmpne blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpeq.j b/dx/tests/103-verify-branch-ops/op_if_icmpeq.j
new file mode 100644
index 0000000..1f141bc
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpeq.j
@@ -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.
+
+.class op_if_icmpeq
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmpeq blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpge.j b/dx/tests/103-verify-branch-ops/op_if_icmpge.j
new file mode 100644
index 0000000..6ae2e3f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpge.j
@@ -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.
+
+.class op_if_icmpge
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmpge blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpgt.j b/dx/tests/103-verify-branch-ops/op_if_icmpgt.j
new file mode 100644
index 0000000..4e67282
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpgt.j
@@ -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.
+
+.class op_if_icmpgt
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmpgt blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmple.j b/dx/tests/103-verify-branch-ops/op_if_icmple.j
new file mode 100644
index 0000000..3511800
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmple.j
@@ -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.
+
+.class op_if_icmple
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmple blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmplt.j b/dx/tests/103-verify-branch-ops/op_if_icmplt.j
new file mode 100644
index 0000000..89527f5
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmplt.j
@@ -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.
+
+.class op_if_icmplt
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmplt blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpne.j b/dx/tests/103-verify-branch-ops/op_if_icmpne.j
new file mode 100644
index 0000000..a94faee
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpne.j
@@ -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.
+
+.class op_if_icmpne
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    fload_1
+    if_icmpne blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifeq.j b/dx/tests/103-verify-branch-ops/op_ifeq.j
new file mode 100644
index 0000000..620e1c9
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifeq.j
@@ -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.
+
+.class op_ifeq
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifeq blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifge.j b/dx/tests/103-verify-branch-ops/op_ifge.j
new file mode 100644
index 0000000..c46b176
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifge.j
@@ -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.
+
+.class op_ifge
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifge blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifgt.j b/dx/tests/103-verify-branch-ops/op_ifgt.j
new file mode 100644
index 0000000..8165038
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifgt.j
@@ -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.
+
+.class op_ifgt
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifgt blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifle.j b/dx/tests/103-verify-branch-ops/op_ifle.j
new file mode 100644
index 0000000..758943f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifle.j
@@ -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.
+
+.class op_ifle
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifle blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_iflt.j b/dx/tests/103-verify-branch-ops/op_iflt.j
new file mode 100644
index 0000000..5091355
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_iflt.j
@@ -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.
+
+.class op_iflt
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iflt blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifne.j b/dx/tests/103-verify-branch-ops/op_ifne.j
new file mode 100644
index 0000000..bb2dadc
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifne.j
@@ -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.
+
+.class op_ifne
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifne blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifnonnull.j b/dx/tests/103-verify-branch-ops/op_ifnonnull.j
new file mode 100644
index 0000000..f2422f0
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifnonnull.j
@@ -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.
+
+.class op_ifnonnull
+.super java/lang/Object
+
+.method public static test(IF)V
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    ifnonnull blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifnull.j b/dx/tests/103-verify-branch-ops/op_ifnull.j
new file mode 100644
index 0000000..c253b09
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifnull.j
@@ -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.
+
+.class op_ifnull
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ifnull blort
+    nop
+blort:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_lookupswitch.j b/dx/tests/103-verify-branch-ops/op_lookupswitch.j
new file mode 100644
index 0000000..21fe8f7
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_lookupswitch.j
@@ -0,0 +1,33 @@
+; 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 op_lookupswitch
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    lookupswitch
+        0x05: t1
+        0x10: t2
+        default: t3
+t1:
+    nop
+t2:
+    nop
+t3:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_tableswitch.j b/dx/tests/103-verify-branch-ops/op_tableswitch.j
new file mode 100644
index 0000000..657feff
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_tableswitch.j
@@ -0,0 +1,33 @@
+; 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 op_tableswitch
+.super java/lang/Object
+
+.method public static test(FF)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    tableswitch 0x10
+        t1
+        t2
+        default: t3
+t1:
+    nop
+t2:
+    nop
+t3:
+    return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/run b/dx/tests/103-verify-branch-ops/run
new file mode 100644
index 0000000..c9ca89f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/run
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# 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.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop if_acmpeq
+oneop if_acmpne
+oneop if_icmpeq
+oneop if_icmpge
+oneop if_icmpgt
+oneop if_icmple
+oneop if_icmplt
+oneop if_icmpne
+oneop ifeq
+oneop ifge
+oneop ifgt
+oneop ifle
+oneop iflt
+oneop ifne
+oneop ifnonnull
+oneop ifnull
+oneop lookupswitch
+oneop tableswitch
diff --git a/dx/tests/104-verify-return-ops/expected.txt b/dx/tests/104-verify-return-ops/expected.txt
new file mode 100644
index 0000000..5bd9dfd
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/expected.txt
@@ -0,0 +1,22 @@
+Generated: ./op_areturn.class
+areturn: expected failure occurred
+Generated: ./op_dreturn.class
+dreturn: expected failure occurred
+Generated: ./op_freturn.class
+freturn: expected failure occurred
+Generated: ./op_ireturn.class
+ireturn: expected failure occurred
+Generated: ./op_lreturn.class
+lreturn: expected failure occurred
+Generated: ./op_sig_areturn.class
+sig_areturn: expected failure occurred
+Generated: ./op_sig_dreturn.class
+sig_dreturn: expected failure occurred
+Generated: ./op_sig_freturn.class
+sig_freturn: expected failure occurred
+Generated: ./op_sig_ireturn.class
+sig_ireturn: expected failure occurred
+Generated: ./op_sig_lreturn.class
+sig_lreturn: expected failure occurred
+Generated: ./op_sig_return.class
+sig_return: expected failure occurred
diff --git a/dx/tests/104-verify-return-ops/info.txt b/dx/tests/104-verify-return-ops/info.txt
new file mode 100644
index 0000000..1f5e634
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that return opcodes verify that their arguments are actually of
+the appropriate types and that the opcode matches the method signature.
diff --git a/dx/tests/104-verify-return-ops/op_areturn.j b/dx/tests/104-verify-return-ops/op_areturn.j
new file mode 100644
index 0000000..0b25088
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_areturn.j
@@ -0,0 +1,24 @@
+; 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 op_areturn
+.super java/lang/Object
+
+.method public static test(F)Ljava/lang/Object;
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    areturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_dreturn.j b/dx/tests/104-verify-return-ops/op_dreturn.j
new file mode 100644
index 0000000..1075fe1
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_dreturn.j
@@ -0,0 +1,24 @@
+; 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 op_dreturn
+.super java/lang/Object
+
+.method public static test(F)D
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    dreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_freturn.j b/dx/tests/104-verify-return-ops/op_freturn.j
new file mode 100644
index 0000000..6586ce6
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_freturn.j
@@ -0,0 +1,24 @@
+; 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 op_freturn
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    freturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_ireturn.j b/dx/tests/104-verify-return-ops/op_ireturn.j
new file mode 100644
index 0000000..e93dda8
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_ireturn.j
@@ -0,0 +1,24 @@
+; 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 op_ireturn
+.super java/lang/Object
+
+.method public static test(F)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    ireturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_lreturn.j b/dx/tests/104-verify-return-ops/op_lreturn.j
new file mode 100644
index 0000000..349f353
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_lreturn.j
@@ -0,0 +1,24 @@
+; 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 op_lreturn
+.super java/lang/Object
+
+.method public static test(F)J
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    lreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_areturn.j b/dx/tests/104-verify-return-ops/op_sig_areturn.j
new file mode 100644
index 0000000..f1ea1b4
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_areturn.j
@@ -0,0 +1,24 @@
+; 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 op_sig_areturn
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    aconst_null
+    areturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_dreturn.j b/dx/tests/104-verify-return-ops/op_sig_dreturn.j
new file mode 100644
index 0000000..fa6fcd2
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_dreturn.j
@@ -0,0 +1,24 @@
+; 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 op_sig_dreturn
+.super java/lang/Object
+
+.method public static test(D)F
+    .limit locals 2
+    .limit stack 3
+
+    dload_0
+    dreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_freturn.j b/dx/tests/104-verify-return-ops/op_sig_freturn.j
new file mode 100644
index 0000000..be5dea8
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_freturn.j
@@ -0,0 +1,24 @@
+; 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 op_sig_freturn
+.super java/lang/Object
+
+.method public static test(F)I
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    freturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_ireturn.j b/dx/tests/104-verify-return-ops/op_sig_ireturn.j
new file mode 100644
index 0000000..ab3aa40
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_ireturn.j
@@ -0,0 +1,24 @@
+; 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 op_sig_ireturn
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    iload_0
+    ireturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_lreturn.j b/dx/tests/104-verify-return-ops/op_sig_lreturn.j
new file mode 100644
index 0000000..e5a121d
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_lreturn.j
@@ -0,0 +1,24 @@
+; 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 op_sig_lreturn
+.super java/lang/Object
+
+.method public static test(J)F
+    .limit locals 2
+    .limit stack 3
+
+    lload_0
+    lreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_return.j b/dx/tests/104-verify-return-ops/op_sig_return.j
new file mode 100644
index 0000000..26c0678
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_return.j
@@ -0,0 +1,23 @@
+; 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 op_sig_return
+.super java/lang/Object
+
+.method public static test(I)F
+    .limit locals 2
+    .limit stack 3
+
+    return
+.end method
diff --git a/dx/tests/104-verify-return-ops/run b/dx/tests/104-verify-return-ops/run
new file mode 100644
index 0000000..fbce332
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/run
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# 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.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop areturn
+oneop dreturn
+oneop freturn
+oneop ireturn
+oneop lreturn
+oneop sig_areturn
+oneop sig_dreturn
+oneop sig_freturn
+oneop sig_ireturn
+oneop sig_lreturn
+oneop sig_return
diff --git a/dx/tests/105-verify-load-store-ops/expected.txt b/dx/tests/105-verify-load-store-ops/expected.txt
new file mode 100644
index 0000000..409ead0
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/expected.txt
@@ -0,0 +1,82 @@
+Generated: ./op_aaload.class
+aaload: expected failure occurred
+Generated: ./op_aastore.class
+aastore: expected failure occurred
+Generated: ./op_astore.class
+astore: expected failure occurred
+Generated: ./op_astore_0.class
+astore_0: expected failure occurred
+Generated: ./op_astore_1.class
+astore_1: expected failure occurred
+Generated: ./op_astore_2.class
+astore_2: expected failure occurred
+Generated: ./op_astore_3.class
+astore_3: expected failure occurred
+Generated: ./op_baload.class
+baload: expected failure occurred
+Generated: ./op_bastore.class
+bastore: expected failure occurred
+Generated: ./op_caload.class
+caload: expected failure occurred
+Generated: ./op_castore.class
+castore: expected failure occurred
+Generated: ./op_daload.class
+daload: expected failure occurred
+Generated: ./op_dastore.class
+dastore: expected failure occurred
+Generated: ./op_dstore.class
+dstore: expected failure occurred
+Generated: ./op_dstore_0.class
+dstore_0: expected failure occurred
+Generated: ./op_dstore_1.class
+dstore_1: expected failure occurred
+Generated: ./op_dstore_2.class
+dstore_2: expected failure occurred
+Generated: ./op_dstore_3.class
+dstore_3: expected failure occurred
+Generated: ./op_faload.class
+faload: expected failure occurred
+Generated: ./op_fastore.class
+fastore: expected failure occurred
+Generated: ./op_fstore.class
+fstore: expected failure occurred
+Generated: ./op_fstore_0.class
+fstore_0: expected failure occurred
+Generated: ./op_fstore_1.class
+fstore_1: expected failure occurred
+Generated: ./op_fstore_2.class
+fstore_2: expected failure occurred
+Generated: ./op_fstore_3.class
+fstore_3: expected failure occurred
+Generated: ./op_iaload.class
+iaload: expected failure occurred
+Generated: ./op_iastore.class
+iastore: expected failure occurred
+Generated: ./op_istore.class
+istore: expected failure occurred
+Generated: ./op_istore_0.class
+istore_0: expected failure occurred
+Generated: ./op_istore_1.class
+istore_1: expected failure occurred
+Generated: ./op_istore_2.class
+istore_2: expected failure occurred
+Generated: ./op_istore_3.class
+istore_3: expected failure occurred
+Generated: ./op_laload.class
+laload: expected failure occurred
+Generated: ./op_lastore.class
+lastore: expected failure occurred
+Generated: ./op_lstore.class
+lstore: expected failure occurred
+Generated: ./op_lstore_0.class
+lstore_0: expected failure occurred
+Generated: ./op_lstore_1.class
+lstore_1: expected failure occurred
+Generated: ./op_lstore_2.class
+lstore_2: expected failure occurred
+Generated: ./op_lstore_3.class
+lstore_3: expected failure occurred
+Generated: ./op_saload.class
+saload: expected failure occurred
+Generated: ./op_sastore.class
+sastore: expected failure occurred
diff --git a/dx/tests/105-verify-load-store-ops/info.txt b/dx/tests/105-verify-load-store-ops/info.txt
new file mode 100644
index 0000000..a3a41d6
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that load and store opcodes verify that their arguments are
+actually of the appropriate types.
diff --git a/dx/tests/105-verify-load-store-ops/op_aaload.j b/dx/tests/105-verify-load-store-ops/op_aaload.j
new file mode 100644
index 0000000..f77827e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_aaload.j
@@ -0,0 +1,26 @@
+; 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 op_aaload
+.super java/lang/Object
+
+.method public static test([F)V
+    .limit locals 2
+    .limit stack 3
+
+    aload_0
+    iconst_0
+    aaload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_aastore.j b/dx/tests/105-verify-load-store-ops/op_aastore.j
new file mode 100644
index 0000000..58e1576
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_aastore.j
@@ -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.
+
+.class op_aastore
+.super java/lang/Object
+
+.method public static test([I)V
+    .limit locals 2
+    .limit stack 4
+
+    aload_0
+    iconst_0
+    aconst_null
+    aastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore.j b/dx/tests/105-verify-load-store-ops/op_astore.j
new file mode 100644
index 0000000..25131bf
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore.j
@@ -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.
+
+.class op_astore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_0.j b/dx/tests/105-verify-load-store-ops/op_astore_0.j
new file mode 100644
index 0000000..b509c12
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_0.j
@@ -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.
+
+.class op_astore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_1.j b/dx/tests/105-verify-load-store-ops/op_astore_1.j
new file mode 100644
index 0000000..a6c1043
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_1.j
@@ -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.
+
+.class op_astore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_2.j b/dx/tests/105-verify-load-store-ops/op_astore_2.j
new file mode 100644
index 0000000..cb84ee8
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_2.j
@@ -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.
+
+.class op_astore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_3.j b/dx/tests/105-verify-load-store-ops/op_astore_3.j
new file mode 100644
index 0000000..c716ba2
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_3.j
@@ -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.
+
+.class op_astore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    astore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_baload.j b/dx/tests/105-verify-load-store-ops/op_baload.j
new file mode 100644
index 0000000..cfcaf74
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_baload.j
@@ -0,0 +1,26 @@
+; 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 op_baload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    baload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_bastore.j b/dx/tests/105-verify-load-store-ops/op_bastore.j
new file mode 100644
index 0000000..587fcd3
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_bastore.j
@@ -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.
+
+.class op_bastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    iconst_0
+    bastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_caload.j b/dx/tests/105-verify-load-store-ops/op_caload.j
new file mode 100644
index 0000000..ceaf09f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_caload.j
@@ -0,0 +1,26 @@
+; 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 op_caload
+.super java/lang/Object
+
+.method public static test(D)V
+    .limit locals 2
+    .limit stack 3
+
+    dload_0
+    iconst_0
+    caload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_castore.j b/dx/tests/105-verify-load-store-ops/op_castore.j
new file mode 100644
index 0000000..5bd493e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_castore.j
@@ -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.
+
+.class op_castore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    iconst_0
+    castore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_daload.j b/dx/tests/105-verify-load-store-ops/op_daload.j
new file mode 100644
index 0000000..895d6be
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_daload.j
@@ -0,0 +1,26 @@
+; 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 op_daload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    daload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dastore.j b/dx/tests/105-verify-load-store-ops/op_dastore.j
new file mode 100644
index 0000000..b102f79
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dastore.j
@@ -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.
+
+.class op_dastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    dconst_0
+    dastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore.j b/dx/tests/105-verify-load-store-ops/op_dstore.j
new file mode 100644
index 0000000..d656a84
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore.j
@@ -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.
+
+.class op_dstore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_0.j b/dx/tests/105-verify-load-store-ops/op_dstore_0.j
new file mode 100644
index 0000000..cb3da3a
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_0.j
@@ -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.
+
+.class op_dstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_1.j b/dx/tests/105-verify-load-store-ops/op_dstore_1.j
new file mode 100644
index 0000000..45fcf9b
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_1.j
@@ -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.
+
+.class op_dstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_2.j b/dx/tests/105-verify-load-store-ops/op_dstore_2.j
new file mode 100644
index 0000000..7c167d4
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_2.j
@@ -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.
+
+.class op_dstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_3.j b/dx/tests/105-verify-load-store-ops/op_dstore_3.j
new file mode 100644
index 0000000..17222e0
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_3.j
@@ -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.
+
+.class op_dstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    dstore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_faload.j b/dx/tests/105-verify-load-store-ops/op_faload.j
new file mode 100644
index 0000000..1c17a8e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_faload.j
@@ -0,0 +1,26 @@
+; 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 op_faload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    faload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fastore.j b/dx/tests/105-verify-load-store-ops/op_fastore.j
new file mode 100644
index 0000000..799555e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fastore.j
@@ -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.
+
+.class op_fastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    fconst_0
+    fastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore.j b/dx/tests/105-verify-load-store-ops/op_fstore.j
new file mode 100644
index 0000000..5c61ebe
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore.j
@@ -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.
+
+.class op_fstore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_0.j b/dx/tests/105-verify-load-store-ops/op_fstore_0.j
new file mode 100644
index 0000000..d3033e9
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_0.j
@@ -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.
+
+.class op_fstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_1.j b/dx/tests/105-verify-load-store-ops/op_fstore_1.j
new file mode 100644
index 0000000..0abca8f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_1.j
@@ -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.
+
+.class op_fstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_2.j b/dx/tests/105-verify-load-store-ops/op_fstore_2.j
new file mode 100644
index 0000000..5cd1ebc
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_2.j
@@ -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.
+
+.class op_fstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_3.j b/dx/tests/105-verify-load-store-ops/op_fstore_3.j
new file mode 100644
index 0000000..a232307
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_3.j
@@ -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.
+
+.class op_fstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    iconst_0
+    fstore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_iaload.j b/dx/tests/105-verify-load-store-ops/op_iaload.j
new file mode 100644
index 0000000..3face08
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_iaload.j
@@ -0,0 +1,26 @@
+; 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 op_iaload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    iaload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_iastore.j b/dx/tests/105-verify-load-store-ops/op_iastore.j
new file mode 100644
index 0000000..d090e37
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_iastore.j
@@ -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.
+
+.class op_iastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    iconst_0
+    iastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore.j b/dx/tests/105-verify-load-store-ops/op_istore.j
new file mode 100644
index 0000000..138d709
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore.j
@@ -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.
+
+.class op_istore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_0.j b/dx/tests/105-verify-load-store-ops/op_istore_0.j
new file mode 100644
index 0000000..2644c3d
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_0.j
@@ -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.
+
+.class op_istore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_1.j b/dx/tests/105-verify-load-store-ops/op_istore_1.j
new file mode 100644
index 0000000..03534ee
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_1.j
@@ -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.
+
+.class op_istore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_2.j b/dx/tests/105-verify-load-store-ops/op_istore_2.j
new file mode 100644
index 0000000..e1a80b3
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_2.j
@@ -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.
+
+.class op_istore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_3.j b/dx/tests/105-verify-load-store-ops/op_istore_3.j
new file mode 100644
index 0000000..43c226f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_3.j
@@ -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.
+
+.class op_istore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    istore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_laload.j b/dx/tests/105-verify-load-store-ops/op_laload.j
new file mode 100644
index 0000000..3143604
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_laload.j
@@ -0,0 +1,26 @@
+; 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 op_laload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    laload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lastore.j b/dx/tests/105-verify-load-store-ops/op_lastore.j
new file mode 100644
index 0000000..b7ea069
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lastore.j
@@ -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.
+
+.class op_lastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    lconst_0
+    lastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore.j b/dx/tests/105-verify-load-store-ops/op_lstore.j
new file mode 100644
index 0000000..fde6974
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore.j
@@ -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.
+
+.class op_lstore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    lstore 5
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_0.j b/dx/tests/105-verify-load-store-ops/op_lstore_0.j
new file mode 100644
index 0000000..e98eab4
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_0.j
@@ -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.
+
+.class op_lstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    lstore_0
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_1.j b/dx/tests/105-verify-load-store-ops/op_lstore_1.j
new file mode 100644
index 0000000..0e2291a
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_1.j
@@ -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.
+
+.class op_lstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    lstore_1
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_2.j b/dx/tests/105-verify-load-store-ops/op_lstore_2.j
new file mode 100644
index 0000000..a84702d
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_2.j
@@ -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.
+
+.class op_lstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    dconst_0
+    lstore_2
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_3.j b/dx/tests/105-verify-load-store-ops/op_lstore_3.j
new file mode 100644
index 0000000..c35ace8
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_3.j
@@ -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.
+
+.class op_lstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 6
+    .limit stack 4
+
+    fconst_0
+    lstore_3
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_saload.j b/dx/tests/105-verify-load-store-ops/op_saload.j
new file mode 100644
index 0000000..4a80939
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_saload.j
@@ -0,0 +1,26 @@
+; 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 op_saload
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 3
+
+    fload_0
+    iconst_0
+    saload
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_sastore.j b/dx/tests/105-verify-load-store-ops/op_sastore.j
new file mode 100644
index 0000000..c97dd66
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_sastore.j
@@ -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.
+
+.class op_sastore
+.super java/lang/Object
+
+.method public static test(F)V
+    .limit locals 2
+    .limit stack 4
+
+    fload_0
+    iconst_0
+    iconst_0
+    sastore
+    return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/run b/dx/tests/105-verify-load-store-ops/run
new file mode 100644
index 0000000..adf987c
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/run
@@ -0,0 +1,68 @@
+#!/bin/bash
+#
+# 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.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop aaload
+oneop aastore
+oneop astore
+oneop astore_0
+oneop astore_1
+oneop astore_2
+oneop astore_3
+oneop baload
+oneop bastore
+oneop caload
+oneop castore
+oneop daload
+oneop dastore
+oneop dstore
+oneop dstore_0
+oneop dstore_1
+oneop dstore_2
+oneop dstore_3
+oneop faload
+oneop fastore
+oneop fstore
+oneop fstore_0
+oneop fstore_1
+oneop fstore_2
+oneop fstore_3
+oneop iaload
+oneop iastore
+oneop istore
+oneop istore_0
+oneop istore_1
+oneop istore_2
+oneop istore_3
+oneop laload
+oneop lastore
+oneop lstore
+oneop lstore_0
+oneop lstore_1
+oneop lstore_2
+oneop lstore_3
+oneop saload
+oneop sastore
diff --git a/dx/tests/106-verify-object-ops/expected.txt b/dx/tests/106-verify-object-ops/expected.txt
new file mode 100644
index 0000000..ce36b7e
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/expected.txt
@@ -0,0 +1,32 @@
+Generated: ./op_anewarray.class
+anewarray: expected failure occurred
+Generated: ./op_arraylength.class
+arraylength: expected failure occurred
+Generated: ./op_athrow.class
+athrow: expected failure occurred
+Generated: ./op_checkcast.class
+checkcast: expected failure occurred
+Generated: ./op_getfield.class
+getfield: expected failure occurred
+Generated: ./op_instanceof.class
+instanceof: expected failure occurred
+Generated: ./op_invokeinterface.class
+invokeinterface: expected failure occurred
+Generated: ./op_invokespecial.class
+invokespecial: expected failure occurred
+Generated: ./op_invokestatic.class
+invokestatic: expected failure occurred
+Generated: ./op_invokevirtual.class
+invokevirtual: expected failure occurred
+Generated: ./op_monitorenter.class
+monitorenter: expected failure occurred
+Generated: ./op_monitorexit.class
+monitorexit: expected failure occurred
+Generated: ./op_multianewarray.class
+multianewarray: expected failure occurred
+Generated: ./op_newarray.class
+newarray: expected failure occurred
+Generated: ./op_putfield.class
+putfield: expected failure occurred
+Generated: ./op_putstatic.class
+putstatic: expected failure occurred
diff --git a/dx/tests/106-verify-object-ops/info.txt b/dx/tests/106-verify-object-ops/info.txt
new file mode 100644
index 0000000..85295d7
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that the various "objecty" opcodes verify that their
+arguments are actually of the appropriate types.
diff --git a/dx/tests/106-verify-object-ops/op_anewarray.j b/dx/tests/106-verify-object-ops/op_anewarray.j
new file mode 100644
index 0000000..348acbd
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_anewarray.j
@@ -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.
+
+.class op_anewarray
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    anewarray java/lang/Object
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_arraylength.j b/dx/tests/106-verify-object-ops/op_arraylength.j
new file mode 100644
index 0000000..df5af82
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_arraylength.j
@@ -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.
+
+.class op_arraylength
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    arraylength
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_athrow.j b/dx/tests/106-verify-object-ops/op_athrow.j
new file mode 100644
index 0000000..a5a5be3
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_athrow.j
@@ -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.
+
+.class op_athrow
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    athrow
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_checkcast.j b/dx/tests/106-verify-object-ops/op_checkcast.j
new file mode 100644
index 0000000..d921ec4
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_checkcast.j
@@ -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.
+
+.class op_checkcast
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    checkcast java/lang/Object
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_getfield.j b/dx/tests/106-verify-object-ops/op_getfield.j
new file mode 100644
index 0000000..4d5f782
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_getfield.j
@@ -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.
+
+.class op_getfield
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    getfield blort/x I
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_instanceof.j b/dx/tests/106-verify-object-ops/op_instanceof.j
new file mode 100644
index 0000000..8a938f5
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_instanceof.j
@@ -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.
+
+.class op_instanceof
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    instanceof java/lang/Object
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokeinterface.j b/dx/tests/106-verify-object-ops/op_invokeinterface.j
new file mode 100644
index 0000000..2f1528f
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokeinterface.j
@@ -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.
+
+.class op_invokeinterface
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    invokeinterface blort/x()V 1
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokespecial.j b/dx/tests/106-verify-object-ops/op_invokespecial.j
new file mode 100644
index 0000000..87baffc
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokespecial.j
@@ -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.
+
+.class op_invokespecial
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    invokespecial blort/x()V
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokestatic.j b/dx/tests/106-verify-object-ops/op_invokestatic.j
new file mode 100644
index 0000000..80247bd
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokestatic.j
@@ -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.
+
+.class op_invokestatic
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    invokestatic blort/x(I)V
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokevirtual.j b/dx/tests/106-verify-object-ops/op_invokevirtual.j
new file mode 100644
index 0000000..d7ba9b5
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokevirtual.j
@@ -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.
+
+.class op_invokevirtual
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    invokevirtual blort/x()V
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_monitorenter.j b/dx/tests/106-verify-object-ops/op_monitorenter.j
new file mode 100644
index 0000000..95e23d8
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_monitorenter.j
@@ -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.
+
+.class op_monitorenter
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    monitorenter
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_monitorexit.j b/dx/tests/106-verify-object-ops/op_monitorexit.j
new file mode 100644
index 0000000..50e5fe2
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_monitorexit.j
@@ -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.
+
+.class op_monitorexit
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    monitorexit
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_multianewarray.j b/dx/tests/106-verify-object-ops/op_multianewarray.j
new file mode 100644
index 0000000..d02f474
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_multianewarray.j
@@ -0,0 +1,26 @@
+; 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 op_multianewarray
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    iconst_0
+    multianewarray [[[I 2
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_newarray.j b/dx/tests/106-verify-object-ops/op_newarray.j
new file mode 100644
index 0000000..16ab256
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_newarray.j
@@ -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.
+
+.class op_newarray
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    newarray short
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_putfield.j b/dx/tests/106-verify-object-ops/op_putfield.j
new file mode 100644
index 0000000..eb33fc9
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_putfield.j
@@ -0,0 +1,26 @@
+; 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 op_putfield
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    iconst_0
+    putfield blort/x I
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_putstatic.j b/dx/tests/106-verify-object-ops/op_putstatic.j
new file mode 100644
index 0000000..221f08b
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_putstatic.j
@@ -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.
+
+.class op_putstatic
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 3
+
+    fconst_0
+    putstatic blort/x I
+    return
+.end method
diff --git a/dx/tests/106-verify-object-ops/run b/dx/tests/106-verify-object-ops/run
new file mode 100644
index 0000000..f512210
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# 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.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop anewarray
+oneop arraylength
+oneop athrow
+oneop checkcast
+oneop getfield
+oneop instanceof
+oneop invokeinterface
+oneop invokespecial
+oneop invokestatic
+oneop invokevirtual
+oneop monitorenter
+oneop monitorexit
+oneop multianewarray
+oneop newarray
+oneop putfield
+oneop putstatic
diff --git a/dx/tests/107-verify-stack-ops/expected.txt b/dx/tests/107-verify-stack-ops/expected.txt
new file mode 100644
index 0000000..812025d
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/expected.txt
@@ -0,0 +1,34 @@
+Generated: ./op_dup.class
+dup: expected failure occurred
+Generated: ./op_dup_x1_case1.class
+dup_x1_case1: expected failure occurred
+Generated: ./op_dup_x1_case2.class
+dup_x1_case2: expected failure occurred
+Generated: ./op_dup_x2_case1.class
+dup_x2_case1: expected failure occurred
+Generated: ./op_dup_x2_case2.class
+dup_x2_case2: expected failure occurred
+Generated: ./op_dup_x2_case3.class
+dup_x2_case3: expected failure occurred
+Generated: ./op_dup2.class
+dup2: expected failure occurred
+Generated: ./op_dup2_x1_case1.class
+dup2_x1_case1: expected failure occurred
+Generated: ./op_dup2_x1_case2.class
+dup2_x1_case2: expected failure occurred
+Generated: ./op_dup2_x1_case3.class
+dup2_x1_case3: expected failure occurred
+Generated: ./op_dup2_x2_case1.class
+dup2_x2_case1: expected failure occurred
+Generated: ./op_dup2_x2_case2.class
+dup2_x2_case2: expected failure occurred
+Generated: ./op_dup2_x2_case3.class
+dup2_x2_case3: expected failure occurred
+Generated: ./op_pop.class
+pop: expected failure occurred
+Generated: ./op_pop2.class
+pop2: expected failure occurred
+Generated: ./op_swap_case1.class
+swap_case1: expected failure occurred
+Generated: ./op_swap_case2.class
+swap_case2: expected failure occurred
diff --git a/dx/tests/107-verify-stack-ops/info.txt b/dx/tests/107-verify-stack-ops/info.txt
new file mode 100644
index 0000000..c489ace
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that the various stack manipulation opcodes verify that their
+arguments are actually of the appropriate categories.
diff --git a/dx/tests/107-verify-stack-ops/op_dup.j b/dx/tests/107-verify-stack-ops/op_dup.j
new file mode 100644
index 0000000..6c9ebc2
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup.j
@@ -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.
+
+.class op_dup
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    dup
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2.j b/dx/tests/107-verify-stack-ops/op_dup2.j
new file mode 100644
index 0000000..d17ce20
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2.j
@@ -0,0 +1,26 @@
+; 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 op_dup2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    dup2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_case1.j
new file mode 100644
index 0000000..7b014f7
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_case1.j
@@ -0,0 +1,26 @@
+; 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 op_dup2_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    dup2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j
new file mode 100644
index 0000000..26667f0
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j
@@ -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.
+
+.class op_dup2_x1_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    dconst_0
+    iconst_0
+    iconst_0
+    dup2_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j
new file mode 100644
index 0000000..35e97c4
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j
@@ -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.
+
+.class op_dup2_x1_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    iconst_0
+    dconst_0
+    iconst_0
+    dup2_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j
new file mode 100644
index 0000000..d15ccc3
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j
@@ -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.
+
+.class op_dup2_x1_case3
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    iconst_0
+    dconst_0
+    dconst_0
+    dup2_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j
new file mode 100644
index 0000000..e2538a0
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j
@@ -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.
+
+.class op_dup2_x2_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    dconst_0
+    iconst_0
+    iconst_0
+    iconst_0
+    dup2_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j
new file mode 100644
index 0000000..1e2645c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j
@@ -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.
+
+.class op_dup2_x2_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    iconst_0
+    iconst_0
+    dconst_0
+    iconst_0
+    dup2_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j
new file mode 100644
index 0000000..dad31e5
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j
@@ -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.
+
+.class op_dup2_x2_case3
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 8
+
+    dconst_0
+    iconst_0
+    dconst_0
+    dup2_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j b/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j
new file mode 100644
index 0000000..037b0f8
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j
@@ -0,0 +1,26 @@
+; 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 op_dup_x1_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    iconst_0
+    dconst_0
+    dup_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j b/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j
new file mode 100644
index 0000000..fa52b16
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j
@@ -0,0 +1,26 @@
+; 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 op_dup_x1_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    dup_x1
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j
new file mode 100644
index 0000000..7c4e89c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j
@@ -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.
+
+.class op_dup_x2_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    iconst_0
+    dup_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j
new file mode 100644
index 0000000..c4aa545
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j
@@ -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.
+
+.class op_dup_x2_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    iconst_0
+    iconst_0
+    dconst_0
+    dup_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j
new file mode 100644
index 0000000..f920d8c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j
@@ -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.
+
+.class op_dup_x2_case3
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    iconst_0
+    dconst_0
+    dconst_0
+    dup_x2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop.j b/dx/tests/107-verify-stack-ops/op_pop.j
new file mode 100644
index 0000000..1b74e2c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop.j
@@ -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.
+
+.class op_pop
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    pop
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop2.j b/dx/tests/107-verify-stack-ops/op_pop2.j
new file mode 100644
index 0000000..ef2d122
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop2.j
@@ -0,0 +1,26 @@
+; 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 op_pop2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    pop2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop2_case2.j b/dx/tests/107-verify-stack-ops/op_pop2_case2.j
new file mode 100644
index 0000000..f9d6ea3
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop2_case2.j
@@ -0,0 +1,26 @@
+; 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 op_pop2_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    pop2
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_swap_case1.j b/dx/tests/107-verify-stack-ops/op_swap_case1.j
new file mode 100644
index 0000000..f6b4ab7
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_swap_case1.j
@@ -0,0 +1,26 @@
+; 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 op_swap_case1
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    dconst_0
+    iconst_0
+    swap
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_swap_case2.j b/dx/tests/107-verify-stack-ops/op_swap_case2.j
new file mode 100644
index 0000000..6e0fcba
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_swap_case2.j
@@ -0,0 +1,26 @@
+; 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 op_swap_case2
+.super java/lang/Object
+
+.method public static test()V
+    .limit locals 2
+    .limit stack 6
+
+    iconst_0
+    dconst_0
+    swap
+    return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/run b/dx/tests/107-verify-stack-ops/run
new file mode 100644
index 0000000..a55c639
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/run
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# 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.
+
+function oneop()
+{
+    jasmin -d . op_"$1".j
+    dx --debug --dex op_"$1".class >/dev/null 2>&1
+    if [ "$?" = "0" ]; then
+        dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+    else
+        echo "$1: expected failure occurred"
+    fi
+}
+
+oneop dup
+oneop dup_x1_case1
+oneop dup_x1_case2
+oneop dup_x2_case1
+oneop dup_x2_case2
+oneop dup_x2_case3
+oneop dup2
+oneop dup2_x1_case1
+oneop dup2_x1_case2
+oneop dup2_x1_case3
+oneop dup2_x2_case1
+oneop dup2_x2_case2
+oneop dup2_x2_case3
+oneop pop
+oneop pop2
+oneop swap_case1
+oneop swap_case2
diff --git a/dx/tests/108-string-annotation/Blort.java b/dx/tests/108-string-annotation/Blort.java
new file mode 100644
index 0000000..9bb52e4
--- /dev/null
+++ b/dx/tests/108-string-annotation/Blort.java
@@ -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.
+ */
+
+public class Blort {
+    @Frotz(name = "grue")
+    public static void testSingle() {
+        // This space intentionally left blank.
+    }
+
+    @Fizmo(names = "gruesome")
+    public static void testArray1() {
+        // This space intentionally left blank.
+    }
+
+    @Fizmo(names = {"awful", "awesome"})
+    public static void testArray2() {
+        // This space intentionally left blank.
+    }
+}
diff --git a/dx/tests/108-string-annotation/Fizmo.java b/dx/tests/108-string-annotation/Fizmo.java
new file mode 100644
index 0000000..a20bba0
--- /dev/null
+++ b/dx/tests/108-string-annotation/Fizmo.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public @interface Fizmo {
+    String[] names();
+}
diff --git a/dx/tests/108-string-annotation/Frotz.java b/dx/tests/108-string-annotation/Frotz.java
new file mode 100644
index 0000000..3ad5426
--- /dev/null
+++ b/dx/tests/108-string-annotation/Frotz.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public @interface Frotz {
+    String name();
+}
diff --git a/dx/tests/108-string-annotation/expected.txt b/dx/tests/108-string-annotation/expected.txt
new file mode 100644
index 0000000..57da807
--- /dev/null
+++ b/dx/tests/108-string-annotation/expected.txt
@@ -0,0 +1,12 @@
+
+  elements[0]:
+    name
+    value: string "grue"
+
+  elements[0]:
+    names
+    value: array {"gruesome"}
+
+  elements[0]:
+    names
+    value: array {"awful", "awesome"}
diff --git a/dx/tests/108-string-annotation/info.txt b/dx/tests/108-string-annotation/info.txt
new file mode 100644
index 0000000..6c85834
--- /dev/null
+++ b/dx/tests/108-string-annotation/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+string annotations get represented reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/108-string-annotation/run b/dx/tests/108-string-annotation/run
new file mode 100644
index 0000000..d89053f
--- /dev/null
+++ b/dx/tests/108-string-annotation/run
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# 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.
+
+$JAVAC -d . Blort.java Fizmo.java Frotz.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-to=- *.class | cut -f 2 -d '|' | awk '
+
+BEGIN {
+    dumping = 0;
+}
+
+/annotation$/ {
+    dumping = 1;
+    printf("\n");
+    next;
+}
+
+/^[ ]*$/ {
+    dumping = 0;
+    next;
+}
+
+dumping && /^  elements/ {
+    print;
+}
+
+dumping && /^    name_idx/ {
+    printf("    %s\n", $4);
+}
+
+dumping && /^    value/ {
+    print;
+}
+'
diff --git a/dx/tests/109-int-branch/blort.j b/dx/tests/109-int-branch/blort.j
new file mode 100644
index 0000000..8517177
--- /dev/null
+++ b/dx/tests/109-int-branch/blort.j
@@ -0,0 +1,99 @@
+; 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 blort
+.super java/lang/Object
+
+.method public static test1(ZBCSI[I)V
+    .limit locals 6
+    .limit stack 3
+
+    iload_0
+    iload_1
+    if_icmpeq zorch
+
+    iload_2
+    iload_3
+    if_icmpne zorch
+
+    iload 4
+    aload 5
+    iconst_0
+    iaload
+    if_icmplt zorch
+
+    aload 5
+    iconst_0
+    iaload
+    iload_0
+    if_icmpgt zorch
+
+    iload 4
+    iload_1
+    if_icmpge zorch
+
+    nop
+
+zorch:
+    return
+.end method
+
+.method public static test2(I)Ljava/lang/Object;
+    .limit locals 2
+    .limit stack 3
+
+    aconst_null
+    astore 1
+
+    aload_1
+    iconst_0
+    iaload
+    iload_0
+    if_icmpge zorch
+
+    nop
+
+zorch:
+    aconst_null
+    areturn
+.end method
+
+.method public static test3(I[I)Ljava/lang/Object;
+    .limit locals 3
+    .limit stack 3
+
+    aconst_null
+    astore 2
+
+frotz:
+    aload_2
+    ifnonnull fizmo
+
+    aload_1
+    astore_2
+    goto frotz
+
+fizmo:
+    aload_2
+    iconst_0
+    iaload
+    iload_0
+    if_icmpge zorch
+
+    nop
+
+zorch:
+    aconst_null
+    areturn
+.end method
diff --git a/dx/tests/109-int-branch/expected.txt b/dx/tests/109-int-branch/expected.txt
new file mode 100644
index 0000000..d73bfa0
--- /dev/null
+++ b/dx/tests/109-int-branch/expected.txt
@@ -0,0 +1,67 @@
+Generated: ./blort.class
+blort.test1:(ZBCSI[I)V:
+regs: 000f; ins: 0006; outs: 0000
+  0000: move v0, v9
+  0001: move v1, v10
+  0002: move v2, v11
+  0003: move v3, v12
+  0004: move v4, v13
+  0005: move-object v5, v14
+  0006: move v6, v0
+  0007: move v7, v1
+  0008: if-eq v6, v7, 0021 // +0019
+  000a: move v6, v2
+  000b: move v7, v3
+  000c: if-ne v6, v7, 0021 // +0015
+  000e: move v6, v4
+  000f: move-object v7, v5
+  0010: const/4 v8, #int 0 // #0
+  0011: aget v7, v7, v8
+  0013: if-lt v6, v7, 0021 // +000e
+  0015: move-object v6, v5
+  0016: const/4 v7, #int 0 // #0
+  0017: aget v6, v6, v7
+  0019: move v7, v0
+  001a: if-gt v6, v7, 0021 // +0007
+  001c: move v6, v4
+  001d: move v7, v1
+  001e: if-ge v6, v7, 0021 // +0003
+  0020: nop
+  0021: return-void
+  source file: "blort.j"
+blort.test2:(I)Ljava/lang/Object;:
+regs: 0005; ins: 0001; outs: 0000
+  0000: move v0, v4
+  0001: const/4 v2, #null // #0
+  0002: move-object v1, v2
+  0003: move-object v2, v1
+  0004: const/4 v3, #int 0 // #0
+  0005: aget-object v2, v2, v3
+  0007: move v3, v0
+  0008: if-ge v2, v3, 000b // +0003
+  000a: nop
+  000b: const/4 v2, #null // #0
+  000c: move-object v0, v2
+  000d: return-object v0
+  source file: "blort.j"
+blort.test3:(I[I)Ljava/lang/Object;:
+regs: 0007; ins: 0002; outs: 0000
+  0000: move v0, v5
+  0001: move-object v1, v6
+  0002: const/4 v3, #null // #0
+  0003: move-object v2, v3
+  0004: move-object v3, v2
+  0005: if-nez v3, 000a // +0005
+  0007: move-object v3, v1
+  0008: move-object v2, v3
+  0009: goto 0004 // -0005
+  000a: move-object v3, v2
+  000b: const/4 v4, #int 0 // #0
+  000c: aget v3, v3, v4
+  000e: move v4, v0
+  000f: if-ge v3, v4, 0012 // +0003
+  0011: nop
+  0012: const/4 v3, #null // #0
+  0013: move-object v0, v3
+  0014: return-object v0
+  source file: "blort.j"
diff --git a/dx/tests/109-int-branch/info.txt b/dx/tests/109-int-branch/info.txt
new file mode 100644
index 0000000..e650a12
--- /dev/null
+++ b/dx/tests/109-int-branch/info.txt
@@ -0,0 +1,6 @@
+This tests that an int branch with valid arguments is properly translated.
+(Regression test.)
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/109-int-branch/run b/dx/tests/109-int-branch/run
new file mode 100644
index 0000000..68b17ea
--- /dev/null
+++ b/dx/tests/109-int-branch/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+jasmin -d . blort.j
+dx --debug --dex --no-optimize --dump-method="blort.test*" blort.class
diff --git a/dx/tests/110-dex-preserve-this/Blort.java b/dx/tests/110-dex-preserve-this/Blort.java
new file mode 100644
index 0000000..e2965b3
--- /dev/null
+++ b/dx/tests/110-dex-preserve-this/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+public class Blort {
+    public int test() {
+        Object z = "";
+        Number t = new Integer(3);
+        if (z instanceof Integer) {
+            return 3;
+        }
+        return ((Integer) t);
+    }
+}
diff --git a/dx/tests/110-dex-preserve-this/expected.txt b/dx/tests/110-dex-preserve-this/expected.txt
new file mode 100644
index 0000000..e91d4bf
--- /dev/null
+++ b/dx/tests/110-dex-preserve-this/expected.txt
@@ -0,0 +1 @@
+this: v4
diff --git a/dx/tests/110-dex-preserve-this/info.txt b/dx/tests/110-dex-preserve-this/info.txt
new file mode 100644
index 0000000..913c196
--- /dev/null
+++ b/dx/tests/110-dex-preserve-this/info.txt
@@ -0,0 +1,10 @@
+This is a smoke test of dex conversion, which checks to see that a
+"this" argument is never reused for a temporary. (Background: Popular
+debuggers will get confused if "this" is reused, and it arguably
+should be the case that the target object of an instance method being
+executed ought never be gc'ed anyway, and overwriting "this" could in
+fact cause that to happen.)
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/110-dex-preserve-this/run b/dx/tests/110-dex-preserve-this/run
new file mode 100644
index 0000000..9770237
--- /dev/null
+++ b/dx/tests/110-dex-preserve-this/run
@@ -0,0 +1,41 @@
+#!/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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none \
+    --dump-to=dump.txt --dump-method="Blort.test" *.class
+
+cat dump.txt | awk '
+BEGIN {
+    thisReg = "BOGUS";
+}
+# Find the number of registers; in this case the last register is "this".
+/^regs:/ {
+    regs = substr($2, 4, 1);
+    thisReg = "v" (regs - 1);
+    printf("this: %s\n", thisReg);
+}
+# Output any lines that mention the "this" register.
+{
+    count = split($0, words, /,? */);
+    for (i = 1; i <= count; i++) {
+        if (words[i] == thisReg) {
+            printf("%s\n", $0);
+            break;
+        }
+    }
+}
+'
\ No newline at end of file
diff --git a/dx/tests/111-use-null-as-array/Blort.java b/dx/tests/111-use-null-as-array/Blort.java
new file mode 100644
index 0000000..79d2b4f
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/Blort.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+public class Blort {
+    public static boolean test_getBooleanArray() {
+        boolean[] arr = null;
+        return arr[1];
+    }
+
+    public static byte test_getByteArray() {
+        byte[] arr = null;
+        return arr[2];
+    }
+
+    public static char test_getCharArray() {
+        char[] arr = null;
+        return arr[3];
+    }
+
+    public static double test_getDoubleArray() {
+        double[] arr = null;
+        return arr[4];
+    }
+
+    public static float test_getFloatArray() {
+        float[] arr = null;
+        return arr[5];
+    }
+
+    public static int test_getIntArray() {
+        int[] arr = null;
+        return arr[6];
+    }
+
+    public static long test_getLongArray() {
+        long[] arr = null;
+        return arr[7];
+    }
+
+    public static Object test_getObjectArray() {
+        Object[] arr = null;
+        return arr[8];
+    }
+
+    public static short test_getShortArray() {
+        short[] arr = null;
+        return arr[9];
+    }
+
+    public static void test_setBooleanArray() {
+        boolean[] arr = null;
+        arr[1] = true;
+    }
+
+    public static void test_setByteArray() {
+        byte[] arr = null;
+        arr[2] = (byte) 3;
+    }
+
+    public static void test_setCharArray() {
+        char[] arr = null;
+        arr[4] = (char) 5;
+    }
+
+    public static void test_setDoubleArray() {
+        double[] arr = null;
+        arr[6] = 7.0F;
+    }
+
+    public static void test_setFloatArray() {
+        float[] arr = null;
+        arr[8] = 9.0F;
+    }
+
+    public static void test_setIntArray() {
+        int[] arr = null;
+        arr[10] = 11;
+    }
+
+    public static void test_setLongArray() {
+        long[] arr = null;
+        arr[12] = 13;
+    }
+
+    public static void test_setObjectArray() {
+        Object[] arr = null;
+        arr[14] = "blort";
+    }
+
+    public static void test_setShortArray() {
+        short[] arr = null;
+        arr[15] = (short) 16;
+    }
+}
diff --git a/dx/tests/111-use-null-as-array/expected.txt b/dx/tests/111-use-null-as-array/expected.txt
new file mode 100644
index 0000000..7e2116b
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/expected.txt
@@ -0,0 +1,116 @@
+Blort.test_getBooleanArray:()Z:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 1 // #1
+  0002: aget-byte v0, v0, v1
+  0004: return v0
+Blort.test_getByteArray:()B:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 2 // #2
+  0002: aget-byte v0, v0, v1
+  0004: return v0
+Blort.test_getCharArray:()C:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 3 // #3
+  0002: aget-char v0, v0, v1
+  0004: return v0
+Blort.test_getDoubleArray:()D:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 4 // #4
+  0002: aget-wide v0, v0, v1
+  0004: return-wide v0
+Blort.test_getFloatArray:()F:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 5 // #5
+  0002: aget v0, v0, v1
+  0004: return v0
+Blort.test_getIntArray:()I:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 6 // #6
+  0002: aget v0, v0, v1
+  0004: return v0
+Blort.test_getLongArray:()J:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 7 // #7
+  0002: aget-wide v0, v0, v1
+  0004: return-wide v0
+Blort.test_getObjectArray:()Ljava/lang/Object;:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/16 v1, #int 8 // #0008
+  0003: aget-object v0, v0, v1
+  0005: return-object v0
+Blort.test_getShortArray:()S:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/16 v1, #int 9 // #0009
+  0003: aget-short v0, v0, v1
+  0005: return v0
+Blort.test_setBooleanArray:()V:
+regs: 0002; ins: 0000; outs: 0000
+  0000: const/4 v1, #int 1 // #1
+  0001: const/4 v0, #null // #0
+  0002: aput v1, v0, v1
+  0004: return-void
+Blort.test_setByteArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 2 // #2
+  0002: const/4 v2, #int 3 // #3
+  0003: aput v2, v0, v1
+  0005: return-void
+Blort.test_setCharArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 4 // #4
+  0002: const/4 v2, #int 5 // #5
+  0003: aput v2, v0, v1
+  0005: return-void
+Blort.test_setDoubleArray:()V:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/4 v1, #int 6 // #6
+  0002: const-wide/high16 v2, #double 7.0 // #401c000000000000
+  0004: aput-wide v2, v0, v1
+  0006: return-void
+Blort.test_setFloatArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/16 v1, #int 8 // #0008
+  0003: const/high16 v2, #float 9.0 // #41100000
+  0005: aput v2, v0, v1
+  0007: return-void
+Blort.test_setIntArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/16 v1, #int 10 // #000a
+  0003: const/16 v2, #int 11 // #000b
+  0005: aput v2, v0, v1
+  0007: return-void
+Blort.test_setLongArray:()V:
+regs: 0004; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/16 v1, #int 12 // #000c
+  0003: const-wide/16 v2, #long 13 // #000d
+  0005: aput-wide v2, v0, v1
+  0007: return-void
+Blort.test_setObjectArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/16 v1, #int 14 // #000e
+  0003: const-string v2, "blort"
+  0005: aput-object v2, v0, v1
+  0007: return-void
+Blort.test_setShortArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+  0000: const/4 v0, #null // #0
+  0001: const/16 v1, #int 15 // #000f
+  0003: const/16 v2, #int 16 // #0010
+  0005: aput v2, v0, v1
+  0007: return-void
diff --git a/dx/tests/111-use-null-as-array/info.txt b/dx/tests/111-use-null-as-array/info.txt
new file mode 100644
index 0000000..624386d
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/info.txt
@@ -0,0 +1,18 @@
+This is a smoke test of dex conversion, which checks to see that uses
+of a known-null in contexts that require a specific type end up getting
+converted to the type in question. When executed, this sort of code
+will inevitably throw a NullPointerException, but if the opcode weren't
+correct, they would instead incorrectly fail verification.
+
+If you inspect the expected output of this test, you will see that
+there are some surprising instructions in there, such as using
+aget-byte for what was a boolean[] in the source code. In these cases,
+the resulting output is still correct (passes verification and will
+throw a NullPointerException if ever executed). However, it happens
+that during translation there simply wasn't enough information to
+recover the "true" original meaning at the level of actual opcode
+selection.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/111-use-null-as-array/run b/dx/tests/111-use-null-as-array/run
new file mode 100644
index 0000000..7e4e1e8
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/run
@@ -0,0 +1,19 @@
+#!/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.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals \
+    --dump-to=- --dump-method="Blort.test*" *.class
diff --git a/dx/tests/112-dex-return-jsr-result/blort.j b/dx/tests/112-dex-return-jsr-result/blort.j
new file mode 100644
index 0000000..6f6cddb
--- /dev/null
+++ b/dx/tests/112-dex-return-jsr-result/blort.j
@@ -0,0 +1,41 @@
+; 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.
+
+.class blort
+.super java/lang/Object
+
+.method public static zorch(ZLjava/lang/Object;)Ljava/lang/Object;
+    .limit locals 3
+    .limit stack 1
+
+    iload_0
+    ifeq thenBlock
+    jsr subroutine
+    goto endBlock
+
+thenBlock:
+    jsr subroutine
+    goto endBlock
+
+subroutine:
+    astore_2
+    aload_1
+    invokestatic java/lang/String/valueOf(Ljava/lang/Object;)Ljava/lang/String;
+    astore_1
+    ret 2
+
+endBlock:
+    aload_1
+    areturn
+.end method
diff --git a/dx/tests/112-dex-return-jsr-result/expected.txt b/dx/tests/112-dex-return-jsr-result/expected.txt
new file mode 100644
index 0000000..0e32069
--- /dev/null
+++ b/dx/tests/112-dex-return-jsr-result/expected.txt
@@ -0,0 +1,2 @@
+Generated: ./blort.class
+total invokes: 2
diff --git a/dx/tests/112-dex-return-jsr-result/info.txt b/dx/tests/112-dex-return-jsr-result/info.txt
new file mode 100644
index 0000000..289ebb2
--- /dev/null
+++ b/dx/tests/112-dex-return-jsr-result/info.txt
@@ -0,0 +1,6 @@
+This test checks to make sure a result returned more-or-less directly
+from a jsr subroutine ends up being represented appropriately.
+
+In particular, this is a regression test for a bug which caused a
+goto instruction to be interposed between an invoke instruction and
+its associated move-result.
diff --git a/dx/tests/112-dex-return-jsr-result/run b/dx/tests/112-dex-return-jsr-result/run
new file mode 100644
index 0000000..4075d16
--- /dev/null
+++ b/dx/tests/112-dex-return-jsr-result/run
@@ -0,0 +1,63 @@
+#!/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.
+
+# The awk fun here tries to cull out all but the salient bits. The aim
+# is to check to see that there are two invoke-static instructions, each
+# followed directly by a move-result-object.
+
+jasmin -d . blort.j
+dx --debug --dex --dump-to=- --dump-method=blort.zorch --dump-width=200 \
+    blort.class | awk '
+
+BEGIN {
+    invokeAt = -1;
+    moveAt = -1;
+    invokeCount = 0;
+    failed = 0;
+}
+
+# Note: This has to be done before the test clause below.
+/move-result-object/ {
+    moveAt = NR;
+}
+
+(invokeAt > 0) {
+    if (moveAt != (invokeAt + 1)) {
+        failed = 1;
+    }
+    invokeAt = -1;
+    moveAt = -1;
+}
+
+# Note: This has to be done after the test clause above.
+/invoke-static/ {
+    invokeAt = NR;
+    invokeCount++;
+}
+
+END {
+    printf("total invokes: %d\n", invokeCount);
+    if (failed) {
+        exit 1;
+    }
+}
+'
+
+if [ "$?" = "1" ]; then
+    # The test failed. Be helpful and print the entire method body.
+    dx --debug --dex --dump-to=- --dump-method=blort.zorch --dump-width=200 \
+        blort.class
+fi
diff --git a/dx/tests/113-old-style-inner-class/Blort.java b/dx/tests/113-old-style-inner-class/Blort.java
new file mode 100644
index 0000000..89b1ba0
--- /dev/null
+++ b/dx/tests/113-old-style-inner-class/Blort.java
@@ -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.
+ */
+
+public class Blort {
+    public static Runnable theRunnable = new Runnable() {
+            public void run() { }
+        };
+
+    public Runnable create() {
+        return new Runnable() {
+                public void run() { }
+            };
+    }
+}
diff --git a/dx/tests/113-old-style-inner-class/expected.txt b/dx/tests/113-old-style-inner-class/expected.txt
new file mode 100644
index 0000000..d58d6db
--- /dev/null
+++ b/dx/tests/113-old-style-inner-class/expected.txt
@@ -0,0 +1,2 @@
+warning:
+warning:
diff --git a/dx/tests/113-old-style-inner-class/info.txt b/dx/tests/113-old-style-inner-class/info.txt
new file mode 100644
index 0000000..91b3aad
--- /dev/null
+++ b/dx/tests/113-old-style-inner-class/info.txt
@@ -0,0 +1,3 @@
+This is a smoke test of dex conversion, which makes sure that
+converstion of old-style anonymous inner classes (whose representation
+is information-lossy) causes a warning to be issued.
diff --git a/dx/tests/113-old-style-inner-class/run b/dx/tests/113-old-style-inner-class/run
new file mode 100644
index 0000000..c93661f
--- /dev/null
+++ b/dx/tests/113-old-style-inner-class/run
@@ -0,0 +1,22 @@
+#!/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.
+
+# We compile for a 1.4 target to suppress the use of EnclosingMethod
+# attributes.
+${JAVAC} -source 1.4 -target 1.4 -d . Blort.java
+
+# We expect there to be two warning lines, one for each of the inner classes.
+dx --debug --dex *.class 2>&1 | awk '/^warning:/ { print $1 }'
diff --git a/dx/tests/run-all-tests b/dx/tests/run-all-tests
new file mode 100755
index 0000000..68062c2
--- /dev/null
+++ b/dx/tests/run-all-tests
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+passed=0
+failed=0
+failNames=""
+
+for i in *; do
+    if [ -d "$i" -a -r "$i" ]; then
+        ./run-test "$i"
+        if [ "$?" = "0" ]; then
+            ((passed += 1))
+        else
+            ((failed += 1))
+            failNames="$failNames $i"
+        fi
+    fi
+done
+
+echo "passed: $passed test(s)"
+echo "failed: $failed test(s)"
+
+for i in $failNames; do
+    echo "failed: $i"
+done
diff --git a/dx/tests/run-test b/dx/tests/run-test
new file mode 100755
index 0000000..a9221de
--- /dev/null
+++ b/dx/tests/run-test
@@ -0,0 +1,149 @@
+#!/bin/bash
+#
+# 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+export JAVAC="${progdir}/../../../prebuilt/common/openjdk/bin/javac"
+if [ "!" -e "$JAVAC" ]; then
+    JAVAC="javac"
+fi
+
+info="info.txt"
+run="run"
+expected="expected.txt"
+output="out.txt"
+
+dev_mode="no"
+if [ "x$1" = "x--dev" ]; then
+    dev_mode="yes"
+    shift
+fi
+
+update_mode="no"
+if [ "x$1" = "x--update" ]; then
+    update_mode="yes"
+    shift
+fi
+
+usage="no"
+if [ "x$1" = "x--help" ]; then
+    usage="yes"
+else
+    if [ "x$1" = "x" ]; then
+        testdir=`basename "$oldwd"`
+    else
+        testdir="$1"
+    fi
+
+    if [ '!' -d "$testdir" ]; then
+        td2=`echo ${testdir}-*`
+        if [ '!' -d "$td2" ]; then
+            echo "${testdir}: no such test directory" 1>&2
+            usage="yes"
+        fi
+        testdir="$td2"
+    fi
+fi
+
+if [ "$usage" = "yes" ]; then
+    prog=`basename $prog`
+    (
+        echo "usage:"
+        echo "  $prog --help             Print this message."
+        echo "  $prog testname           Run test normally."
+        echo "  $prog --dev testname     Development mode (dump to stdout)."
+        echo "  $prog --update testname  Update mode (replace expected.txt)."
+        echo "  Omitting the test name uses the current directory as the test."
+    ) 1>&2
+    exit 1
+fi
+
+td_info="$testdir"/"$info"
+td_run="$testdir"/"$run"
+td_expected="$testdir"/"$expected"
+
+tmpdir=/tmp/test-$$
+
+if [ '!' '(' -r "$td_info" -a -r "$td_run" -a -r "$td_expected" ')' ]; then
+    echo "${testdir}: missing files" 1>&2
+    exit 1
+fi
+
+# copy the test to a temp dir and run it
+
+echo "${testdir}: running..." 1>&2
+
+rm -rf "$tmpdir"
+cp -Rp "$testdir" "$tmpdir"
+cd "$tmpdir"
+chmod 755 "$run"
+
+#PATH="${progdir}/../build/bin:${PATH}"
+
+good="no"
+if [ "$dev_mode" = "yes" ]; then
+    "./$run" 2>&1
+    echo "exit status: $?" 1>&2
+    good="yes"
+elif [ "$update_mode" = "yes" ]; then
+    "./$run" >"${progdir}/$td_expected" 2>&1
+    good="yes"
+else
+    "./$run" >"$output" 2>&1
+    cmp -s "$expected" "$output"
+    if [ "$?" = "0" ]; then
+        # output == expected
+        good="yes"
+        echo "$testdir"': succeeded!' 1>&2
+    fi
+fi
+
+if [ "$good" = "yes" ]; then
+    cd "$oldwd"
+    rm -rf "$tmpdir"
+    exit 0
+fi
+
+(
+    echo "${testdir}: FAILED!"
+    echo ' '
+    echo '#################### info'
+    cat "$info" | sed 's/^/# /g'
+    echo '#################### diffs'
+    diff -u "$expected" "$output"
+    echo '####################'
+    echo ' '
+    echo "files left in $tmpdir"
+) 1>&2
+
+exit 1
diff --git a/hit/samples/android.hprof b/hit/samples/android.hprof
new file mode 100644
index 0000000..bb8d2e0
--- /dev/null
+++ b/hit/samples/android.hprof
Binary files differ
diff --git a/hit/src/com/android/hit/ArrayInstance.java b/hit/src/com/android/hit/ArrayInstance.java
new file mode 100644
index 0000000..42e8803
--- /dev/null
+++ b/hit/src/com/android/hit/ArrayInstance.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.util.Set;
+
+public class ArrayInstance extends Instance {
+    private int mType;
+    private int mNumEntries;
+    private byte[] mData;
+
+    public ArrayInstance(long id, StackTrace stack, int type, int numEntries,
+            byte[] data) {
+        mId = id;
+        mStack = stack;
+        mType = type;
+        mNumEntries = numEntries;
+        mData = data;
+    }
+
+    public final void resolveReferences(State state) {
+        if (mType != Types.OBJECT) {
+            return;
+        }
+
+        /*
+         * mData holds a stream of object instance ids
+         * Spin through them all and list ourselves as a reference holder.
+         */
+        int idSize = Types.getTypeSize(mType);
+        final int N = mNumEntries;
+
+        ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+        DataInputStream dis = new DataInputStream(bais);
+
+        for (int i = 0; i < N; i++) {
+            long id;
+
+            try {
+                if (idSize == 4) {
+                    id = dis.readInt();
+                } else {
+                    id = dis.readLong();
+                }
+
+                Instance instance = state.findReference(id);
+
+                if (instance != null) {
+                    instance.addParent(this);
+                }
+            } catch (java.io.IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public final int getSize() {
+        return mData.length;
+    }
+
+    @Override
+    public final void visit(Set<Instance> resultSet, Filter filter) {
+        //  If we're in the set then we and our children have been visited
+        if (resultSet.contains(this)) {
+            return;
+        }
+
+        if (null != filter) {
+            if (filter.accept(this)) {
+                resultSet.add(this);
+            }
+        } else {
+            resultSet.add(this);
+        }
+
+        if (mType != Types.OBJECT) {
+            return;
+        }
+
+        /*
+         * mData holds a stream of object instance ids
+         * Spin through them all and visit them
+         */
+        int idSize = Types.getTypeSize(mType);
+        final int N = mNumEntries;
+
+        ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+        DataInputStream dis = new DataInputStream(bais);
+        State state = mHeap.mState;
+
+        for (int i = 0; i < N; i++) {
+            long id;
+
+            try {
+                if (idSize == 4) {
+                    id = dis.readInt();
+                } else {
+                    id = dis.readLong();
+                }
+
+                Instance instance = state.findReference(id);
+
+                if (instance != null) {
+                    instance.visit(resultSet, filter);
+                }
+            } catch (java.io.IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public final String getTypeName() {
+        return Types.getTypeName(mType) + "[" + mNumEntries + "]";
+    }
+
+    public final String toString() {
+        return String.format("%s@0x08x", getTypeName(), mId);
+    }
+
+    @Override
+    public String describeReferenceTo(long referent) {
+        //  If this isn't an object array then we can't refer to an object
+        if (mType != Types.OBJECT) {
+            return super.describeReferenceTo(referent);
+        }
+
+        int idSize = Types.getTypeSize(mType);
+        final int N = mNumEntries;
+        int numRefs = 0;
+        StringBuilder result = new StringBuilder("Elements [");
+        ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+        DataInputStream dis = new DataInputStream(bais);
+
+        /*
+         * Spin through all the objects and build up a string describing
+         * all of the array elements that refer to the target object.
+         */
+        for (int i = 0; i < N; i++) {
+            long id;
+
+            try {
+                if (idSize == 4) {
+                    id = dis.readInt();
+                } else {
+                    id = dis.readLong();
+                }
+
+                if (id == referent) {
+                    numRefs++;
+
+                    if (numRefs > 1) {
+                        result.append(", ");
+                    }
+
+                    result.append(i);
+                }
+            } catch (java.io.IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (numRefs == 0) {
+            return super.describeReferenceTo(referent);
+        }
+
+        result.append("]");
+
+        return result.toString();
+    }
+}
diff --git a/hit/src/com/android/hit/ClassInstance.java b/hit/src/com/android/hit/ClassInstance.java
new file mode 100644
index 0000000..0869769
--- /dev/null
+++ b/hit/src/com/android/hit/ClassInstance.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Set;
+
+public class ClassInstance extends Instance {
+    private byte[] mFieldValues;
+
+    public ClassInstance(long id, StackTrace stack, long classId) {
+        mId = id;
+        mStack = stack;
+        mClassId = classId;
+    }
+
+    public final void loadFieldData(DataInputStream in, int numBytes)
+            throws IOException {
+        mFieldValues = new byte[numBytes];
+        in.readFully(mFieldValues);
+    }
+
+    @Override
+    public void resolveReferences(State state) {
+        ClassObj isa = mHeap.mState.findClass(mClassId);
+
+        resolve(state, isa, isa.mStaticFieldTypes, isa.mStaticFieldValues);
+        resolve(state, isa, isa.mFieldTypes, mFieldValues);
+    }
+
+    private void resolve(State state, ClassObj isa, int[] types,
+            byte[] values) {
+        ByteArrayInputStream bais = new ByteArrayInputStream(values);
+        DataInputStream dis = new DataInputStream(bais);
+        final int N = types.length;
+
+        /*
+         * Spin through the list of fields, find all object references,
+         * and list ourselves as a reference holder.
+         */
+        try {
+            for (int i = 0; i < N; i++) {
+                int type = types[i];
+                int size = Types.getTypeSize(type);
+
+                    if (type == Types.OBJECT) {
+                        long id;
+
+                        if (size == 4) {
+                            id = dis.readInt();
+                        } else {
+                            id = dis.readLong();
+                        }
+
+                        Instance instance = state.findReference(id);
+
+                        if (instance != null) {
+                            instance.addParent(this);
+                        }
+                    } else {
+                        dis.skipBytes(size);
+                    }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public final int getSize() {
+        ClassObj isa = mHeap.mState.findClass(mClassId);
+
+        return isa.getSize();
+    }
+
+    @Override
+    public final void visit(Set<Instance> resultSet, Filter filter) {
+        if (resultSet.contains(this)) {
+            return;
+        }
+
+        if (filter != null) {
+            if (filter.accept(this)) {
+                resultSet.add(this);
+            }
+        } else {
+            resultSet.add(this);
+        }
+
+        State state = mHeap.mState;
+        ClassObj isa = state.findClass(mClassId);
+        int[] types = isa.mFieldTypes;
+        ByteArrayInputStream bais = new ByteArrayInputStream(mFieldValues);
+        DataInputStream dis = new DataInputStream(bais);
+        final int N = types.length;
+
+        /*
+         * Spin through the list of fields, find all object references,
+         * and list ourselves as a reference holder.
+         */
+        try {
+            for (int i = 0; i < N; i++) {
+                int type = types[i];
+                int size = Types.getTypeSize(type);
+
+                if (type == Types.OBJECT) {
+                    long id;
+
+                    if (size == 4) {
+                        id = dis.readInt();
+                    } else {
+                        id = dis.readLong();
+                    }
+
+                    Instance instance = state.findReference(id);
+
+                    if (instance != null) {
+                        instance.visit(resultSet, filter);
+                    }
+                } else {
+                    dis.skipBytes(size);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public final String getTypeName() {
+        ClassObj theClass = mHeap.mState.findClass(mClassId);
+
+        return theClass.mClassName;
+    }
+
+    public final String toString() {
+        return String.format("%s@0x%08x", getTypeName(), mId);
+    }
+
+    @Override
+    public String describeReferenceTo(long referent) {
+        ClassObj isa = mHeap.mState.findClass(mClassId);
+        int[] types = isa.mFieldTypes;
+        String[] fieldNames = isa.mFieldNames;
+        ByteArrayInputStream bais = new ByteArrayInputStream(mFieldValues);
+        DataInputStream dis = new DataInputStream(bais);
+        final int N = types.length;
+        StringBuilder result = new StringBuilder("Referenced in field(s):");
+        int numReferences = 0;
+
+        /*
+         * Spin through the list of fields, add info about the field
+         * references to the output text.
+         */
+        try {
+            for (int i = 0; i < N; i++) {
+                int type = types[i];
+                int size = Types.getTypeSize(type);
+
+                if (type == Types.OBJECT) {
+                    long id;
+
+                    if (size == 4) {
+                        id = dis.readInt();
+                    } else {
+                        id = dis.readLong();
+                    }
+
+                    if (id == referent) {
+                        numReferences++;
+                        result.append("\n    ");
+                        result.append(fieldNames[i]);
+                    }
+                } else {
+                    dis.skipBytes(size);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        /*
+         *  TODO:  perform a similar loop over the static fields of isa
+         */
+
+        if (numReferences == 0) {
+            return super.describeReferenceTo(referent);
+        }
+
+        return result.toString();
+    }
+}
diff --git a/hit/src/com/android/hit/ClassObj.java b/hit/src/com/android/hit/ClassObj.java
new file mode 100644
index 0000000..1e3ac28
--- /dev/null
+++ b/hit/src/com/android/hit/ClassObj.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public class ClassObj extends Instance implements Comparable<ClassObj> {
+    String mClassName;
+    long mSuperclassId;
+
+    String[] mFieldNames;
+    int[] mFieldTypes;
+
+    String[] mStaticFieldNames;
+    int[] mStaticFieldTypes;
+    byte[] mStaticFieldValues;
+
+    ArrayList<Instance> mInstances = new ArrayList<Instance>();
+    Set<ClassObj> mSubclasses = new HashSet<ClassObj>();
+
+    int mSize;
+
+    public ClassObj(long id, StackTrace stack, String className) {
+        mId = id;
+        mStack = stack;
+        mClassName = className;
+    }
+
+    @Override
+    public final void resolveReferences(State state) {
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(mStaticFieldValues);
+        DataInputStream dis = new DataInputStream(bais);
+        int[] types = mStaticFieldTypes;
+        final int N = types.length;
+
+        /*
+         * Spin through the list of static fields, find all object references,
+         * and list ourselves as a reference holder.  Also add them to
+         * the list of root objects.
+         */
+        try {
+            for (int i = 0; i < N; i++) {
+                int type = types[i];
+                int size = Types.getTypeSize(type);
+
+                if (type == Types.OBJECT) {
+                    long id;
+
+                    if (size == 4) {
+                        id = dis.readInt();
+                    } else {
+                        id = dis.readLong();
+                    }
+
+                    RootObj root = new RootObj(RootType.JAVA_STATIC, id);
+
+                    if (id == 0) {
+                        root.mComment = String.format(
+                            "Static field %s:%s null",
+                                mClassName,
+                                mStaticFieldNames[i]);
+                    } else {
+                        Instance instance = state.findReference(id);
+
+                        instance.addParent(this);
+
+                        root.mComment = String.format(
+                            "Static field %s:%s %s [%s] 0x%08x",
+                                mClassName,
+                                mStaticFieldNames[i],
+                                instance.getTypeName(),
+                                instance.mHeap.mName,
+                                id);
+                    }
+
+                    mHeap.addRoot(root);
+                } else {
+                    dis.skipBytes(size);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+
+        //  Lastly, add ourself as a subclass of our superclass
+        if (mSuperclassId != 0) {
+            ClassObj superclass = state.findClass(mSuperclassId);
+
+            superclass.addSubclass(this);
+        }
+    }
+
+    public final void addSubclass(ClassObj subclass) {
+        mSubclasses.add(subclass);
+    }
+
+    public final void dumpSubclasses() {
+        for (ClassObj subclass: mSubclasses) {
+            System.out.println("     " + subclass.mClassName);
+        }
+    }
+
+    public final String toString() {
+        return mClassName.replace('/', '.');
+    }
+
+    public final void addInstance(Instance instance) {
+        mInstances.add(instance);
+    }
+
+    public final void setSuperclassId(long id) {
+        mSuperclassId = id;
+    }
+
+    public final void setFieldNames(String[] names) {
+        mFieldNames = names;
+    }
+
+    public final void setFieldTypes(int[] types) {
+        mFieldTypes = types;
+    }
+
+    public final void setStaticFieldNames(String[] names) {
+        mStaticFieldNames = names;
+    }
+
+    public final void setStaticFieldTypes(int[] types) {
+        mStaticFieldTypes = types;
+    }
+
+    public final void setStaticFieldValues(byte[] values) {
+        mStaticFieldValues = values;
+    }
+
+    public final void dump() {
+        System.out.println("+----------  ClassObj dump for: " + mClassName);
+
+        System.out.println("+-----  Static fields");
+
+        for (int i = 0; i < mStaticFieldNames.length; i++) {
+            System.out.println(mStaticFieldNames[i] + ": "
+                + mStaticFieldTypes[i]);
+        }
+
+        System.out.println("+-----  Instance fields");
+
+        for (int i = 0; i < mFieldNames.length; i++) {
+            System.out.println(mFieldNames[i] + ": " + mFieldTypes[i]);
+        }
+    }
+
+    @Override
+    public final String getTypeName() {
+        return "class " + mClassName;
+    }
+
+    @Override
+    public final void visit(Set<Instance> resultSet, Filter filter) {
+        if (resultSet.contains(this)) {
+            return;
+        }
+
+        if (filter != null) {
+            if (filter.accept(this)) {
+                resultSet.add(this);
+            }
+        } else {
+            resultSet.add(this);
+        }
+
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(mStaticFieldValues);
+        DataInputStream dis = new DataInputStream(bais);
+        int[] types = mStaticFieldTypes;
+        final int N = types.length;
+        State state = mHeap.mState;
+
+        /*
+         * Spin through the list of static fields, find all object references,
+         * and visit them.
+         */
+        try {
+            for (int i = 0; i < N; i++) {
+                int type = types[i];
+                int size = Types.getTypeSize(type);
+
+                if (type == Types.OBJECT) {
+                    long id;
+
+                    if (size == 4) {
+                        id = dis.readInt();
+                    } else {
+                        id = dis.readLong();
+                    }
+
+                    Instance instance = state.findReference(id);
+
+                    if (instance != null) {
+                        instance.visit(resultSet, filter);
+                    }
+                } else {
+                    dis.skipBytes(size);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public final int compareTo(ClassObj o) {
+        return mClassName.compareTo(o.mClassName);
+    }
+
+    public final boolean equals(Object o) {
+        if (! (o instanceof ClassObj)) {
+            return false;
+        }
+
+        return 0 == compareTo((ClassObj) o);
+    }
+}
diff --git a/hit/src/com/android/hit/Heap.java b/hit/src/com/android/hit/Heap.java
new file mode 100644
index 0000000..37b15dd
--- /dev/null
+++ b/hit/src/com/android/hit/Heap.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class Heap {
+    String mName;
+
+    //  List of individual stack frames
+    HashMap<Long, StackFrame> mFrames = new HashMap<Long, StackFrame>();
+
+    //  List stack traces, which are lists of stack frames
+    HashMap<Integer, StackTrace> mTraces = new HashMap<Integer, StackTrace>();
+
+    //  Root objects such as interned strings, jni locals, etc
+    ArrayList<RootObj> mRoots = new ArrayList<RootObj>();
+
+    //  List of threads
+    HashMap<Integer, ThreadObj> mThreads = new HashMap<Integer, ThreadObj>();
+
+    //  Class definitions
+    HashMap<Long, ClassObj> mClassesById = new HashMap<Long, ClassObj>();
+    HashMap<String, ClassObj> mClassesByName = new HashMap<String, ClassObj>();
+
+    //  List of instances of above class definitions
+    HashMap<Long, Instance> mInstances = new HashMap<Long, Instance>();
+
+    //  The super-state that this heap is part of
+    State mState;
+
+    public Heap(String name) {
+        mName = name;
+    }
+
+    public final void addStackFrame(StackFrame theFrame) {
+        mFrames.put(theFrame.mId, theFrame);
+    }
+
+    public final StackFrame getStackFrame(long id) {
+        return mFrames.get(id);
+    }
+
+    public final void addStackTrace(StackTrace theTrace) {
+        mTraces.put(theTrace.mSerialNumber, theTrace);
+    }
+
+    public final StackTrace getStackTrace(int traceSerialNumber) {
+        return mTraces.get(traceSerialNumber);
+    }
+
+    public final StackTrace getStackTraceAtDepth(int traceSerialNumber,
+            int depth) {
+        StackTrace trace = mTraces.get(traceSerialNumber);
+
+        if (trace != null) {
+            trace = trace.fromDepth(depth);
+        }
+
+        return trace;
+    }
+
+    public final void addRoot(RootObj root) {
+        root.mIndex = mRoots.size();
+        mRoots.add(root);
+    }
+
+    public final void addThread(ThreadObj thread, int serialNumber) {
+        mThreads.put(serialNumber, thread);
+    }
+
+    public final ThreadObj getThread(int serialNumber) {
+        return mThreads.get(serialNumber);
+    }
+
+    public final void addInstance(long id, Instance instance) {
+        mInstances.put(id, instance);
+    }
+
+    public final Instance getInstance(long id) {
+        return mInstances.get(id);
+    }
+
+    public final void addClass(long id, ClassObj theClass) {
+        mClassesById.put(id, theClass);
+        mClassesByName.put(theClass.mClassName, theClass);
+    }
+
+    public final ClassObj getClass(long id) {
+        return mClassesById.get(id);
+    }
+
+    public final ClassObj getClass(String name) {
+        return mClassesByName.get(name);
+    }
+
+    public final void dumpInstanceCounts() {
+        for (ClassObj theClass: mClassesById.values()) {
+            int count = theClass.mInstances.size();
+
+            if (count > 0) {
+                System.out.println(theClass + ": " + count);
+            }
+        }
+    }
+
+    public final void dumpSubclasses() {
+        for (ClassObj theClass: mClassesById.values()) {
+            int count = theClass.mSubclasses.size();
+
+            if (count > 0) {
+                System.out.println(theClass);
+                theClass.dumpSubclasses();
+            }
+        }
+    }
+
+    public final void dumpSizes() {
+        for (ClassObj theClass: mClassesById.values()) {
+            int size = 0;
+
+            for (Instance instance: theClass.mInstances) {
+                size += instance.getCompositeSize();
+            }
+
+            if (size > 0) {
+                System.out.println(theClass + ": base " + theClass.getSize()
+                    + ", composite " + size);
+            }
+        }
+    }
+
+    /*
+     * Spin through all of the class instances and link them to their
+     * parent class definition objects.  Then have each instance resolve
+     * its own internal object references.
+     */
+    public final void resolveInstanceRefs(State state) {
+        for (Instance instance : mInstances.values()) {
+            ClassObj theClass = mClassesById.get(instance.mClassId);
+
+            if (theClass == null) {
+                continue;
+            }
+
+            String name = theClass.mClassName;
+            String superclassName = "none";
+            ClassObj superClass = mClassesById.get(theClass.mSuperclassId);
+
+            if (superClass != null) {
+                superclassName = superClass.mClassName;
+            }
+
+            theClass.addInstance(instance);
+            instance.resolveReferences(state);
+        }
+    }
+
+    public final void resolveClassStatics(State state) {
+        for (ClassObj theClass: mClassesById.values()) {
+            theClass.resolveReferences(state);
+        }
+    }
+
+    public final void resolveRoots(State state) {
+        for (RootObj root: mRoots) {
+            root.resolveReferences(state);
+        }
+    }
+}
diff --git a/hit/src/com/android/hit/HprofParser.java b/hit/src/com/android/hit/HprofParser.java
new file mode 100644
index 0000000..98fef1e
--- /dev/null
+++ b/hit/src/com/android/hit/HprofParser.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.InputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.HashMap;
+
+public class HprofParser
+{
+    private static final int STRING_IN_UTF8             =   0x01;
+    private static final int LOAD_CLASS                 =   0x02;
+    private static final int UNLOAD_CLASS               =   0x03;   //  unused
+    private static final int STACK_FRAME                =   0x04;
+    private static final int STACK_TRACE                =   0x05;
+    private static final int ALLOC_SITES                =   0x06;   //  unused
+    private static final int HEAP_SUMMARY               =   0x07;
+    private static final int START_THREAD               =   0x0a;   //  unused
+    private static final int END_THREAD                 =   0x0b;   //  unused
+    private static final int HEAP_DUMP                  =   0x0c;
+    private static final int HEAP_DUMP_SEGMENT          =   0x1c;
+    private static final int HEAP_DUMP_END              =   0x2c;
+    private static final int CPU_SAMPLES                =   0x0d;   //  unused
+    private static final int CONTROL_SETTINGS           =   0x0e;   //  unused
+
+    private static final int ROOT_UNKNOWN               =   0xff;
+    private static final int ROOT_JNI_GLOBAL            =   0x01;
+    private static final int ROOT_JNI_LOCAL             =   0x02;
+    private static final int ROOT_JAVA_FRAME            =   0x03;
+    private static final int ROOT_NATIVE_STACK          =   0x04;
+    private static final int ROOT_STICKY_CLASS          =   0x05;
+    private static final int ROOT_THREAD_BLOCK          =   0x06;
+    private static final int ROOT_MONITOR_USED          =   0x07;
+    private static final int ROOT_THREAD_OBJECT         =   0x08;
+    private static final int ROOT_CLASS_DUMP            =   0x20;
+    private static final int ROOT_INSTANCE_DUMP         =   0x21;
+    private static final int ROOT_OBJECT_ARRAY_DUMP     =   0x22;
+    private static final int ROOT_PRIMITIVE_ARRAY_DUMP  =   0x23;
+
+    /**
+     * Android format addition
+     *
+     * Specifies information about which heap certain objects came from.
+     * When a sub-tag of this type appears in a HPROF_HEAP_DUMP or
+     * HPROF_HEAP_DUMP_SEGMENT record, entries that follow it will be
+     * associated with the specified heap.  The HEAP_DUMP_INFO data is reset
+     * at the end of the HEAP_DUMP[_SEGMENT].  Multiple HEAP_DUMP_INFO entries
+     * may appear in a single HEAP_DUMP[_SEGMENT].
+     *
+     * Format:
+     *     u1: Tag value (0xFE)
+     *     u4: heap ID
+     *     ID: heap name string ID
+     */
+    private static final int ROOT_HEAP_DUMP_INFO        =   0xfe;
+    private static final int ROOT_INTERNED_STRING       =   0x89;
+    private static final int ROOT_FINALIZING            =   0x8a;
+    private static final int ROOT_DEBUGGER              =   0x8b;
+    private static final int ROOT_REFERENCE_CLEANUP     =   0x8c;
+    private static final int ROOT_VM_INTERNAL           =   0x8d;
+    private static final int ROOT_JNI_MONITOR           =   0x8e;
+    private static final int ROOT_UNREACHABLE           =   0x90;
+    private static final int ROOT_PRIMITIVE_ARRAY_NODATA=   0xc3;
+
+    DataInputStream mInput;
+    int mIdSize;
+    State mState;
+
+    byte[] mFieldBuffer = new byte[8];
+
+    /*
+     * These are only needed while parsing so are not kept as part of the
+     * heap data.
+     */
+    HashMap<Long, String> mStrings = new HashMap<Long, String>();
+    HashMap<Long, String> mClassNames = new HashMap<Long, String>();
+
+    public HprofParser(DataInputStream in) {
+        mInput = in;
+    }
+
+    public final State parse() {
+        State state = new State();
+        mState = state;
+
+        try {
+            String  s = readNullTerminatedString();
+            DataInputStream in = mInput;
+
+            mIdSize = in.readInt();
+            Types.setIdSize(mIdSize);
+
+            in.readLong();  //  Timestamp, ignored for now
+
+            while (true) {
+                int tag = in.readUnsignedByte();
+                int timestamp = in.readInt();
+                int length = in.readInt();
+
+                switch (tag) {
+                    case STRING_IN_UTF8:
+                        loadString(length - 4);
+                        break;
+
+                    case LOAD_CLASS:
+                        loadClass();
+                        break;
+
+                    case STACK_FRAME:
+                        loadStackFrame();
+                        break;
+
+                    case STACK_TRACE:
+                        loadStackTrace();
+                        break;
+
+                    case HEAP_DUMP:
+                        loadHeapDump(length);
+                        mState.setToDefaultHeap();
+                        break;
+
+                    case HEAP_DUMP_SEGMENT:
+                        loadHeapDump(length);
+                        mState.setToDefaultHeap();
+                        break;
+
+                    default:
+                        skipFully(length);
+                }
+
+            }
+        } catch (EOFException eof) {
+            //  this is fine
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        mState.resolveReferences();
+
+        return state;
+    }
+
+    private String readNullTerminatedString() throws IOException {
+        StringBuilder s = new StringBuilder();
+        DataInputStream in = mInput;
+
+        for (int c = in.read(); c != 0; c = in.read()) {
+            s.append((char) c);
+        }
+
+        return s.toString();
+    }
+
+    private long readId() throws IOException {
+        switch (mIdSize) {
+            case 1: return mInput.readUnsignedByte();
+            case 2: return mInput.readUnsignedShort();
+            case 4: return ((long) mInput.readInt()) & 0x00000000ffffffffL;
+            case 8: return mInput.readLong();
+        }
+
+        throw new IllegalArgumentException("ID Length must be 1, 2, 4, or 8");
+    }
+
+    private String readUTF8(int length) throws IOException {
+        byte[] b = new byte[length];
+
+        mInput.read(b);
+
+        return new String(b, "utf-8");
+    }
+
+    private void loadString(int length) throws IOException {
+        long id = readId();
+        String string = readUTF8(length);
+
+        mStrings.put(id, string);
+    }
+
+    private void loadClass() throws IOException {
+        DataInputStream in = mInput;
+        int serial = in.readInt();
+        long id = readId();
+        int stackTrace = in.readInt();              //  unused
+        String name = mStrings.get(readId());
+
+        mClassNames.put(id, name);
+    }
+
+    private void loadStackFrame() throws IOException {
+        long id = readId();
+        String methodName = mStrings.get(readId());
+        String methodSignature = mStrings.get(readId());
+        String sourceFile = mStrings.get(readId());
+        int serial = mInput.readInt();
+        int lineNumber = mInput.readInt();
+
+        StackFrame frame = new StackFrame(id, methodName, methodSignature,
+            sourceFile, serial, lineNumber);
+
+        mState.addStackFrame(frame);
+    }
+
+    private void loadStackTrace() throws IOException {
+        int serialNumber = mInput.readInt();
+        int threadSerialNumber = mInput.readInt();
+        final int numFrames = mInput.readInt();
+        StackFrame[] frames = new StackFrame[numFrames];
+
+        for (int i = 0; i < numFrames; i++) {
+            frames[i] = mState.getStackFrame(readId());
+        }
+
+        StackTrace trace = new StackTrace(serialNumber, threadSerialNumber,
+            frames);
+
+        mState.addStackTrace(trace);
+    }
+
+    private void loadHeapDump(int length) throws IOException {
+        DataInputStream in = mInput;
+
+        while (length > 0) {
+            int tag = in.readUnsignedByte();
+            length--;
+
+            switch (tag) {
+                case ROOT_UNKNOWN:
+                    length -= loadBasicObj(RootType.UNKNOWN);
+                    break;
+
+                case ROOT_JNI_GLOBAL:
+                    length -= loadBasicObj(RootType.NATIVE_STATIC);
+                    readId();   //  ignored
+                    length -= mIdSize;
+                    break;
+
+                case ROOT_JNI_LOCAL:
+                    length -= loadJniLocal();
+                    break;
+
+                case ROOT_JAVA_FRAME:
+                    length -= loadJavaFrame();
+                    break;
+
+                case ROOT_NATIVE_STACK:
+                    length -= loadNativeStack();
+                    break;
+
+                case ROOT_STICKY_CLASS:
+                    length -= loadBasicObj(RootType.SYSTEM_CLASS);
+                    break;
+
+                case ROOT_THREAD_BLOCK:
+                    length -= loadThreadBlock();
+                    break;
+
+                case ROOT_MONITOR_USED:
+                    length -= loadBasicObj(RootType.BUSY_MONITOR);
+                    break;
+
+                case ROOT_THREAD_OBJECT:
+                    length -= loadThreadObject();
+                    break;
+
+                case ROOT_CLASS_DUMP:
+                    length -= loadClassDump();
+                    break;
+
+                case ROOT_INSTANCE_DUMP:
+                    length -= loadInstanceDump();
+                    break;
+
+                case ROOT_OBJECT_ARRAY_DUMP:
+                    length -= loadObjectArrayDump();
+                    break;
+
+                case ROOT_PRIMITIVE_ARRAY_DUMP:
+                    length -= loadPrimitiveArrayDump();
+                    break;
+
+                case ROOT_PRIMITIVE_ARRAY_NODATA:
+                    System.err.println("+--- PRIMITIVE ARRAY NODATA DUMP");
+                    length -= loadPrimitiveArrayDump();
+
+                    throw new IllegalArgumentException(
+                        "Don't know how to load a nodata array");
+
+                case ROOT_HEAP_DUMP_INFO:
+                    int heapId = mInput.readInt();
+                    long heapNameId = readId();
+                    String heapName = mStrings.get(heapNameId);
+
+                    mState.setHeapTo(heapId, heapName);
+                    length -= 4 + mIdSize;
+                    break;
+
+                case ROOT_INTERNED_STRING:
+                    length -= loadBasicObj(RootType.INTERNED_STRING);
+                    break;
+
+                case ROOT_FINALIZING:
+                    length -= loadBasicObj(RootType.FINALIZING);
+                    break;
+
+                case ROOT_DEBUGGER:
+                    length -= loadBasicObj(RootType.DEBUGGER);
+                    break;
+
+                case ROOT_REFERENCE_CLEANUP:
+                    length -= loadBasicObj(RootType.REFERENCE_CLEANUP);
+                    break;
+
+                case ROOT_VM_INTERNAL:
+                    length -= loadBasicObj(RootType.VM_INTERNAL);
+                    break;
+
+                case ROOT_JNI_MONITOR:
+                    length -= loadJniMonitor();
+                    break;
+
+                case ROOT_UNREACHABLE:
+                    length -= loadBasicObj(RootType.UNREACHABLE);
+                    break;
+
+                default:
+                    throw new IllegalArgumentException(
+                        "loadHeapDump loop with unknown tag " + tag
+                        + " with " + mInput.available()
+                        + " bytes possibly remaining");
+            }
+        }
+    }
+
+    private int loadJniLocal() throws IOException {
+        long id = readId();
+        int threadSerialNumber = mInput.readInt();
+        int stackFrameNumber = mInput.readInt();
+        ThreadObj thread = mState.getThread(threadSerialNumber);
+        StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+            stackFrameNumber);
+        RootObj root = new RootObj(RootType.NATIVE_LOCAL, id,
+            threadSerialNumber, trace);
+
+        root.setHeap(mState.mCurrentHeap);
+        mState.addRoot(root);
+
+        return mIdSize + 4 + 4;
+    }
+
+    private int loadJavaFrame() throws IOException {
+        long id = readId();
+        int threadSerialNumber = mInput.readInt();
+        int stackFrameNumber = mInput.readInt();
+        ThreadObj thread = mState.getThread(threadSerialNumber);
+        StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+            stackFrameNumber);
+        RootObj root = new RootObj(RootType.JAVA_LOCAL, id, threadSerialNumber,
+            trace);
+
+        root.setHeap(mState.mCurrentHeap);
+        mState.addRoot(root);
+
+        return mIdSize + 4 + 4;
+    }
+
+    private int loadNativeStack() throws IOException {
+        long id = readId();
+        int threadSerialNumber = mInput.readInt();
+        ThreadObj thread = mState.getThread(threadSerialNumber);
+        StackTrace trace = mState.getStackTrace(thread.mStackTrace);
+        RootObj root = new RootObj(RootType.NATIVE_STACK, id,
+            threadSerialNumber, trace);
+
+        root.setHeap(mState.mCurrentHeap);
+        mState.addRoot(root);
+
+        return mIdSize + 4;
+    }
+
+    private int loadBasicObj(RootType type) throws IOException {
+        long id = readId();
+        RootObj root = new RootObj(type, id);
+
+        root.setHeap(mState.mCurrentHeap);
+        mState.addRoot(root);
+
+        return mIdSize;
+    }
+
+    private int loadThreadBlock() throws IOException {
+        long id = readId();
+        int threadSerialNumber = mInput.readInt();
+        ThreadObj thread = mState.getThread(threadSerialNumber);
+        StackTrace stack = mState.getStackTrace(thread.mStackTrace);
+        RootObj root = new RootObj(RootType.THREAD_BLOCK, id,
+            threadSerialNumber, stack);
+
+        root.setHeap(mState.mCurrentHeap);
+        mState.addRoot(root);
+
+        return mIdSize + 4;
+    }
+
+    private int loadThreadObject() throws IOException {
+        long id = readId();
+        int threadSerialNumber = mInput.readInt();
+        int stackSerialNumber = mInput.readInt();
+        ThreadObj thread = new ThreadObj(id, stackSerialNumber);
+
+        mState.addThread(thread, threadSerialNumber);
+
+        return mIdSize + 4 + 4;
+    }
+
+    private int loadClassDump() throws IOException {
+        int bytesRead = 0;
+        DataInputStream in = mInput;
+        long id = readId();
+        int stackSerialNumber = in.readInt();
+        StackTrace stack = mState.getStackTrace(stackSerialNumber);
+        long superClassId = readId();
+        long classLoaderId = readId();
+        long signersId = readId();
+        long protectionDomainId = readId();
+        long reserved1 = readId();
+        long reserved2 = readId();
+        int instanceSize = in.readInt();
+
+        bytesRead = (7 * mIdSize) + 4 + 4;
+
+        //  Skip over the constant pool
+        int numEntries = in.readUnsignedShort();
+        bytesRead += 2;
+
+        for (int i = 0; i < numEntries; i++) {
+            in.readUnsignedShort();
+            bytesRead += 2 + skipValue();
+        }
+
+        //  Static fields
+        numEntries = in.readUnsignedShort();
+        bytesRead += 2;
+
+        String[] staticFieldNames = new String[numEntries];
+        int[] staticFieldTypes = new int[numEntries];
+        ByteArrayOutputStream staticFieldValues = new ByteArrayOutputStream();
+        byte[] buffer = mFieldBuffer;
+
+        for (int i = 0; i < numEntries; i++) {
+            staticFieldNames[i] = mStrings.get(readId());
+
+            int fieldType = in.readByte();
+            int fieldSize = Types.getTypeSize(fieldType);
+            staticFieldTypes[i] = fieldType;
+
+            in.readFully(buffer, 0, fieldSize);
+            staticFieldValues.write(buffer, 0, fieldSize);
+
+            bytesRead += mIdSize + 1 + fieldSize;
+        }
+
+        //  Instance fields
+        numEntries = in.readUnsignedShort();
+        bytesRead += 2;
+
+        String[] names = new String[numEntries];
+        int[] types = new int[numEntries];
+
+        for (int i = 0; i < numEntries; i++) {
+            long fieldName = readId();
+            int type = in.readUnsignedByte();
+
+            names[i] = mStrings.get(fieldName);
+            types[i] = type;
+
+            bytesRead += mIdSize + 1;
+        }
+
+        ClassObj theClass = new ClassObj(id, stack, mClassNames.get(id));
+
+        theClass.setStaticFieldNames(staticFieldNames);
+        theClass.setStaticFieldTypes(staticFieldTypes);
+        theClass.setStaticFieldValues(staticFieldValues.toByteArray());
+
+        theClass.setSuperclassId(superClassId);
+        theClass.setFieldNames(names);
+        theClass.setFieldTypes(types);
+        theClass.setSize(instanceSize);
+
+        theClass.setHeap(mState.mCurrentHeap);
+
+        mState.addClass(id, theClass);
+
+        return bytesRead;
+    }
+
+    private int loadInstanceDump() throws IOException {
+        long id = readId();
+        int stackId = mInput.readInt();
+        StackTrace stack = mState.getStackTrace(stackId);
+        long classId = readId();
+        int remaining = mInput.readInt();
+        ClassInstance instance = new ClassInstance(id, stack, classId);
+
+        instance.loadFieldData(mInput, remaining);
+        instance.setHeap(mState.mCurrentHeap);
+        mState.addInstance(id, instance);
+
+        return mIdSize + 4 + mIdSize + 4 + remaining;
+    }
+
+    private int loadObjectArrayDump() throws IOException {
+        long id = readId();
+        int stackId = mInput.readInt();
+        StackTrace stack = mState.getStackTrace(stackId);
+        int numElements = mInput.readInt();
+        long classId = readId();
+        int totalBytes = numElements * mIdSize;
+        byte[] data = new byte[totalBytes];
+        String className = mClassNames.get(classId);
+
+        mInput.readFully(data);
+
+        ArrayInstance array = new ArrayInstance(id, stack, Types.OBJECT,
+            numElements, data);
+
+        array.mClassId = classId;
+        array.setHeap(mState.mCurrentHeap);
+        mState.addInstance(id, array);
+
+        return mIdSize + 4 + 4 + mIdSize + totalBytes;
+    }
+
+    private int loadPrimitiveArrayDump() throws IOException {
+        long id = readId();
+        int stackId = mInput.readInt();
+        StackTrace stack = mState.getStackTrace(stackId);
+        int numElements = mInput.readInt();
+        int type = mInput.readUnsignedByte();
+        int size = Types.getTypeSize(type);
+        int totalBytes = numElements * size;
+        byte[] data = new byte[totalBytes];
+
+        mInput.readFully(data);
+
+        ArrayInstance array = new ArrayInstance(id, stack, type, numElements,
+            data);
+
+        array.setHeap(mState.mCurrentHeap);
+        mState.addInstance(id, array);
+
+        return mIdSize + 4 + 4 + 1 + totalBytes;
+    }
+
+    private int loadJniMonitor() throws IOException {
+        long id = readId();
+        int threadSerialNumber = mInput.readInt();
+        int stackDepth = mInput.readInt();
+        ThreadObj thread = mState.getThread(threadSerialNumber);
+        StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+            stackDepth);
+        RootObj root = new RootObj(RootType.NATIVE_MONITOR, id,
+            threadSerialNumber, trace);
+
+        root.setHeap(mState.mCurrentHeap);
+        mState.addRoot(root);
+
+        return mIdSize + 4 + 4;
+    }
+
+    private int skipValue() throws IOException {
+        int type = mInput.readUnsignedByte();
+        int size = Types.getTypeSize(type);
+
+        skipFully(size);
+
+        return size + 1;
+    }
+
+    /*
+     * BufferedInputStream will not skip(int) the entire requested number
+     * of bytes if it extends past the current buffer boundary.  So, this
+     * routine is needed to actually skip over the requested number of bytes
+     * using as many iterations as needed.
+     */
+    private void skipFully(long numBytes) throws IOException {
+        while (numBytes > 0) {
+            long skipped = mInput.skip(numBytes);
+
+            numBytes -= skipped;
+        }
+    }
+}
diff --git a/hit/src/com/android/hit/Instance.java b/hit/src/com/android/hit/Instance.java
new file mode 100644
index 0000000..6afa2b2
--- /dev/null
+++ b/hit/src/com/android/hit/Instance.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class Instance {
+    long mId;
+
+    //  Id of the ClassObj of which this object is an instance
+    long mClassId;
+
+    //  The stack in which this object was allocated
+    StackTrace mStack;
+
+    //  The heap in which this object was allocated (app, zygote, etc)
+    Heap mHeap;
+
+    //  The size of this object
+    int mSize;
+
+    public interface Filter {
+        public boolean accept(Instance instance);
+    }
+
+    //  List of all objects that hold a live reference to this object
+    private ArrayList<Instance> mParents;
+
+    /*
+     * After the whole HPROF file is read and parsed this method will be
+     * called on all heap objects so that they can resolve their internal
+     * object references.
+     *
+     * The super-State is passed in because some object references (such
+     * as interned Strings and static class fields) may need to be searched
+     * for in a heap other than the one this Instance is in.
+     */
+    public abstract void resolveReferences(State state);
+
+    /*
+     * Some operations require gathering all the objects in a given section
+     * of the object graph.  If non-null, the filter is applied to each
+     * node in the graph to determine if it should be added to the result
+     * set.
+     */
+    public abstract void visit(Set<Instance> resultSet, Filter filter);
+
+    public void setSize(int size) {
+        mSize = size;
+    }
+
+    public final int getCompositeSize() {
+        HashSet<Instance> set = new HashSet<Instance>();
+
+        visit(set, null);
+
+        int size = 0;
+
+        for (Instance instance: set) {
+            size += instance.getSize();
+        }
+
+        return size;
+    }
+
+    //  Returns the instrinsic size of a given object
+    public int getSize() {
+        return mSize;
+    }
+
+    public abstract String getTypeName();
+
+    public void setHeap(Heap heap) {
+        mHeap = heap;
+    }
+
+    //  Add to the list of objects that have a hard reference to this Instance
+    public void addParent(Instance parent) {
+        if (mParents == null) {
+            mParents = new ArrayList<Instance>();
+        }
+
+        mParents.add(parent);
+    }
+
+    public ArrayList<Instance> getParents() {
+        if (mParents == null) {
+            mParents = new ArrayList<Instance>();
+        }
+
+        return mParents;
+    }
+
+    /*
+     * If this object has a reference to the object identified by id, return
+     * a String describing the reference in detail.
+     */
+    public String describeReferenceTo(long id) {
+        return "No reference to 0x" + Long.toHexString(id);
+    }
+}
diff --git a/hit/src/com/android/hit/Main.java b/hit/src/com/android/hit/Main.java
new file mode 100644
index 0000000..4ed5c11
--- /dev/null
+++ b/hit/src/com/android/hit/Main.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.util.Map;
+import java.util.Set;
+
+public class Main
+{
+    public static void main(String argv[]) {
+        FileInputStream fis;
+        BufferedInputStream bis;
+        DataInputStream dis;
+
+        try {
+            fis = new FileInputStream(argv[0]);
+            bis = new BufferedInputStream(fis);
+            dis = new DataInputStream(bis);
+
+            State state = (new HprofParser(dis)).parse();
+
+            dis.close();
+
+            testClassesQuery(state);
+            testAllClassesQuery(state);
+            testFindInstancesOf(state);
+            testFindAllInstancesOf(state);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void testClassesQuery(State state) {
+        String[] x = new String[] {
+            "char[",
+            "javax.",
+            "org.xml.sax"
+        };
+
+        Map<String, Set<ClassObj>> someClasses = Queries.classes(state, x);
+
+        for (String thePackage: someClasses.keySet()) {
+            System.out.println("------------------- " + thePackage);
+
+            Set<ClassObj> classes = someClasses.get(thePackage);
+
+            for (ClassObj theClass: classes) {
+                System.out.println("     " + theClass.mClassName);
+            }
+        }
+    }
+
+    private static void testAllClassesQuery(State state) {
+        Map<String, Set<ClassObj>> allClasses = Queries.allClasses(state);
+
+        for (String thePackage: allClasses.keySet()) {
+            System.out.println("------------------- " + thePackage);
+
+            Set<ClassObj> classes = allClasses.get(thePackage);
+
+            for (ClassObj theClass: classes) {
+                System.out.println("     " + theClass.mClassName);
+            }
+        }
+    }
+
+    private static void testFindInstancesOf(State state) {
+        Instance[] instances = Queries.instancesOf(state, "java.lang.String");
+
+        System.out.println("There are " + instances.length + " Strings.");
+    }
+
+    private static void testFindAllInstancesOf(State state) {
+        Instance[] instances = Queries.allInstancesOf(state,
+            "android.graphics.drawable.Drawable");
+
+        System.out.println("There are " + instances.length
+            + " instances of Drawables and its subclasses.");
+    }
+}
diff --git a/hit/src/com/android/hit/Queries.java b/hit/src/com/android/hit/Queries.java
new file mode 100644
index 0000000..0dac796
--- /dev/null
+++ b/hit/src/com/android/hit/Queries.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+public class Queries {
+    /*
+     * NOTES:  Here's a list of the queries that can be done in hat and
+     * how you'd perform a similar query here in hit:
+     *
+     * hat                      hit
+     * ------------------------------------------------------------------------
+     * allClasses               classes
+     * allClassesWithPlatform   allClasses
+     * class                    findClass
+     * instances                instancesOf
+     * allInstances             allInstancesOf
+     * object                   findObject
+     * showRoots                getRoots
+     * newInstances             newInstances
+     *
+     * reachableFrom            make a call to findObject to get the target
+     *                          parent object, this will give you an Instance.
+     *                          Then call visit(Set, Filter) on that to have
+     *                          it build the set of objects in its subgraph.
+     *
+     * rootsTo                  make a call to findObject on the leaf node
+     *                          in question, this will give you an Instance.
+     *                          Instances have an ArrayList of all of the
+     *                          parent objects that refer to it.  You can
+     *                          follow those parent links until you hit an
+     *                          object whose parent is null or a ThreadObj.
+     *                          You've not successfully traced the paths to
+     *                          the roots.
+     */
+
+    private static final String DEFAULT_PACKAGE = "<default>";
+
+    /*
+     * Produce a collection of all classes, broken down by package.
+     * The keys of the resultant map iterate in sorted package order.
+     * The values of the map are the classes defined in each package.
+     */
+    public static Map<String, Set<ClassObj>> allClasses(State state) {
+        return classes(state, null);
+    }
+
+    public static Map<String, Set<ClassObj>> classes(State state,
+            String[] excludedPrefixes) {
+        TreeMap<String, Set<ClassObj>> result =
+        new TreeMap<String, Set<ClassObj>>();
+
+        Set<ClassObj> classes = new TreeSet<ClassObj>();
+
+        //  Build a set of all classes across all heaps
+        for (Heap heap: state.mHeaps.values()) {
+            classes.addAll(heap.mClassesById.values());
+        }
+
+        //  Filter it if needed
+        if (excludedPrefixes != null) {
+            final int N = excludedPrefixes.length;
+            Iterator<ClassObj> iter = classes.iterator();
+
+            while (iter.hasNext()) {
+                ClassObj theClass = iter.next();
+                String classPath = theClass.toString();
+
+                for (int i = 0; i < N; i++) {
+                    if (classPath.startsWith(excludedPrefixes[i])) {
+                        iter.remove();
+                        break;
+                    }
+                }
+            }
+        }
+
+        //  Now that we have a final list of classes, group them by package
+        for (ClassObj theClass: classes) {
+            String packageName = DEFAULT_PACKAGE;
+            int lastDot = theClass.mClassName.lastIndexOf('.');
+
+            if (lastDot != -1) {
+                packageName = theClass.mClassName.substring(0, lastDot);
+            }
+
+            Set<ClassObj> classSet = result.get(packageName);
+
+            if (classSet == null) {
+                classSet = new TreeSet<ClassObj>();
+                result.put(packageName, classSet);
+            }
+
+            classSet.add(theClass);
+        }
+
+        return result;
+    }
+
+    /*
+     * It's sorta sad that this is a pass-through call, but it seems like
+     * having all of the hat-like query methods in one place is a good thing
+     * even if there is duplication of effort.
+     */
+    public static ClassObj findClass(State state, String name) {
+        return state.findClass(name);
+    }
+
+    /*
+     * Return an array of instances of the given class.  This does not include
+     * instances of subclasses.
+     */
+     public static Instance[] instancesOf(State state, String baseClassName) {
+         ClassObj theClass = state.findClass(baseClassName);
+
+         if (theClass == null) {
+             throw new IllegalArgumentException("Class not found: "
+                + baseClassName);
+         }
+
+         Instance[] instances = new Instance[theClass.mInstances.size()];
+
+         return theClass.mInstances.toArray(instances);
+     }
+
+    /*
+     * Return an array of instances of the given class.  This includes
+     * instances of subclasses.
+     */
+    public static Instance[] allInstancesOf(State state, String baseClassName) {
+        ClassObj theClass = state.findClass(baseClassName);
+
+        if (theClass == null) {
+            throw new IllegalArgumentException("Class not found: "
+                + baseClassName);
+        }
+
+        ArrayList<ClassObj> classList = new ArrayList<ClassObj>();
+
+        classList.add(theClass);
+        classList.addAll(traverseSubclasses(theClass));
+
+        ArrayList<Instance> instanceList = new ArrayList<Instance>();
+
+        for (ClassObj someClass: classList) {
+            instanceList.addAll(someClass.mInstances);
+        }
+
+        Instance[] result = new Instance[instanceList.size()];
+
+        instanceList.toArray(result);
+
+        return result;
+    }
+
+    private static ArrayList<ClassObj> traverseSubclasses(ClassObj base) {
+        ArrayList<ClassObj> result = new ArrayList<ClassObj>();
+
+        for (ClassObj subclass: base.mSubclasses) {
+            result.add(subclass);
+            result.addAll(traverseSubclasses(subclass));
+        }
+
+        return result;
+    }
+
+    /*
+     * Find a reference to an object based on its id.  The id should be
+     * in hexadecimal.
+     */
+    public static Instance findObject(State state, String id) {
+        long id2 = Long.parseLong(id, 16);
+
+        return state.findReference(id2);
+    }
+
+    public static Collection<RootObj> getRoots(State state) {
+        HashSet<RootObj> result = new HashSet<RootObj>();
+
+        for (Heap heap: state.mHeaps.values()) {
+            result.addAll(heap.mRoots);
+        }
+
+        return result;
+    }
+
+    public static final Instance[] newInstances(State older, State newer) {
+        ArrayList<Instance> resultList = new ArrayList<Instance>();
+
+        for (Heap newHeap: newer.mHeaps.values()) {
+            Heap oldHeap = older.getHeap(newHeap.mName);
+
+            if (oldHeap == null) {
+                continue;
+            }
+
+            for (Instance instance: newHeap.mInstances.values()) {
+                Instance oldInstance = oldHeap.getInstance(instance.mId);
+
+                /*
+                 * If this instance wasn't in the old heap, or was there,
+                 * but that ID was for an obj of a different type, then we have
+                 * a newly allocated object and we should report it in the
+                 * results.
+                 */
+                if ((oldInstance == null)
+                        || (instance.mClassId != oldInstance.mClassId)) {
+                    resultList.add(instance);
+                }
+            }
+        }
+
+        Instance[] resultArray = new Instance[resultList.size()];
+
+        return resultList.toArray(resultArray);
+    }
+}
diff --git a/hit/src/com/android/hit/RootObj.java b/hit/src/com/android/hit/RootObj.java
new file mode 100644
index 0000000..a9acd35
--- /dev/null
+++ b/hit/src/com/android/hit/RootObj.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.Set;
+
+public class RootObj extends Instance {
+    RootType mType = RootType.UNKNOWN;
+    int mIndex;
+    int mThread;
+
+    /*
+     * These two fields are only used by roots that are static
+     * fields of class objects
+     */
+    long mParent;
+    String mComment;
+
+    public RootObj(RootType type) {
+        this(type, 0, 0, null);
+    }
+
+    public RootObj(RootType type, long id) {
+        this(type, id, 0, null);
+    }
+
+    public RootObj(RootType type, long id, int thread, StackTrace stack) {
+        mType = type;
+        mId = id;
+        mThread = thread;
+        mStack = stack;
+    }
+
+    public final String getClassName(State state) {
+        ClassObj theClass;
+
+        if (mType == RootType.SYSTEM_CLASS) {
+            theClass = state.findClass(mId);
+        } else {
+            Instance instance = state.findReference(mId);
+
+            theClass = state.findClass(instance.mClassId);
+        }
+
+        if (theClass == null) {
+            return "no class defined!!";
+        }
+
+        return theClass.mClassName;
+    }
+
+    @Override
+    public final int getSize() {
+        Instance instance = null;
+
+        if (mType == RootType.SYSTEM_CLASS) {
+            instance = mHeap.mState.findClass(mId);
+        } else {
+            instance = mHeap.mState.findReference(mId);
+        }
+
+        if (instance == null) {
+            return 0;
+        }
+
+        return instance.getSize();
+    }
+
+    @Override
+    public final void visit(Set<Instance> resultSet, Filter filter) {
+        if (resultSet.contains(this)) {
+            return;
+        }
+
+        if (filter != null) {
+            if (filter.accept(this)) {
+                resultSet.add(this);
+            }
+        } else {
+            resultSet.add(this);
+        }
+    }
+
+    @Override
+    public final void resolveReferences(State state) {
+        //  Nothing to do here
+    }
+
+    @Override
+    public final String getTypeName() {
+        return "root " + mType.getName();
+    }
+
+    public final String toString() {
+        return String.format("%s@0x08x", mType.getName(), mId);
+    }
+}
diff --git a/hit/src/com/android/hit/RootType.java b/hit/src/com/android/hit/RootType.java
new file mode 100644
index 0000000..209ed1b
--- /dev/null
+++ b/hit/src/com/android/hit/RootType.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public enum RootType {
+    UNREACHABLE         (0, "unreachable object"),
+    INVALID_TYPE        (1, "invalid type"),
+    INTERNED_STRING     (2, "interned string"),
+    UNKNOWN             (3, "unknown"),
+    SYSTEM_CLASS        (4, "system class"),
+    VM_INTERNAL         (5, "vm internal"),
+    DEBUGGER            (6, "debugger"),
+    NATIVE_LOCAL        (7, "native local"),
+    NATIVE_STATIC       (8, "native static"),
+    THREAD_BLOCK        (9, "thread block"),
+    BUSY_MONITOR        (10, "busy monitor"),
+    NATIVE_MONITOR      (11, "native monitor"),
+    REFERENCE_CLEANUP   (12, "reference cleanup"),
+    FINALIZING          (13, "finalizing"),
+    JAVA_LOCAL          (14, "java local"),
+    NATIVE_STACK        (15, "native stack"),
+    JAVA_STATIC         (16, "java static");
+
+    private final int mType;
+    private final String mName;
+
+    RootType(int type, String name) {
+        mType = type;
+        mName = name;
+    }
+
+    public final int getType() {
+        return mType;
+    }
+
+    public final String getName() {
+        return mName;
+    }
+}
diff --git a/hit/src/com/android/hit/StackFrame.java b/hit/src/com/android/hit/StackFrame.java
new file mode 100644
index 0000000..2ae7f32
--- /dev/null
+++ b/hit/src/com/android/hit/StackFrame.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class StackFrame {
+    public static final int NO_LINE_NUMBER          =   0;
+    public static final int UNKNOWN_LOCATION        =   -1;
+    public static final int COMPILED_METHOD         =   -2;
+    public static final int NATIVE_METHOD           =   -3;
+
+    long mId;
+    String mMethodName;
+    String mSignature;
+    String mFilename;
+    int mSerialNumber;
+    int mLineNumber;
+
+    public StackFrame(long id, String method, String sig, String file,
+            int serial, int line) {
+        mId = id;
+        mMethodName = method;
+        mSignature = sig;
+        mFilename = file;
+        mSerialNumber = serial;
+        mLineNumber = line;
+    }
+
+    private final String lineNumberString() {
+        switch (mLineNumber) {
+            case NO_LINE_NUMBER:    return "No line number";
+            case UNKNOWN_LOCATION:  return "Unknown line number";
+            case COMPILED_METHOD:   return "Compiled method";
+            case NATIVE_METHOD:     return "Native method";
+
+            default:                return String.valueOf(mLineNumber);
+        }
+    }
+
+    public final String toString() {
+        return mMethodName
+            + mSignature.replace('/', '.')
+            + " - "
+            + mFilename + ":"
+            + lineNumberString();
+    }
+}
diff --git a/hit/src/com/android/hit/StackTrace.java b/hit/src/com/android/hit/StackTrace.java
new file mode 100644
index 0000000..53cb86d
--- /dev/null
+++ b/hit/src/com/android/hit/StackTrace.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class StackTrace {
+    int mSerialNumber;
+    int mThreadSerialNumber;
+    StackFrame[] mFrames;
+
+    /*
+     * For subsets of the stack frame we'll reference the parent list of frames
+     * but keep track of its offset into the parent's list of stack frame ids.
+     * This alleviates the need to constantly be duplicating subsections of the
+     * list of stack frame ids.
+     */
+    StackTrace mParent = null;
+    int mOffset = 0;
+
+    private StackTrace() {
+
+    }
+
+    public StackTrace(int serial, int thread, StackFrame[] frames) {
+        mSerialNumber = serial;
+        mThreadSerialNumber = thread;
+        mFrames = frames;
+    }
+
+    public final StackTrace fromDepth(int startingDepth) {
+        StackTrace result = new StackTrace();
+
+        if (mParent != null) {
+            result.mParent = mParent;
+        } else {
+            result.mParent = this;
+        }
+
+        result.mOffset = startingDepth + mOffset;
+
+        return result;
+    }
+
+    public final void dump() {
+        final int N = mFrames.length;
+
+        for (int i = 0; i < N; i++) {
+            System.out.println(mFrames[i].toString());
+        }
+    }
+}
diff --git a/hit/src/com/android/hit/State.java b/hit/src/com/android/hit/State.java
new file mode 100644
index 0000000..96c944d
--- /dev/null
+++ b/hit/src/com/android/hit/State.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/*
+ * State is a snapshot of all of the heaps, and related meta-data, for
+ * the runtime at a given instant.
+ *
+ * During parsing of the HPROF file HEAP_DUMP_INFO chunks change which heap
+ * is being referenced.
+ */
+public class State {
+    HashMap<Integer, Heap> mHeaps;
+    Heap mCurrentHeap;
+
+    public State() {
+        mHeaps = new HashMap<Integer, Heap>();
+        setToDefaultHeap();
+    }
+
+    public Heap setToDefaultHeap() {
+        return setHeapTo(0, "default");
+    }
+
+    public Heap setHeapTo(int id, String name) {
+        Heap heap = mHeaps.get(id);
+
+        if (heap == null) {
+            heap = new Heap(name);
+            heap.mState = this;
+            mHeaps.put(id, heap);
+        }
+
+        mCurrentHeap = heap;
+
+        return mCurrentHeap;
+    }
+
+    public Heap getHeap(int id) {
+        return mHeaps.get(id);
+    }
+
+    public Heap getHeap(String name) {
+        for (Heap heap: mHeaps.values()) {
+            if (heap.mName.equals(name)) {
+                return heap;
+            }
+        }
+
+        return null;
+    }
+
+    public final void addStackFrame(StackFrame theFrame) {
+        mCurrentHeap.addStackFrame(theFrame);
+    }
+
+    public final StackFrame getStackFrame(long id) {
+        return mCurrentHeap.getStackFrame(id);
+    }
+
+    public final void addStackTrace(StackTrace theTrace) {
+        mCurrentHeap.addStackTrace(theTrace);
+    }
+
+    public final StackTrace getStackTrace(int traceSerialNumber) {
+        return mCurrentHeap.getStackTrace(traceSerialNumber);
+    }
+
+    public final StackTrace getStackTraceAtDepth(int traceSerialNumber,
+            int depth) {
+        return mCurrentHeap.getStackTraceAtDepth(traceSerialNumber, depth);
+    }
+
+    public final void addRoot(RootObj root) {
+        mCurrentHeap.addRoot(root);
+    }
+
+    public final void addThread(ThreadObj thread, int serialNumber) {
+        mCurrentHeap.addThread(thread, serialNumber);
+    }
+
+    public final ThreadObj getThread(int serialNumber) {
+        return mCurrentHeap.getThread(serialNumber);
+    }
+
+    public final void addInstance(long id, Instance instance) {
+        mCurrentHeap.addInstance(id, instance);
+    }
+
+    public final void addClass(long id, ClassObj theClass) {
+        mCurrentHeap.addClass(id, theClass);
+    }
+
+    public final Instance findReference(long id) {
+        for (Heap heap: mHeaps.values()) {
+            Instance instance = heap.getInstance(id);
+
+            if (instance != null) {
+                return instance;
+            }
+        }
+
+        //  Couldn't find an instance of a class, look for a class object
+        return findClass(id);
+    }
+
+    public final ClassObj findClass(long id) {
+        for (Heap heap: mHeaps.values()) {
+            ClassObj theClass = heap.getClass(id);
+
+            if (theClass != null) {
+                return theClass;
+            }
+        }
+
+        return null;
+    }
+
+    public final ClassObj findClass(String name) {
+        for (Heap heap: mHeaps.values()) {
+            ClassObj theClass = heap.getClass(name);
+
+            if (theClass != null) {
+                return theClass;
+            }
+        }
+
+        return null;
+    }
+
+    public final void dumpInstanceCounts() {
+        for (Heap heap: mHeaps.values()) {
+            System.out.println(
+                "+------------------ instance counts for heap: " + heap.mName);
+            heap.dumpInstanceCounts();
+        }
+    }
+
+    public final void dumpSizes() {
+        for (Heap heap: mHeaps.values()) {
+            System.out.println(
+                "+------------------ sizes for heap: " + heap.mName);
+            heap.dumpSizes();
+        }
+    }
+
+    public final void dumpSubclasses() {
+        for (Heap heap: mHeaps.values()) {
+            System.out.println(
+                "+------------------ subclasses for heap: " + heap.mName);
+            heap.dumpSubclasses();
+        }
+    }
+
+    public final void resolveReferences() {
+        for (Heap heap: mHeaps.values()) {
+            heap.resolveInstanceRefs(this);
+            heap.resolveClassStatics(this);
+            heap.resolveRoots(this);
+        }
+    }
+}
diff --git a/hit/src/com/android/hit/ThreadObj.java b/hit/src/com/android/hit/ThreadObj.java
new file mode 100644
index 0000000..32a73f0
--- /dev/null
+++ b/hit/src/com/android/hit/ThreadObj.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class ThreadObj {
+    long mId;
+    int mStackTrace;
+
+    public ThreadObj(long id, int stackTrace) {
+        mId = id;
+        mStackTrace = stackTrace;
+    }
+}
diff --git a/hit/src/com/android/hit/Types.java b/hit/src/com/android/hit/Types.java
new file mode 100644
index 0000000..62228ce
--- /dev/null
+++ b/hit/src/com/android/hit/Types.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class Types {
+    private static int mIdSize = 4;
+
+    public static final int OBJECT     =   2;
+    public static final int BOOLEAN    =   4;
+    public static final int CHAR       =   5;
+    public static final int FLOAT      =   6;
+    public static final int DOUBLE     =   7;
+    public static final int BYTE       =   8;
+    public static final int SHORT      =   9;
+    public static final int INT        =   10;
+    public static final int LONG       =   11;
+
+    public static final void setIdSize(int size) {
+        mIdSize = size;
+    }
+
+    public static final int getTypeSize(int type) {
+        switch (type) {
+            case '[':      return mIdSize; //  array object
+            case 'L':      return mIdSize; //  object
+            case 'Z':      return 1;       //  boolean
+            case 'C':      return 2;       //  char
+            case 'F':      return 4;       //  float
+            case 'D':      return 8;       //  double
+            case 'B':      return 1;       //  byte
+            case 'S':      return 2;       //  short
+            case 'I':      return 4;       //  int
+            case 'J':      return 8;       //  long
+
+            case OBJECT:   return mIdSize;
+            case BOOLEAN:  return 1;
+            case CHAR:     return 2;
+            case FLOAT:    return 4;
+            case DOUBLE:   return 8;
+            case BYTE:     return 1;
+            case SHORT:    return 2;
+            case INT:      return 4;
+            case LONG:     return 8;
+        }
+
+        throw new IllegalArgumentException("Illegal type signature: " + type);
+    }
+
+    public static final String getTypeName(int type) {
+        switch (type) {
+            case '[':      return "array";
+            case 'L':      return "object";
+            case 'Z':      return "boolean";
+            case 'C':      return "char";
+            case 'F':      return "float";
+            case 'D':      return "double";
+            case 'B':      return "byte";
+            case 'S':      return "short";
+            case 'I':      return "int";
+            case 'J':      return "long";
+
+            case OBJECT:   return "object";
+            case BOOLEAN:  return "boolean";
+            case CHAR:     return "char";
+            case FLOAT:    return "float";
+            case DOUBLE:   return "double";
+            case BYTE:     return "byte";
+            case SHORT:    return "short";
+            case INT:      return "int";
+            case LONG:     return "long";
+        }
+
+        throw new IllegalArgumentException("Illegal type signature: " + type);
+    }
+}
diff --git a/hit/test/testparser b/hit/test/testparser
new file mode 100755
index 0000000..a3a722e
--- /dev/null
+++ b/hit/test/testparser
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+find ../src -name \*java | xargs javac -d build -Xlint:unchecked
+
+# debug launch line tha turns off the jit and runs a debug server
+#java -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,address=53635,server=y,suspend=y -cp build com.android.hit.Main ../samples/android.hprof
+
+java -cp build com.android.hit.Main ../samples/android.hprof
diff --git a/libdex/Android.mk b/libdex/Android.mk
new file mode 100644
index 0000000..558a7e5
--- /dev/null
+++ b/libdex/Android.mk
@@ -0,0 +1,68 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+dex_src_files := \
+	CmdUtils.c \
+	DexCatch.c \
+	DexClass.c \
+	DexDataMap.c \
+	DexFile.c \
+	DexInlines.c \
+	DexOptData.c \
+	DexProto.c \
+	DexSwapVerify.c \
+	InstrUtils.c \
+	Leb128.c \
+	OpCodeNames.c \
+	OptInvocation.c \
+	sha1.c \
+	SysUtil.c \
+	ZipArchive.c
+
+dex_include_files := \
+	dalvik \
+	$(JNI_H_INCLUDE) \
+	external/zlib \
+	external/safe-iop/include
+
+##
+##
+## Build the device version of libdex
+##
+##
+ifneq ($(SDK_ONLY),true)  # SDK_only doesn't need device version
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(dex_src_files)
+LOCAL_C_INCLUDES += $(dex_include_files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libdex
+include $(BUILD_STATIC_LIBRARY)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host version of libdex
+##
+##
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(dex_src_files)
+LOCAL_C_INCLUDES += $(dex_include_files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libdex
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libdex/CmdUtils.c b/libdex/CmdUtils.c
new file mode 100644
index 0000000..e8fc635
--- /dev/null
+++ b/libdex/CmdUtils.c
@@ -0,0 +1,217 @@
+/*
+ * 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 utility functions for use with command-line utilities.
+ */
+#include "DexFile.h"
+#include "ZipArchive.h"
+#include "CmdUtils.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/*
+ * Extract "classes.dex" from archive file.
+ *
+ * If "quiet" is set, don't report common errors.
+ */
+UnzipToFileResult dexUnzipToFile(const char* zipFileName,
+    const char* outFileName, bool quiet)
+{
+    UnzipToFileResult result = kUTFRSuccess;
+    static const char* kFileToExtract = "classes.dex";
+    ZipArchive archive;
+    ZipEntry entry;
+    bool unlinkOnFailure = false;
+    int fd = -1;
+
+    if (dexZipOpenArchive(zipFileName, &archive) != 0) {
+        if (!quiet) {
+            fprintf(stderr, "Unable to open '%s' as zip archive\n",
+                zipFileName);
+        }
+        result = kUTFRNotZip;
+        goto bail;
+    }
+
+    fd = open(outFileName, O_WRONLY | O_CREAT | O_EXCL, 0600);
+    if (fd < 0) {
+        fprintf(stderr, "Unable to create output file '%s': %s\n",
+            outFileName, strerror(errno));
+        result = kUTFROutputFileProblem;
+        goto bail;
+    }
+
+    unlinkOnFailure = true;
+
+    entry = dexZipFindEntry(&archive, kFileToExtract);
+    if (entry == NULL) {
+        if (!quiet) {
+            fprintf(stderr, "Unable to find '%s' in '%s'\n",
+                kFileToExtract, zipFileName);
+        }
+        result = kUTFRNoClassesDex;
+        goto bail;
+    }
+
+    if (dexZipExtractEntryToFile(&archive, entry, fd) != 0) {
+        fprintf(stderr, "Extract of '%s' from '%s' failed\n",
+            kFileToExtract, zipFileName);
+        result = kUTFRBadZip;
+        goto bail;
+    }
+
+bail:
+    if (fd >= 0)
+        close(fd);
+    if (unlinkOnFailure && result != kUTFRSuccess)
+        unlink(outFileName);
+    dexZipCloseArchive(&archive);
+    return result;
+}
+
+/*
+ * Map the specified DEX file read-only (possibly after expanding it into a
+ * temp file from a Jar).  Pass in a MemMapping struct to hold the info.
+ * If the file is an unoptimized DEX file, then byte-swapping and structural
+ * verification are performed on it before the memory is made read-only.
+ *
+ * The temp file is deleted after the map succeeds.
+ *
+ * This is intended for use by tools (e.g. dexdump) that need to get a
+ * read-only copy of a DEX file that could be in a number of different states.
+ *
+ * If "tempFileName" is NULL, a default value is used.  The temp file is
+ * deleted after the map succeeds.
+ *
+ * If "quiet" is set, don't report common errors.
+ *
+ * Returns 0 (kUTFRSuccess) on success.
+ */
+UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
+    MemMapping* pMap, bool quiet)
+{
+    UnzipToFileResult result = kUTFRGenericFailure;
+    int len = strlen(fileName);
+    char tempNameBuf[32];
+    bool removeTemp = false;
+    int fd = -1;
+
+    if (len < 5) {
+        if (!quiet) {
+            fprintf(stderr,
+                "ERROR: filename must end in .dex, .zip, .jar, or .apk\n");
+        }
+        result = kUTFRBadArgs;
+        goto bail;
+    }
+
+    if (strcasecmp(fileName + len -3, "dex") != 0) {
+        if (tempFileName == NULL) {
+            /*
+             * Try .zip/.jar/.apk, all of which are Zip archives with
+             * "classes.dex" inside.  We need to extract the compressed
+             * data to a temp file, the location of which varies.
+             */
+            if (access("/tmp", W_OK) == 0)
+                sprintf(tempNameBuf, "/tmp/dex-temp-%d", getpid());
+            else
+                sprintf(tempNameBuf, "/sdcard/dex-temp-%d", getpid());
+
+            tempFileName = tempNameBuf;
+        }
+
+        result = dexUnzipToFile(fileName, tempFileName, quiet);
+
+        if (result == kUTFRSuccess) {
+            //printf("+++ Good unzip to '%s'\n", tempFileName);
+            fileName = tempFileName;
+            removeTemp = true;
+        } else if (result == kUTFRNotZip) {
+            if (!quiet) {
+                fprintf(stderr, "Not Zip, retrying as DEX\n");
+            }
+        } else {
+            if (!quiet && result == kUTFRNoClassesDex) {
+                fprintf(stderr, "Zip has no classes.dex\n");
+            }
+            goto bail;
+        }
+    }
+
+    result = kUTFRGenericFailure;
+
+    /*
+     * Pop open the (presumed) DEX file.
+     */
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+    fd = open(fileName, O_RDONLY | O_BINARY);
+    if (fd < 0) {
+        if (!quiet) {
+            fprintf(stderr, "ERROR: unable to open '%s': %s\n",
+                fileName, strerror(errno));
+        }
+        goto bail;
+    }
+
+    if (sysMapFileInShmemWritableReadOnly(fd, pMap) != 0) {
+        fprintf(stderr, "ERROR: Unable to map '%s'\n", fileName);
+        goto bail;
+    }
+
+    /*
+     * This call will fail if the file exists on a filesystem that
+     * doesn't support mprotect(). If that's the case, then the file
+     * will have already been mapped private-writable by the previous
+     * call, so we don't need to do anything special if this call
+     * returns non-zero.
+     */
+    sysChangeMapAccess(pMap->addr, pMap->length, true, pMap);
+
+    if (dexSwapAndVerifyIfNecessary(pMap->addr, pMap->length)) {
+        fprintf(stderr, "ERROR: Failed structural verification of '%s'\n",
+            fileName);
+        goto bail;
+    }
+
+    /*
+     * Similar to above, this call will fail if the file wasn't ever
+     * read-only to begin with. This is innocuous, though it is
+     * undesirable from a memory hygiene perspective.
+     */
+    sysChangeMapAccess(pMap->addr, pMap->length, false, pMap);
+
+    /*
+     * Success!  Close the file and return with the start/length in pMap.
+     */
+    result = 0;
+
+bail:
+    if (fd >= 0)
+        close(fd);
+    if (removeTemp) {
+        if (unlink(tempFileName) != 0) {
+            fprintf(stderr, "WARNING: unable to remove temp '%s'\n",
+                tempFileName);
+        }
+    }
+    return result;
+}
diff --git a/libdex/CmdUtils.h b/libdex/CmdUtils.h
new file mode 100644
index 0000000..7a85287
--- /dev/null
+++ b/libdex/CmdUtils.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.
+ */
+/*
+ * Access .dex (Dalvik Executable Format) files.  The code here assumes that
+ * the DEX file has been rewritten (byte-swapped, word-aligned) and that
+ * the contents can be directly accessed as a collection of C arrays.  Please
+ * see docs/dalvik/dex-format.html for a detailed description.
+ *
+ * The structure and field names were chosen to match those in the DEX spec.
+ *
+ * It's generally assumed that the DEX file will be stored in shared memory,
+ * obviating the need to copy code and constant pool entries into newly
+ * allocated storage.  Maintaining local pointers to items in the shared area
+ * is valid and encouraged.
+ *
+ * All memory-mapped structures are 32-bit aligned unless otherwise noted.
+ */
+#ifndef _LIBDEX_CMDUTILS
+#define _LIBDEX_CMDUTILS
+
+/* encode the result of unzipping to a file */
+typedef enum UnzipToFileResult {
+    kUTFRSuccess = 0,
+    kUTFRGenericFailure,
+    kUTFRBadArgs,
+    kUTFRNotZip,
+    kUTFRNoClassesDex,
+    kUTFROutputFileProblem,
+    kUTFRBadZip,
+} UnzipToFileResult;
+
+/*
+ * Map the specified DEX file read-only (possibly after expanding it into a
+ * temp file from a Jar).  Pass in a MemMapping struct to hold the info.
+ * If the file is an unoptimized DEX file, then byte-swapping and structural
+ * verification are performed on it before the memory is made read-only.
+ *
+ * The temp file is deleted after the map succeeds.
+ *
+ * This is intended for use by tools (e.g. dexdump) that need to get a
+ * read-only copy of a DEX file that could be in a number of different states.
+ *
+ * If "tempFileName" is NULL, a default value is used.  The temp file is
+ * deleted after the map succeeds.
+ *
+ * If "quiet" is set, don't report common errors.
+ *
+ * Returns 0 (kUTFRSuccess) on success.
+ */
+UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
+    MemMapping* pMap, bool quiet);
+
+/*
+ * Utility function to open a Zip archive, find "classes.dex", and extract
+ * it to a file.
+ */
+UnzipToFileResult dexUnzipToFile(const char* zipFileName,
+    const char* outFileName, bool quiet);
+
+#endif /*_LIBDEX_CMDUTILS*/
diff --git a/libdex/DexCatch.c b/libdex/DexCatch.c
new file mode 100644
index 0000000..ed97e87
--- /dev/null
+++ b/libdex/DexCatch.c
@@ -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.
+ */
+
+/*
+ * Functions for dealing with try-catch info.
+ */
+
+#include "DexCatch.h"
+
+/* Get the first handler offset for the given DexCode.
+ * It's not 0 because the handlers list is prefixed with its size
+ * (in entries) as a uleb128. */
+u4 dexGetFirstHandlerOffset(const DexCode* pCode) {
+    if (pCode->triesSize == 0) {
+        return 0;
+    }
+
+    const u1* baseData = dexGetCatchHandlerData(pCode);
+    const u1* data = baseData;
+
+    readUnsignedLeb128(&data);
+
+    return data - baseData;
+}
+
+/* Get count of handler lists for the given DexCode. */
+u4 dexGetHandlersSize(const DexCode* pCode) {
+    if (pCode->triesSize == 0) {
+        return 0;
+    }
+
+    const u1* data = dexGetCatchHandlerData(pCode);
+
+    return readUnsignedLeb128(&data);
+}
+
+/* Helper for dexFindCatchHandlerOffset(), which does an actual search
+ * in the tries table. Returns -1 if there is no applicable handler. */
+int dexFindCatchHandlerOffset0(u2 triesSize, const DexTry* pTries,
+        u4 address) {
+    // Note: Signed type is important for max and min.
+    int min = 0;
+    int max = triesSize - 1;
+
+    while (max >= min) {
+        int guess = (min + max) >> 1;
+        const DexTry* pTry = &pTries[guess];
+        u4 start = pTry->startAddr;
+
+        if (address < start) {
+            max = guess - 1;
+            continue;
+        }
+
+        u4 end = start + pTry->insnCount;
+
+        if (address >= end) {
+            min = guess + 1;
+            continue;
+        }
+
+        // We have a winner!
+        return (int) pTry->handlerOff;
+    }
+
+    // No match.
+    return -1;
+}
+
+/* Get the handler offset just past the end of the one just iterated over.
+ * This ends the iteration if it wasn't already. */
+u4 dexCatchIteratorGetEndOffset(DexCatchIterator* pIterator,
+        const DexCode* pCode) {
+    while (dexCatchIteratorNext(pIterator) != NULL) /* empty */ ;
+
+    return (u4) (pIterator->pEncodedData - dexGetCatchHandlerData(pCode));
+}
diff --git a/libdex/DexCatch.h b/libdex/DexCatch.h
new file mode 100644
index 0000000..c0eec7c
--- /dev/null
+++ b/libdex/DexCatch.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions for dealing with try-catch info.
+ */
+
+#ifndef _LIBDEX_DEXCATCH
+#define _LIBDEX_DEXCATCH
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/*
+ * Catch handler entry, used while iterating over catch_handler_items.
+ */
+typedef struct DexCatchHandler {
+    u4          typeIdx;    /* type index of the caught exception type */
+    u4          address;    /* handler address */
+} DexCatchHandler;
+
+/* Get the first handler offset for the given DexCode.
+ * It's not 0 because the handlers list is prefixed with its size
+ * (in entries) as a uleb128. */
+u4 dexGetFirstHandlerOffset(const DexCode* pCode);
+
+/* Get count of handler lists for the given DexCode. */
+u4 dexGetHandlersSize(const DexCode* pCode);
+
+/*
+ * Iterator over catch handler data. This structure should be treated as
+ * opaque.
+ */
+typedef struct DexCatchIterator {
+    const u1* pEncodedData;
+    bool catchesAll;
+    u4 countRemaining;
+    DexCatchHandler handler;
+} DexCatchIterator;
+
+/* Initialize a DexCatchIterator to emptiness. This mostly exists to
+ * squelch innocuous warnings. */
+DEX_INLINE void dexCatchIteratorClear(DexCatchIterator* pIterator) {
+    pIterator->pEncodedData = NULL;
+    pIterator->catchesAll = false;
+    pIterator->countRemaining = 0;
+    pIterator->handler.typeIdx = 0;
+    pIterator->handler.address = 0;
+}
+
+/* Initialize a DexCatchIterator with a direct pointer to encoded handlers. */
+DEX_INLINE void dexCatchIteratorInitToPointer(DexCatchIterator* pIterator,
+    const u1* pEncodedData)
+{
+    s4 count = readSignedLeb128(&pEncodedData);
+
+    if (count <= 0) {
+        pIterator->catchesAll = true;
+        count = -count;
+    } else {
+        pIterator->catchesAll = false;
+    }
+
+    pIterator->pEncodedData = pEncodedData;
+    pIterator->countRemaining = count;
+}
+
+/* Initialize a DexCatchIterator to a particular handler offset. */
+DEX_INLINE void dexCatchIteratorInit(DexCatchIterator* pIterator,
+    const DexCode* pCode, u4 offset)
+{
+    dexCatchIteratorInitToPointer(pIterator,
+            dexGetCatchHandlerData(pCode) + offset);
+}
+
+/* Get the next item from a DexCatchIterator. Returns NULL if at end. */
+DEX_INLINE DexCatchHandler* dexCatchIteratorNext(DexCatchIterator* pIterator) {
+    if (pIterator->countRemaining == 0) {
+        if (! pIterator->catchesAll) {
+            return NULL;
+        }
+
+        pIterator->catchesAll = false;
+        pIterator->handler.typeIdx = kDexNoIndex;
+    } else {
+        u4 typeIdx = readUnsignedLeb128(&pIterator->pEncodedData);
+        pIterator->handler.typeIdx = typeIdx;
+        pIterator->countRemaining--;
+    }
+
+    pIterator->handler.address = readUnsignedLeb128(&pIterator->pEncodedData);
+    return &pIterator->handler;
+}
+
+/* Get the handler offset just past the end of the one just iterated over.
+ * This ends the iteration if it wasn't already. */
+u4 dexCatchIteratorGetEndOffset(DexCatchIterator* pIterator,
+    const DexCode* pCode);
+
+/* Helper for dexFindCatchHandler(). Do not call directly. */
+int dexFindCatchHandlerOffset0(u2 triesSize, const DexTry* pTries,
+        u4 address);
+
+/* Find the handler associated with a given address, if any.
+ * Initializes the given iterator and returns true if a match is
+ * found. Returns false if there is no applicable handler. */
+DEX_INLINE bool dexFindCatchHandler(DexCatchIterator *pIterator,
+        const DexCode* pCode, u4 address) {
+    u2 triesSize = pCode->triesSize;
+    int offset = -1;
+
+    // Short-circuit the overwhelmingly common cases.
+    switch (triesSize) {
+        case 0: {
+            break;
+        }
+        case 1: {
+            const DexTry* tries = dexGetTries(pCode);
+            u4 start = tries[0].startAddr;
+
+            if (address < start) {
+                break;
+            }
+
+            u4 end = start + tries[0].insnCount;
+
+            if (address >= end) {
+                break;
+            }
+
+            offset = tries[0].handlerOff;
+            break;
+        }
+        default: {
+            offset = dexFindCatchHandlerOffset0(triesSize, dexGetTries(pCode),
+                    address);
+        }
+    }
+
+    if (offset < 0) {
+        dexCatchIteratorClear(pIterator); // This squelches warnings.
+        return false;
+    } else {
+        dexCatchIteratorInit(pIterator, pCode, offset);
+        return true;
+    }
+}
+
+#endif
diff --git a/libdex/DexClass.c b/libdex/DexClass.c
new file mode 100644
index 0000000..8a59e09
--- /dev/null
+++ b/libdex/DexClass.c
@@ -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.
+ */
+
+/*
+ * Functions to deal with class definition structures in DEX files
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "DexClass.h"
+#include "Leb128.h"
+
+/* Helper for verification which reads and verifies a given number
+ * of uleb128 values. */
+static bool verifyUlebs(const u1* pData, const u1* pLimit, u4 count) {
+    bool okay = true;
+    u4 i;
+
+    while (okay && (count-- != 0)) {
+        readAndVerifyUnsignedLeb128(&pData, pLimit, &okay);
+    }
+
+    return okay;
+}
+
+/* Read and verify the header of a class_data_item. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure). */
+bool dexReadAndVerifyClassDataHeader(const u1** pData, const u1* pLimit,
+        DexClassDataHeader *pHeader) {
+    if (! verifyUlebs(*pData, pLimit, 4)) {
+        return false;
+    }
+
+    dexReadClassDataHeader(pData, pHeader);
+    return true;
+}
+
+/* Read and verify an encoded_field. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags or indices
+ * are valid. */
+bool dexReadAndVerifyClassDataField(const u1** pData, const u1* pLimit,
+        DexField* pField, u4* lastIndex) {
+    if (! verifyUlebs(*pData, pLimit, 2)) {
+        return false;
+    }
+
+    dexReadClassDataField(pData, pField, lastIndex);
+    return true;
+}
+
+/* Read and verify an encoded_method. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+bool dexReadAndVerifyClassDataMethod(const u1** pData, const u1* pLimit,
+        DexMethod* pMethod, u4* lastIndex) {
+    if (! verifyUlebs(*pData, pLimit, 3)) {
+        return false;
+    }
+
+    dexReadClassDataMethod(pData, pMethod, lastIndex);
+    return true;
+}
+
+/* Read, verify, and return an entire class_data_item. This updates
+ * the given data pointer to point past the end of the read data. This
+ * function allocates a single chunk of memory for the result, which
+ * must subsequently be free()d. This function returns NULL if there
+ * was trouble parsing the data. If this function is passed NULL, it
+ * returns an initialized empty DexClassData structure.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+DexClassData* dexReadAndVerifyClassData(const u1** pData, const u1* pLimit) {
+    DexClassDataHeader header;
+    u4 lastIndex;
+
+    if (*pData == NULL) {
+        DexClassData* result = malloc(sizeof(DexClassData));
+        memset(result, 0, sizeof(*result));
+        return result;
+    }
+
+    if (! dexReadAndVerifyClassDataHeader(pData, pLimit, &header)) {
+        return NULL;
+    }
+
+    size_t resultSize = sizeof(DexClassData) +
+        (header.staticFieldsSize * sizeof(DexField)) +
+        (header.instanceFieldsSize * sizeof(DexField)) +
+        (header.directMethodsSize * sizeof(DexMethod)) +
+        (header.virtualMethodsSize * sizeof(DexMethod));
+
+    DexClassData* result = malloc(resultSize);
+    u1* ptr = ((u1*) result) + sizeof(DexClassData);
+    bool okay = true;
+    u4 i;
+
+    if (result == NULL) {
+        return NULL;
+    }
+
+    result->header = header;
+
+    if (header.staticFieldsSize != 0) {
+        result->staticFields = (DexField*) ptr;
+        ptr += header.staticFieldsSize * sizeof(DexField);
+    } else {
+        result->staticFields = NULL;
+    }
+
+    if (header.instanceFieldsSize != 0) {
+        result->instanceFields = (DexField*) ptr;
+        ptr += header.instanceFieldsSize * sizeof(DexField);
+    } else {
+        result->instanceFields = NULL;
+    }
+
+    if (header.directMethodsSize != 0) {
+        result->directMethods = (DexMethod*) ptr;
+        ptr += header.directMethodsSize * sizeof(DexMethod);
+    } else {
+        result->directMethods = NULL;
+    }
+
+    if (header.virtualMethodsSize != 0) {
+        result->virtualMethods = (DexMethod*) ptr;
+    } else {
+        result->virtualMethods = NULL;
+    }
+
+    lastIndex = 0;
+    for (i = 0; okay && (i < header.staticFieldsSize); i++) {
+        okay = dexReadAndVerifyClassDataField(pData, pLimit,
+                &result->staticFields[i], &lastIndex);
+    }
+
+    lastIndex = 0;
+    for (i = 0; okay && (i < header.instanceFieldsSize); i++) {
+        okay = dexReadAndVerifyClassDataField(pData, pLimit,
+                &result->instanceFields[i], &lastIndex);
+    }
+
+    lastIndex = 0;
+    for (i = 0; okay && (i < header.directMethodsSize); i++) {
+        okay = dexReadAndVerifyClassDataMethod(pData, pLimit,
+                &result->directMethods[i], &lastIndex);
+    }
+
+    lastIndex = 0;
+    for (i = 0; okay && (i < header.virtualMethodsSize); i++) {
+        okay = dexReadAndVerifyClassDataMethod(pData, pLimit,
+                &result->virtualMethods[i], &lastIndex);
+    }
+
+    if (! okay) {
+        free(result);
+        return NULL;
+    }
+
+    return result;
+}
diff --git a/libdex/DexClass.h b/libdex/DexClass.h
new file mode 100644
index 0000000..3d1e11b
--- /dev/null
+++ b/libdex/DexClass.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions to deal with class definition structures in DEX files
+ */
+
+#ifndef _LIBDEX_DEXCLASS
+#define _LIBDEX_DEXCLASS
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/* expanded form of a class_data_item header */
+typedef struct DexClassDataHeader {
+    u4 staticFieldsSize;
+    u4 instanceFieldsSize;
+    u4 directMethodsSize;
+    u4 virtualMethodsSize;
+} DexClassDataHeader;
+
+/* expanded form of encoded_field */
+typedef struct DexField {
+    u4 fieldIdx;    /* index to a field_id_item */
+    u4 accessFlags;
+} DexField;
+
+/* expanded form of encoded_method */
+typedef struct DexMethod {
+    u4 methodIdx;    /* index to a method_id_item */
+    u4 accessFlags;
+    u4 codeOff;      /* file offset to a code_item */
+} DexMethod;
+
+/* expanded form of class_data_item. Note: If a particular item is
+ * absent (e.g., no static fields), then the corresponding pointer
+ * is set to NULL. */
+typedef struct DexClassData {
+    DexClassDataHeader header;
+    DexField*          staticFields;
+    DexField*          instanceFields;
+    DexMethod*         directMethods;
+    DexMethod*         virtualMethods;
+} DexClassData;
+
+/* Read and verify the header of a class_data_item. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure). */
+bool dexReadAndVerifyClassDataHeader(const u1** pData, const u1* pLimit,
+        DexClassDataHeader *pHeader);
+
+/* Read and verify an encoded_field. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags or indices
+ * are valid. */
+bool dexReadAndVerifyClassDataField(const u1** pData, const u1* pLimit,
+        DexField* pField, u4* lastIndex);
+
+/* Read and verify an encoded_method. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+bool dexReadAndVerifyClassDataMethod(const u1** pData, const u1* pLimit,
+        DexMethod* pMethod, u4* lastIndex);
+
+/* Read, verify, and return an entire class_data_item. This updates
+ * the given data pointer to point past the end of the read data. This
+ * function allocates a single chunk of memory for the result, which
+ * must subsequently be free()d. This function returns NULL if there
+ * was trouble parsing the data. If this function is passed NULL, it
+ * returns an initialized empty DexClassData structure.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+DexClassData* dexReadAndVerifyClassData(const u1** pData, const u1* pLimit);
+
+/*
+ * Get the DexCode for a DexMethod.  Returns NULL if the class is native
+ * or abstract.
+ */
+DEX_INLINE const DexCode* dexGetCode(const DexFile* pDexFile,
+    const DexMethod* pDexMethod)
+{
+    if (pDexMethod->codeOff == 0)
+        return NULL;
+    return (const DexCode*) (pDexFile->baseAddr + pDexMethod->codeOff);
+}
+
+
+/* Read the header of a class_data_item without verification. This
+ * updates the given data pointer to point past the end of the read
+ * data. */
+DEX_INLINE void dexReadClassDataHeader(const u1** pData,
+        DexClassDataHeader *pHeader) {
+    pHeader->staticFieldsSize = readUnsignedLeb128(pData);
+    pHeader->instanceFieldsSize = readUnsignedLeb128(pData);
+    pHeader->directMethodsSize = readUnsignedLeb128(pData);
+    pHeader->virtualMethodsSize = readUnsignedLeb128(pData);
+}
+
+/* Read an encoded_field without verification. This updates the
+ * given data pointer to point past the end of the read data.
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ */
+DEX_INLINE void dexReadClassDataField(const u1** pData, DexField* pField,
+        u4* lastIndex) {
+    u4 index = *lastIndex + readUnsignedLeb128(pData);
+
+    pField->accessFlags = readUnsignedLeb128(pData);
+    pField->fieldIdx = index;
+    *lastIndex = index;
+}
+
+/* Read an encoded_method without verification. This updates the
+ * given data pointer to point past the end of the read data.
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ */
+DEX_INLINE void dexReadClassDataMethod(const u1** pData, DexMethod* pMethod,
+        u4* lastIndex) {
+    u4 index = *lastIndex + readUnsignedLeb128(pData);
+
+    pMethod->accessFlags = readUnsignedLeb128(pData);
+    pMethod->codeOff = readUnsignedLeb128(pData);
+    pMethod->methodIdx = index;
+    *lastIndex = index;
+}
+
+#endif
diff --git a/libdex/DexDataMap.c b/libdex/DexDataMap.c
new file mode 100644
index 0000000..a9d429e
--- /dev/null
+++ b/libdex/DexDataMap.c
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+/*
+ * Verification-time map of data section items
+ */
+
+#include "DexDataMap.h"
+#include <safe_iop.h>
+#include <stdlib.h>
+
+/*
+ * Allocate and initialize a DexDataMap. Returns NULL on failure.
+ */
+DexDataMap* dexDataMapAlloc(u4 maxCount) {
+    /*
+     * Allocate a single chunk for the DexDataMap per se as well as the
+     * two arrays.
+     */
+    size_t size = 0;
+    DexDataMap* map = NULL;
+
+    /*
+     * Avoiding pulling in safe_iop for safe_iopf.
+     */
+    if (!safe_mul(&size, maxCount, sizeof(u4) + sizeof(u2)) ||
+        !safe_add(&size, size, sizeof(DexDataMap))) {
+      return NULL;
+    }
+
+    map = malloc(size);
+
+    if (map == NULL) {
+        return NULL;
+    }
+
+    map->count = 0;
+    map->max = maxCount;
+    map->offsets = (u4*) (map + 1);
+    map->types = (u2*) (map->offsets + maxCount);
+
+    return map;
+}
+
+/*
+ * Free a DexDataMap.
+ */
+void dexDataMapFree(DexDataMap* map) {
+    /*
+     * Since everything got allocated together, everything can be freed
+     * in one fell swoop. Also, free(NULL) is a nop (per spec), so we
+     * don't have to worry about an explicit test for that.
+     */
+    free(map);
+}
+
+/*
+ * Add a new element to the map. The offset must be greater than the
+ * all previously added offsets.
+ */
+void dexDataMapAdd(DexDataMap* map, u4 offset, u2 type) {
+    assert(map != NULL);
+    assert(map->count < map->max);
+
+    if ((map->count != 0) &&
+            (map->offsets[map->count - 1] >= offset)) {
+        LOGE("Out-of-order data map offset: 0x%x then 0x%x\n",
+                map->offsets[map->count - 1], offset);
+        return;
+    }
+
+    map->offsets[map->count] = offset;
+    map->types[map->count] = type;
+    map->count++;
+}
+
+/*
+ * Get the type associated with the given offset. This returns -1 if
+ * there is no entry for the given offset.
+ */
+int dexDataMapGet(DexDataMap* map, u4 offset) {
+    assert(map != NULL);
+
+    // Note: Signed type is important for max and min.
+    int min = 0;
+    int max = map->count - 1;
+    u4* offsets = map->offsets;
+
+    while (max >= min) {
+        int guessIdx = (min + max) >> 1;
+        u4 guess = offsets[guessIdx];
+
+        if (offset < guess) {
+            max = guessIdx - 1;
+        } else if (offset > guess) {
+            min = guessIdx + 1;
+        } else {
+            // We have a winner!
+            return map->types[guessIdx];
+        }
+    }
+
+    // No match.
+    return -1;
+}
+
+/*
+ * Verify that there is an entry in the map, mapping the given offset to
+ * the given type. This will return true if such an entry exists and
+ * return false as well as log an error if not.
+ */
+bool dexDataMapVerify(DexDataMap* map, u4 offset, u2 type) {
+    int found = dexDataMapGet(map, offset);
+
+    if (found == type) {
+        return true;
+    }
+
+    if (found < 0) {
+        LOGE("No data map entry found @ 0x%x; expected %x\n",
+                offset, type);
+    } else {
+        LOGE("Unexpected data map entry @ 0x%x: expected %x, found %x\n",
+                offset, type, found);
+    }
+
+    return false;
+}
diff --git a/libdex/DexDataMap.h b/libdex/DexDataMap.h
new file mode 100644
index 0000000..fa556d5
--- /dev/null
+++ b/libdex/DexDataMap.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.
+ */
+
+/*
+ * Verification-time map of data section items
+ */
+
+#ifndef _LIBDEX_DEXDATAMAP
+#define _LIBDEX_DEXDATAMAP
+
+#include "DexFile.h"
+
+typedef struct DexDataMap {
+    u4 count;    /* number of items currently in the map */
+    u4 max;      /* maximum number of items that may be held */
+    u4* offsets; /* array of item offsets */
+    u2* types;   /* corresponding array of item types */
+} DexDataMap;
+
+/*
+ * Allocate and initialize a DexDataMap. Returns NULL on failure.
+ */
+DexDataMap* dexDataMapAlloc(u4 maxCount);
+
+/*
+ * Free a DexDataMap.
+ */
+void dexDataMapFree(DexDataMap* map);
+
+/*
+ * Add a new element to the map. The offset must be greater than the
+ * all previously added offsets.
+ */
+void dexDataMapAdd(DexDataMap* map, u4 offset, u2 type);
+
+/*
+ * Get the type associated with the given offset. This returns -1 if
+ * there is no entry for the given offset.
+ */
+int dexDataMapGet(DexDataMap* map, u4 offset);
+
+/*
+ * Verify that there is an entry in the map, mapping the given offset to
+ * the given type. This will return true if such an entry exists and
+ * return false as well as log an error if not.
+ */
+bool dexDataMapVerify(DexDataMap* map, u4 offset, u2 type);
+
+/*
+ * Like dexDataMapVerify(), but also accept a 0 offset as valid.
+ */
+DEX_INLINE bool dexDataMapVerify0Ok(DexDataMap* map, u4 offset, u2 type) {
+    if (offset == 0) {
+        return true;
+    }
+
+    return dexDataMapVerify(map, offset, type);
+}
+
+#endif /*_LIBDEX_DEXDATAMAP*/
diff --git a/libdex/DexFile.c b/libdex/DexFile.c
new file mode 100644
index 0000000..f997b94
--- /dev/null
+++ b/libdex/DexFile.c
@@ -0,0 +1,1032 @@
+/*
+ * 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 .dex file.
+ */
+
+#include "DexFile.h"
+#include "DexOptData.h"
+#include "DexProto.h"
+#include "DexCatch.h"
+#include "Leb128.h"
+#include "sha1.h"
+#include "ZipArchive.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/*
+ * Verifying checksums is good, but it slows things down and causes us to
+ * touch every page.  In the "optimized" world, it doesn't work at all,
+ * because we rewrite the contents.
+ */
+static const bool kVerifyChecksum = false;
+static const bool kVerifySignature = false;
+
+
+/* Compare two '\0'-terminated modified UTF-8 strings, using Unicode
+ * code point values for comparison. This treats different encodings
+ * for the same code point as equivalent, except that only a real '\0'
+ * byte is considered the string terminator. The return value is as
+ * for strcmp(). */
+int dexUtf8Cmp(const char* s1, const char* s2) {
+    for (;;) {
+        if (*s1 == '\0') {
+            if (*s2 == '\0') {
+                return 0;
+            }
+            return -1;
+        } else if (*s2 == '\0') {
+            return 1;
+        }
+
+        int utf1 = dexGetUtf16FromUtf8(&s1);
+        int utf2 = dexGetUtf16FromUtf8(&s2);
+        int diff = utf1 - utf2;
+
+        if (diff != 0) {
+            return diff;
+        }
+    }
+}
+
+/* for dexIsValidMemberNameUtf8(), a bit vector indicating valid low ascii */
+u4 DEX_MEMBER_VALID_LOW_ASCII[4] = {
+    0x00000000, // 00..1f low control characters; nothing valid
+    0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-'
+    0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_'
+    0x07fffffe  // 60..7f lowercase etc.; valid: 'a'..'z'
+};
+
+/* Helper for dexIsValidMemberNameUtf8(); do not call directly. */
+bool dexIsValidMemberNameUtf8_0(const char** pUtf8Ptr) {
+    /*
+     * It's a multibyte encoded character. Decode it and analyze. We
+     * accept anything that isn't (a) an improperly encoded low value,
+     * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high
+     * control character, or (e) a high space, layout, or special
+     * character (U+00a0, U+2000..U+200f, U+2028..U+202f,
+     * U+fff0..U+ffff).
+     */
+
+    u2 utf16 = dexGetUtf16FromUtf8(pUtf8Ptr);
+
+    // Perform follow-up tests based on the high 8 bits.
+    switch (utf16 >> 8) {
+        case 0x00: {
+            // It's only valid if it's above the ISO-8859-1 high space (0xa0).
+            return (utf16 > 0x00a0);
+        }
+        case 0xd8:
+        case 0xd9:
+        case 0xda:
+        case 0xdb: {
+            /*
+             * It's a leading surrogate. Check to see that a trailing
+             * surrogate follows.
+             */
+            utf16 = dexGetUtf16FromUtf8(pUtf8Ptr);
+            return (utf16 >= 0xdc00) && (utf16 <= 0xdfff);
+        }
+        case 0xdc:
+        case 0xdd:
+        case 0xde:
+        case 0xdf: {
+            // It's a trailing surrogate, which is not valid at this point.
+            return false;
+        }
+        case 0x20:
+        case 0xff: {
+            // It's in the range that has spaces, controls, and specials.
+            switch (utf16 & 0xfff8) {
+                case 0x2000:
+                case 0x2008:
+                case 0x2028:
+                case 0xfff0:
+                case 0xfff8: {
+                    return false;
+                }
+            }
+            break;
+        }
+    }
+
+    return true;
+}
+
+/* Return whether the given string is a valid field or method name. */
+bool dexIsValidMemberName(const char* s) {
+    bool angleName = false;
+
+    switch (*s) {
+        case '\0': {
+            // The empty string is not a valid name.
+            return false;
+        }
+        case '<': {
+            /*
+             * '<' is allowed only at the start of a name, and if present,
+             * means that the name must end with '>'.
+             */
+            angleName = true;
+            s++;
+            break;
+        }
+    }
+
+    for (;;) {
+        switch (*s) {
+            case '\0': {
+                return !angleName;
+            }
+            case '>': {
+                return angleName && s[1] == '\0';
+            }
+        }
+        if (!dexIsValidMemberNameUtf8(&s)) {
+            return false;
+        }
+    }
+}
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s) {
+    int arrayCount = 0;
+
+    while (*s == '[') {
+        arrayCount++;
+        s++;
+    }
+
+    if (arrayCount > 255) {
+        // Arrays may have no more than 255 dimensions.
+        return false;
+    }
+
+    switch (*(s++)) {
+        case 'B':
+        case 'C':
+        case 'D':
+        case 'F':
+        case 'I':
+        case 'J':
+        case 'S':
+        case 'Z': {
+            // These are all single-character descriptors for primitive types.
+            return (*s == '\0');
+        }
+        case 'V': {
+            // You can't have an array of void.
+            return (arrayCount == 0) && (*s == '\0');
+        }
+        case 'L': {
+            // Break out and continue below.
+            break;
+        }
+        default: {
+            // Oddball descriptor character.
+            return false;
+        }
+    }
+
+    // We just consumed the 'L' that introduces a class name.
+
+    bool slashOrFirst = true; // first character or just encountered a slash
+    for (;;) {
+        u1 c = (u1) *s;
+        switch (c) {
+            case '\0': {
+                // Premature end.
+                return false;
+            }
+            case ';': {
+                /*
+                 * Make sure that this is the end of the string and that
+                 * it doesn't end with an empty component (including the
+                 * degenerate case of "L;").
+                 */
+                return (s[1] == '\0') && !slashOrFirst;
+            }
+            case '/': {
+                if (slashOrFirst) {
+                    // Slash at start or two slashes in a row.
+                    return false;
+                }
+                slashOrFirst = true;
+                s++;
+                break;
+            }
+            default: {
+                if (!dexIsValidMemberNameUtf8(&s)) {
+                    return false;
+                }
+                slashOrFirst = false;
+                break;
+            }
+        }
+    }
+}
+
+/* Return whether the given string is a valid reference descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class or array and not a primitive type. */
+bool dexIsReferenceDescriptor(const char* s) {
+    if (!dexIsValidTypeDescriptor(s)) {
+        return false;
+    }
+
+    return (s[0] == 'L') || (s[0] == '[');
+}
+
+/* Return whether the given string is a valid class descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class and not an array or primitive type. */
+bool dexIsClassDescriptor(const char* s) {
+    if (!dexIsValidTypeDescriptor(s)) {
+        return false;
+    }
+
+    return s[0] == 'L';
+}
+
+/* Return whether the given string is a valid field type descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for anything but "void". */
+bool dexIsFieldDescriptor(const char* s) {
+    if (!dexIsValidTypeDescriptor(s)) {
+        return false;
+    }
+
+    return s[0] != 'V';
+}
+
+/* Return the UTF-8 encoded string with the specified string_id index,
+ * also filling in the UTF-16 size (number of 16-bit code points).*/
+const char* dexStringAndSizeById(const DexFile* pDexFile, u4 idx,
+        u4* utf16Size) {
+    const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
+    const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;
+
+    *utf16Size = readUnsignedLeb128(&ptr);
+    return (const char*) ptr;
+}
+
+/*
+ * Format an SHA-1 digest for printing.  tmpBuf must be able to hold at
+ * least kSHA1DigestOutputLen bytes.
+ */
+const char* dvmSHA1DigestToStr(const unsigned char digest[], char* tmpBuf);
+
+/*
+ * Compute a SHA-1 digest on a range of bytes.
+ */
+static void dexComputeSHA1Digest(const unsigned char* data, size_t length,
+    unsigned char digest[])
+{
+    SHA1_CTX context;
+    SHA1Init(&context);
+    SHA1Update(&context, data, length);
+    SHA1Final(digest, &context);
+}
+
+/*
+ * Format the SHA-1 digest into the buffer, which must be able to hold at
+ * least kSHA1DigestOutputLen bytes.  Returns a pointer to the buffer,
+ */
+static const char* dexSHA1DigestToStr(const unsigned char digest[],char* tmpBuf)
+{
+    static const char hexDigit[] = "0123456789abcdef";
+    char* cp;
+    int i;
+
+    cp = tmpBuf;
+    for (i = 0; i < kSHA1DigestLen; i++) {
+        *cp++ = hexDigit[digest[i] >> 4];
+        *cp++ = hexDigit[digest[i] & 0x0f];
+    }
+    *cp++ = '\0';
+
+    assert(cp == tmpBuf + kSHA1DigestOutputLen);
+
+    return tmpBuf;
+}
+
+/*
+ * Compute a hash code on a UTF-8 string, for use with internal hash tables.
+ *
+ * This may or may not be compatible with UTF-8 hash functions used inside
+ * the Dalvik VM.
+ *
+ * The basic "multiply by 31 and add" approach does better on class names
+ * than most other things tried (e.g. adler32).
+ */
+static u4 classDescriptorHash(const char* str)
+{
+    u4 hash = 1;
+
+    while (*str != '\0')
+        hash = hash * 31 + *str++;
+
+    return hash;
+}
+
+/*
+ * Add an entry to the class lookup table.  We hash the string and probe
+ * until we find an open slot.
+ */
+static void classLookupAdd(DexFile* pDexFile, DexClassLookup* pLookup,
+    int stringOff, int classDefOff, int* pNumProbes)
+{
+    const char* classDescriptor =
+        (const char*) (pDexFile->baseAddr + stringOff);
+    const DexClassDef* pClassDef =
+        (const DexClassDef*) (pDexFile->baseAddr + classDefOff);
+    u4 hash = classDescriptorHash(classDescriptor);
+    int mask = pLookup->numEntries-1;
+    int idx = hash & mask;
+
+    /*
+     * Find the first empty slot.  We oversized the table, so this is
+     * guaranteed to finish.
+     */
+    int probes = 0;
+    while (pLookup->table[idx].classDescriptorOffset != 0) {
+        idx = (idx + 1) & mask;
+        probes++;
+    }
+    //if (probes > 1)
+    //    LOGW("classLookupAdd: probes=%d\n", probes);
+
+    pLookup->table[idx].classDescriptorHash = hash;
+    pLookup->table[idx].classDescriptorOffset = stringOff;
+    pLookup->table[idx].classDefOffset = classDefOff;
+    *pNumProbes = probes;
+}
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+u4 dexRoundUpPower2(u4 val)
+{
+    val--;
+    val |= val >> 1;
+    val |= val >> 2;
+    val |= val >> 4;
+    val |= val >> 8;
+    val |= val >> 16;
+    val++;
+
+    return val;
+}
+
+/*
+ * Create the class lookup hash table.
+ *
+ * Returns newly-allocated storage.
+ */
+DexClassLookup* dexCreateClassLookup(DexFile* pDexFile)
+{
+    DexClassLookup* pLookup;
+    int allocSize;
+    int i, numEntries;
+    int numProbes, totalProbes, maxProbes;
+
+    numProbes = totalProbes = maxProbes = 0;
+
+    assert(pDexFile != NULL);
+
+    /*
+     * Using a factor of 3 results in far less probing than a factor of 2,
+     * but almost doubles the flash storage requirements for the bootstrap
+     * DEX files.  The overall impact on class loading performance seems
+     * to be minor.  We could probably get some performance improvement by
+     * using a secondary hash.
+     */
+    numEntries = dexRoundUpPower2(pDexFile->pHeader->classDefsSize * 2);
+    allocSize = offsetof(DexClassLookup, table)
+                    + numEntries * sizeof(pLookup->table[0]);
+
+    pLookup = (DexClassLookup*) calloc(1, allocSize);
+    if (pLookup == NULL)
+        return NULL;
+    pLookup->size = allocSize;
+    pLookup->numEntries = numEntries;
+
+    for (i = 0; i < (int)pDexFile->pHeader->classDefsSize; i++) {
+        const DexClassDef* pClassDef;
+        const char* pString;
+
+        pClassDef = dexGetClassDef(pDexFile, i);
+        pString = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+        classLookupAdd(pDexFile, pLookup,
+            (u1*)pString - pDexFile->baseAddr,
+            (u1*)pClassDef - pDexFile->baseAddr, &numProbes);
+
+        if (numProbes > maxProbes)
+            maxProbes = numProbes;
+        totalProbes += numProbes;
+    }
+
+    LOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"
+         " total=%d max=%d\n",
+        pDexFile->pHeader->classDefsSize, numEntries,
+        (100 * pDexFile->pHeader->classDefsSize) / numEntries,
+        allocSize, totalProbes, maxProbes);
+
+    return pLookup;
+}
+
+
+/*
+ * Set up the basic raw data pointers of a DexFile. This function isn't
+ * meant for general use.
+ */
+void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
+    DexHeader *pHeader = (DexHeader*) data;
+
+    pDexFile->baseAddr = data;
+    pDexFile->pHeader = pHeader;
+    pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
+    pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
+    pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
+    pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
+    pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
+    pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
+    pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
+}
+
+/*
+ * Parse an optimized or unoptimized .dex file sitting in memory.  This is
+ * called after the byte-ordering and structure alignment has been fixed up.
+ *
+ * On success, return a newly-allocated DexFile.
+ */
+DexFile* dexFileParse(const u1* data, size_t length, int flags)
+{
+    DexFile* pDexFile = NULL;
+    const DexHeader* pHeader;
+    const u1* magic;
+    int result = -1;
+
+    if (length < sizeof(DexHeader)) {
+        LOGE("too short to be a valid .dex\n");
+        goto bail;      /* bad file format */
+    }
+
+    pDexFile = (DexFile*) malloc(sizeof(DexFile));
+    if (pDexFile == NULL)
+        goto bail;      /* alloc failure */
+    memset(pDexFile, 0, sizeof(DexFile));
+
+    /*
+     * Peel off the optimized header.
+     */
+    if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
+        magic = data;
+        if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+            LOGE("bad opt version (0x%02x %02x %02x %02x)\n",
+                 magic[4], magic[5], magic[6], magic[7]);
+            goto bail;
+        }
+
+        pDexFile->pOptHeader = (const DexOptHeader*) data;
+        LOGV("Good opt header, DEX offset is %d, flags=0x%02x\n",
+            pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
+
+        /* parse the optimized dex file tables */
+        if (!dexParseOptData(data, length, pDexFile))
+            goto bail;
+
+        /* ignore the opt header and appended data from here on out */
+        data += pDexFile->pOptHeader->dexOffset;
+        length -= pDexFile->pOptHeader->dexOffset;
+        if (pDexFile->pOptHeader->dexLength > length) {
+            LOGE("File truncated? stored len=%d, rem len=%d\n",
+                pDexFile->pOptHeader->dexLength, (int) length);
+            goto bail;
+        }
+        length = pDexFile->pOptHeader->dexLength;
+    }
+
+    dexFileSetupBasicPointers(pDexFile, data);
+    pHeader = pDexFile->pHeader;
+
+    magic = pHeader->magic;
+    if (memcmp(magic, DEX_MAGIC, 4) != 0) {
+        /* not expected */
+        LOGE("bad magic number (0x%02x %02x %02x %02x)\n",
+             magic[0], magic[1], magic[2], magic[3]);
+        goto bail;
+    }
+    if (memcmp(magic+4, DEX_MAGIC_VERS, 4) != 0) {
+        LOGE("bad dex version (0x%02x %02x %02x %02x)\n",
+             magic[4], magic[5], magic[6], magic[7]);
+        goto bail;
+    }
+
+    /*
+     * Verify the checksum(s).  This is reasonably quick, but does require
+     * touching every byte in the DEX file.  The base checksum changes after
+     * byte-swapping and DEX optimization.
+     */
+    if (flags & kDexParseVerifyChecksum) {
+        u4 adler = dexComputeChecksum(pHeader);
+        if (adler != pHeader->checksum) {
+            LOGE("ERROR: bad checksum (%08x vs %08x)\n",
+                adler, pHeader->checksum);
+            if (!(flags & kDexParseContinueOnError))
+                goto bail;
+        } else {
+            LOGV("+++ adler32 checksum (%08x) verified\n", adler);
+        }
+
+        const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+        if (pOptHeader != NULL) {
+            adler = dexComputeOptChecksum(pOptHeader);
+            if (adler != pOptHeader->checksum) {
+                LOGE("ERROR: bad opt checksum (%08x vs %08x)\n",
+                    adler, pOptHeader->checksum);
+                if (!(flags & kDexParseContinueOnError))
+                    goto bail;
+            } else {
+                LOGV("+++ adler32 opt checksum (%08x) verified\n", adler);
+            }
+        }
+    }
+
+    /*
+     * Verify the SHA-1 digest.  (Normally we don't want to do this --
+     * the digest is used to uniquely identify the original DEX file, and
+     * can't be computed for verification after the DEX is byte-swapped
+     * and optimized.)
+     */
+    if (kVerifySignature) {
+        unsigned char sha1Digest[kSHA1DigestLen];
+        const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
+                            kSHA1DigestLen;
+
+        dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
+        if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
+            char tmpBuf1[kSHA1DigestOutputLen];
+            char tmpBuf2[kSHA1DigestOutputLen];
+            LOGE("ERROR: bad SHA1 digest (%s vs %s)\n",
+                dexSHA1DigestToStr(sha1Digest, tmpBuf1),
+                dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
+            if (!(flags & kDexParseContinueOnError))
+                goto bail;
+        } else {
+            LOGV("+++ sha1 digest verified\n");
+        }
+    }
+
+    if (pHeader->fileSize != length) {
+        LOGE("ERROR: stored file size (%d) != expected (%d)\n",
+            (int) pHeader->fileSize, (int) length);
+        if (!(flags & kDexParseContinueOnError))
+            goto bail;
+    }
+
+    if (pHeader->classDefsSize == 0) {
+        LOGE("ERROR: DEX file has no classes in it, failing\n");
+        goto bail;
+    }
+
+    /*
+     * Success!
+     */
+    result = 0;
+
+bail:
+    if (result != 0 && pDexFile != NULL) {
+        dexFileFree(pDexFile);
+        pDexFile = NULL;
+    }
+    return pDexFile;
+}
+
+/*
+ * Free up the DexFile and any associated data structures.
+ *
+ * Note we may be called with a partially-initialized DexFile.
+ */
+void dexFileFree(DexFile* pDexFile)
+{
+    if (pDexFile == NULL)
+        return;
+
+    free(pDexFile);
+}
+
+/*
+ * Look up a class definition entry by descriptor.
+ *
+ * "descriptor" should look like "Landroid/debug/Stuff;".
+ */
+const DexClassDef* dexFindClass(const DexFile* pDexFile,
+    const char* descriptor)
+{
+    const DexClassLookup* pLookup = pDexFile->pClassLookup;
+    u4 hash;
+    int idx, mask;
+
+    hash = classDescriptorHash(descriptor);
+    mask = pLookup->numEntries - 1;
+    idx = hash & mask;
+
+    /*
+     * Search until we find a matching entry or an empty slot.
+     */
+    while (true) {
+        int offset;
+
+        offset = pLookup->table[idx].classDescriptorOffset;
+        if (offset == 0)
+            return NULL;
+
+        if (pLookup->table[idx].classDescriptorHash == hash) {
+            const char* str;
+
+            str = (const char*) (pDexFile->baseAddr + offset);
+            if (strcmp(str, descriptor) == 0) {
+                return (const DexClassDef*)
+                    (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
+            }
+        }
+
+        idx = (idx + 1) & mask;
+    }
+}
+
+
+/*
+ * Compute the DEX file checksum for a memory-mapped DEX file.
+ */
+u4 dexComputeChecksum(const DexHeader* pHeader)
+{
+    const u1* start = (const u1*) pHeader;
+
+    uLong adler = adler32(0L, Z_NULL, 0);
+    const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+    return (u4) adler32(adler, start + nonSum, pHeader->fileSize - nonSum);
+}
+
+/*
+ * Compute the size, in bytes, of a DexCode.
+ */
+size_t dexGetDexCodeSize(const DexCode* pCode)
+{
+    /*
+     * The catch handler data is the last entry.  It has a variable number
+     * of variable-size pieces, so we need to create an iterator.
+     */
+    u4 handlersSize;
+    u4 offset;
+    u4 ui;
+
+    if (pCode->triesSize != 0) {
+        handlersSize = dexGetHandlersSize(pCode);
+        offset = dexGetFirstHandlerOffset(pCode);
+    } else {
+        handlersSize = 0;
+        offset = 0;
+    }
+
+    for (ui = 0; ui < handlersSize; ui++) {
+        DexCatchIterator iterator;
+        dexCatchIteratorInit(&iterator, pCode, offset);
+        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+    }
+
+    const u1* handlerData = dexGetCatchHandlerData(pCode);
+
+    //LOGD("+++ pCode=%p handlerData=%p last offset=%d\n",
+    //    pCode, handlerData, offset);
+
+    /* return the size of the catch handler + everything before it */
+    return (handlerData - (u1*) pCode) + offset;
+}
+
+
+/*
+ * ===========================================================================
+ *      Debug info
+ * ===========================================================================
+ */
+
+/*
+ * Decode the arguments in a method signature, which looks something
+ * like "(ID[Ljava/lang/String;)V".
+ *
+ * Returns the type signature letter for the next argument, or ')' if
+ * there are no more args.  Advances "pSig" to point to the character
+ * after the one returned.
+ */
+static char decodeSignature(const char** pSig)
+{
+    const char* sig = *pSig;
+
+    if (*sig == '(')
+        sig++;
+
+    if (*sig == 'L') {
+        /* object ref */
+        while (*++sig != ';')
+            ;
+        *pSig = sig+1;
+        return 'L';
+    }
+    if (*sig == '[') {
+        /* array; advance past array type */
+        while (*++sig == '[')
+            ;
+        if (*sig == 'L') {
+            while (*++sig != ';')
+                ;
+        }
+        *pSig = sig+1;
+        return '[';
+    }
+    if (*sig == '\0')
+        return *sig;        /* don't advance further */
+
+    *pSig = sig+1;
+    return *sig;
+}
+
+/*
+ * returns the length of a type string, given the start of the
+ * type string. Used for the case where the debug info format
+ * references types that are inside a method type signature.
+ */
+static int typeLength (const char *type) {
+    // Assumes any leading '(' has already been gobbled
+    const char *end = type;
+    decodeSignature(&end);
+    return end - type;
+}
+
+/*
+ * Reads a string index as encoded for the debug info format,
+ * returning a string pointer or NULL as appropriate.
+ */
+static const char* readStringIdx(const DexFile* pDexFile,
+        const u1** pStream) {
+    u4 stringIdx = readUnsignedLeb128(pStream);
+
+    // Remember, encoded string indicies have 1 added to them.
+    if (stringIdx == 0) {
+        return NULL;
+    } else {
+        return dexStringById(pDexFile, stringIdx - 1);
+    }
+}
+
+/*
+ * Reads a type index as encoded for the debug info format, returning
+ * a string pointer for its descriptor or NULL as appropriate.
+ */
+static const char* readTypeIdx(const DexFile* pDexFile,
+        const u1** pStream) {
+    u4 typeIdx = readUnsignedLeb128(pStream);
+
+    // Remember, encoded type indicies have 1 added to them.
+    if (typeIdx == 0) {
+        return NULL;
+    } else {
+        return dexStringByTypeIdx(pDexFile, typeIdx - 1);
+    }
+}
+
+/* access_flag value indicating that a method is static */
+#define ACC_STATIC              0x0008
+
+typedef struct LocalInfo {
+    const char *name;
+    const char *descriptor;
+    const char *signature;
+    u2 startAddress;
+    bool live;
+} LocalInfo;
+
+static void emitLocalCbIfLive (void *cnxt, int reg, u4 endAddress,
+        LocalInfo *localInReg, DexDebugNewLocalCb localCb)
+{
+    if (localCb != NULL && localInReg[reg].live) {
+        localCb(cnxt, reg, localInReg[reg].startAddress, endAddress,
+                localInReg[reg].name,
+                localInReg[reg].descriptor,
+                localInReg[reg].signature == NULL
+                ? "" : localInReg[reg].signature );
+    }
+}
+
+// TODO optimize localCb == NULL case
+void dexDecodeDebugInfo(
+            const DexFile* pDexFile,
+            const DexCode* pCode,
+            const char* classDescriptor,
+            u4 protoIdx,
+            u4 accessFlags,
+            DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+            void* cnxt)
+{
+    const u1 *stream = dexGetDebugInfoStream(pDexFile, pCode);
+    u4 line;
+    u4 parametersSize;
+    u4 address = 0;
+    LocalInfo localInReg[pCode->registersSize];
+    u4 insnsSize = pCode->insnsSize;
+    DexProto proto = { pDexFile, protoIdx };
+
+    memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
+
+    if (stream == NULL) {
+        goto end;
+    }
+
+    line = readUnsignedLeb128(&stream);
+    parametersSize = readUnsignedLeb128(&stream);
+
+    u2 argReg = pCode->registersSize - pCode->insSize;
+
+    if ((accessFlags & ACC_STATIC) == 0) {
+        /*
+         * The code is an instance method, which means that there is
+         * an initial this parameter. Also, the proto list should
+         * contain exactly one fewer argument word than the insSize
+         * indicates.
+         */
+        assert(pCode->insSize == (dexProtoComputeArgsSize(&proto) + 1));
+        localInReg[argReg].name = "this";
+        localInReg[argReg].descriptor = classDescriptor;
+        localInReg[argReg].startAddress = 0;
+        localInReg[argReg].live = true;
+        argReg++;
+    } else {
+        assert(pCode->insSize == dexProtoComputeArgsSize(&proto));
+    }
+
+    DexParameterIterator iterator;
+    dexParameterIteratorInit(&iterator, &proto);
+
+    while (parametersSize-- != 0) {
+        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+        const char *name;
+        int reg;
+
+        if ((argReg >= pCode->registersSize) || (descriptor == NULL)) {
+            goto invalid_stream;
+        }
+
+        name = readStringIdx(pDexFile, &stream);
+        reg = argReg;
+
+        switch (descriptor[0]) {
+            case 'D':
+            case 'J':
+                argReg += 2;
+                break;
+            default:
+                argReg += 1;
+                break;
+        }
+
+        if (name != NULL) {
+            localInReg[reg].name = name;
+            localInReg[reg].descriptor = descriptor;
+            localInReg[reg].signature = NULL;
+            localInReg[reg].startAddress = address;
+            localInReg[reg].live = true;
+        }
+    }
+
+    for (;;)  {
+        u1 opcode = *stream++;
+        u2 reg;
+
+        switch (opcode) {
+            case DBG_END_SEQUENCE:
+                goto end;
+
+            case DBG_ADVANCE_PC:
+                address += readUnsignedLeb128(&stream);
+                break;
+
+            case DBG_ADVANCE_LINE:
+                line += readSignedLeb128(&stream);
+                break;
+
+            case DBG_START_LOCAL:
+            case DBG_START_LOCAL_EXTENDED:
+                reg = readUnsignedLeb128(&stream);
+                if (reg > pCode->registersSize) goto invalid_stream;
+
+                // Emit what was previously there, if anything
+                emitLocalCbIfLive (cnxt, reg, address,
+                    localInReg, localCb);
+
+                localInReg[reg].name = readStringIdx(pDexFile, &stream);
+                localInReg[reg].descriptor = readTypeIdx(pDexFile, &stream);
+                if (opcode == DBG_START_LOCAL_EXTENDED) {
+                    localInReg[reg].signature
+                        = readStringIdx(pDexFile, &stream);
+                } else {
+                    localInReg[reg].signature = NULL;
+                }
+                localInReg[reg].startAddress = address;
+                localInReg[reg].live = true;
+                break;
+
+            case DBG_END_LOCAL:
+                reg = readUnsignedLeb128(&stream);
+                if (reg > pCode->registersSize) goto invalid_stream;
+
+                emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
+                localInReg[reg].live = false;
+                break;
+
+            case DBG_RESTART_LOCAL:
+                reg = readUnsignedLeb128(&stream);
+                if (reg > pCode->registersSize) goto invalid_stream;
+
+                if (localInReg[reg].name == NULL
+                        || localInReg[reg].descriptor == NULL) {
+                    goto invalid_stream;
+                }
+
+                /*
+                 * If the register is live, the "restart" is superfluous,
+                 * and we don't want to mess with the existing start address.
+                 */
+                if (!localInReg[reg].live) {
+                    localInReg[reg].startAddress = address;
+                    localInReg[reg].live = true;
+                }
+                break;
+
+            case DBG_SET_PROLOGUE_END:
+            case DBG_SET_EPILOGUE_BEGIN:
+            case DBG_SET_FILE:
+                break;
+
+            default: {
+                int adjopcode = opcode - DBG_FIRST_SPECIAL;
+
+                address += adjopcode / DBG_LINE_RANGE;
+                line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+
+                if (posCb != NULL) {
+                    int done;
+                    done = posCb(cnxt, address, line);
+
+                    if (done) {
+                        // early exit
+                        goto end;
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+end:
+    {
+        int reg;
+        for (reg = 0; reg < pCode->registersSize; reg++) {
+            emitLocalCbIfLive (cnxt, reg, insnsSize, localInReg, localCb);
+        }
+    }
+    return;
+
+invalid_stream:
+    IF_LOGE() {
+        char* methodDescriptor = dexProtoCopyMethodDescriptor(&proto);
+        LOGE("Invalid debug info stream. class %s; proto %s",
+                classDescriptor, methodDescriptor);
+        free(methodDescriptor);
+    }
+}
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
new file mode 100644
index 0000000..06dc864
--- /dev/null
+++ b/libdex/DexFile.h
@@ -0,0 +1,1056 @@
+/*
+ * 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 .dex (Dalvik Executable Format) files.  The code here assumes that
+ * the DEX file has been rewritten (byte-swapped, word-aligned) and that
+ * the contents can be directly accessed as a collection of C arrays.  Please
+ * see docs/dalvik/dex-format.html for a detailed description.
+ *
+ * The structure and field names were chosen to match those in the DEX spec.
+ *
+ * It's generally assumed that the DEX file will be stored in shared memory,
+ * obviating the need to copy code and constant pool entries into newly
+ * allocated storage.  Maintaining local pointers to items in the shared area
+ * is valid and encouraged.
+ *
+ * All memory-mapped structures are 32-bit aligned unless otherwise noted.
+ */
+
+#ifndef _LIBDEX_DEXFILE
+#define _LIBDEX_DEXFILE
+
+#include "vm/Common.h"      // basic type defs, e.g. u1/u2/u4/u8, and LOG
+#include "libdex/SysUtil.h"
+
+/*
+ * gcc-style inline management -- ensures we have a copy of all functions
+ * in the library, so code that links against us will work whether or not
+ * it was built with optimizations enabled.
+ */
+#ifndef _DEX_GEN_INLINES             /* only defined by DexInlines.c */
+# define DEX_INLINE extern __inline__
+#else
+# define DEX_INLINE
+#endif
+
+/* DEX file magic number */
+#define DEX_MAGIC       "dex\n"
+/* version, encoded in 4 bytes of ASCII */
+#define DEX_MAGIC_VERS  "035\0"
+
+/* same, but for optimized DEX header */
+#define DEX_OPT_MAGIC   "dey\n"
+#define DEX_OPT_MAGIC_VERS  "036\0"
+
+#define DEX_DEP_MAGIC   "deps"
+
+/*
+ * 160-bit SHA-1 digest.
+ */
+enum { kSHA1DigestLen = 20,
+       kSHA1DigestOutputLen = kSHA1DigestLen*2 +1 };
+
+/* general constants */
+enum {
+    kDexEndianConstant = 0x12345678,    /* the endianness indicator */
+    kDexNoIndex = 0xffffffff,           /* not a valid index value */
+};
+
+/*
+ * access flags and masks; the "standard" ones are all <= 0x4000
+ *
+ * Note: There are related declarations in vm/oo/Object.h in the ClassFlags
+ * enum.
+ */
+enum {
+    ACC_PUBLIC       = 0x00000001,       // class, field, method, ic
+    ACC_PRIVATE      = 0x00000002,       // field, method, ic
+    ACC_PROTECTED    = 0x00000004,       // field, method, ic
+    ACC_STATIC       = 0x00000008,       // field, method, ic
+    ACC_FINAL        = 0x00000010,       // class, field, method, ic
+    ACC_SYNCHRONIZED = 0x00000020,       // method (only allowed on natives)
+    ACC_SUPER        = 0x00000020,       // class (not used in Dalvik)
+    ACC_VOLATILE     = 0x00000040,       // field
+    ACC_BRIDGE       = 0x00000040,       // method (1.5)
+    ACC_TRANSIENT    = 0x00000080,       // field
+    ACC_VARARGS      = 0x00000080,       // method (1.5)
+    ACC_NATIVE       = 0x00000100,       // method
+    ACC_INTERFACE    = 0x00000200,       // class, ic
+    ACC_ABSTRACT     = 0x00000400,       // class, method, ic
+    ACC_STRICT       = 0x00000800,       // method
+    ACC_SYNTHETIC    = 0x00001000,       // field, method, ic
+    ACC_ANNOTATION   = 0x00002000,       // class, ic (1.5)
+    ACC_ENUM         = 0x00004000,       // class, field, ic (1.5)
+    ACC_CONSTRUCTOR  = 0x00010000,       // method (Dalvik only)
+    ACC_DECLARED_SYNCHRONIZED =
+                       0x00020000,       // method (Dalvik only)
+    ACC_CLASS_MASK =
+        (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
+                | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
+    ACC_INNER_CLASS_MASK =
+        (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC),
+    ACC_FIELD_MASK =
+        (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
+                | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM),
+    ACC_METHOD_MASK =
+        (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
+                | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE
+                | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR
+                | ACC_DECLARED_SYNCHRONIZED),
+};
+
+/* annotation constants */
+enum {
+    kDexVisibilityBuild         = 0x00,     /* annotation visibility */
+    kDexVisibilityRuntime       = 0x01,
+    kDexVisibilitySystem        = 0x02,
+
+    kDexAnnotationByte          = 0x00,
+    kDexAnnotationShort         = 0x02,
+    kDexAnnotationChar          = 0x03,
+    kDexAnnotationInt           = 0x04,
+    kDexAnnotationLong          = 0x06,
+    kDexAnnotationFloat         = 0x10,
+    kDexAnnotationDouble        = 0x11,
+    kDexAnnotationString        = 0x17,
+    kDexAnnotationType          = 0x18,
+    kDexAnnotationField         = 0x19,
+    kDexAnnotationMethod        = 0x1a,
+    kDexAnnotationEnum          = 0x1b,
+    kDexAnnotationArray         = 0x1c,
+    kDexAnnotationAnnotation    = 0x1d,
+    kDexAnnotationNull          = 0x1e,
+    kDexAnnotationBoolean       = 0x1f,
+
+    kDexAnnotationValueTypeMask = 0x1f,     /* low 5 bits */
+    kDexAnnotationValueArgShift = 5,
+};
+
+/* map item type codes */
+enum {
+    kDexTypeHeaderItem               = 0x0000,
+    kDexTypeStringIdItem             = 0x0001,
+    kDexTypeTypeIdItem               = 0x0002,
+    kDexTypeProtoIdItem              = 0x0003,
+    kDexTypeFieldIdItem              = 0x0004,
+    kDexTypeMethodIdItem             = 0x0005,
+    kDexTypeClassDefItem             = 0x0006,
+    kDexTypeMapList                  = 0x1000,
+    kDexTypeTypeList                 = 0x1001,
+    kDexTypeAnnotationSetRefList     = 0x1002,
+    kDexTypeAnnotationSetItem        = 0x1003,
+    kDexTypeClassDataItem            = 0x2000,
+    kDexTypeCodeItem                 = 0x2001,
+    kDexTypeStringDataItem           = 0x2002,
+    kDexTypeDebugInfoItem            = 0x2003,
+    kDexTypeAnnotationItem           = 0x2004,
+    kDexTypeEncodedArrayItem         = 0x2005,
+    kDexTypeAnnotationsDirectoryItem = 0x2006,
+};
+
+/* auxillary data section chunk codes */
+enum {
+    kDexChunkClassLookup            = 0x434c4b50,   /* CLKP */
+    kDexChunkRegisterMaps           = 0x524d4150,   /* RMAP */
+
+    kDexChunkEnd                    = 0x41454e44,   /* AEND */
+};
+
+/* debug info opcodes and constants */
+enum {
+    DBG_END_SEQUENCE         = 0x00,
+    DBG_ADVANCE_PC           = 0x01,
+    DBG_ADVANCE_LINE         = 0x02,
+    DBG_START_LOCAL          = 0x03,
+    DBG_START_LOCAL_EXTENDED = 0x04,
+    DBG_END_LOCAL            = 0x05,
+    DBG_RESTART_LOCAL        = 0x06,
+    DBG_SET_PROLOGUE_END     = 0x07,
+    DBG_SET_EPILOGUE_BEGIN   = 0x08,
+    DBG_SET_FILE             = 0x09,
+    DBG_FIRST_SPECIAL        = 0x0a,
+    DBG_LINE_BASE            = -4,
+    DBG_LINE_RANGE           = 15,
+};
+
+/*
+ * Direct-mapped "header_item" struct.
+ */
+typedef struct DexHeader {
+    u1  magic[8];           /* includes version number */
+    u4  checksum;           /* adler32 checksum */
+    u1  signature[kSHA1DigestLen]; /* SHA-1 hash */
+    u4  fileSize;           /* length of entire file */
+    u4  headerSize;         /* offset to start of next section */
+    u4  endianTag;
+    u4  linkSize;
+    u4  linkOff;
+    u4  mapOff;
+    u4  stringIdsSize;
+    u4  stringIdsOff;
+    u4  typeIdsSize;
+    u4  typeIdsOff;
+    u4  protoIdsSize;
+    u4  protoIdsOff;
+    u4  fieldIdsSize;
+    u4  fieldIdsOff;
+    u4  methodIdsSize;
+    u4  methodIdsOff;
+    u4  classDefsSize;
+    u4  classDefsOff;
+    u4  dataSize;
+    u4  dataOff;
+} DexHeader;
+
+/*
+ * Direct-mapped "map_item".
+ */
+typedef struct DexMapItem {
+    u2  type;              /* type code (see kDexType* above) */
+    u2  unused;
+    u4  size;              /* count of items of the indicated type */
+    u4  offset;            /* file offset to the start of data */
+} DexMapItem;
+
+/*
+ * Direct-mapped "map_list".
+ */
+typedef struct DexMapList {
+    u4  size;               /* #of entries in list */
+    DexMapItem list[1];     /* entries */
+} DexMapList;
+
+/*
+ * Direct-mapped "string_id_item".
+ */
+typedef struct DexStringId {
+    u4  stringDataOff;      /* file offset to string_data_item */
+} DexStringId;
+
+/*
+ * Direct-mapped "type_id_item".
+ */
+typedef struct DexTypeId {
+    u4  descriptorIdx;      /* index into stringIds list for type descriptor */
+} DexTypeId;
+
+/*
+ * Direct-mapped "field_id_item".
+ */
+typedef struct DexFieldId {
+    u2  classIdx;           /* index into typeIds list for defining class */
+    u2  typeIdx;            /* index into typeIds for field type */
+    u4  nameIdx;            /* index into stringIds for field name */
+} DexFieldId;
+
+/*
+ * Direct-mapped "method_id_item".
+ */
+typedef struct DexMethodId {
+    u2  classIdx;           /* index into typeIds list for defining class */
+    u2  protoIdx;           /* index into protoIds for method prototype */
+    u4  nameIdx;            /* index into stringIds for method name */
+} DexMethodId;
+
+/*
+ * Direct-mapped "proto_id_item".
+ */
+typedef struct DexProtoId {
+    u4  shortyIdx;          /* index into stringIds for shorty descriptor */
+    u4  returnTypeIdx;      /* index into typeIds list for return type */
+    u4  parametersOff;      /* file offset to type_list for parameter types */
+} DexProtoId;
+
+/*
+ * Direct-mapped "class_def_item".
+ */
+typedef struct DexClassDef {
+    u4  classIdx;           /* index into typeIds for this class */
+    u4  accessFlags;
+    u4  superclassIdx;      /* index into typeIds for superclass */
+    u4  interfacesOff;      /* file offset to DexTypeList */
+    u4  sourceFileIdx;      /* index into stringIds for source file name */
+    u4  annotationsOff;     /* file offset to annotations_directory_item */
+    u4  classDataOff;       /* file offset to class_data_item */
+    u4  staticValuesOff;    /* file offset to DexEncodedArray */
+} DexClassDef;
+
+/*
+ * Direct-mapped "type_item".
+ */
+typedef struct DexTypeItem {
+    u2  typeIdx;            /* index into typeIds */
+} DexTypeItem;
+
+/*
+ * Direct-mapped "type_list".
+ */
+typedef struct DexTypeList {
+    u4  size;               /* #of entries in list */
+    DexTypeItem list[1];    /* entries */
+} DexTypeList;
+
+/*
+ * Direct-mapped "code_item".
+ *
+ * The "catches" table is used when throwing an exception,
+ * "debugInfo" is used when displaying an exception stack trace or
+ * debugging. An offset of zero indicates that there are no entries.
+ */
+typedef struct DexCode {
+    u2  registersSize;
+    u2  insSize;
+    u2  outsSize;
+    u2  triesSize;
+    u4  debugInfoOff;       /* file offset to debug info stream */
+    u4  insnsSize;          /* size of the insns array, in u2 units */
+    u2  insns[1];
+    /* followed by optional u2 padding */
+    /* followed by try_item[triesSize] */
+    /* followed by uleb128 handlersSize */
+    /* followed by catch_handler_item[handlersSize] */
+} DexCode;
+
+/*
+ * Direct-mapped "try_item".
+ */
+typedef struct DexTry {
+    u4  startAddr;          /* start address, in 16-bit code units */
+    u2  insnCount;          /* instruction count, in 16-bit code units */
+    u2  handlerOff;         /* offset in encoded handler data to handlers */
+} DexTry;
+
+/*
+ * Link table.  Currently undefined.
+ */
+typedef struct DexLink {
+    u1  bleargh;
+} DexLink;
+
+
+/*
+ * Direct-mapped "annotations_directory_item".
+ */
+typedef struct DexAnnotationsDirectoryItem {
+    u4  classAnnotationsOff;  /* offset to DexAnnotationSetItem */
+    u4  fieldsSize;           /* count of DexFieldAnnotationsItem */
+    u4  methodsSize;          /* count of DexMethodAnnotationsItem */
+    u4  parametersSize;       /* count of DexParameterAnnotationsItem */
+    /* followed by DexFieldAnnotationsItem[fieldsSize] */
+    /* followed by DexMethodAnnotationsItem[methodsSize] */
+    /* followed by DexParameterAnnotationsItem[parametersSize] */
+} DexAnnotationsDirectoryItem;
+
+/*
+ * Direct-mapped "field_annotations_item".
+ */
+typedef struct DexFieldAnnotationsItem {
+    u4  fieldIdx;
+    u4  annotationsOff;             /* offset to DexAnnotationSetItem */
+} DexFieldAnnotationsItem;
+
+/*
+ * Direct-mapped "method_annotations_item".
+ */
+typedef struct DexMethodAnnotationsItem {
+    u4  methodIdx;
+    u4  annotationsOff;             /* offset to DexAnnotationSetItem */
+} DexMethodAnnotationsItem;
+
+/*
+ * Direct-mapped "parameter_annotations_item".
+ */
+typedef struct DexParameterAnnotationsItem {
+    u4  methodIdx;
+    u4  annotationsOff;             /* offset to DexAnotationSetRefList */
+} DexParameterAnnotationsItem;
+
+/*
+ * Direct-mapped "annotation_set_ref_item".
+ */
+typedef struct DexAnnotationSetRefItem {
+    u4  annotationsOff;             /* offset to DexAnnotationSetItem */
+} DexAnnotationSetRefItem;
+
+/*
+ * Direct-mapped "annotation_set_ref_list".
+ */
+typedef struct DexAnnotationSetRefList {
+    u4  size;
+    DexAnnotationSetRefItem list[1];
+} DexAnnotationSetRefList;
+
+/*
+ * Direct-mapped "anotation_set_item".
+ */
+typedef struct DexAnnotationSetItem {
+    u4  size;
+    u4  entries[1];                 /* offset to DexAnnotationItem */
+} DexAnnotationSetItem;
+
+/*
+ * Direct-mapped "annotation_item".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+typedef struct DexAnnotationItem {
+    u1  visibility;
+    u1  annotation[1];              /* data in encoded_annotation format */
+} DexAnnotationItem;
+
+/*
+ * Direct-mapped "encoded_array".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+typedef struct DexEncodedArray {
+    u1  array[1];                   /* data in encoded_array format */
+} DexEncodedArray;
+
+/*
+ * Lookup table for classes.  It provides a mapping from class name to
+ * class definition.  Used by dexFindClass().
+ *
+ * We calculate this at DEX optimization time and embed it in the file so we
+ * don't need the same hash table in every VM.  This is slightly slower than
+ * a hash table with direct pointers to the items, but because it's shared
+ * there's less of a penalty for using a fairly sparse table.
+ */
+typedef struct DexClassLookup {
+    int     size;                       // total size, including "size"
+    int     numEntries;                 // size of table[]; always power of 2
+    struct {
+        u4      classDescriptorHash;    // class descriptor hash code
+        int     classDescriptorOffset;  // in bytes, from start of DEX
+        int     classDefOffset;         // in bytes, from start of DEX
+    } table[1];
+} DexClassLookup;
+
+/*
+ * Header added by DEX optimization pass.  Values are always written in
+ * local byte and structure padding.  The first field (magic + version)
+ * is guaranteed to be present and directly readable for all expected
+ * compiler configurations; the rest is version-dependent.
+ *
+ * Try to keep this simple and fixed-size.
+ */
+typedef struct DexOptHeader {
+    u1  magic[8];           /* includes version number */
+
+    u4  dexOffset;          /* file offset of DEX header */
+    u4  dexLength;
+    u4  depsOffset;         /* offset of optimized DEX dependency table */
+    u4  depsLength;
+    u4  optOffset;          /* file offset of optimized data tables */
+    u4  optLength;
+
+    u4  flags;              /* some info flags */
+    u4  checksum;           /* adler32 checksum covering deps/opt */
+
+    /* pad for 64-bit alignment if necessary */
+} DexOptHeader;
+
+#define DEX_FLAG_VERIFIED           (1)     /* tried to verify all classes */
+#define DEX_OPT_FLAG_BIG            (1<<1)  /* swapped to big-endian */
+#define DEX_OPT_FLAG_FIELDS         (1<<2)  /* field access optimized */
+#define DEX_OPT_FLAG_INVOCATIONS    (1<<3)  /* method calls optimized */
+
+#define DEX_INTERFACE_CACHE_SIZE    128     /* must be power of 2 */
+
+/*
+ * Structure representing a DEX file.
+ *
+ * Code should regard DexFile as opaque, using the API calls provided here
+ * to access specific structures.
+ */
+typedef struct DexFile {
+    /* directly-mapped "opt" header */
+    const DexOptHeader* pOptHeader;
+
+    /* pointers to directly-mapped structs and arrays in base DEX */
+    const DexHeader*    pHeader;
+    const DexStringId*  pStringIds;
+    const DexTypeId*    pTypeIds;
+    const DexFieldId*   pFieldIds;
+    const DexMethodId*  pMethodIds;
+    const DexProtoId*   pProtoIds;
+    const DexClassDef*  pClassDefs;
+    const DexLink*      pLinkData;
+
+    /*
+     * These are mapped out of the "auxillary" section, and may not be
+     * included in the file.
+     */
+    const DexClassLookup* pClassLookup;
+    const void*         pRegisterMapPool;       // RegisterMapClassPool
+
+    /* points to start of DEX file data */
+    const u1*           baseAddr;
+
+    /* track memory overhead for auxillary structures */
+    int                 overhead;
+
+    /* additional app-specific data structures associated with the DEX */
+    //void*               auxData;
+} DexFile;
+
+/*
+ * Utility function -- rounds up to the nearest power of 2.
+ */
+u4 dexRoundUpPower2(u4 val);
+
+/*
+ * Parse an optimized or unoptimized .dex file sitting in memory.
+ *
+ * On success, return a newly-allocated DexFile.
+ */
+DexFile* dexFileParse(const u1* data, size_t length, int flags);
+
+/* bit values for "flags" argument to dexFileParse */
+enum {
+    kDexParseDefault            = 0,
+    kDexParseVerifyChecksum     = 1,
+    kDexParseContinueOnError    = (1 << 1),
+};
+
+/*
+ * Fix the byte ordering of all fields in the DEX file, and do
+ * structural verification. This is only required for code that opens
+ * "raw" DEX files, such as the DEX optimizer.
+ *
+ * Return 0 on success.
+ */
+int dexSwapAndVerify(u1* addr, int len);
+
+/*
+ * Detect the file type of the given memory buffer via magic number.
+ * Call dexSwapAndVerify() on an unoptimized DEX file, do nothing
+ * but return successfully on an optimized DEX file, and report an
+ * error for all other cases.
+ *
+ * Return 0 on success.
+ */
+int dexSwapAndVerifyIfNecessary(u1* addr, int len);
+
+/*
+ * Compute DEX checksum.
+ */
+u4 dexComputeChecksum(const DexHeader* pHeader);
+
+/*
+ * Free a DexFile structure, along with any associated structures.
+ */
+void dexFileFree(DexFile* pDexFile);
+
+/*
+ * Create class lookup table.
+ */
+DexClassLookup* dexCreateClassLookup(DexFile* pDexFile);
+
+/*
+ * Find a class definition by descriptor.
+ */
+const DexClassDef* dexFindClass(const DexFile* pFile, const char* descriptor);
+
+/*
+ * Set up the basic raw data pointers of a DexFile. This function isn't
+ * meant for general use.
+ */
+void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data);
+
+/* return the DexMapList of the file, if any */
+DEX_INLINE const DexMapList* dexGetMap(const DexFile* pDexFile) {
+    u4 mapOff = pDexFile->pHeader->mapOff;
+
+    if (mapOff == 0) {
+        return NULL;
+    } else {
+        return (const DexMapList*) (pDexFile->baseAddr + mapOff);
+    }
+}
+
+/* return the const char* string data referred to by the given string_id */
+DEX_INLINE const char* dexGetStringData(const DexFile* pDexFile,
+        const DexStringId* pStringId) {
+    const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;
+
+    // Skip the uleb128 length.
+    while (*(ptr++) > 0x7f) /* empty */ ;
+
+    return (const char*) ptr;
+}
+/* return the StringId with the specified index */
+DEX_INLINE const DexStringId* dexGetStringId(const DexFile* pDexFile, u4 idx) {
+    assert(idx < pDexFile->pHeader->stringIdsSize);
+    return &pDexFile->pStringIds[idx];
+}
+/* return the UTF-8 encoded string with the specified string_id index */
+DEX_INLINE const char* dexStringById(const DexFile* pDexFile, u4 idx) {
+    const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
+    return dexGetStringData(pDexFile, pStringId);
+}
+
+/* Return the UTF-8 encoded string with the specified string_id index,
+ * also filling in the UTF-16 size (number of 16-bit code points).*/
+const char* dexStringAndSizeById(const DexFile* pDexFile, u4 idx,
+        u4* utf16Size);
+
+/* return the TypeId with the specified index */
+DEX_INLINE const DexTypeId* dexGetTypeId(const DexFile* pDexFile, u4 idx) {
+    assert(idx < pDexFile->pHeader->typeIdsSize);
+    return &pDexFile->pTypeIds[idx];
+}
+
+/*
+ * Get the descriptor string associated with a given type index.
+ * The caller should not free() the returned string.
+ */
+DEX_INLINE const char* dexStringByTypeIdx(const DexFile* pDexFile, u4 idx) {
+    const DexTypeId* typeId = dexGetTypeId(pDexFile, idx);
+    return dexStringById(pDexFile, typeId->descriptorIdx);
+}
+
+/* return the MethodId with the specified index */
+DEX_INLINE const DexMethodId* dexGetMethodId(const DexFile* pDexFile, u4 idx) {
+    assert(idx < pDexFile->pHeader->methodIdsSize);
+    return &pDexFile->pMethodIds[idx];
+}
+
+/* return the FieldId with the specified index */
+DEX_INLINE const DexFieldId* dexGetFieldId(const DexFile* pDexFile, u4 idx) {
+    assert(idx < pDexFile->pHeader->fieldIdsSize);
+    return &pDexFile->pFieldIds[idx];
+}
+
+/* return the ProtoId with the specified index */
+DEX_INLINE const DexProtoId* dexGetProtoId(const DexFile* pDexFile, u4 idx) {
+    assert(idx < pDexFile->pHeader->protoIdsSize);
+    return &pDexFile->pProtoIds[idx];
+}
+
+/*
+ * Get the parameter list from a ProtoId. The returns NULL if the ProtoId
+ * does not have a parameter list.
+ */
+DEX_INLINE const DexTypeList* dexGetProtoParameters(
+    const DexFile *pDexFile, const DexProtoId* pProtoId) {
+    if (pProtoId->parametersOff == 0) {
+        return NULL;
+    }
+    return (const DexTypeList*)
+        (pDexFile->baseAddr + pProtoId->parametersOff);
+}
+
+/* return the ClassDef with the specified index */
+DEX_INLINE const DexClassDef* dexGetClassDef(const DexFile* pDexFile, u4 idx) {
+    assert(idx < pDexFile->pHeader->classDefsSize);
+    return &pDexFile->pClassDefs[idx];
+}
+
+/* given a ClassDef pointer, recover its index */
+DEX_INLINE u4 dexGetIndexForClassDef(const DexFile* pDexFile,
+    const DexClassDef* pClassDef)
+{
+    assert(pClassDef >= pDexFile->pClassDefs &&
+           pClassDef < pDexFile->pClassDefs + pDexFile->pHeader->classDefsSize);
+    return pClassDef - pDexFile->pClassDefs;
+}
+
+/* get the interface list for a DexClass */
+DEX_INLINE const DexTypeList* dexGetInterfacesList(const DexFile* pDexFile,
+    const DexClassDef* pClassDef)
+{
+    if (pClassDef->interfacesOff == 0)
+        return NULL;
+    return (const DexTypeList*)
+        (pDexFile->baseAddr + pClassDef->interfacesOff);
+}
+/* return the Nth entry in a DexTypeList. */
+DEX_INLINE const DexTypeItem* dexGetTypeItem(const DexTypeList* pList,
+    u4 idx)
+{
+    assert(idx < pList->size);
+    return &pList->list[idx];
+}
+/* return the type_idx for the Nth entry in a TypeList */
+DEX_INLINE u4 dexTypeListGetIdx(const DexTypeList* pList, u4 idx) {
+    const DexTypeItem* pItem = dexGetTypeItem(pList, idx);
+    return pItem->typeIdx;
+}
+
+/* get the static values list for a DexClass */
+DEX_INLINE const DexEncodedArray* dexGetStaticValuesList(
+    const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+    if (pClassDef->staticValuesOff == 0)
+        return NULL;
+    return (const DexEncodedArray*)
+        (pDexFile->baseAddr + pClassDef->staticValuesOff);
+}
+
+/* get the annotations directory item for a DexClass */
+DEX_INLINE const DexAnnotationsDirectoryItem* dexGetAnnotationsDirectoryItem(
+    const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+    if (pClassDef->annotationsOff == 0)
+        return NULL;
+    return (const DexAnnotationsDirectoryItem*)
+        (pDexFile->baseAddr + pClassDef->annotationsOff);
+}
+
+/* get the source file string */
+DEX_INLINE const char* dexGetSourceFile(
+    const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+    if (pClassDef->sourceFileIdx == 0xffffffff)
+        return NULL;
+    return dexStringById(pDexFile, pClassDef->sourceFileIdx);
+}
+
+/* get the size, in bytes, of a DexCode */
+size_t dexGetDexCodeSize(const DexCode* pCode);
+
+/* Get the list of "tries" for the given DexCode. */
+DEX_INLINE const DexTry* dexGetTries(const DexCode* pCode) {
+    const u2* insnsEnd = &pCode->insns[pCode->insnsSize];
+
+    // Round to four bytes.
+    if ((((u4) insnsEnd) & 3) != 0) {
+        insnsEnd++;
+    }
+
+    return (const DexTry*) insnsEnd;
+}
+
+/* Get the base of the encoded data for the given DexCode. */
+DEX_INLINE const u1* dexGetCatchHandlerData(const DexCode* pCode) {
+    const DexTry* pTries = dexGetTries(pCode);
+    return (const u1*) &pTries[pCode->triesSize];
+}
+
+/* get a pointer to the start of the debugging data */
+DEX_INLINE const u1* dexGetDebugInfoStream(const DexFile* pDexFile,
+    const DexCode* pCode)
+{
+    if (pCode->debugInfoOff == 0) {
+        return NULL;
+    } else {
+        return pDexFile->baseAddr + pCode->debugInfoOff;
+    }
+}
+
+/*
+ * Callback for "new position table entry".
+ * Returning non-0 causes the decoder to stop early.
+ */
+typedef int (*DexDebugNewPositionCb)(void *cnxt, u4 address, u4 lineNum);
+
+/*
+ * Callback for "new locals table entry". "signature" is an empty string
+ * if no signature is available for an entry.
+ */
+typedef void (*DexDebugNewLocalCb)(void *cnxt, u2 reg, u4 startAddress,
+        u4 endAddress, const char *name, const char *descriptor,
+        const char *signature);
+
+/*
+ * Decode debug info for method.
+ *
+ * posCb is called in ascending address order.
+ * localCb is called in order of ascending end address.
+ */
+void dexDecodeDebugInfo(
+            const DexFile* pDexFile,
+            const DexCode* pDexCode,
+            const char* classDescriptor,
+            u4 protoIdx,
+            u4 accessFlags,
+            DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+            void* cnxt);
+
+/* DexClassDef convenience - get class descriptor */
+DEX_INLINE const char* dexGetClassDescriptor(const DexFile* pDexFile,
+    const DexClassDef* pClassDef)
+{
+    return dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+}
+
+/* DexClassDef convenience - get superclass descriptor */
+DEX_INLINE const char* dexGetSuperClassDescriptor(const DexFile* pDexFile,
+    const DexClassDef* pClassDef)
+{
+    if (pClassDef->superclassIdx == 0)
+        return NULL;
+    return dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
+}
+
+/* DexClassDef convenience - get class_data_item pointer */
+DEX_INLINE const u1* dexGetClassData(const DexFile* pDexFile,
+    const DexClassDef* pClassDef)
+{
+    if (pClassDef->classDataOff == 0)
+        return NULL;
+    return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff);
+}
+
+/* Get an annotation set at a particular offset. */
+DEX_INLINE const DexAnnotationSetItem* dexGetAnnotationSetItem(
+    const DexFile* pDexFile, u4 offset)
+{
+    return (const DexAnnotationSetItem*) (pDexFile->baseAddr + offset);
+}
+/* get the class' annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetClassAnnotationSet(
+    const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+    if (pAnnoDir->classAnnotationsOff == 0)
+        return NULL;
+    return dexGetAnnotationSetItem(pDexFile, pAnnoDir->classAnnotationsOff);
+}
+
+/* get the class' field annotation list */
+DEX_INLINE const DexFieldAnnotationsItem* dexGetFieldAnnotations(
+    const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+    if (pAnnoDir->fieldsSize == 0)
+        return NULL;
+
+    // Skip past the header to the start of the field annotations.
+    return (const DexFieldAnnotationsItem*) &pAnnoDir[1];
+}
+
+/* get field annotation list size */
+DEX_INLINE int dexGetFieldAnnotationsSize(const DexFile* pDexFile,
+    const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+    return pAnnoDir->fieldsSize;
+}
+
+/* return a pointer to the field's annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetFieldAnnotationSetItem(
+    const DexFile* pDexFile, const DexFieldAnnotationsItem* pItem)
+{
+    return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* get the class' method annotation list */
+DEX_INLINE const DexMethodAnnotationsItem* dexGetMethodAnnotations(
+    const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+    if (pAnnoDir->methodsSize == 0)
+        return NULL;
+
+    /*
+     * Skip past the header and field annotations to the start of the
+     * method annotations.
+     */
+    const u1* addr = (const u1*) &pAnnoDir[1];
+    addr += pAnnoDir->fieldsSize * sizeof (DexFieldAnnotationsItem);
+    return (const DexMethodAnnotationsItem*) addr;
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetMethodAnnotationsSize(const DexFile* pDexFile,
+    const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+    return pAnnoDir->methodsSize;
+}
+
+/* return a pointer to the method's annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetMethodAnnotationSetItem(
+    const DexFile* pDexFile, const DexMethodAnnotationsItem* pItem)
+{
+    return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* get the class' parameter annotation list */
+DEX_INLINE const DexParameterAnnotationsItem* dexGetParameterAnnotations(
+    const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+    if (pAnnoDir->parametersSize == 0)
+        return NULL;
+
+    /*
+     * Skip past the header, field annotations, and method annotations
+     * to the start of the parameter annotations.
+     */
+    const u1* addr = (const u1*) &pAnnoDir[1];
+    addr += pAnnoDir->fieldsSize * sizeof (DexFieldAnnotationsItem);
+    addr += pAnnoDir->methodsSize * sizeof (DexMethodAnnotationsItem);
+    return (const DexParameterAnnotationsItem*) addr;
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetParameterAnnotationsSize(const DexFile* pDexFile,
+    const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+    return pAnnoDir->parametersSize;
+}
+
+/* return the parameter annotation ref list */
+DEX_INLINE const DexAnnotationSetRefList* dexGetParameterAnnotationSetRefList(
+    const DexFile* pDexFile, const DexParameterAnnotationsItem* pItem)
+{
+    return (const DexAnnotationSetRefList*)
+        (pDexFile->baseAddr + pItem->annotationsOff);
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetParameterAnnotationSetRefSize(const DexFile* pDexFile,
+    const DexParameterAnnotationsItem* pItem)
+{
+    if (pItem->annotationsOff == 0)
+        return 0;
+    return dexGetParameterAnnotationSetRefList(pDexFile, pItem)->size;
+}
+
+/* return the Nth entry from an annotation set ref list */
+DEX_INLINE const DexAnnotationSetRefItem* dexGetParameterAnnotationSetRef(
+    const DexAnnotationSetRefList* pList, u4 idx)
+{
+    assert(idx < pList->size);
+    return &pList->list[idx];
+}
+
+/* given a DexAnnotationSetRefItem, return the DexAnnotationSetItem */
+DEX_INLINE const DexAnnotationSetItem* dexGetSetRefItemItem(
+    const DexFile* pDexFile, const DexAnnotationSetRefItem* pItem)
+{
+    return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* return the Nth annotation offset from a DexAnnotationSetItem */
+DEX_INLINE u4 dexGetAnnotationOff(
+    const DexAnnotationSetItem* pAnnoSet, u4 idx)
+{
+    assert(idx < pAnnoSet->size);
+    return pAnnoSet->entries[idx];
+}
+
+/* return the Nth annotation item from a DexAnnotationSetItem */
+DEX_INLINE const DexAnnotationItem* dexGetAnnotationItem(
+    const DexFile* pDexFile, const DexAnnotationSetItem* pAnnoSet, u4 idx)
+{
+    return (const DexAnnotationItem*)
+        (pDexFile->baseAddr + dexGetAnnotationOff(pAnnoSet, idx));
+}
+
+
+/*
+ * ===========================================================================
+ *      Utility Functions
+ * ===========================================================================
+ */
+
+/*
+ * Retrieve the next UTF-16 character from a UTF-8 string.
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ *
+ * WARNING: If a string is corrupted by dropping a '\0' in the middle
+ * of a 3-byte sequence, you can end up overrunning the buffer with
+ * reads (and possibly with the writes if the length was computed and
+ * cached before the damage). For performance reasons, this function
+ * assumes that the string being parsed is known to be valid (e.g., by
+ * already being verified). Most strings we process here are coming
+ * out of dex files or other internal translations, so the only real
+ * risk comes from the JNI NewStringUTF call.
+ */
+DEX_INLINE u2 dexGetUtf16FromUtf8(const char** pUtf8Ptr)
+{
+    unsigned int one, two, three;
+
+    one = *(*pUtf8Ptr)++;
+    if ((one & 0x80) != 0) {
+        /* two- or three-byte encoding */
+        two = *(*pUtf8Ptr)++;
+        if ((one & 0x20) != 0) {
+            /* three-byte encoding */
+            three = *(*pUtf8Ptr)++;
+            return ((one & 0x0f) << 12) |
+                   ((two & 0x3f) << 6) |
+                   (three & 0x3f);
+        } else {
+            /* two-byte encoding */
+            return ((one & 0x1f) << 6) |
+                   (two & 0x3f);
+        }
+    } else {
+        /* one-byte encoding */
+        return one;
+    }
+}
+
+/* Compare two '\0'-terminated modified UTF-8 strings, using Unicode
+ * code point values for comparison. This treats different encodings
+ * for the same code point as equivalent, except that only a real '\0'
+ * byte is considered the string terminator. The return value is as
+ * for strcmp(). */
+int dexUtf8Cmp(const char* s1, const char* s2);
+
+
+/* for dexIsValidMemberNameUtf8(), a bit vector indicating valid low ascii */
+extern u4 DEX_MEMBER_VALID_LOW_ASCII[4];
+
+/* Helper for dexIsValidMemberUtf8(); do not call directly. */
+bool dexIsValidMemberNameUtf8_0(const char** pUtf8Ptr);
+
+/* Return whether the pointed-at modified-UTF-8 encoded character is
+ * valid as part of a member name, updating the pointer to point past
+ * the consumed character. This will consume two encoded UTF-16 code
+ * points if the character is encoded as a surrogate pair. Also, if
+ * this function returns false, then the given pointer may only have
+ * been partially advanced. */
+DEX_INLINE bool dexIsValidMemberNameUtf8(const char** pUtf8Ptr) {
+    u1 c = (u1) **pUtf8Ptr;
+    if (c <= 0x7f) {
+        // It's low-ascii, so check the table.
+        u4 wordIdx = c >> 5;
+        u4 bitIdx = c & 0x1f;
+        (*pUtf8Ptr)++;
+        return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0;
+    }
+
+    /*
+     * It's a multibyte encoded character. Call a non-inline function
+     * for the heavy lifting.
+     */
+    return dexIsValidMemberNameUtf8_0(pUtf8Ptr);
+}
+
+/* Return whether the given string is a valid field or method name. */
+bool dexIsValidMemberName(const char* s);
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s);
+
+/* Return whether the given string is a valid reference descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class or array and not a primitive type. */
+bool dexIsReferenceDescriptor(const char* s);
+
+/* Return whether the given string is a valid class descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class and not an array or primitive type. */
+bool dexIsClassDescriptor(const char* s);
+
+/* Return whether the given string is a valid field type descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for anything but "void". */
+bool dexIsFieldDescriptor(const char* s);
+
+#endif /*_LIBDEX_DEXFILE*/
diff --git a/libdex/DexInlines.c b/libdex/DexInlines.c
new file mode 100644
index 0000000..6b3aed8
--- /dev/null
+++ b/libdex/DexInlines.c
@@ -0,0 +1,30 @@
+/*
+ * 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 in header files.
+ */
+
+#define _DEX_GEN_INLINES
+
+#include "DexFile.h"
+
+#include "DexCatch.h"
+#include "DexClass.h"
+#include "DexDataMap.h"
+#include "DexProto.h"
+#include "InstrUtils.h"
+#include "Leb128.h"
+#include "ZipArchive.h"
diff --git a/libdex/DexOptData.c b/libdex/DexOptData.c
new file mode 100644
index 0000000..10a4b5d
--- /dev/null
+++ b/libdex/DexOptData.c
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions to parse and manipulate the additional data tables added
+ * to optimized .dex files.
+ */
+
+#include <zlib.h>
+
+#include "DexOptData.h"
+
+/*
+ * Check to see if a given data pointer is a valid double-word-aligned
+ * pointer into the given memory range (from start inclusive to end
+ * exclusive). Returns true if valid.
+ */
+static bool isValidPointer(const void* ptr, const void* start, const void* end)
+{
+    return (ptr >= start) && (ptr < end) && (((u4) ptr & 7) == 0);
+}
+
+/* (documented in header file) */
+u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader)
+{
+    const u1* start = (const u1*) pOptHeader + pOptHeader->depsOffset;
+    const u1* end = (const u1*) pOptHeader +
+        pOptHeader->optOffset + pOptHeader->optLength;
+
+    uLong adler = adler32(0L, Z_NULL, 0);
+
+    return (u4) adler32(adler, start, end - start);
+}
+
+/* (documented in header file) */
+bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile)
+{
+    const void* pOptStart = data + pDexFile->pOptHeader->optOffset;
+    const void* pOptEnd = data + length;
+    const u4* pOpt = pOptStart;
+    u4 optLength = (const u1*) pOptEnd - (const u1*) pOptStart;
+
+    /*
+     * Make sure the opt data start is in range and aligned. This may
+     * seem like a superfluous check, but (a) if the file got
+     * truncated, it might turn out that pOpt >= pOptEnd; and (b)
+     * if the opt data header got corrupted, pOpt might not be
+     * properly aligned. This test will catch both of these cases.
+     */
+    if (!isValidPointer(pOpt, pOptStart, pOptEnd)) {
+        LOGE("Bogus opt data start pointer\n");
+        return false;
+    }
+
+    /* Make sure that the opt data length is a whole number of words. */
+    if ((optLength & 3) != 0) {
+        LOGE("Unaligned opt data area end\n");
+        return false;
+    }
+
+    /*
+     * Make sure that the opt data area is large enough to have at least
+     * one chunk header.
+     */
+    if (optLength < 8) {
+        LOGE("Undersized opt data area (%u)\n", optLength);
+        return false;
+    }
+
+    /* Process chunks until we see the end marker. */
+    while (*pOpt != kDexChunkEnd) {
+        if (!isValidPointer(pOpt + 2, pOptStart, pOptEnd)) {
+            LOGE("Bogus opt data content pointer at offset %u\n",
+                    ((const u1*) pOpt) - data);
+            return false;
+        }
+
+        u4 size = *(pOpt + 1);
+        const u1* pOptData = (const u1*) (pOpt + 2);
+
+        /*
+         * The rounded size is 64-bit aligned and includes +8 for the
+         * type/size header (which was extracted immediately above).
+         */
+        u4 roundedSize = (size + 8 + 7) & ~7;
+        const u4* pNextOpt = pOpt + (roundedSize / sizeof(u4));
+
+        if (!isValidPointer(pNextOpt, pOptStart, pOptEnd)) {
+            LOGE("Opt data area problem for chunk of size %u at offset %u\n",
+                    size, ((const u1*) pOpt) - data);
+            return false;
+        }
+
+        switch (*pOpt) {
+        case kDexChunkClassLookup:
+            pDexFile->pClassLookup = (const DexClassLookup*) pOptData;
+            break;
+        case kDexChunkRegisterMaps:
+            LOGV("+++ found register maps, size=%u\n", size);
+            pDexFile->pRegisterMapPool = pOptData;
+            break;
+        default:
+            LOGI("Unknown chunk 0x%08x (%c%c%c%c), size=%d in opt data area\n",
+                *pOpt,
+                (char) ((*pOpt) >> 24), (char) ((*pOpt) >> 16),
+                (char) ((*pOpt) >> 8),  (char)  (*pOpt),
+                size);
+            break;
+        }
+
+        pOpt = pNextOpt;
+    }
+
+    return true;
+}
diff --git a/libdex/DexOptData.h b/libdex/DexOptData.h
new file mode 100644
index 0000000..69eda01
--- /dev/null
+++ b/libdex/DexOptData.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions to parse and manipulate the additional data tables added
+ * to optimized .dex files.
+ */
+
+#ifndef _LIBDEX_DEXOPTDATA
+#define _LIBDEX_DEXOPTDATA
+
+#include "libdex/DexFile.h"
+
+/*
+ * Parse the optimized data tables in the given dex file.
+ *
+ * @param data pointer to the start of the entire dex file
+ * @param length length of the entire dex file, in bytes
+ * @param pDexFile pointer to the associated dex file structure
+ */
+bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile);
+
+/*
+ * Compute the checksum of the optimized data tables pointed at by the given
+ * header.
+ */
+u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader);
+
+#endif /* def _LIBDEX_DEXOPTDATA */
diff --git a/libdex/DexProto.c b/libdex/DexProto.c
new file mode 100644
index 0000000..b5574dc
--- /dev/null
+++ b/libdex/DexProto.c
@@ -0,0 +1,533 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions for dealing with method prototypes
+ */
+
+#include "DexProto.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * ===========================================================================
+ *      String Cache
+ * ===========================================================================
+ */
+
+/*
+ * Make sure that the given cache can hold a string of the given length,
+ * including the final '\0' byte.
+ */
+static void dexStringCacheAlloc(DexStringCache* pCache, size_t length) {
+    if (pCache->allocatedSize != 0) {
+        if (pCache->allocatedSize >= length) {
+            return;
+        }
+        free((void*) pCache->value);
+    }
+
+    if (length <= sizeof(pCache->buffer)) {
+        pCache->value = pCache->buffer;
+        pCache->allocatedSize = 0;
+    } else {
+        pCache->value = malloc(length);
+        pCache->allocatedSize = length;
+    }
+}
+
+/*
+ * Initialize the given DexStringCache. Use this function before passing
+ * one into any other function.
+ */
+void dexStringCacheInit(DexStringCache* pCache) {
+    pCache->value = pCache->buffer;
+    pCache->allocatedSize = 0;
+    pCache->buffer[0] = '\0';
+}
+
+/*
+ * Release the allocated contents of the given DexStringCache, if any.
+ * Use this function after your last use of a DexStringCache.
+ */
+void dexStringCacheRelease(DexStringCache* pCache) {
+    if (pCache->allocatedSize != 0) {
+        free((void*) pCache->value);
+        pCache->value = pCache->buffer;
+        pCache->allocatedSize = 0;
+    }
+}
+
+/*
+ * If the given DexStringCache doesn't already point at the given value,
+ * make a copy of it into the cache. This always returns a writable
+ * pointer to the contents (whether or not a copy had to be made). This
+ * function is intended to be used after making a call that at least
+ * sometimes doesn't populate a DexStringCache.
+ */
+char* dexStringCacheEnsureCopy(DexStringCache* pCache, const char* value) {
+    if (value != pCache->value) {
+        size_t length = strlen(value) + 1;
+        dexStringCacheAlloc(pCache, length);
+        memcpy(pCache->value, value, length);
+    }
+
+    return pCache->value;
+}
+
+/*
+ * Abandon the given DexStringCache, and return a writable copy of the
+ * given value (reusing the string cache's allocation if possible).
+ * The return value must be free()d by the caller. Use this instead of
+ * dexStringCacheRelease() if you want the buffer to survive past the
+ * scope of the DexStringCache.
+ */
+char* dexStringCacheAbandon(DexStringCache* pCache, const char* value) {
+    if ((value == pCache->value) && (pCache->allocatedSize != 0)) {
+        char* result = pCache->value;
+        pCache->allocatedSize = 0;
+        pCache->value = pCache->buffer;
+        return result;
+    } else {
+        return strdup(value);
+    }
+}
+
+
+/*
+ * ===========================================================================
+ *      Method Prototypes
+ * ===========================================================================
+ */
+
+/*
+ * Return the DexProtoId from the given DexProto. The DexProto must
+ * actually refer to a DexProtoId.
+ */
+static inline const DexProtoId* getProtoId(const DexProto* pProto) {
+    return dexGetProtoId(pProto->dexFile, pProto->protoIdx);
+}
+
+/*
+ * Get the short-form method descriptor for the given prototype. The
+ * prototype must be protoIdx-based.
+ */
+const char* dexProtoGetShorty(const DexProto* pProto) {
+    const DexProtoId* protoId = getProtoId(pProto);
+
+    return dexStringById(pProto->dexFile, protoId->shortyIdx);
+}
+
+/*
+ * Get the full method descriptor for the given prototype.
+ */
+const char* dexProtoGetMethodDescriptor(const DexProto* pProto,
+        DexStringCache* pCache) {
+    const DexFile* dexFile = pProto->dexFile;
+    const DexProtoId* protoId = getProtoId(pProto);
+    const DexTypeList* typeList = dexGetProtoParameters(dexFile, protoId);
+    size_t length = 3; // parens and terminating '\0'
+    u4 paramCount = (typeList == NULL) ? 0 : typeList->size;
+    u4 i;
+
+    for (i = 0; i < paramCount; i++) {
+        u4 idx = dexTypeListGetIdx(typeList, i);
+        length += strlen(dexStringByTypeIdx(dexFile, idx));
+    }
+
+    length += strlen(dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
+
+    dexStringCacheAlloc(pCache, length);
+
+    char *at = (char*) pCache->value;
+    *(at++) = '(';
+
+    for (i = 0; i < paramCount; i++) {
+        u4 idx = dexTypeListGetIdx(typeList, i);
+        const char* desc = dexStringByTypeIdx(dexFile, idx);
+        strcpy(at, desc);
+        at += strlen(desc);
+    }
+
+    *(at++) = ')';
+
+    strcpy(at, dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
+    return pCache->value;
+}
+
+/*
+ * Get a copy of the descriptor string associated with the given prototype.
+ * The returned pointer must be free()ed by the caller.
+ */
+char* dexProtoCopyMethodDescriptor(const DexProto* pProto) {
+    DexStringCache cache;
+
+    dexStringCacheInit(&cache);
+    return dexStringCacheAbandon(&cache,
+            dexProtoGetMethodDescriptor(pProto, &cache));
+}
+
+/*
+ * Get the parameter descriptors for the given prototype. This is the
+ * concatenation of all the descriptors for all the parameters, in
+ * order, with no other adornment.
+ */
+const char* dexProtoGetParameterDescriptors(const DexProto* pProto,
+        DexStringCache* pCache) {
+    DexParameterIterator iterator;
+    size_t length = 1; /* +1 for the terminating '\0' */
+
+    dexParameterIteratorInit(&iterator, pProto);
+
+    for (;;) {
+        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+        if (descriptor == NULL) {
+            break;
+        }
+
+        length += strlen(descriptor);
+    }
+
+    dexParameterIteratorInit(&iterator, pProto);
+
+    dexStringCacheAlloc(pCache, length);
+    char *at = (char*) pCache->value;
+
+    for (;;) {
+        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+        if (descriptor == NULL) {
+            break;
+        }
+
+        strcpy(at, descriptor);
+        at += strlen(descriptor);
+    }
+
+    return pCache->value;
+}
+
+/*
+ * Get the type descriptor for the return type of the given prototype.
+ */
+const char* dexProtoGetReturnType(const DexProto* pProto) {
+    const DexProtoId* protoId = getProtoId(pProto);
+    return dexStringByTypeIdx(pProto->dexFile, protoId->returnTypeIdx);
+}
+
+/*
+ * Get the parameter count of the given prototype.
+ */
+size_t dexProtoGetParameterCount(const DexProto* pProto) {
+    const DexProtoId* protoId = getProtoId(pProto);
+    const DexTypeList* typeList =
+        dexGetProtoParameters(pProto->dexFile, protoId);
+    return (typeList == NULL) ? 0 : typeList->size;
+}
+
+/*
+ * Compute the number of parameter words (u4 units) required by the
+ * given prototype. For example, if the method takes (int, long) and
+ * returns double, this would return 3 (one for the int, two for the
+ * long, and the return type isn't relevant).
+ */
+int dexProtoComputeArgsSize(const DexProto* pProto) {
+    const char* shorty = dexProtoGetShorty(pProto);
+    int count = 0;
+
+    /* Skip the return type. */
+    shorty++;
+
+    for (;;) {
+        switch (*(shorty++)) {
+            case '\0': {
+                return count;
+            }
+            case 'D':
+            case 'J': {
+                count += 2;
+                break;
+            }
+            default: {
+                count++;
+                break;
+            }
+        }
+    }
+}
+
+/*
+ * Common implementation for dexProtoCompare() and dexProtoCompareParameters().
+ */
+static int protoCompare(const DexProto* pProto1, const DexProto* pProto2,
+        bool compareReturnType) {
+
+    if (pProto1 == pProto2) {
+        // Easy out.
+        return 0;
+    } else {
+        const DexFile* dexFile1 = pProto1->dexFile;
+        const DexProtoId* protoId1 = getProtoId(pProto1);
+        const DexTypeList* typeList1 =
+            dexGetProtoParameters(dexFile1, protoId1);
+        int paramCount1 = (typeList1 == NULL) ? 0 : typeList1->size;
+
+        const DexFile* dexFile2 = pProto2->dexFile;
+        const DexProtoId* protoId2 = getProtoId(pProto2);
+        const DexTypeList* typeList2 =
+            dexGetProtoParameters(dexFile2, protoId2);
+        int paramCount2 = (typeList2 == NULL) ? 0 : typeList2->size;
+
+        if (protoId1 == protoId2) {
+            // Another easy out.
+            return 0;
+        }
+
+        // Compare return types.
+
+        if (compareReturnType) {
+            int result =
+                strcmp(dexStringByTypeIdx(dexFile1, protoId1->returnTypeIdx),
+                        dexStringByTypeIdx(dexFile2, protoId2->returnTypeIdx));
+
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        // Compare parameters.
+
+        int minParam = (paramCount1 > paramCount2) ? paramCount2 : paramCount1;
+        int i;
+
+        for (i = 0; i < minParam; i++) {
+            u4 idx1 = dexTypeListGetIdx(typeList1, i);
+            u4 idx2 = dexTypeListGetIdx(typeList2, i);
+            int result =
+                strcmp(dexStringByTypeIdx(dexFile1, idx1),
+                        dexStringByTypeIdx(dexFile2, idx2));
+
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        if (paramCount1 < paramCount2) {
+            return -1;
+        } else if (paramCount1 > paramCount2) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+}
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the return type as the major order, then the first arguments,
+ * then second, etc. If two prototypes are identical except that one
+ * has extra arguments, then the shorter argument is considered the
+ * earlier one in sort order (similar to strcmp()).
+ */
+int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
+    return protoCompare(pProto1, pProto2, true);
+}
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the first argument as the major order, then second, etc. If two
+ * prototypes are identical except that one has extra arguments, then the
+ * shorter argument is considered the earlier one in sort order (similar
+ * to strcmp()).
+ */
+int dexProtoCompareParameters(const DexProto* pProto1, const DexProto* pProto2){
+    return protoCompare(pProto1, pProto2, false);
+}
+
+
+/*
+ * Helper for dexProtoCompareToDescriptor(), which gets the return type
+ * descriptor from a method descriptor string.
+ */
+static const char* methodDescriptorReturnType(const char* descriptor) {
+    const char* result = strchr(descriptor, ')');
+
+    if (result == NULL) {
+        return NULL;
+    }
+
+    // The return type is the character just past the ')'.
+    return result + 1;
+}
+
+/*
+ * Helper for dexProtoCompareToDescriptor(), which indicates the end
+ * of an embedded argument type descriptor, which is also the
+ * beginning of the next argument type descriptor. Since this is for
+ * argument types, it doesn't accept 'V' as a valid type descriptor.
+ */
+static const char* methodDescriptorNextType(const char* descriptor) {
+    // Skip any array references.
+
+    while (*descriptor == '[') {
+        descriptor++;
+    }
+
+    switch (*descriptor) {
+        case 'B': case 'C': case 'D': case 'F':
+        case 'I': case 'J': case 'S': case 'Z': {
+            return descriptor + 1;
+        }
+        case 'L': {
+            const char* result = strchr(descriptor + 1, ';');
+            if (result != NULL) {
+                // The type ends just past the ';'.
+                return result + 1;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/*
+ * Compare a prototype and a string method descriptor. The comparison
+ * is done as if the descriptor were converted to a prototype and compared
+ * with dexProtoCompare().
+ */
+int dexProtoCompareToDescriptor(const DexProto* proto,
+        const char* descriptor) {
+    // First compare the return types.
+
+    int result = strcmp(dexProtoGetReturnType(proto),
+            methodDescriptorReturnType(descriptor));
+
+    if (result != 0) {
+        return result;
+    }
+
+    // The return types match, so we have to check arguments.
+
+    DexParameterIterator iterator;
+    dexParameterIteratorInit(&iterator, proto);
+
+    // Skip the '('.
+    assert (*descriptor == '(');
+    descriptor++;
+
+    for (;;) {
+        const char* protoDesc = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (*descriptor == ')') {
+            // It's the end of the descriptor string.
+            if (protoDesc == NULL) {
+                // It's also the end of the prototype's arguments.
+                return 0;
+            } else {
+                // The prototype still has more arguments.
+                return 1;
+            }
+        }
+
+        if (protoDesc == NULL) {
+            /*
+             * The prototype doesn't have arguments left, but the
+             * descriptor string does.
+             */
+            return -1;
+        }
+
+        // Both prototype and descriptor have arguments. Compare them.
+
+        const char* nextDesc = methodDescriptorNextType(descriptor);
+
+        for (;;) {
+            char c1 = *(protoDesc++);
+            char c2 = (descriptor < nextDesc) ? *(descriptor++) : '\0';
+
+            if (c1 < c2) {
+                // This includes the case where the proto is shorter.
+                return -1;
+            } else if (c1 > c2) {
+                // This includes the case where the desc is shorter.
+                return 1;
+            } else if (c1 == '\0') {
+                // The two types are equal in length. (c2 necessarily == '\0'.)
+                break;
+            }
+        }
+
+        /*
+         * If we made it here, the two arguments matched, and
+         * descriptor == nextDesc.
+         */
+    }
+}
+
+
+/*
+ * ===========================================================================
+ *      Parameter Iterators
+ * ===========================================================================
+ */
+
+/*
+ * Initialize the given DexParameterIterator to be at the start of the
+ * parameters of the given prototype.
+ */
+void dexParameterIteratorInit(DexParameterIterator* pIterator,
+        const DexProto* pProto) {
+    pIterator->proto = pProto;
+    pIterator->cursor = 0;
+
+    pIterator->parameters =
+        dexGetProtoParameters(pProto->dexFile, getProtoId(pProto));
+    pIterator->parameterCount = (pIterator->parameters == NULL) ? 0
+        : pIterator->parameters->size;
+}
+
+/*
+ * Get the type_id index for the next parameter, if any. This returns
+ * kDexNoIndex if the last parameter has already been consumed.
+ */
+u4 dexParameterIteratorNextIndex(DexParameterIterator* pIterator) {
+    int cursor = pIterator->cursor;
+    int parameterCount = pIterator->parameterCount;
+
+    if (cursor >= parameterCount) {
+        // The iteration is complete.
+        return kDexNoIndex;
+    } else {
+        u4 idx = dexTypeListGetIdx(pIterator->parameters, cursor);
+        pIterator->cursor++;
+        return idx;
+    }
+}
+
+/*
+ * Get the type descriptor for the next parameter, if any. This returns
+ * NULL if the last parameter has already been consumed.
+ */
+const char* dexParameterIteratorNextDescriptor(
+        DexParameterIterator* pIterator) {
+    u4 idx = dexParameterIteratorNextIndex(pIterator);
+
+    if (idx == kDexNoIndex) {
+        return NULL;
+    }
+
+    return dexStringByTypeIdx(pIterator->proto->dexFile, idx);
+}
diff --git a/libdex/DexProto.h b/libdex/DexProto.h
new file mode 100644
index 0000000..1ef577b
--- /dev/null
+++ b/libdex/DexProto.h
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions for dealing with method prototypes
+ */
+
+#ifndef _LIBDEX_DEXPROTO
+#define _LIBDEX_DEXPROTO
+
+#include "DexFile.h"
+
+/*
+ * Single-thread single-string cache. This structure holds a pointer to
+ * a string which is semi-automatically manipulated by some of the
+ * method prototype functions. Functions which use in this struct
+ * generally return a string that is valid until the next
+ * time the same DexStringCache is used.
+ */
+typedef struct DexStringCache {
+    char* value;          /* the latest value */
+    size_t allocatedSize; /* size of the allocated buffer, if allocated */
+    char buffer[120];     /* buffer used to hold small-enough results */
+} DexStringCache;
+
+/*
+ * Initialize the given DexStringCache. Use this function before passing
+ * one into any other function.
+ */
+void dexStringCacheInit(DexStringCache* pCache);
+
+/*
+ * Release the allocated contents of the given DexStringCache, if any.
+ * Use this function after your last use of a DexStringCache.
+ */
+void dexStringCacheRelease(DexStringCache* pCache);
+
+/*
+ * If the given DexStringCache doesn't already point at the given value,
+ * make a copy of it into the cache. This always returns a writable
+ * pointer to the contents (whether or not a copy had to be made). This
+ * function is intended to be used after making a call that at least
+ * sometimes doesn't populate a DexStringCache.
+ */
+char* dexStringCacheEnsureCopy(DexStringCache* pCache, const char* value);
+
+/*
+ * Abandon the given DexStringCache, and return a writable copy of the
+ * given value (reusing the string cache's allocation if possible).
+ * The return value must be free()d by the caller. Use this instead of
+ * dexStringCacheRelease() if you want the buffer to survive past the
+ * scope of the DexStringCache.
+ */
+char* dexStringCacheAbandon(DexStringCache* pCache, const char* value);
+
+/*
+ * Method prototype structure, which refers to a protoIdx in a
+ * particular DexFile.
+ */
+typedef struct DexProto {
+    const DexFile* dexFile;     /* file the idx refers to */
+    u4 protoIdx;                /* index into proto_ids table of dexFile */
+} DexProto;
+
+/*
+ * Set the given DexProto to refer to the prototype of the given MethodId.
+ */
+DEX_INLINE void dexProtoSetFromMethodId(DexProto* pProto,
+    const DexFile* pDexFile, const DexMethodId* pMethodId)
+{
+    pProto->dexFile = pDexFile;
+    pProto->protoIdx = pMethodId->protoIdx;
+}
+
+/*
+ * Get the short-form method descriptor for the given prototype. The
+ * prototype must be protoIdx-based.
+ */
+const char* dexProtoGetShorty(const DexProto* pProto);
+
+/*
+ * Get the full method descriptor for the given prototype.
+ */
+const char* dexProtoGetMethodDescriptor(const DexProto* pProto,
+    DexStringCache* pCache);
+
+/*
+ * Get a copy of the descriptor string associated with the given prototype.
+ * The returned pointer must be free()ed by the caller.
+ */
+char* dexProtoCopyMethodDescriptor(const DexProto* pProto);
+
+/*
+ * Get the parameter descriptors for the given prototype. This is the
+ * concatenation of all the descriptors for all the parameters, in
+ * order, with no other adornment.
+ */
+const char* dexProtoGetParameterDescriptors(const DexProto* pProto,
+    DexStringCache* pCache);
+
+/*
+ * Return the utf-8 encoded descriptor string from the proto of a MethodId.
+ */
+DEX_INLINE const char* dexGetDescriptorFromMethodId(const DexFile* pDexFile,
+        const DexMethodId* pMethodId, DexStringCache* pCache)
+{
+    DexProto proto;
+
+    dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+    return dexProtoGetMethodDescriptor(&proto, pCache);
+}
+
+/*
+ * Get a copy of the utf-8 encoded method descriptor string from the
+ * proto of a MethodId. The returned pointer must be free()ed by the
+ * caller.
+ */
+DEX_INLINE char* dexCopyDescriptorFromMethodId(const DexFile* pDexFile,
+    const DexMethodId* pMethodId)
+{
+    DexProto proto;
+
+    dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+    return dexProtoCopyMethodDescriptor(&proto);
+}
+
+/*
+ * Get the type descriptor for the return type of the given prototype.
+ */
+const char* dexProtoGetReturnType(const DexProto* pProto);
+
+/*
+ * Get the parameter count of the given prototype.
+ */
+size_t dexProtoGetParameterCount(const DexProto* pProto);
+
+/*
+ * Compute the number of parameter words (u4 units) required by the
+ * given prototype. For example, if the method takes (int, long) and
+ * returns double, this would return 3 (one for the int, two for the
+ * long, and the return type isn't relevant).
+ */
+int dexProtoComputeArgsSize(const DexProto* pProto);
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the return type as the major order, then the first arguments,
+ * then second, etc. If two prototypes are identical except that one
+ * has extra arguments, then the shorter argument is considered the
+ * earlier one in sort order (similar to strcmp()).
+ */
+int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2);
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the first argument as the major order, then second, etc. If two
+ * prototypes are identical except that one has extra arguments, then the
+ * shorter argument is considered the earlier one in sort order (similar
+ * to strcmp()).
+ */
+int dexProtoCompareParameters(const DexProto* pProto1, const DexProto* pProto2);
+
+/*
+ * Compare a prototype and a string method descriptor. The comparison
+ * is done as if the descriptor were converted to a prototype and compared
+ * with dexProtoCompare().
+ */
+int dexProtoCompareToDescriptor(const DexProto* proto, const char* descriptor);
+
+/*
+ * Single-thread prototype parameter iterator. This structure holds a
+ * pointer to a prototype and its parts, along with a cursor.
+ */
+typedef struct DexParameterIterator {
+    const DexProto* proto;
+    const DexTypeList* parameters;
+    int parameterCount;
+    int cursor;
+} DexParameterIterator;
+
+/*
+ * Initialize the given DexParameterIterator to be at the start of the
+ * parameters of the given prototype.
+ */
+void dexParameterIteratorInit(DexParameterIterator* pIterator,
+        const DexProto* pProto);
+
+/*
+ * Get the type_id index for the next parameter, if any. This returns
+ * kDexNoIndex if the last parameter has already been consumed.
+ */
+u4 dexParameterIteratorNextIndex(DexParameterIterator* pIterator);
+
+/*
+ * Get the type descriptor for the next parameter, if any. This returns
+ * NULL if the last parameter has already been consumed.
+ */
+const char* dexParameterIteratorNextDescriptor(
+        DexParameterIterator* pIterator);
+
+
+
+#endif /*_LIBDEX_DEXPROTO*/
diff --git a/libdex/DexSwapVerify.c b/libdex/DexSwapVerify.c
new file mode 100644
index 0000000..a467fa7
--- /dev/null
+++ b/libdex/DexSwapVerify.c
@@ -0,0 +1,2944 @@
+/*
+ * 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.
+ */
+
+/*
+ * Byte-swapping and verification of dex files.
+ */
+
+#include "DexFile.h"
+#include "DexClass.h"
+#include "DexDataMap.h"
+#include "DexProto.h"
+#include "Leb128.h"
+
+#include <safe_iop.h>
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __BYTE_ORDER
+# error "byte ordering not defined"
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define SWAP2(_value)      (_value)
+# define SWAP4(_value)      (_value)
+# define SWAP8(_value)      (_value)
+#else
+# define SWAP2(_value)      endianSwapU2((_value))
+# define SWAP4(_value)      endianSwapU4((_value))
+# define SWAP8(_value)      endianSwapU8((_value))
+static u2 endianSwapU2(u2 value) {
+    return (value >> 8) | (value << 8);
+}
+static u4 endianSwapU4(u4 value) {
+    /* ABCD --> CDAB --> DCBA */
+    value = (value >> 16) | (value << 16);
+    return ((value & 0xff00ff00) >> 8) | ((value << 8) & 0xff00ff00);
+}
+static u8 endianSwapU8(u8 value) {
+    /* ABCDEFGH --> EFGHABCD --> GHEFCDAB --> HGFEDCBA */
+    value = (value >> 32) | (value << 32);
+    value = ((value & 0xffff0000ffff0000ULL) >> 16) |
+            ((value << 16) & 0xffff0000ffff0000ULL);
+    return ((value & 0xff00ff00ff00ff00ULL) >> 8) |
+           ((value << 8) & 0xff00ff00ff00ff00ULL);
+}
+#endif
+
+#define SWAP_FIELD2(_field) (_field) = SWAP2(_field)
+#define SWAP_FIELD4(_field) (_field) = SWAP4(_field)
+#define SWAP_FIELD8(_field) (_field) = SWAP8(_field)
+
+/*
+ * Some information we pass around to help verify values.
+ */
+typedef struct CheckState {
+    const DexHeader*  pHeader;
+    const u1*         fileStart;
+    const u1*         fileEnd;      // points to fileStart + fileLen
+    u4                fileLen;
+    DexDataMap*       pDataMap;     // set after map verification
+    const DexFile*    pDexFile;     // set after intraitem verification
+
+    /*
+     * bitmap of type_id indices that have been used to define classes;
+     * initialized immediately before class_def cross-verification, and
+     * freed immediately after it
+     */
+    u4*               pDefinedClassBits;
+
+    const void*       previousItem; // set during section iteration
+} CheckState;
+
+/*
+ * Return the file offset of the given pointer.
+ */
+static inline u4 fileOffset(const CheckState* state, const void* ptr) {
+    return ((const u1*) ptr) - state->fileStart;
+}
+
+/*
+ * Return a pointer for the given file offset.
+ */
+static inline void* filePointer(const CheckState* state, u4 offset) {
+    return (void*) (state->fileStart + offset);
+}
+
+/*
+ * Verify that a pointer range, start inclusive to end exclusive, only
+ * covers bytes in the file and doesn't point beyond the end of the
+ * file. That is, the start must indicate a valid byte or may point at
+ * the byte just past the end of the file (but no further), and the
+ * end must be no less than the start and must also not point beyond
+ * the byte just past the end of the file.
+ */
+static inline bool checkPtrRange(const CheckState* state,
+        const void* start, const void* end, const char* label) {
+    const void* fileStart = state->fileStart;
+    const void* fileEnd = state->fileEnd;
+    if ((start < fileStart) || (start > fileEnd)
+            || (end < start) || (end > fileEnd)) {
+        LOGW("Bad offset range for %s: 0x%x..0x%x\n", label,
+                fileOffset(state, start), fileOffset(state, end));
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Verify that a range of offsets, start inclusive to end exclusive,
+ * are all valid. That is, the start must indicate a valid byte or may
+ * point at the byte just past the end of the file (but no further),
+ * and the end must be no less than the start and must also not point
+ * beyond the byte just past the end of the file.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define CHECK_OFFSET_RANGE(_start, _end) {                                  \
+        const u1* _startPtr = filePointer(state, (_start));                 \
+        const u1* _endPtr = filePointer(state, (_end));                     \
+        if (!checkPtrRange(state, _startPtr, _endPtr,                       \
+                        #_start ".." #_end)) {                              \
+            return 0;                                                       \
+        }                                                                   \
+    }
+
+/*
+ * Verify that a pointer range, start inclusive to end exclusive, only
+ * covers bytes in the file and doesn't point beyond the end of the
+ * file. That is, the start must indicate a valid byte or may point at
+ * the byte just past the end of the file (but no further), and the
+ * end must be no less than the start and must also not point beyond
+ * the byte just past the end of the file.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define CHECK_PTR_RANGE(_start, _end) {                                     \
+        if (!checkPtrRange(state, (_start), (_end), #_start ".." #_end)) {  \
+            return 0;                                                       \
+        }                                                                   \
+    }
+
+/*
+ * Make sure a list of items fits entirely within the file.
+ *
+ * Assumes "const CheckState* state" and "typeof(_count) == typeof(_elemSize)"
+ * If the type sizes or signs are mismatched, this will return 0.
+ */
+#define CHECK_LIST_SIZE(_ptr, _count, _elemSize) {                          \
+        const u1* _start = (const u1*) (_ptr);                              \
+        const u1* _end = _start + ((_count) * (_elemSize));                 \
+        if (!safe_mul(NULL, (_count), (_elemSize)) ||                       \
+            !checkPtrRange(state, _start, _end, #_ptr)) {                   \
+            return 0;                                                       \
+        }                                                                   \
+    }
+
+/*
+ * Swap a field that is known to hold an absolute DEX file offset. Note:
+ * This does not check to see that the swapped offset points within the
+ * mapped file, since that should be handled (with even more rigor) by
+ * the cross-verification phase.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define SWAP_OFFSET4(_field) {                                              \
+        SWAP_FIELD4((_field));                                              \
+    }
+
+/*
+ * Verify that an index falls in a valid range.
+ */
+#define CHECK_INDEX(_field, _limit) {                                       \
+        if ((_field) >= (_limit)) {                                         \
+            LOGW("Bad index: %s(%u) > %s(%u)\n",                            \
+                #_field, (u4)(_field), #_limit, (u4)(_limit));              \
+            return 0;                                                       \
+        }                                                                   \
+    }
+
+/*
+ * Swap an index, and verify that it falls in a valid range.
+ */
+#define SWAP_INDEX2(_field, _limit) {                                       \
+        SWAP_FIELD2((_field));                                              \
+        CHECK_INDEX((_field), (_limit));                                    \
+    }
+
+/*
+ * Verify that an index falls in a valid range or is kDexNoIndex.
+ */
+#define CHECK_INDEX_OR_NOINDEX(_field, _limit) {                            \
+        if ((_field) != kDexNoIndex && (_field) >= (_limit)) {              \
+            LOGW("Bad index: %s(%u) > %s(%u)\n",                            \
+                #_field, (u4)(_field), #_limit, (u4)(_limit));              \
+            return 0;                                                       \
+        }                                                                   \
+    }
+
+/*
+ * Swap an index, and verify that it falls in a valid range.
+ */
+#define SWAP_INDEX4(_field, _limit) {                                       \
+        SWAP_FIELD4((_field));                                              \
+        CHECK_INDEX((_field), (_limit));                                    \
+    }
+
+/*
+ * Swap an index, and verify that it falls in a valid range or is
+ * kDexNoIndex.
+ */
+#define SWAP_INDEX4_OR_NOINDEX(_field, _limit) {                            \
+        SWAP_FIELD4((_field));                                              \
+        CHECK_INDEX_OR_NOINDEX((_field), (_limit));                         \
+    }
+
+/* Verify the definer of a given field_idx. */
+static bool verifyFieldDefiner(const CheckState* state, u4 definingClass,
+        u4 fieldIdx) {
+    const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+    return field->classIdx == definingClass;
+}
+
+/* Verify the definer of a given method_idx. */
+static bool verifyMethodDefiner(const CheckState* state, u4 definingClass,
+        u4 methodIdx) {
+    const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+    return meth->classIdx == definingClass;
+}
+
+/*
+ * Calculate the required size (in elements) of the array pointed at by
+ * pDefinedClassBits.
+ */
+static size_t calcDefinedClassBitsSize(const CheckState* state)
+{
+    // Divide typeIdsSize by 32 (0x20), rounding up.
+    return (state->pHeader->typeIdsSize + 0x1f) >> 5;
+}
+
+/*
+ * Set the given bit in pDefinedClassBits, returning its former value.
+ */
+static bool setDefinedClassBit(const CheckState* state, u4 typeIdx) {
+    u4 arrayIdx = typeIdx >> 5;
+    u4 bit = 1 << (typeIdx & 0x1f);
+    u4* element = &state->pDefinedClassBits[arrayIdx];
+    bool result = (*element & bit) != 0;
+
+    *element |= bit;
+
+    return result;
+}
+
+/*
+ * Swap the header_item.
+ */
+static bool swapDexHeader(const CheckState* state, DexHeader* pHeader)
+{
+    CHECK_PTR_RANGE(pHeader, pHeader + 1);
+
+    // magic is ok
+    SWAP_FIELD4(pHeader->checksum);
+    // signature is ok
+    SWAP_FIELD4(pHeader->fileSize);
+    SWAP_FIELD4(pHeader->headerSize);
+    SWAP_FIELD4(pHeader->endianTag);
+    SWAP_FIELD4(pHeader->linkSize);
+    SWAP_OFFSET4(pHeader->linkOff);
+    SWAP_OFFSET4(pHeader->mapOff);
+    SWAP_FIELD4(pHeader->stringIdsSize);
+    SWAP_OFFSET4(pHeader->stringIdsOff);
+    SWAP_FIELD4(pHeader->typeIdsSize);
+    SWAP_OFFSET4(pHeader->typeIdsOff);
+    SWAP_FIELD4(pHeader->fieldIdsSize);
+    SWAP_OFFSET4(pHeader->fieldIdsOff);
+    SWAP_FIELD4(pHeader->methodIdsSize);
+    SWAP_OFFSET4(pHeader->methodIdsOff);
+    SWAP_FIELD4(pHeader->protoIdsSize);
+    SWAP_OFFSET4(pHeader->protoIdsOff);
+    SWAP_FIELD4(pHeader->classDefsSize);
+    SWAP_OFFSET4(pHeader->classDefsOff);
+    SWAP_FIELD4(pHeader->dataSize);
+    SWAP_OFFSET4(pHeader->dataOff);
+
+    if (pHeader->endianTag != kDexEndianConstant) {
+        LOGE("Unexpected endian_tag: 0x%x\n", pHeader->endianTag);
+        return false;
+    }
+
+    // Assign variables so the diagnostic is prettier. (Hooray for macros.)
+    u4 linkOff = pHeader->linkOff;
+    u4 linkEnd = linkOff + pHeader->linkSize;
+    u4 dataOff = pHeader->dataOff;
+    u4 dataEnd = dataOff + pHeader->dataSize;
+    CHECK_OFFSET_RANGE(linkOff, linkEnd);
+    CHECK_OFFSET_RANGE(dataOff, dataEnd);
+
+    /*
+     * Note: The offsets and ranges of the other header items end up getting
+     * checked during the first iteration over the map.
+     */
+
+    return true;
+}
+
+/* Check the header section for sanity. */
+static bool checkHeaderSection(const CheckState* state, u4 sectionOffset,
+        u4 sectionCount, u4* endOffset) {
+    if (sectionCount != 1) {
+        LOGE("Multiple header items\n");
+        return false;
+    }
+
+    if (sectionOffset != 0) {
+        LOGE("Header at 0x%x; not at start of file\n", sectionOffset);
+        return false;
+    }
+
+    const DexHeader* pHeader = filePointer(state, 0);
+    *endOffset = pHeader->headerSize;
+    return true;
+}
+
+/*
+ * Helper for swapMap(), which turns a map type constant into a small
+ * one-bit-on integer, suitable for use in an int-sized bit set.
+ */
+static u4 mapTypeToBitMask(int mapType) {
+    switch (mapType) {
+        case kDexTypeHeaderItem:               return 1 << 0;
+        case kDexTypeStringIdItem:             return 1 << 1;
+        case kDexTypeTypeIdItem:               return 1 << 2;
+        case kDexTypeProtoIdItem:              return 1 << 3;
+        case kDexTypeFieldIdItem:              return 1 << 4;
+        case kDexTypeMethodIdItem:             return 1 << 5;
+        case kDexTypeClassDefItem:             return 1 << 6;
+        case kDexTypeMapList:                  return 1 << 7;
+        case kDexTypeTypeList:                 return 1 << 8;
+        case kDexTypeAnnotationSetRefList:     return 1 << 9;
+        case kDexTypeAnnotationSetItem:        return 1 << 10;
+        case kDexTypeClassDataItem:            return 1 << 11;
+        case kDexTypeCodeItem:                 return 1 << 12;
+        case kDexTypeStringDataItem:           return 1 << 13;
+        case kDexTypeDebugInfoItem:            return 1 << 14;
+        case kDexTypeAnnotationItem:           return 1 << 15;
+        case kDexTypeEncodedArrayItem:         return 1 << 16;
+        case kDexTypeAnnotationsDirectoryItem: return 1 << 17;
+        default: {
+            LOGE("Unknown map item type %04x\n", mapType);
+            return 0;
+        }
+    }
+}
+
+/*
+ * Helper for swapMap(), which indicates if an item type should appear
+ * in the data section.
+ */
+static bool isDataSectionType(int mapType) {
+    switch (mapType) {
+        case kDexTypeHeaderItem:
+        case kDexTypeStringIdItem:
+        case kDexTypeTypeIdItem:
+        case kDexTypeProtoIdItem:
+        case kDexTypeFieldIdItem:
+        case kDexTypeMethodIdItem:
+        case kDexTypeClassDefItem: {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Swap the map_list and verify what we can about it. Also, if verification
+ * passes, allocate the state's DexDataMap.
+ */
+static bool swapMap(CheckState* state, DexMapList* pMap)
+{
+    DexMapItem* item = pMap->list;
+    u4 count;
+    u4 dataItemCount = 0; // Total count of items in the data section.
+    u4 dataItemsLeft = state->pHeader->dataSize; // See use below.
+    u4 usedBits = 0;      // Bit set: one bit per section
+    bool first = true;
+    u4 lastOffset = 0;
+
+    SWAP_FIELD4(pMap->size);
+    count = pMap->size;
+
+    CHECK_LIST_SIZE(item, count, sizeof(DexMapItem));
+
+    while (count--) {
+        SWAP_FIELD2(item->type);
+        SWAP_FIELD2(item->unused);
+        SWAP_FIELD4(item->size);
+        SWAP_OFFSET4(item->offset);
+
+        if (first) {
+            first = false;
+        } else if (lastOffset >= item->offset) {
+            LOGE("Out-of-order map item: 0x%x then 0x%x\n",
+                    lastOffset, item->offset);
+            return false;
+        }
+
+        if (item->offset >= state->pHeader->fileSize) {
+            LOGE("Map item after end of file: %x, size 0x%x\n",
+                    item->offset, state->pHeader->fileSize);
+            return false;
+        }
+
+        if (isDataSectionType(item->type)) {
+            u4 icount = item->size;
+
+            /*
+             * This sanity check on the data section items ensures that
+             * there are no more items than the number of bytes in
+             * the data section.
+             */
+            if (icount > dataItemsLeft) {
+                LOGE("Unrealistically many items in the data section: "
+                        "at least %d\n", dataItemCount + icount);
+                return false;
+            }
+
+            dataItemsLeft -= icount;
+            dataItemCount += icount;
+        }
+
+        u4 bit = mapTypeToBitMask(item->type);
+
+        if (bit == 0) {
+            return false;
+        }
+
+        if ((usedBits & bit) != 0) {
+            LOGE("Duplicate map section of type 0x%x\n", item->type);
+            return false;
+        }
+
+        usedBits |= bit;
+        lastOffset = item->offset;
+        item++;
+    }
+
+    if ((usedBits & mapTypeToBitMask(kDexTypeHeaderItem)) == 0) {
+        LOGE("Map is missing header entry\n");
+        return false;
+    }
+
+    if ((usedBits & mapTypeToBitMask(kDexTypeMapList)) == 0) {
+        LOGE("Map is missing map_list entry\n");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeStringIdItem)) == 0)
+            && ((state->pHeader->stringIdsOff != 0)
+                    || (state->pHeader->stringIdsSize != 0))) {
+        LOGE("Map is missing string_ids entry\n");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeTypeIdItem)) == 0)
+            && ((state->pHeader->typeIdsOff != 0)
+                    || (state->pHeader->typeIdsSize != 0))) {
+        LOGE("Map is missing type_ids entry\n");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeProtoIdItem)) == 0)
+            && ((state->pHeader->protoIdsOff != 0)
+                    || (state->pHeader->protoIdsSize != 0))) {
+        LOGE("Map is missing proto_ids entry\n");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeFieldIdItem)) == 0)
+            && ((state->pHeader->fieldIdsOff != 0)
+                    || (state->pHeader->fieldIdsSize != 0))) {
+        LOGE("Map is missing field_ids entry\n");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeMethodIdItem)) == 0)
+            && ((state->pHeader->methodIdsOff != 0)
+                    || (state->pHeader->methodIdsSize != 0))) {
+        LOGE("Map is missing method_ids entry\n");
+        return false;
+    }
+
+    if (((usedBits & mapTypeToBitMask(kDexTypeClassDefItem)) == 0)
+            && ((state->pHeader->classDefsOff != 0)
+                    || (state->pHeader->classDefsSize != 0))) {
+        LOGE("Map is missing class_defs entry\n");
+        return false;
+    }
+
+    state->pDataMap = dexDataMapAlloc(dataItemCount);
+    if (state->pDataMap == NULL) {
+        LOGE("Unable to allocate data map (size 0x%x)\n", dataItemCount);
+        return false;
+    }
+
+    return true;
+}
+
+/* Check the map section for sanity. */
+static bool checkMapSection(const CheckState* state, u4 sectionOffset,
+        u4 sectionCount, u4* endOffset) {
+    if (sectionCount != 1) {
+        LOGE("Multiple map list items");
+        return false;
+    }
+
+    if (sectionOffset != state->pHeader->mapOff) {
+        LOGE("Map not at header-defined offset: 0x%x, expected 0x%x\n",
+                sectionOffset, state->pHeader->mapOff);
+        return false;
+    }
+
+    const DexMapList* pMap = filePointer(state, sectionOffset);
+
+    *endOffset =
+        sectionOffset + sizeof(u4) + (pMap->size * sizeof(DexMapItem));
+    return true;
+}
+
+/* Perform byte-swapping and intra-item verification on string_id_item. */
+static void* swapStringIdItem(const CheckState* state, void* ptr) {
+    DexStringId* item = ptr;
+
+    CHECK_PTR_RANGE(item, item + 1);
+    SWAP_OFFSET4(item->stringDataOff);
+
+    return item + 1;
+}
+
+/* Perform cross-item verification of string_id_item. */
+static void* crossVerifyStringIdItem(const CheckState* state, void* ptr) {
+    const DexStringId* item = ptr;
+
+    if (!dexDataMapVerify(state->pDataMap,
+                    item->stringDataOff, kDexTypeStringDataItem)) {
+        return NULL;
+    }
+
+    const DexStringId* item0 = state->previousItem;
+    if (item0 != NULL) {
+        // Check ordering.
+        const char* s0 = dexGetStringData(state->pDexFile, item0);
+        const char* s1 = dexGetStringData(state->pDexFile, item);
+        if (dexUtf8Cmp(s0, s1) >= 0) {
+            LOGE("Out-of-order string_ids: '%s' then '%s'\n", s0, s1);
+            return NULL;
+        }
+    }
+
+    return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on type_id_item. */
+static void* swapTypeIdItem(const CheckState* state, void* ptr) {
+    DexTypeId* item = ptr;
+
+    CHECK_PTR_RANGE(item, item + 1);
+    SWAP_INDEX4(item->descriptorIdx, state->pHeader->stringIdsSize);
+
+    return item + 1;
+}
+
+/* Perform cross-item verification of type_id_item. */
+static void* crossVerifyTypeIdItem(const CheckState* state, void* ptr) {
+    const DexTypeId* item = ptr;
+    const char* descriptor =
+        dexStringById(state->pDexFile, item->descriptorIdx);
+
+    if (!dexIsValidTypeDescriptor(descriptor)) {
+        LOGE("Invalid type descriptor: '%s'\n", descriptor);
+        return NULL;
+    }
+
+    const DexTypeId* item0 = state->previousItem;
+    if (item0 != NULL) {
+        // Check ordering. This relies on string_ids being in order.
+        if (item0->descriptorIdx >= item->descriptorIdx) {
+            LOGE("Out-of-order type_ids: 0x%x then 0x%x\n",
+                    item0->descriptorIdx, item->descriptorIdx);
+            return NULL;
+        }
+    }
+
+    return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on proto_id_item. */
+static void* swapProtoIdItem(const CheckState* state, void* ptr) {
+    DexProtoId* item = ptr;
+
+    CHECK_PTR_RANGE(item, item + 1);
+    SWAP_INDEX4(item->shortyIdx, state->pHeader->stringIdsSize);
+    SWAP_INDEX4(item->returnTypeIdx, state->pHeader->typeIdsSize);
+    SWAP_OFFSET4(item->parametersOff);
+
+    return item + 1;
+}
+
+/* Helper for crossVerifyProtoIdItem(), which checks a shorty character
+ * to see if it is compatible with a type descriptor. Returns true if
+ * so, false if not. */
+static bool shortyDescMatch(char shorty, const char* descriptor, bool
+        isReturnType) {
+    switch (shorty) {
+        case 'V': {
+            if (!isReturnType) {
+                LOGE("Invalid use of void\n");
+                return false;
+            }
+            // Fall through.
+        }
+        case 'B':
+        case 'C':
+        case 'D':
+        case 'F':
+        case 'I':
+        case 'J':
+        case 'S':
+        case 'Z': {
+            if ((descriptor[0] != shorty) || (descriptor[1] != '\0')) {
+                LOGE("Shorty vs. primitive type mismatch: '%c', '%s'\n",
+                        shorty, descriptor);
+                return false;
+            }
+            break;
+        }
+        case 'L': {
+            if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+                LOGE("Shorty vs. type mismatch: '%c', '%s'\n",
+                        shorty, descriptor);
+                return false;
+            }
+            break;
+        }
+        default: {
+            LOGE("Bogus shorty: '%c'\n", shorty);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/* Perform cross-item verification of proto_id_item. */
+static void* crossVerifyProtoIdItem(const CheckState* state, void* ptr) {
+    const DexProtoId* item = ptr;
+    const char* shorty =
+        dexStringById(state->pDexFile, item->shortyIdx);
+
+    if (!dexDataMapVerify0Ok(state->pDataMap,
+                    item->parametersOff, kDexTypeTypeList)) {
+        return NULL;
+    }
+
+    if (!shortyDescMatch(*shorty,
+                    dexStringByTypeIdx(state->pDexFile, item->returnTypeIdx),
+                    true)) {
+        return NULL;
+    }
+
+    u4 protoIdx = item - state->pDexFile->pProtoIds;
+    DexProto proto = { state->pDexFile, protoIdx };
+    DexParameterIterator iterator;
+
+    dexParameterIteratorInit(&iterator, &proto);
+    shorty++; // Skip the return type.
+
+    for (;;) {
+        const char *desc = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (desc == NULL) {
+            break;
+        }
+
+        if (*shorty == '\0') {
+            LOGE("Shorty is too short\n");
+            return NULL;
+        }
+
+        if (!shortyDescMatch(*shorty, desc, false)) {
+            return NULL;
+        }
+
+        shorty++;
+    }
+
+    if (*shorty != '\0') {
+        LOGE("Shorty is too long\n");
+        return NULL;
+    }
+
+    const DexProtoId* item0 = state->previousItem;
+    if (item0 != NULL) {
+        // Check ordering. This relies on type_ids being in order.
+        if (item0->returnTypeIdx > item->returnTypeIdx) {
+            LOGE("Out-of-order proto_id return types\n");
+            return NULL;
+        } else if (item0->returnTypeIdx == item->returnTypeIdx) {
+            bool badOrder = false;
+            DexProto proto0 = { state->pDexFile, protoIdx - 1 };
+            DexParameterIterator iterator0;
+
+            dexParameterIteratorInit(&iterator, &proto);
+            dexParameterIteratorInit(&iterator0, &proto0);
+
+            for (;;) {
+                u4 idx0 = dexParameterIteratorNextIndex(&iterator0);
+                u4 idx1 = dexParameterIteratorNextIndex(&iterator);
+
+                if (idx1 == kDexNoIndex) {
+                    badOrder = true;
+                    break;
+                }
+
+                if (idx0 == kDexNoIndex) {
+                    break;
+                }
+
+                if (idx0 < idx1) {
+                    break;
+                } else if (idx0 > idx1) {
+                    badOrder = true;
+                    break;
+                }
+            }
+
+            if (badOrder) {
+                LOGE("Out-of-order proto_id arguments\n");
+                return NULL;
+            }
+        }
+    }
+
+    return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on field_id_item. */
+static void* swapFieldIdItem(const CheckState* state, void* ptr) {
+    DexFieldId* item = ptr;
+
+    CHECK_PTR_RANGE(item, item + 1);
+    SWAP_INDEX2(item->classIdx, state->pHeader->typeIdsSize);
+    SWAP_INDEX2(item->typeIdx, state->pHeader->typeIdsSize);
+    SWAP_INDEX4(item->nameIdx, state->pHeader->stringIdsSize);
+
+    return item + 1;
+}
+
+/* Perform cross-item verification of field_id_item. */
+static void* crossVerifyFieldIdItem(const CheckState* state, void* ptr) {
+    const DexFieldId* item = ptr;
+    const char* s;
+
+    s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+    if (!dexIsClassDescriptor(s)) {
+        LOGE("Invalid descriptor for class_idx: '%s'\n", s);
+        return NULL;
+    }
+
+    s = dexStringByTypeIdx(state->pDexFile, item->typeIdx);
+    if (!dexIsFieldDescriptor(s)) {
+        LOGE("Invalid descriptor for type_idx: '%s'\n", s);
+        return NULL;
+    }
+
+    s = dexStringById(state->pDexFile, item->nameIdx);
+    if (!dexIsValidMemberName(s)) {
+        LOGE("Invalid name: '%s'\n", s);
+        return NULL;
+    }
+
+    const DexFieldId* item0 = state->previousItem;
+    if (item0 != NULL) {
+        // Check ordering. This relies on the other sections being in order.
+        bool done = false;
+        bool bogus = false;
+
+        if (item0->classIdx > item->classIdx) {
+            bogus = true;
+            done = true;
+        } else if (item0->classIdx < item->classIdx) {
+            done = true;
+        }
+
+        if (!done) {
+            if (item0->nameIdx > item->nameIdx) {
+                bogus = true;
+                done = true;
+            } else if (item0->nameIdx < item->nameIdx) {
+                done = true;
+            }
+        }
+
+        if (!done) {
+            if (item0->typeIdx >= item->typeIdx) {
+                bogus = true;
+            }
+        }
+
+        if (bogus) {
+            LOGE("Out-of-order field_ids\n");
+            return NULL;
+        }
+    }
+
+    return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on method_id_item. */
+static void* swapMethodIdItem(const CheckState* state, void* ptr) {
+    DexMethodId* item = ptr;
+
+    CHECK_PTR_RANGE(item, item + 1);
+    SWAP_INDEX2(item->classIdx, state->pHeader->typeIdsSize);
+    SWAP_INDEX2(item->protoIdx, state->pHeader->protoIdsSize);
+    SWAP_INDEX4(item->nameIdx, state->pHeader->stringIdsSize);
+
+    return item + 1;
+}
+
+/* Perform cross-item verification of method_id_item. */
+static void* crossVerifyMethodIdItem(const CheckState* state, void* ptr) {
+    const DexMethodId* item = ptr;
+    const char* s;
+
+    s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+    if (!dexIsReferenceDescriptor(s)) {
+        LOGE("Invalid descriptor for class_idx: '%s'\n", s);
+        return NULL;
+    }
+
+    s = dexStringById(state->pDexFile, item->nameIdx);
+    if (!dexIsValidMemberName(s)) {
+        LOGE("Invalid name: '%s'\n", s);
+        return NULL;
+    }
+
+    const DexMethodId* item0 = state->previousItem;
+    if (item0 != NULL) {
+        // Check ordering. This relies on the other sections being in order.
+        bool done = false;
+        bool bogus = false;
+
+        if (item0->classIdx > item->classIdx) {
+            bogus = true;
+            done = true;
+        } else if (item0->classIdx < item->classIdx) {
+            done = true;
+        }
+
+        if (!done) {
+            if (item0->nameIdx > item->nameIdx) {
+                bogus = true;
+                done = true;
+            } else if (item0->nameIdx < item->nameIdx) {
+                done = true;
+            }
+        }
+
+        if (!done) {
+            if (item0->protoIdx >= item->protoIdx) {
+                bogus = true;
+            }
+        }
+
+        if (bogus) {
+            LOGE("Out-of-order method_ids\n");
+            return NULL;
+        }
+    }
+
+    return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on class_def_item. */
+static void* swapClassDefItem(const CheckState* state, void* ptr) {
+    DexClassDef* item = ptr;
+
+    CHECK_PTR_RANGE(item, item + 1);
+    SWAP_INDEX4(item->classIdx, state->pHeader->typeIdsSize);
+    SWAP_FIELD4(item->accessFlags);
+    SWAP_INDEX4_OR_NOINDEX(item->superclassIdx, state->pHeader->typeIdsSize);
+    SWAP_OFFSET4(item->interfacesOff);
+    SWAP_INDEX4_OR_NOINDEX(item->sourceFileIdx, state->pHeader->stringIdsSize);
+    SWAP_OFFSET4(item->annotationsOff);
+    SWAP_OFFSET4(item->classDataOff);
+
+    return item + 1;
+}
+
+/* defined below */
+static u4 findFirstClassDataDefiner(const CheckState* state,
+        DexClassData* classData);
+static u4 findFirstAnnotationsDirectoryDefiner(const CheckState* state,
+        const DexAnnotationsDirectoryItem* dir);
+
+/* Helper for crossVerifyClassDefItem(), which checks a class_data_item to
+ * make sure all its references are to a given class. */
+static bool verifyClassDataIsForDef(const CheckState* state, u4 offset,
+        u4 definerIdx) {
+    if (offset == 0) {
+        return true;
+    }
+
+    const u1* data = filePointer(state, offset);
+    DexClassData* classData = dexReadAndVerifyClassData(&data, NULL);
+
+    if (classData == NULL) {
+        // Shouldn't happen, but bail here just in case.
+        return false;
+    }
+
+    /*
+     * The class_data_item verification ensures that
+     * it consistently refers to the same definer, so all we need to
+     * do is check the first one.
+     */
+    u4 dataDefiner = findFirstClassDataDefiner(state, classData);
+    bool result = (dataDefiner == definerIdx) || (dataDefiner == kDexNoIndex);
+
+    free(classData);
+    return result;
+}
+
+/* Helper for crossVerifyClassDefItem(), which checks an
+ * annotations_directory_item to make sure all its references are to a
+ * given class. */
+static bool verifyAnnotationsDirectoryIsForDef(const CheckState* state,
+        u4 offset, u4 definerIdx) {
+    if (offset == 0) {
+        return true;
+    }
+
+    const DexAnnotationsDirectoryItem* dir = filePointer(state, offset);
+    u4 annoDefiner = findFirstAnnotationsDirectoryDefiner(state, dir);
+
+    return (annoDefiner == definerIdx) || (annoDefiner == kDexNoIndex);
+}
+
+/* Perform cross-item verification of class_def_item. */
+static void* crossVerifyClassDefItem(const CheckState* state, void* ptr) {
+    const DexClassDef* item = ptr;
+    u4 classIdx = item->classIdx;
+    const char* descriptor = dexStringByTypeIdx(state->pDexFile, classIdx);
+
+    if (!dexIsClassDescriptor(descriptor)) {
+        LOGE("Invalid class: '%s'\n", descriptor);
+        return NULL;
+    }
+
+    if (setDefinedClassBit(state, classIdx)) {
+        LOGE("Duplicate class definition: '%s'\n", descriptor);
+        return NULL;
+    }
+
+    bool okay =
+        dexDataMapVerify0Ok(state->pDataMap,
+                item->interfacesOff, kDexTypeTypeList)
+        && dexDataMapVerify0Ok(state->pDataMap,
+                item->annotationsOff, kDexTypeAnnotationsDirectoryItem)
+        && dexDataMapVerify0Ok(state->pDataMap,
+                item->classDataOff, kDexTypeClassDataItem)
+        && dexDataMapVerify0Ok(state->pDataMap,
+                item->staticValuesOff, kDexTypeEncodedArrayItem);
+
+    if (!okay) {
+        return NULL;
+    }
+
+    if (item->superclassIdx != kDexNoIndex) {
+        descriptor = dexStringByTypeIdx(state->pDexFile, item->superclassIdx);
+        if (!dexIsClassDescriptor(descriptor)) {
+            LOGE("Invalid superclass: '%s'\n", descriptor);
+            return NULL;
+        }
+    }
+
+    const DexTypeList* interfaces =
+        dexGetInterfacesList(state->pDexFile, item);
+    if (interfaces != NULL) {
+        u4 size = interfaces->size;
+        u4 i;
+
+        /*
+         * Ensure that all interfaces refer to classes (not arrays or
+         * primitives).
+         */
+        for (i = 0; i < size; i++) {
+            descriptor = dexStringByTypeIdx(state->pDexFile,
+                    dexTypeListGetIdx(interfaces, i));
+            if (!dexIsClassDescriptor(descriptor)) {
+                LOGE("Invalid interface: '%s'\n", descriptor);
+                return NULL;
+            }
+        }
+
+        /*
+         * Ensure that there are no duplicates. This is an O(N^2) test,
+         * but in practice the number of interfaces implemented by any
+         * given class is low. I will buy a milkshake for the
+         * first person to show me a realistic case for which this test
+         * would be unacceptably slow.
+         */
+        for (i = 1; i < size; i++) {
+            u4 idx1 = dexTypeListGetIdx(interfaces, i);
+            u4 j;
+            for (j = 0; j < i; j++) {
+                u4 idx2 = dexTypeListGetIdx(interfaces, j);
+                if (idx1 == idx2) {
+                    LOGE("Duplicate interface: '%s'\n",
+                            dexStringByTypeIdx(state->pDexFile, idx1));
+                    return NULL;
+                }
+            }
+        }
+    }
+
+    if (!verifyClassDataIsForDef(state, item->classDataOff, item->classIdx)) {
+        LOGE("Invalid class_data_item\n");
+        return NULL;
+    }
+
+    if (!verifyAnnotationsDirectoryIsForDef(state, item->annotationsOff,
+                    item->classIdx)) {
+        LOGE("Invalid annotations_directory_item\n");
+        return NULL;
+    }
+
+    return (void*) (item + 1);
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's field elements. */
+static u1* swapFieldAnnotations(const CheckState* state, u4 count, u1* addr) {
+    DexFieldAnnotationsItem* item = (DexFieldAnnotationsItem*) addr;
+    bool first = true;
+    u4 lastIdx = 0;
+
+    CHECK_LIST_SIZE(item, count, sizeof(DexFieldAnnotationsItem));
+
+    while (count--) {
+        SWAP_INDEX4(item->fieldIdx, state->pHeader->fieldIdsSize);
+        SWAP_OFFSET4(item->annotationsOff);
+
+        if (first) {
+            first = false;
+        } else if (lastIdx >= item->fieldIdx) {
+            LOGE("Out-of-order field_idx: 0x%x then 0x%x\n", lastIdx,
+                 item->fieldIdx);
+            return NULL;
+        }
+
+        lastIdx = item->fieldIdx;
+        item++;
+    }
+
+    return (u1*) item;
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's method elements. */
+static u1* swapMethodAnnotations(const CheckState* state, u4 count, u1* addr) {
+    DexMethodAnnotationsItem* item = (DexMethodAnnotationsItem*) addr;
+    bool first = true;
+    u4 lastIdx = 0;
+
+    CHECK_LIST_SIZE(item, count, sizeof(DexMethodAnnotationsItem));
+
+    while (count--) {
+        SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+        SWAP_OFFSET4(item->annotationsOff);
+
+        if (first) {
+            first = false;
+        } else if (lastIdx >= item->methodIdx) {
+            LOGE("Out-of-order method_idx: 0x%x then 0x%x\n", lastIdx,
+                 item->methodIdx);
+            return NULL;
+        }
+
+        lastIdx = item->methodIdx;
+        item++;
+    }
+
+    return (u1*) item;
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's parameter elements. */
+static u1* swapParameterAnnotations(const CheckState* state, u4 count,
+        u1* addr) {
+    DexParameterAnnotationsItem* item = (DexParameterAnnotationsItem*) addr;
+    bool first = true;
+    u4 lastIdx = 0;
+
+    CHECK_LIST_SIZE(item, count, sizeof(DexParameterAnnotationsItem));
+
+    while (count--) {
+        SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+        SWAP_OFFSET4(item->annotationsOff);
+
+        if (first) {
+            first = false;
+        } else if (lastIdx >= item->methodIdx) {
+            LOGE("Out-of-order method_idx: 0x%x then 0x%x\n", lastIdx,
+                 item->methodIdx);
+            return NULL;
+        }
+
+        lastIdx = item->methodIdx;
+        item++;
+    }
+
+    return (u1*) item;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotations_directory_item. */
+static void* swapAnnotationsDirectoryItem(const CheckState* state, void* ptr) {
+    DexAnnotationsDirectoryItem* item = ptr;
+
+    CHECK_PTR_RANGE(item, item + 1);
+    SWAP_OFFSET4(item->classAnnotationsOff);
+    SWAP_FIELD4(item->fieldsSize);
+    SWAP_FIELD4(item->methodsSize);
+    SWAP_FIELD4(item->parametersSize);
+
+    u1* addr = (u1*) (item + 1);
+
+    if (item->fieldsSize != 0) {
+        addr = swapFieldAnnotations(state, item->fieldsSize, addr);
+        if (addr == NULL) {
+            return NULL;
+        }
+    }
+
+    if (item->methodsSize != 0) {
+        addr = swapMethodAnnotations(state, item->methodsSize, addr);
+        if (addr == NULL) {
+            return NULL;
+        }
+    }
+
+    if (item->parametersSize != 0) {
+        addr = swapParameterAnnotations(state, item->parametersSize, addr);
+        if (addr == NULL) {
+            return NULL;
+        }
+    }
+
+    return addr;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * field elements. */
+static const u1* crossVerifyFieldAnnotations(const CheckState* state, u4 count,
+        const u1* addr, u4 definingClass) {
+    const DexFieldAnnotationsItem* item = (DexFieldAnnotationsItem*) addr;
+
+    while (count--) {
+        if (!verifyFieldDefiner(state, definingClass, item->fieldIdx)) {
+            return NULL;
+        }
+        if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+                        kDexTypeAnnotationSetItem)) {
+            return NULL;
+        }
+        item++;
+    }
+
+    return (const u1*) item;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * method elements. */
+static const u1* crossVerifyMethodAnnotations(const CheckState* state,
+        u4 count, const u1* addr, u4 definingClass) {
+    const DexMethodAnnotationsItem* item = (DexMethodAnnotationsItem*) addr;
+
+    while (count--) {
+        if (!verifyMethodDefiner(state, definingClass, item->methodIdx)) {
+            return NULL;
+        }
+        if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+                        kDexTypeAnnotationSetItem)) {
+            return NULL;
+        }
+        item++;
+    }
+
+    return (const u1*) item;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * parameter elements. */
+static const u1* crossVerifyParameterAnnotations(const CheckState* state,
+        u4 count, const u1* addr, u4 definingClass) {
+    const DexParameterAnnotationsItem* item =
+        (DexParameterAnnotationsItem*) addr;
+
+    while (count--) {
+        if (!verifyMethodDefiner(state, definingClass, item->methodIdx)) {
+            return NULL;
+        }
+        if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+                        kDexTypeAnnotationSetRefList)) {
+            return NULL;
+        }
+        item++;
+    }
+
+    return (const u1*) item;
+}
+
+/* Helper for crossVerifyClassDefItem() and
+ * crossVerifyAnnotationsDirectoryItem(), which finds the type_idx of
+ * the definer of the first item in the data. */
+static u4 findFirstAnnotationsDirectoryDefiner(const CheckState* state,
+        const DexAnnotationsDirectoryItem* dir) {
+    if (dir->fieldsSize != 0) {
+        const DexFieldAnnotationsItem* fields =
+            dexGetFieldAnnotations(state->pDexFile, dir);
+        const DexFieldId* field =
+            dexGetFieldId(state->pDexFile, fields[0].fieldIdx);
+        return field->classIdx;
+    }
+
+    if (dir->methodsSize != 0) {
+        const DexMethodAnnotationsItem* methods =
+            dexGetMethodAnnotations(state->pDexFile, dir);
+        const DexMethodId* method =
+            dexGetMethodId(state->pDexFile, methods[0].methodIdx);
+        return method->classIdx;
+    }
+
+    if (dir->parametersSize != 0) {
+        const DexParameterAnnotationsItem* parameters =
+            dexGetParameterAnnotations(state->pDexFile, dir);
+        const DexMethodId* method =
+            dexGetMethodId(state->pDexFile, parameters[0].methodIdx);
+        return method->classIdx;
+    }
+
+    return kDexNoIndex;
+}
+
+/* Perform cross-item verification of annotations_directory_item. */
+static void* crossVerifyAnnotationsDirectoryItem(const CheckState* state,
+        void* ptr) {
+    const DexAnnotationsDirectoryItem* item = ptr;
+    u4 definingClass = findFirstAnnotationsDirectoryDefiner(state, item);
+
+    if (!dexDataMapVerify0Ok(state->pDataMap,
+                    item->classAnnotationsOff, kDexTypeAnnotationSetItem)) {
+        return NULL;
+    }
+
+    const u1* addr = (const u1*) (item + 1);
+
+    if (item->fieldsSize != 0) {
+        addr = crossVerifyFieldAnnotations(state, item->fieldsSize, addr,
+                definingClass);
+        if (addr == NULL) {
+            return NULL;
+        }
+    }
+
+    if (item->methodsSize != 0) {
+        addr = crossVerifyMethodAnnotations(state, item->methodsSize, addr,
+                definingClass);
+        if (addr == NULL) {
+            return NULL;
+        }
+    }
+
+    if (item->parametersSize != 0) {
+        addr = crossVerifyParameterAnnotations(state, item->parametersSize,
+                addr, definingClass);
+        if (addr == NULL) {
+            return NULL;
+        }
+    }
+
+    return (void*) addr;
+}
+
+/* Perform byte-swapping and intra-item verification on type_list. */
+static void* swapTypeList(const CheckState* state, void* ptr)
+{
+    DexTypeList* pTypeList = ptr;
+    DexTypeItem* pType;
+    u4 count;
+
+    CHECK_PTR_RANGE(pTypeList, pTypeList + 1);
+    SWAP_FIELD4(pTypeList->size);
+    count = pTypeList->size;
+    pType = pTypeList->list;
+    CHECK_LIST_SIZE(pType, count, sizeof(DexTypeItem));
+
+    while (count--) {
+        SWAP_INDEX2(pType->typeIdx, state->pHeader->typeIdsSize);
+        pType++;
+    }
+
+    return pType;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotation_set_ref_list. */
+static void* swapAnnotationSetRefList(const CheckState* state, void* ptr) {
+    DexAnnotationSetRefList* list = ptr;
+    DexAnnotationSetRefItem* item;
+    u4 count;
+
+    CHECK_PTR_RANGE(list, list + 1);
+    SWAP_FIELD4(list->size);
+    count = list->size;
+    item = list->list;
+    CHECK_LIST_SIZE(item, count, sizeof(DexAnnotationSetRefItem));
+
+    while (count--) {
+        SWAP_OFFSET4(item->annotationsOff);
+        item++;
+    }
+
+    return item;
+}
+
+/* Perform cross-item verification of annotation_set_ref_list. */
+static void* crossVerifyAnnotationSetRefList(const CheckState* state,
+        void* ptr) {
+    const DexAnnotationSetRefList* list = ptr;
+    const DexAnnotationSetRefItem* item = list->list;
+    int count = list->size;
+
+    while (count--) {
+        if (!dexDataMapVerify0Ok(state->pDataMap,
+                        item->annotationsOff, kDexTypeAnnotationSetItem)) {
+            return NULL;
+        }
+        item++;
+    }
+
+    return (void*) item;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotation_set_item. */
+static void* swapAnnotationSetItem(const CheckState* state, void* ptr) {
+    DexAnnotationSetItem* set = ptr;
+    u4* item;
+    u4 count;
+
+    CHECK_PTR_RANGE(set, set + 1);
+    SWAP_FIELD4(set->size);
+    count = set->size;
+    item = set->entries;
+    CHECK_LIST_SIZE(item, count, sizeof(u4));
+
+    while (count--) {
+        SWAP_OFFSET4(*item);
+        item++;
+    }
+
+    return item;
+}
+
+/* Helper for crossVerifyAnnotationSetItem(), which extracts the type_idx
+ * out of an annotation_item. */
+static u4 annotationItemTypeIdx(const DexAnnotationItem* item) {
+    const u1* data = item->annotation;
+    return readUnsignedLeb128(&data);
+}
+
+/* Perform cross-item verification of annotation_set_item. */
+static void* crossVerifyAnnotationSetItem(const CheckState* state, void* ptr) {
+    const DexAnnotationSetItem* set = ptr;
+    int count = set->size;
+    u4 lastIdx = 0;
+    bool first = true;
+    int i;
+
+    for (i = 0; i < count; i++) {
+        if (!dexDataMapVerify0Ok(state->pDataMap,
+                        dexGetAnnotationOff(set, i), kDexTypeAnnotationItem)) {
+            return NULL;
+        }
+
+        const DexAnnotationItem* annotation =
+            dexGetAnnotationItem(state->pDexFile, set, i);
+        u4 idx = annotationItemTypeIdx(annotation);
+
+        if (first) {
+            first = false;
+        } else if (lastIdx >= idx) {
+            LOGE("Out-of-order entry types: 0x%x then 0x%x\n",
+                    lastIdx, idx);
+            return NULL;
+        }
+
+        lastIdx = idx;
+    }
+
+    return (void*) (set->entries + count);
+}
+
+/* Helper for verifyClassDataItem(), which checks a list of fields. */
+static bool verifyFields(const CheckState* state, u4 size,
+        DexField* fields, bool expectStatic) {
+    u4 i;
+
+    for (i = 0; i < size; i++) {
+        DexField* field = &fields[i];
+        u4 accessFlags = field->accessFlags;
+        bool isStatic = (accessFlags & ACC_STATIC) != 0;
+
+        CHECK_INDEX(field->fieldIdx, state->pHeader->fieldIdsSize);
+
+        if (isStatic != expectStatic) {
+            LOGE("Field in wrong list @ %d\n", i);
+            return false;
+        }
+
+        if ((accessFlags & ~ACC_FIELD_MASK) != 0) {
+            LOGE("Bogus field access flags %x @ %d\n", accessFlags, i);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/* Helper for verifyClassDataItem(), which checks a list of methods. */
+static bool verifyMethods(const CheckState* state, u4 size,
+        DexMethod* methods, bool expectDirect) {
+    u4 i;
+
+    for (i = 0; i < size; i++) {
+        DexMethod* method = &methods[i];
+
+        CHECK_INDEX(method->methodIdx, state->pHeader->methodIdsSize);
+
+        u4 accessFlags = method->accessFlags;
+        bool isDirect =
+            (accessFlags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) != 0;
+        bool expectCode = (accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) == 0;
+        bool isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;
+        bool allowSynchronized = (accessFlags & ACC_NATIVE) != 0;
+
+        if (isDirect != expectDirect) {
+            LOGE("Method in wrong list @ %d\n", i);
+            return false;
+        }
+
+        if (((accessFlags & ~ACC_METHOD_MASK) != 0)
+                || (isSynchronized && !allowSynchronized)) {
+            LOGE("Bogus method access flags %x @ %d\n", accessFlags, i);
+            return false;
+        }
+
+        if (expectCode) {
+            if (method->codeOff == 0) {
+                LOGE("Unexpected zero code_off for access_flags %x\n",
+                        accessFlags);
+                return false;
+            }
+        } else if (method->codeOff != 0) {
+            LOGE("Unexpected non-zero code_off 0x%x for access_flags %x\n",
+                    method->codeOff, accessFlags);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/* Helper for verifyClassDataItem(), which does most of the work. */
+static bool verifyClassDataItem0(const CheckState* state,
+        DexClassData* classData) {
+    bool okay;
+
+    okay = verifyFields(state, classData->header.staticFieldsSize,
+            classData->staticFields, true);
+
+    if (!okay) {
+        LOGE("Trouble with static fields\n");
+        return false;
+    }
+
+    verifyFields(state, classData->header.instanceFieldsSize,
+            classData->instanceFields, false);
+
+    if (!okay) {
+        LOGE("Trouble with instance fields\n");
+        return false;
+    }
+
+    okay = verifyMethods(state, classData->header.directMethodsSize,
+            classData->directMethods, true);
+
+    if (!okay) {
+        LOGE("Trouble with direct methods\n");
+        return false;
+    }
+
+    okay = verifyMethods(state, classData->header.virtualMethodsSize,
+            classData->virtualMethods, false);
+
+    if (!okay) {
+        LOGE("Trouble with virtual methods\n");
+        return false;
+    }
+
+    return true;
+}
+
+/* Perform intra-item verification on class_data_item. */
+static void* intraVerifyClassDataItem(const CheckState* state, void* ptr) {
+    const u1* data = ptr;
+    DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+
+    if (classData == NULL) {
+        LOGE("Unable to parse class_data_item\n");
+        return NULL;
+    }
+
+    bool okay = verifyClassDataItem0(state, classData);
+
+    free(classData);
+
+    if (!okay) {
+        return NULL;
+    }
+
+    return (void*) data;
+}
+
+/* Helper for crossVerifyClassDefItem() and
+ * crossVerifyClassDataItem(), which finds the type_idx of the definer
+ * of the first item in the data. */
+static u4 findFirstClassDataDefiner(const CheckState* state,
+        DexClassData* classData) {
+    if (classData->header.staticFieldsSize != 0) {
+        u4 fieldIdx = classData->staticFields[0].fieldIdx;
+        const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+        return field->classIdx;
+    }
+
+    if (classData->header.instanceFieldsSize != 0) {
+        u4 fieldIdx = classData->instanceFields[0].fieldIdx;
+        const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+        return field->classIdx;
+    }
+
+    if (classData->header.directMethodsSize != 0) {
+        u4 methodIdx = classData->directMethods[0].methodIdx;
+        const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+        return meth->classIdx;
+    }
+
+    if (classData->header.virtualMethodsSize != 0) {
+        u4 methodIdx = classData->virtualMethods[0].methodIdx;
+        const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+        return meth->classIdx;
+    }
+
+    return kDexNoIndex;
+}
+
+/* Perform cross-item verification of class_data_item. */
+static void* crossVerifyClassDataItem(const CheckState* state, void* ptr) {
+    const u1* data = ptr;
+    DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+    u4 definingClass = findFirstClassDataDefiner(state, classData);
+    bool okay = true;
+    u4 i;
+
+    for (i = classData->header.staticFieldsSize; okay && (i > 0); /*i*/) {
+        i--;
+        const DexField* field = &classData->staticFields[i];
+        okay = verifyFieldDefiner(state, definingClass, field->fieldIdx);
+    }
+
+    for (i = classData->header.instanceFieldsSize; okay && (i > 0); /*i*/) {
+        i--;
+        const DexField* field = &classData->instanceFields[i];
+        okay = verifyFieldDefiner(state, definingClass, field->fieldIdx);
+    }
+
+    for (i = classData->header.directMethodsSize; okay && (i > 0); /*i*/) {
+        i--;
+        const DexMethod* meth = &classData->directMethods[i];
+        okay = dexDataMapVerify0Ok(state->pDataMap, meth->codeOff,
+                kDexTypeCodeItem)
+            && verifyMethodDefiner(state, definingClass, meth->methodIdx);
+    }
+
+    for (i = classData->header.virtualMethodsSize; okay && (i > 0); /*i*/) {
+        i--;
+        const DexMethod* meth = &classData->virtualMethods[i];
+        okay = dexDataMapVerify0Ok(state->pDataMap, meth->codeOff,
+                kDexTypeCodeItem)
+            && verifyMethodDefiner(state, definingClass, meth->methodIdx);
+    }
+
+    free(classData);
+
+    if (!okay) {
+        return NULL;
+    }
+
+    return (void*) data;
+}
+
+/* Helper for swapCodeItem(), which fills an array with all the valid
+ * handlerOff values for catch handlers and also verifies the handler
+ * contents. */
+static u4 setHandlerOffsAndVerify(const CheckState* state,
+        DexCode* code, u4 firstOffset, u4 handlersSize, u4* handlerOffs) {
+    const u1* fileEnd = state->fileEnd;
+    const u1* handlersBase = dexGetCatchHandlerData(code);
+    u4 offset = firstOffset;
+    bool okay = true;
+    u4 i;
+
+    for (i = 0; i < handlersSize; i++) {
+        const u1* ptr = handlersBase + offset;
+        int size = readAndVerifySignedLeb128(&ptr, fileEnd, &okay);
+        bool catchAll;
+
+        if (!okay) {
+            LOGE("Bogus size\n");
+            return 0;
+        }
+
+        if ((size < -65536) || (size > 65536)) {
+            LOGE("Invalid size: %d\n", size);
+            return 0;
+        }
+
+        if (size <= 0) {
+            catchAll = true;
+            size = -size;
+        } else {
+            catchAll = false;
+        }
+
+        handlerOffs[i] = offset;
+
+        while (size-- > 0) {
+            u4 typeIdx =
+                readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+            if (!okay) {
+                LOGE("Bogus type_idx");
+                return 0;
+            }
+
+            CHECK_INDEX(typeIdx, state->pHeader->typeIdsSize);
+
+            u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+            if (!okay) {
+                LOGE("Bogus addr");
+                return 0;
+            }
+
+            if (addr >= code->insnsSize) {
+                LOGE("Invalid addr: 0x%x", addr);
+                return 0;
+            }
+        }
+
+        if (catchAll) {
+            u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+            if (!okay) {
+                LOGE("Bogus catch_all_addr");
+                return 0;
+            }
+
+            if (addr >= code->insnsSize) {
+                LOGE("Invalid catch_all_addr: 0x%x", addr);
+                return 0;
+            }
+        }
+
+        offset = ptr - handlersBase;
+    }
+
+    return offset;
+}
+
+/* Helper for swapCodeItem(), which does all the try-catch related
+ * swapping and verification. */
+static void* swapTriesAndCatches(const CheckState* state, DexCode* code) {
+    const u1* encodedHandlers = dexGetCatchHandlerData(code);
+    const u1* encodedPtr = encodedHandlers;
+    bool okay = true;
+    u4 handlersSize =
+        readAndVerifyUnsignedLeb128(&encodedPtr, state->fileEnd, &okay);
+
+    if (!okay) {
+        LOGE("Bogus handlers_size\n");
+        return NULL;
+    }
+
+    if ((handlersSize == 0) || (handlersSize >= 65536)) {
+        LOGE("Invalid handlers_size: %d\n", handlersSize);
+        return NULL;
+    }
+
+    u4 handlerOffs[handlersSize]; // list of valid handlerOff values
+    u4 endOffset = setHandlerOffsAndVerify(state, code,
+            encodedPtr - encodedHandlers,
+            handlersSize, handlerOffs);
+
+    if (endOffset == 0) {
+        return NULL;
+    }
+
+    DexTry* tries = (DexTry*) dexGetTries(code);
+    u4 count = code->triesSize;
+    u4 lastEnd = 0;
+
+    CHECK_LIST_SIZE(tries, count, sizeof(DexTry));
+
+    while (count--) {
+        u4 i;
+
+        SWAP_FIELD4(tries->startAddr);
+        SWAP_FIELD2(tries->insnCount);
+        SWAP_FIELD2(tries->handlerOff);
+
+        if (tries->startAddr < lastEnd) {
+            LOGE("Out-of-order try\n");
+            return NULL;
+        }
+
+        if (tries->startAddr >= code->insnsSize) {
+            LOGE("Invalid start_addr: 0x%x\n", tries->startAddr);
+            return NULL;
+        }
+
+        for (i = 0; i < handlersSize; i++) {
+            if (tries->handlerOff == handlerOffs[i]) {
+                break;
+            }
+        }
+
+        if (i == handlersSize) {
+            LOGE("Bogus handler offset: 0x%x\n", tries->handlerOff);
+            return NULL;
+        }
+
+        lastEnd = tries->startAddr + tries->insnCount;
+
+        if (lastEnd > code->insnsSize) {
+            LOGE("Invalid insn_count: 0x%x (end addr 0x%x)\n",
+                    tries->insnCount, lastEnd);
+            return NULL;
+        }
+
+        tries++;
+    }
+
+    return (u1*) encodedHandlers + endOffset;
+}
+
+/* Perform byte-swapping and intra-item verification on code_item. */
+static void* swapCodeItem(const CheckState* state, void* ptr) {
+    DexCode* item = ptr;
+    u2* insns;
+    u4 count;
+
+    CHECK_PTR_RANGE(item, item + 1);
+    SWAP_FIELD2(item->registersSize);
+    SWAP_FIELD2(item->insSize);
+    SWAP_FIELD2(item->outsSize);
+    SWAP_FIELD2(item->triesSize);
+    SWAP_OFFSET4(item->debugInfoOff);
+    SWAP_FIELD4(item->insnsSize);
+
+    if (item->insSize > item->registersSize) {
+        LOGE("insSize (%u) > registersSize (%u)\n", item->insSize,
+                item->registersSize);
+        return NULL;
+    }
+
+    if ((item->outsSize > 5) && (item->outsSize > item->registersSize)) {
+        /*
+         * It's okay for outsSize to be up to five, even if registersSize
+         * is smaller, since the short forms of method invocation allow
+         * repetition of a register multiple times within a single parameter
+         * list. Longer parameter lists, though, need to be represented
+         * in-order in the register file.
+         */
+        LOGE("outsSize (%u) > registersSize (%u)\n", item->outsSize,
+                item->registersSize);
+        return NULL;
+    }
+
+    count = item->insnsSize;
+    insns = item->insns;
+    CHECK_LIST_SIZE(insns, count, sizeof(u2));
+
+    while (count--) {
+        *insns = SWAP2(*insns);
+        insns++;
+    }
+
+    if (item->triesSize == 0) {
+        ptr = insns;
+    } else {
+        if ((((u4) insns) & 3) != 0) {
+            // Four-byte alignment for the tries. Verify the spacer is a 0.
+            if (*insns != 0) {
+                LOGE("Non-zero padding: 0x%x\n", (u4) *insns);
+                return NULL;
+            }
+        }
+
+        ptr = swapTriesAndCatches(state, item);
+    }
+
+    return ptr;
+}
+
+/* Perform intra-item verification on string_data_item. */
+static void* intraVerifyStringDataItem(const CheckState* state, void* ptr) {
+    const u1* fileEnd = state->fileEnd;
+    const u1* data = ptr;
+    bool okay = true;
+    u4 utf16Size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+    u4 i;
+
+    if (!okay) {
+        LOGE("Bogus utf16_size\n");
+        return NULL;
+    }
+
+    for (i = 0; i < utf16Size; i++) {
+        if (data >= fileEnd) {
+            LOGE("String data would go beyond end-of-file\n");
+            return NULL;
+        }
+
+        u1 byte1 = *(data++);
+
+        // Switch on the high four bits.
+        switch (byte1 >> 4) {
+            case 0x00: {
+                // Special case of bit pattern 0xxx.
+                if (byte1 == 0) {
+                    LOGE("String shorter than indicated utf16_size 0x%x\n",
+                            utf16Size);
+                    return NULL;
+                }
+                break;
+            }
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07: {
+                // Bit pattern 0xxx. No need for any extra bytes or checks.
+                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.
+                 */
+                LOGE("Illegal start byte 0x%x\n", byte1);
+                return NULL;
+            }
+            case 0x0e: {
+                // Bit pattern 1110, so there are two additional bytes.
+                u1 byte2 = *(data++);
+                if ((byte2 & 0xc0) != 0x80) {
+                    LOGE("Illegal continuation byte 0x%x\n", byte2);
+                    return NULL;
+                }
+                u1 byte3 = *(data++);
+                if ((byte3 & 0xc0) != 0x80) {
+                    LOGE("Illegal continuation byte 0x%x\n", byte3);
+                    return NULL;
+                }
+                u2 value = ((byte1 & 0x0f) << 12) | ((byte2 & 0x3f) << 6)
+                    | (byte3 & 0x3f);
+                if (value < 0x800) {
+                    LOGE("Illegal representation for value %x\n", value);
+                    return NULL;
+                }
+                break;
+            }
+            case 0x0c:
+            case 0x0d: {
+                // Bit pattern 110x, so there is one additional byte.
+                u1 byte2 = *(data++);
+                if ((byte2 & 0xc0) != 0x80) {
+                    LOGE("Illegal continuation byte 0x%x\n", byte2);
+                    return NULL;
+                }
+                u2 value = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f);
+                if ((value != 0) && (value < 0x80)) {
+                    LOGE("Illegal representation for value %x\n", value);
+                    return NULL;
+                }
+                break;
+            }
+        }
+    }
+
+    if (*(data++) != '\0') {
+        LOGE("String longer than indicated utf16_size 0x%x\n", utf16Size);
+        return NULL;
+    }
+
+    return (void*) data;
+}
+
+/* Perform intra-item verification on debug_info_item. */
+static void* intraVerifyDebugInfoItem(const CheckState* state, void* ptr) {
+    const u1* fileEnd = state->fileEnd;
+    const u1* data = ptr;
+    bool okay = true;
+    u4 i;
+
+    readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+    if (!okay) {
+        LOGE("Bogus line_start\n");
+        return NULL;
+    }
+
+    u4 parametersSize =
+        readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+    if (!okay) {
+        LOGE("Bogus parameters_size\n");
+        return NULL;
+    }
+
+    if (parametersSize > 65536) {
+        LOGE("Invalid parameters_size: 0x%x\n", parametersSize);
+        return NULL;
+    }
+
+    for (i = 0; i < parametersSize; i++) {
+        u4 parameterName =
+            readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+        if (!okay) {
+            LOGE("Bogus parameter_name\n");
+            return NULL;
+        }
+
+        if (parameterName != 0) {
+            parameterName--;
+            CHECK_INDEX(parameterName, state->pHeader->stringIdsSize);
+        }
+    }
+
+    bool done = false;
+    while (!done) {
+        u1 opcode = *(data++);
+
+        switch (opcode) {
+            case DBG_END_SEQUENCE: {
+                done = true;
+                break;
+            }
+            case DBG_ADVANCE_PC: {
+                readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                break;
+            }
+            case DBG_ADVANCE_LINE: {
+                readAndVerifySignedLeb128(&data, fileEnd, &okay);
+                break;
+            }
+            case DBG_START_LOCAL: {
+                u4 idx;
+                u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (regNum >= 65536) {
+                    okay = false;
+                    break;
+                }
+                idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (idx != 0) {
+                    idx--;
+                    CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+                }
+                idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (idx != 0) {
+                    idx--;
+                    CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+                }
+                break;
+            }
+            case DBG_END_LOCAL:
+            case DBG_RESTART_LOCAL: {
+                u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (regNum >= 65536) {
+                    okay = false;
+                    break;
+                }
+                break;
+            }
+            case DBG_START_LOCAL_EXTENDED: {
+                u4 idx;
+                u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (regNum >= 65536) {
+                    okay = false;
+                    break;
+                }
+                idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (idx != 0) {
+                    idx--;
+                    CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+                }
+                idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (idx != 0) {
+                    idx--;
+                    CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+                }
+                idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (idx != 0) {
+                    idx--;
+                    CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+                }
+                break;
+            }
+            case DBG_SET_FILE: {
+                u4 idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+                if (!okay) break;
+                if (idx != 0) {
+                    idx--;
+                    CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+                }
+                break;
+            }
+            default: {
+                // No arguments to parse for anything else.
+            }
+        }
+
+        if (!okay) {
+            LOGE("Bogus syntax for opcode %02x\n", opcode);
+            return NULL;
+        }
+    }
+
+    return (void*) data;
+}
+
+/* defined below */
+static const u1* verifyEncodedValue(const CheckState* state, const u1* data,
+        bool crossVerify);
+static const u1* verifyEncodedAnnotation(const CheckState* state,
+        const u1* data, bool crossVerify);
+
+/* Helper for verifyEncodedValue(), which reads a 1- to 4- byte unsigned
+ * little endian value. */
+static u4 readUnsignedLittleEndian(const CheckState* state, const u1** pData,
+        u4 size) {
+    const u1* data = *pData;
+    u4 result = 0;
+    u4 i;
+
+    CHECK_PTR_RANGE(data, data + size);
+
+    for (i = 0; i < size; i++) {
+        result |= ((u4) *(data++)) << (i * 8);
+    }
+
+    *pData = data;
+    return result;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_array. */
+static const u1* verifyEncodedArray(const CheckState* state,
+        const u1* data, bool crossVerify) {
+    bool okay = true;
+    u4 size = readAndVerifyUnsignedLeb128(&data, state->fileEnd, &okay);
+
+    if (!okay) {
+        LOGE("Bogus encoded_array size\n");
+        return NULL;
+    }
+
+    while (size--) {
+        data = verifyEncodedValue(state, data, crossVerify);
+        if (data == NULL) {
+            LOGE("Bogus encoded_array value\n");
+            return NULL;
+        }
+    }
+
+    return data;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_value. */
+static const u1* verifyEncodedValue(const CheckState* state,
+        const u1* data, bool crossVerify) {
+    CHECK_PTR_RANGE(data, data + 1);
+
+    u1 headerByte = *(data++);
+    u4 valueType = headerByte & kDexAnnotationValueTypeMask;
+    u4 valueArg = headerByte >> kDexAnnotationValueArgShift;
+
+    switch (valueType) {
+        case kDexAnnotationByte: {
+            if (valueArg != 0) {
+                LOGE("Bogus byte size 0x%x\n", valueArg);
+                return NULL;
+            }
+            data++;
+            break;
+        }
+        case kDexAnnotationShort:
+        case kDexAnnotationChar: {
+            if (valueArg > 1) {
+                LOGE("Bogus char/short size 0x%x\n", valueArg);
+                return NULL;
+            }
+            data += valueArg + 1;
+            break;
+        }
+        case kDexAnnotationInt:
+        case kDexAnnotationFloat: {
+            if (valueArg > 3) {
+                LOGE("Bogus int/float size 0x%x\n", valueArg);
+                return NULL;
+            }
+            data += valueArg + 1;
+            break;
+        }
+        case kDexAnnotationLong:
+        case kDexAnnotationDouble: {
+            data += valueArg + 1;
+            break;
+        }
+        case kDexAnnotationString: {
+            if (valueArg > 3) {
+                LOGE("Bogus string size 0x%x\n", valueArg);
+                return NULL;
+            }
+            u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+            CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+            break;
+        }
+        case kDexAnnotationType: {
+            if (valueArg > 3) {
+                LOGE("Bogus type size 0x%x\n", valueArg);
+                return NULL;
+            }
+            u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+            CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+            break;
+        }
+        case kDexAnnotationField:
+        case kDexAnnotationEnum: {
+            if (valueArg > 3) {
+                LOGE("Bogus field/enum size 0x%x\n", valueArg);
+                return NULL;
+            }
+            u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+            CHECK_INDEX(idx, state->pHeader->fieldIdsSize);
+            break;
+        }
+        case kDexAnnotationMethod: {
+            if (valueArg > 3) {
+                LOGE("Bogus method size 0x%x\n", valueArg);
+                return NULL;
+            }
+            u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+            CHECK_INDEX(idx, state->pHeader->methodIdsSize);
+            break;
+        }
+        case kDexAnnotationArray: {
+            if (valueArg != 0) {
+                LOGE("Bogus array value_arg 0x%x\n", valueArg);
+                return NULL;
+            }
+            data = verifyEncodedArray(state, data, crossVerify);
+            break;
+        }
+        case kDexAnnotationAnnotation: {
+            if (valueArg != 0) {
+                LOGE("Bogus annotation value_arg 0x%x\n", valueArg);
+                return NULL;
+            }
+            data = verifyEncodedAnnotation(state, data, crossVerify);
+            break;
+        }
+        case kDexAnnotationNull: {
+            if (valueArg != 0) {
+                LOGE("Bogus null value_arg 0x%x\n", valueArg);
+                return NULL;
+            }
+            // Nothing else to do for this type.
+            break;
+        }
+        case kDexAnnotationBoolean: {
+            if (valueArg > 1) {
+                LOGE("Bogus boolean value_arg 0x%x\n", valueArg);
+                return NULL;
+            }
+            // Nothing else to do for this type.
+            break;
+        }
+        default: {
+            LOGE("Bogus value_type 0x%x\n", valueType);
+            return NULL;
+        }
+    }
+
+    return data;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_annotation. */
+static const u1* verifyEncodedAnnotation(const CheckState* state,
+        const u1* data, bool crossVerify) {
+    const u1* fileEnd = state->fileEnd;
+    bool okay = true;
+    u4 idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+    if (!okay) {
+        LOGE("Bogus encoded_annotation type_idx\n");
+        return NULL;
+    }
+
+    CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+
+    if (crossVerify) {
+        const char* descriptor = dexStringByTypeIdx(state->pDexFile, idx);
+        if (!dexIsClassDescriptor(descriptor)) {
+            LOGE("Bogus annotation type: '%s'\n", descriptor);
+            return NULL;
+        }
+    }
+
+    u4 size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+    u4 lastIdx = 0;
+    bool first = true;
+
+    if (!okay) {
+        LOGE("Bogus encoded_annotation size\n");
+        return NULL;
+    }
+
+    while (size--) {
+        idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+        if (!okay) {
+            LOGE("Bogus encoded_annotation name_idx\n");
+            return NULL;
+        }
+
+        CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+
+        if (crossVerify) {
+            const char* name = dexStringById(state->pDexFile, idx);
+            if (!dexIsValidMemberName(name)) {
+                LOGE("Bogus annotation member name: '%s'\n", name);
+                return NULL;
+            }
+        }
+
+        if (first) {
+            first = false;
+        } else if (lastIdx >= idx) {
+            LOGE("Out-of-order encoded_annotation name_idx: 0x%x then 0x%x\n",
+                    lastIdx, idx);
+            return NULL;
+        }
+
+        data = verifyEncodedValue(state, data, crossVerify);
+        lastIdx = idx;
+
+        if (data == NULL) {
+            return NULL;
+        }
+    }
+
+    return data;
+}
+
+/* Perform intra-item verification on encoded_array_item. */
+static void* intraVerifyEncodedArrayItem(const CheckState* state, void* ptr) {
+    return (void*) verifyEncodedArray(state, (const u1*) ptr, false);
+}
+
+/* Perform intra-item verification on annotation_item. */
+static void* intraVerifyAnnotationItem(const CheckState* state, void* ptr) {
+    const u1* data = ptr;
+
+    CHECK_PTR_RANGE(data, data + 1);
+
+    switch (*(data++)) {
+        case kDexVisibilityBuild:
+        case kDexVisibilityRuntime:
+        case kDexVisibilitySystem: {
+            break;
+        }
+        default: {
+            LOGE("Bogus annotation visibility: 0x%x\n", *data);
+            return NULL;
+        }
+    }
+
+    return (void*) verifyEncodedAnnotation(state, data, false);
+}
+
+/* Perform cross-item verification on annotation_item. */
+static void* crossVerifyAnnotationItem(const CheckState* state, void* ptr) {
+    const u1* data = ptr;
+
+    // Skip the visibility byte.
+    data++;
+
+    return (void*) verifyEncodedAnnotation(state, data, true);
+}
+
+
+
+
+/*
+ * Function to visit an individual top-level item type.
+ */
+typedef void* ItemVisitorFunction(const CheckState* state, void* ptr);
+
+/*
+ * Iterate over all the items in a section, optionally updating the
+ * data map (done if mapType is passed as non-negative). The section
+ * must consist of concatenated items of the same type.
+ */
+static bool iterateSectionWithOptionalUpdate(CheckState* state,
+        u4 offset, u4 count, ItemVisitorFunction* func, u4 alignment,
+        u4* nextOffset, int mapType) {
+    u4 alignmentMask = alignment - 1;
+    u4 i;
+
+    state->previousItem = NULL;
+
+    for (i = 0; i < count; i++) {
+        u4 newOffset = (offset + alignmentMask) & ~alignmentMask;
+        u1* ptr = filePointer(state, newOffset);
+
+        if (offset < newOffset) {
+            ptr = filePointer(state, offset);
+            if (offset < newOffset) {
+                CHECK_OFFSET_RANGE(offset, newOffset);
+                while (offset < newOffset) {
+                    if (*ptr != '\0') {
+                        LOGE("Non-zero padding 0x%02x @ %x\n", *ptr, offset);
+                        return false;
+                    }
+                    ptr++;
+                    offset++;
+                }
+            }
+        }
+
+        u1* newPtr = (u1*) func(state, ptr);
+        newOffset = fileOffset(state, newPtr);
+
+        if (newPtr == NULL) {
+            LOGE("Trouble with item %d @ offset 0x%x\n", i, offset);
+            return false;
+        }
+
+        if (newOffset > state->fileLen) {
+            LOGE("Item %d @ offset 0x%x ends out of bounds\n", i, offset);
+            return false;
+        }
+
+        if (mapType >= 0) {
+            dexDataMapAdd(state->pDataMap, offset, mapType);
+        }
+
+        state->previousItem = ptr;
+        offset = newOffset;
+    }
+
+    if (nextOffset != NULL) {
+        *nextOffset = offset;
+    }
+
+    return true;
+}
+
+/*
+ * Iterate over all the items in a section. The section must consist of
+ * concatenated items of the same type. This variant will not update the data
+ * map.
+ */
+static bool iterateSection(CheckState* state, u4 offset, u4 count,
+        ItemVisitorFunction* func, u4 alignment, u4* nextOffset) {
+    return iterateSectionWithOptionalUpdate(state, offset, count, func,
+            alignment, nextOffset, -1);
+}
+
+/*
+ * Like iterateSection(), but also check that the offset and count match
+ * a given pair of expected values.
+ */
+static bool checkBoundsAndIterateSection(CheckState* state,
+        u4 offset, u4 count, u4 expectedOffset, u4 expectedCount,
+        ItemVisitorFunction* func, u4 alignment, u4* nextOffset) {
+    if (offset != expectedOffset) {
+        LOGE("Bogus offset for section: got 0x%x; expected 0x%x\n",
+                offset, expectedOffset);
+        return false;
+    }
+
+    if (count != expectedCount) {
+        LOGE("Bogus size for section: got 0x%x; expected 0x%x\n",
+                count, expectedCount);
+        return false;
+    }
+
+    return iterateSection(state, offset, count, func, alignment, nextOffset);
+}
+
+/*
+ * Like iterateSection(), but also update the data section map and
+ * check that all the items fall within the data section.
+ */
+static bool iterateDataSection(CheckState* state, u4 offset, u4 count,
+        ItemVisitorFunction* func, u4 alignment, u4* nextOffset, int mapType) {
+    u4 dataStart = state->pHeader->dataOff;
+    u4 dataEnd = dataStart + state->pHeader->dataSize;
+
+    assert(nextOffset != NULL);
+
+    if ((offset < dataStart) || (offset >= dataEnd)) {
+        LOGE("Bogus offset for data subsection: 0x%x\n", offset);
+        return false;
+    }
+
+    if (!iterateSectionWithOptionalUpdate(state, offset, count, func,
+                    alignment, nextOffset, mapType)) {
+        return false;
+    }
+
+    if (*nextOffset > dataEnd) {
+        LOGE("Out-of-bounds end of data subsection: 0x%x\n", *nextOffset);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Byte-swap all items in the given map except the header and the map
+ * itself, both of which should have already gotten swapped. This also
+ * does all possible intra-item verification, that is, verification
+ * that doesn't need to assume the sanctity of the contents of *other*
+ * items. The intra-item limitation is because at the time an item is
+ * asked to verify itself, it can't assume that the items it refers to
+ * have been byte-swapped and verified.
+ */
+static bool swapEverythingButHeaderAndMap(CheckState* state,
+        DexMapList* pMap) {
+    const DexMapItem* item = pMap->list;
+    u4 lastOffset = 0;
+    u4 count = pMap->size;
+    bool okay = true;
+
+    while (okay && count--) {
+        u4 sectionOffset = item->offset;
+        u4 sectionCount = item->size;
+        u2 type = item->type;
+
+        if (lastOffset < sectionOffset) {
+            CHECK_OFFSET_RANGE(lastOffset, sectionOffset);
+            const u1* ptr = filePointer(state, lastOffset);
+            while (lastOffset < sectionOffset) {
+                if (*ptr != '\0') {
+                    LOGE("Non-zero padding 0x%02x before section start @ %x\n",
+                            *ptr, lastOffset);
+                    okay = false;
+                    break;
+                }
+                ptr++;
+                lastOffset++;
+            }
+        } else if (lastOffset > sectionOffset) {
+            LOGE("Section overlap or out-of-order map: %x, %x\n",
+                    lastOffset, sectionOffset);
+            okay = false;
+        }
+
+        if (!okay) {
+            break;
+        }
+
+        switch (type) {
+            case kDexTypeHeaderItem: {
+                /*
+                 * The header got swapped very early on, but do some
+                 * additional sanity checking here.
+                 */
+                okay = checkHeaderSection(state, sectionOffset, sectionCount,
+                        &lastOffset);
+                break;
+            }
+            case kDexTypeStringIdItem: {
+                okay = checkBoundsAndIterateSection(state, sectionOffset,
+                        sectionCount, state->pHeader->stringIdsOff,
+                        state->pHeader->stringIdsSize, swapStringIdItem,
+                        sizeof(u4), &lastOffset);
+                break;
+            }
+            case kDexTypeTypeIdItem: {
+                okay = checkBoundsAndIterateSection(state, sectionOffset,
+                        sectionCount, state->pHeader->typeIdsOff,
+                        state->pHeader->typeIdsSize, swapTypeIdItem,
+                        sizeof(u4), &lastOffset);
+                break;
+            }
+            case kDexTypeProtoIdItem: {
+                okay = checkBoundsAndIterateSection(state, sectionOffset,
+                        sectionCount, state->pHeader->protoIdsOff,
+                        state->pHeader->protoIdsSize, swapProtoIdItem,
+                        sizeof(u4), &lastOffset);
+                break;
+            }
+            case kDexTypeFieldIdItem: {
+                okay = checkBoundsAndIterateSection(state, sectionOffset,
+                        sectionCount, state->pHeader->fieldIdsOff,
+                        state->pHeader->fieldIdsSize, swapFieldIdItem,
+                        sizeof(u4), &lastOffset);
+                break;
+            }
+            case kDexTypeMethodIdItem: {
+                okay = checkBoundsAndIterateSection(state, sectionOffset,
+                        sectionCount, state->pHeader->methodIdsOff,
+                        state->pHeader->methodIdsSize, swapMethodIdItem,
+                        sizeof(u4), &lastOffset);
+                break;
+            }
+            case kDexTypeClassDefItem: {
+                okay = checkBoundsAndIterateSection(state, sectionOffset,
+                        sectionCount, state->pHeader->classDefsOff,
+                        state->pHeader->classDefsSize, swapClassDefItem,
+                        sizeof(u4), &lastOffset);
+                break;
+            }
+            case kDexTypeMapList: {
+                /*
+                 * The map section was swapped early on, but do some
+                 * additional sanity checking here.
+                 */
+                okay = checkMapSection(state, sectionOffset, sectionCount,
+                        &lastOffset);
+                break;
+            }
+            case kDexTypeTypeList: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        swapTypeList, sizeof(u4), &lastOffset, type);
+                break;
+            }
+            case kDexTypeAnnotationSetRefList: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        swapAnnotationSetRefList, sizeof(u4), &lastOffset,
+                        type);
+                break;
+            }
+            case kDexTypeAnnotationSetItem: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        swapAnnotationSetItem, sizeof(u4), &lastOffset, type);
+                break;
+            }
+            case kDexTypeClassDataItem: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        intraVerifyClassDataItem, sizeof(u1), &lastOffset,
+                        type);
+                break;
+            }
+            case kDexTypeCodeItem: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        swapCodeItem, sizeof(u4), &lastOffset, type);
+                break;
+            }
+            case kDexTypeStringDataItem: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        intraVerifyStringDataItem, sizeof(u1), &lastOffset,
+                        type);
+                break;
+            }
+            case kDexTypeDebugInfoItem: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        intraVerifyDebugInfoItem, sizeof(u1), &lastOffset,
+                        type);
+                break;
+            }
+            case kDexTypeAnnotationItem: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        intraVerifyAnnotationItem, sizeof(u1), &lastOffset,
+                        type);
+                break;
+            }
+            case kDexTypeEncodedArrayItem: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        intraVerifyEncodedArrayItem, sizeof(u1), &lastOffset,
+                        type);
+                break;
+            }
+            case kDexTypeAnnotationsDirectoryItem: {
+                okay = iterateDataSection(state, sectionOffset, sectionCount,
+                        swapAnnotationsDirectoryItem, sizeof(u4), &lastOffset,
+                        type);
+                break;
+            }
+            default: {
+                LOGE("Unknown map item type %04x\n", type);
+                return false;
+            }
+        }
+
+        if (!okay) {
+            LOGE("Swap of section type %04x failed\n", type);
+        }
+
+        item++;
+    }
+
+    return okay;
+}
+
+/*
+ * Perform cross-item verification on everything that needs it. This
+ * pass is only called after all items are byte-swapped and
+ * intra-verified (checked for internal consistency).
+ */
+static bool crossVerifyEverything(CheckState* state, DexMapList* pMap)
+{
+    const DexMapItem* item = pMap->list;
+    u4 count = pMap->size;
+    bool okay = true;
+
+    while (okay && count--) {
+        u4 sectionOffset = item->offset;
+        u4 sectionCount = item->size;
+
+        switch (item->type) {
+            case kDexTypeHeaderItem:
+            case kDexTypeMapList:
+            case kDexTypeTypeList:
+            case kDexTypeCodeItem:
+            case kDexTypeStringDataItem:
+            case kDexTypeDebugInfoItem:
+            case kDexTypeAnnotationItem:
+            case kDexTypeEncodedArrayItem: {
+                // There is no need for cross-item verification for these.
+                break;
+            }
+            case kDexTypeStringIdItem: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyStringIdItem, sizeof(u4), NULL);
+                break;
+            }
+            case kDexTypeTypeIdItem: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyTypeIdItem, sizeof(u4), NULL);
+                break;
+            }
+            case kDexTypeProtoIdItem: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyProtoIdItem, sizeof(u4), NULL);
+                break;
+            }
+            case kDexTypeFieldIdItem: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyFieldIdItem, sizeof(u4), NULL);
+                break;
+            }
+            case kDexTypeMethodIdItem: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyMethodIdItem, sizeof(u4), NULL);
+                break;
+            }
+            case kDexTypeClassDefItem: {
+                // Allocate (on the stack) the "observed class_def" bits.
+                size_t arraySize = calcDefinedClassBitsSize(state);
+                u4 definedClassBits[arraySize];
+                memset(definedClassBits, 0, arraySize * sizeof(u4));
+                state->pDefinedClassBits = definedClassBits;
+
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyClassDefItem, sizeof(u4), NULL);
+
+                state->pDefinedClassBits = NULL;
+                break;
+            }
+            case kDexTypeAnnotationSetRefList: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyAnnotationSetRefList, sizeof(u4), NULL);
+                break;
+            }
+            case kDexTypeAnnotationSetItem: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyAnnotationSetItem, sizeof(u4), NULL);
+                break;
+            }
+            case kDexTypeClassDataItem: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyClassDataItem, sizeof(u1), NULL);
+                break;
+            }
+            case kDexTypeAnnotationsDirectoryItem: {
+                okay = iterateSection(state, sectionOffset, sectionCount,
+                        crossVerifyAnnotationsDirectoryItem, sizeof(u4), NULL);
+                break;
+            }
+            default: {
+                LOGE("Unknown map item type %04x\n", item->type);
+                return false;
+            }
+        }
+
+        if (!okay) {
+            LOGE("Cross-item verify of section type %04x failed\n",
+                    item->type);
+        }
+
+        item++;
+    }
+
+    return okay;
+}
+
+/*
+ * Fix the byte ordering of all fields in the DEX file, and do
+ * structural verification. This is only required for code that opens
+ * "raw" DEX files, such as the DEX optimizer.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexSwapAndVerify(u1* addr, int len)
+{
+    DexHeader* pHeader;
+    CheckState state;
+    bool okay = true;
+
+    memset(&state, 0, sizeof(state));
+    LOGV("+++ swapping and verifying\n");
+
+    /*
+     * Start by verifying the magic number.  The caller verified that "len"
+     * says we have at least a header's worth of data.
+     */
+    pHeader = (DexHeader*) addr;
+    if (memcmp(pHeader->magic, DEX_MAGIC, 4) != 0) {
+        /* really shouldn't be here -- this is weird */
+        LOGE("ERROR: Can't byte swap: bad magic number "
+                "(0x%02x %02x %02x %02x)\n",
+             pHeader->magic[0], pHeader->magic[1],
+             pHeader->magic[2], pHeader->magic[3]);
+        okay = false;
+    }
+
+    if (okay && memcmp(pHeader->magic+4, DEX_MAGIC_VERS, 4) != 0) {
+        /* older or newer version we don't know how to read */
+        LOGE("ERROR: Can't byte swap: bad dex version "
+                "(0x%02x %02x %02x %02x)\n",
+             pHeader->magic[4], pHeader->magic[5],
+             pHeader->magic[6], pHeader->magic[7]);
+        okay = false;
+    }
+
+    if (okay) {
+        int expectedLen = (int) SWAP4(pHeader->fileSize);
+        if (len < expectedLen) {
+            LOGE("ERROR: Bad length: expected %d, got %d\n", expectedLen, len);
+            okay = false;
+        } else if (len != expectedLen) {
+            LOGW("WARNING: Odd length: expected %d, got %d\n", expectedLen,
+                    len);
+            // keep going
+        }
+    }
+
+    if (okay) {
+        /*
+         * Compute the adler32 checksum and compare it to what's stored in
+         * the file.  This isn't free, but chances are good that we just
+         * unpacked this from a jar file and have all of the pages sitting
+         * in memory, so it's pretty quick.
+         *
+         * This might be a big-endian system, so we need to do this before
+         * we byte-swap the header.
+         */
+        uLong adler = adler32(0L, Z_NULL, 0);
+        const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+        u4 storedFileSize = SWAP4(pHeader->fileSize);
+        u4 expectedChecksum = SWAP4(pHeader->checksum);
+
+        adler = adler32(adler, ((const u1*) pHeader) + nonSum,
+                    storedFileSize - nonSum);
+
+        if (adler != expectedChecksum) {
+            LOGE("ERROR: bad checksum (%08lx, expected %08x)\n",
+                adler, expectedChecksum);
+            okay = false;
+        }
+    }
+
+    if (okay) {
+        state.fileStart = addr;
+        state.fileEnd = addr + len;
+        state.fileLen = len;
+        state.pDexFile = NULL;
+        state.pDataMap = NULL;
+        state.pDefinedClassBits = NULL;
+        state.previousItem = NULL;
+
+        /*
+         * Swap the header and check the contents.
+         */
+        okay = swapDexHeader(&state, pHeader);
+    }
+
+    if (okay) {
+        state.pHeader = pHeader;
+
+        if (pHeader->headerSize < sizeof(DexHeader)) {
+            LOGE("ERROR: Small header size %d, struct %d\n",
+                    pHeader->headerSize, (int) sizeof(DexHeader));
+            okay = false;
+        } else if (pHeader->headerSize > sizeof(DexHeader)) {
+            LOGW("WARNING: Large header size %d, struct %d\n",
+                    pHeader->headerSize, (int) sizeof(DexHeader));
+            // keep going?
+        }
+    }
+
+    if (okay) {
+        /*
+         * Look for the map. Swap it and then use it to find and swap
+         * everything else.
+         */
+        if (pHeader->mapOff != 0) {
+            DexFile dexFile;
+            DexMapList* pDexMap = (DexMapList*) (addr + pHeader->mapOff);
+
+            okay = okay && swapMap(&state, pDexMap);
+            okay = okay && swapEverythingButHeaderAndMap(&state, pDexMap);
+
+            dexFileSetupBasicPointers(&dexFile, addr);
+            state.pDexFile = &dexFile;
+
+            okay = okay && crossVerifyEverything(&state, pDexMap);
+        } else {
+            LOGE("ERROR: No map found; impossible to byte-swap and verify");
+            okay = false;
+        }
+    }
+
+    if (!okay) {
+        LOGE("ERROR: Byte swap + verify failed\n");
+    }
+
+    if (state.pDataMap != NULL) {
+        dexDataMapFree(state.pDataMap);
+    }
+
+    return !okay;       // 0 == success
+}
+
+/*
+ * Detect the file type of the given memory buffer via magic number.
+ * Call dexSwapAndVerify() on an unoptimized DEX file, do nothing
+ * but return successfully on an optimized DEX file, and report an
+ * error for all other cases.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexSwapAndVerifyIfNecessary(u1* addr, int len)
+{
+    if (memcmp(addr, DEX_OPT_MAGIC, 4) == 0) {
+        // It is an optimized dex file.
+        return 0;
+    }
+
+    if (memcmp(addr, DEX_MAGIC, 4) == 0) {
+        // It is an unoptimized dex file.
+        return dexSwapAndVerify(addr, len);
+    }
+
+    LOGE("ERROR: Bad magic number (0x%02x %02x %02x %02x)\n",
+             addr[0], addr[1], addr[2], addr[3]);
+
+    return 1;
+}
diff --git a/libdex/InstrUtils.c b/libdex/InstrUtils.c
new file mode 100644
index 0000000..b98f002
--- /dev/null
+++ b/libdex/InstrUtils.c
@@ -0,0 +1,1261 @@
+/*
+ * 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 instruction utility functions.
+ */
+#include "InstrUtils.h"
+
+#include <stdlib.h>
+
+
+/*
+ * Generate a table that holds the width of all instructions.
+ *
+ * Standard instructions have positive values, optimizer instructions
+ * have negative values, unimplemented instructions have a width of zero.
+ *
+ * I'm doing it with a giant switch statement because it's easier to
+ * maintain and update than a static table with 256 unadorned integers,
+ * and if we're missing a case gcc emits a "warning: enumeration value not
+ * handled" message.
+ *
+ * (To save space in the binary we could generate a static table with a
+ * command-line utility.)
+ *
+ * TODO: it doesn't look like we're using the negative values anymore.
+ * Consider switching to only positive values.
+ */
+InstructionWidth* dexCreateInstrWidthTable(void)
+{
+#ifdef __ARM_ARCH_7A__
+    /* hack to work around mysterious problem on emulator */
+    LOGD("creating instr width table\n");
+#endif
+    InstructionWidth* instrWidth;
+    int i;
+
+    instrWidth = malloc(sizeof(InstructionWidth) * kNumDalvikInstructions);
+    if (instrWidth == NULL)
+        return NULL;
+
+    for (i = 0; i < kNumDalvikInstructions; i++) {
+        OpCode opc = (OpCode) i;
+        int width = 0;
+
+        switch (opc) {
+        case OP_NOP:    /* note data for e.g. switch-* encoded "inside" a NOP */
+        case OP_MOVE:
+        case OP_MOVE_WIDE:
+        case OP_MOVE_OBJECT:
+        case OP_MOVE_RESULT:
+        case OP_MOVE_RESULT_WIDE:
+        case OP_MOVE_RESULT_OBJECT:
+        case OP_MOVE_EXCEPTION:
+        case OP_RETURN_VOID:
+        case OP_RETURN:
+        case OP_RETURN_WIDE:
+        case OP_RETURN_OBJECT:
+        case OP_CONST_4:
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_ARRAY_LENGTH:
+        case OP_THROW:
+        case OP_GOTO:
+        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:
+        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:
+            width = 1;
+            break;
+
+        case OP_MOVE_FROM16:
+        case OP_MOVE_WIDE_FROM16:
+        case OP_MOVE_OBJECT_FROM16:
+        case OP_CONST_16:
+        case OP_CONST_HIGH16:
+        case OP_CONST_WIDE_16:
+        case OP_CONST_WIDE_HIGH16:
+        case OP_CONST_STRING:
+        case OP_CONST_CLASS:
+        case OP_CHECK_CAST:
+        case OP_INSTANCE_OF:
+        case OP_NEW_INSTANCE:
+        case OP_NEW_ARRAY:
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+        case OP_CMP_LONG:
+        case OP_GOTO_16:
+        case OP_IF_EQ:
+        case OP_IF_NE:
+        case OP_IF_LT:
+        case OP_IF_GE:
+        case OP_IF_GT:
+        case OP_IF_LE:
+        case OP_IF_EQZ:
+        case OP_IF_NEZ:
+        case OP_IF_LTZ:
+        case OP_IF_GEZ:
+        case OP_IF_GTZ:
+        case OP_IF_LEZ:
+        case OP_AGET:
+        case OP_AGET_WIDE:
+        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_WIDE:
+        case OP_APUT_OBJECT:
+        case OP_APUT_BOOLEAN:
+        case OP_APUT_BYTE:
+        case OP_APUT_CHAR:
+        case OP_APUT_SHORT:
+        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:
+        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:
+        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:
+        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:
+            width = 2;
+            break;
+
+        case OP_MOVE_16:
+        case OP_MOVE_WIDE_16:
+        case OP_MOVE_OBJECT_16:
+        case OP_CONST:
+        case OP_CONST_WIDE_32:
+        case OP_CONST_STRING_JUMBO:
+        case OP_GOTO_32:
+        case OP_FILLED_NEW_ARRAY:
+        case OP_FILLED_NEW_ARRAY_RANGE:
+        case OP_FILL_ARRAY_DATA:
+        case OP_PACKED_SWITCH:
+        case OP_SPARSE_SWITCH:
+        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:
+            width = 3;
+            break;
+
+        case OP_CONST_WIDE:
+            width = 5;
+            break;
+
+        /*
+         * Optimized instructions.  We return negative size values for these
+         * to distinguish them.
+         */
+        case OP_IGET_QUICK:
+        case OP_IGET_WIDE_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+        case OP_IPUT_QUICK:
+        case OP_IPUT_WIDE_QUICK:
+        case OP_IPUT_OBJECT_QUICK:
+        case OP_IGET_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_THROW_VERIFICATION_ERROR:
+            width = -2;
+            break;
+        case OP_INVOKE_VIRTUAL_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE:
+        case OP_EXECUTE_INLINE:
+        case OP_EXECUTE_INLINE_RANGE:
+        case OP_INVOKE_DIRECT_EMPTY:
+            width = -3;
+            break;
+
+        /* these should never appear when scanning bytecode */
+        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_F1:
+        case OP_UNUSED_FF:
+            assert(width == 0);
+            break;
+
+        /*
+         * DO NOT add a "default" clause here.  Without it the compiler will
+         * complain if an instruction is missing (which is desirable).
+         */
+        }
+
+        instrWidth[opc] = width;
+    }
+
+    return instrWidth;
+}
+
+/*
+ * Generate a table that holds instruction flags.
+ */
+InstructionFlags* dexCreateInstrFlagsTable(void)
+{
+    InstructionFlags* instrFlags;
+    int i;
+
+    instrFlags = malloc(sizeof(InstructionFlags) * kNumDalvikInstructions);
+    if (instrFlags == NULL)
+        return NULL;
+
+    for (i = 0; i < kNumDalvikInstructions; i++) {
+        OpCode opc = (OpCode) i;
+        InstructionFlags flags = 0;
+
+        switch (opc) {
+        /* these don't affect the PC and can't cause an exception */
+        case OP_NOP:
+        case OP_MOVE:
+        case OP_MOVE_FROM16:
+        case OP_MOVE_16:
+        case OP_MOVE_WIDE:
+        case OP_MOVE_WIDE_FROM16:
+        case OP_MOVE_WIDE_16:
+        case OP_MOVE_OBJECT:
+        case OP_MOVE_OBJECT_FROM16:
+        case OP_MOVE_OBJECT_16:
+        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_WIDE_16:
+        case OP_CONST_WIDE_32:
+        case OP_CONST_WIDE:
+        case OP_CONST_WIDE_HIGH16:
+        case OP_FILL_ARRAY_DATA:
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+        case OP_CMP_LONG:
+        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:
+        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:
+        case OP_ADD_LONG:
+        case OP_SUB_LONG:
+        case OP_MUL_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:         // div by zero just returns NaN
+        case OP_REM_DOUBLE:
+        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:
+        case OP_ADD_LONG_2ADDR:
+        case OP_SUB_LONG_2ADDR:
+        case OP_MUL_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:
+        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:
+        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:
+            flags = kInstrCanContinue;
+            break;
+
+        /* these don't affect the PC, but can cause exceptions */
+        case OP_CONST_STRING:
+        case OP_CONST_STRING_JUMBO:
+        case OP_CONST_CLASS:
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_CHECK_CAST:
+        case OP_INSTANCE_OF:
+        case OP_ARRAY_LENGTH:
+        case OP_NEW_INSTANCE:
+        case OP_NEW_ARRAY:
+        case OP_FILLED_NEW_ARRAY:
+        case OP_FILLED_NEW_ARRAY_RANGE:
+        case OP_AGET:
+        case OP_AGET_BOOLEAN:
+        case OP_AGET_BYTE:
+        case OP_AGET_CHAR:
+        case OP_AGET_SHORT:
+        case OP_AGET_WIDE:
+        case OP_AGET_OBJECT:
+        case OP_APUT:
+        case OP_APUT_BOOLEAN:
+        case OP_APUT_BYTE:
+        case OP_APUT_CHAR:
+        case OP_APUT_SHORT:
+        case OP_APUT_WIDE:
+        case OP_APUT_OBJECT:
+        case OP_IGET:
+        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:
+        case OP_DIV_INT:
+        case OP_REM_INT:
+        case OP_DIV_LONG:
+        case OP_REM_LONG:
+        case OP_DIV_INT_2ADDR:
+        case OP_REM_INT_2ADDR:
+        case OP_DIV_LONG_2ADDR:
+        case OP_REM_LONG_2ADDR:
+        case OP_DIV_INT_LIT16:
+        case OP_REM_INT_LIT16:
+        case OP_DIV_INT_LIT8:
+        case OP_REM_INT_LIT8:
+            flags = kInstrCanContinue | kInstrCanThrow;
+            break;
+
+        case OP_INVOKE_VIRTUAL:
+        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:
+            flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke;
+            break;
+
+        case OP_RETURN_VOID:
+        case OP_RETURN:
+        case OP_RETURN_WIDE:
+        case OP_RETURN_OBJECT:
+            flags = kInstrCanReturn;
+            break;
+
+        case OP_THROW:
+            flags = kInstrCanThrow;
+            break;
+
+        /* unconditional branches */
+        case OP_GOTO:
+        case OP_GOTO_16:
+        case OP_GOTO_32:
+            flags = kInstrCanBranch | kInstrUnconditional;
+            break;
+
+        /* conditional branches */
+        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:
+            flags = kInstrCanBranch | kInstrCanContinue;
+            break;
+
+        /* switch statements; if value not in switch, it continues */
+        case OP_PACKED_SWITCH:
+        case OP_SPARSE_SWITCH:
+            flags = kInstrCanSwitch | kInstrCanContinue;
+            break;
+
+        /* verifier/optimizer-generated instructions */
+        case OP_THROW_VERIFICATION_ERROR:
+            flags = kInstrCanThrow;
+            break;
+        case OP_EXECUTE_INLINE:
+        case OP_EXECUTE_INLINE_RANGE:
+            flags = kInstrCanContinue | kInstrCanThrow;
+            break;
+        case OP_IGET_QUICK:
+        case OP_IGET_WIDE_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+        case OP_IPUT_QUICK:
+        case OP_IPUT_WIDE_QUICK:
+        case OP_IPUT_OBJECT_QUICK:
+        case OP_IGET_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:
+            flags = kInstrCanContinue | kInstrCanThrow;
+            break;
+
+        case OP_INVOKE_VIRTUAL_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE:
+        case OP_INVOKE_DIRECT_EMPTY:
+            flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke;
+            break;
+
+        /* these should never appear when scanning code */
+        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_F1:
+        case OP_UNUSED_FF:
+            break;
+
+        /*
+         * DO NOT add a "default" clause here.  Without it the compiler will
+         * complain if an instruction is missing (which is desirable).
+         */
+        }
+
+        instrFlags[opc] = flags;
+    }
+
+    return instrFlags;
+}
+
+/*
+ * Allocate and populate a 256-element array with instruction formats.
+ * Used in conjunction with dexDecodeInstruction.
+ */
+InstructionFormat* dexCreateInstrFormatTable(void)
+{
+    InstructionFormat* instFmt;
+    int i;
+
+    instFmt = malloc(sizeof(InstructionFormat) * kNumDalvikInstructions);
+    if (instFmt == NULL)
+        return NULL;
+
+    for (i = 0; i < kNumDalvikInstructions; i++) {
+        OpCode opc = (OpCode) i;
+        InstructionFormat fmt = kFmtUnknown;
+
+        switch (opc) {
+        case OP_GOTO:
+            fmt = kFmt10t;
+            break;
+        case OP_NOP:
+        case OP_RETURN_VOID:
+            fmt = kFmt10x;
+            break;
+        case OP_CONST_4:
+            fmt = kFmt11n;
+            break;
+        case OP_CONST_HIGH16:
+        case OP_CONST_WIDE_HIGH16:
+            fmt = kFmt21h;
+            break;
+        case OP_MOVE_RESULT:
+        case OP_MOVE_RESULT_WIDE:
+        case OP_MOVE_RESULT_OBJECT:
+        case OP_MOVE_EXCEPTION:
+        case OP_RETURN:
+        case OP_RETURN_WIDE:
+        case OP_RETURN_OBJECT:
+        case OP_MONITOR_ENTER:
+        case OP_MONITOR_EXIT:
+        case OP_THROW:
+            fmt = kFmt11x;
+            break;
+        case OP_MOVE:
+        case OP_MOVE_WIDE:
+        case OP_MOVE_OBJECT:
+        case OP_ARRAY_LENGTH:
+        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:
+        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:
+            fmt = kFmt12x;
+            break;
+        case OP_GOTO_16:
+            fmt = kFmt20t;
+            break;
+        case OP_GOTO_32:
+            fmt = kFmt30t;
+            break;
+        case OP_CONST_STRING:
+        case OP_CONST_CLASS:
+        case OP_CHECK_CAST:
+        case OP_NEW_INSTANCE:
+        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:
+            fmt = kFmt21c;
+            break;
+        case OP_CONST_16:
+        case OP_CONST_WIDE_16:
+            fmt = kFmt21s;
+            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:
+            fmt = kFmt21t;
+            break;
+        case OP_FILL_ARRAY_DATA:
+        case OP_PACKED_SWITCH:
+        case OP_SPARSE_SWITCH:
+            fmt = kFmt31t;
+            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_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:
+            fmt = kFmt22b;
+            break;
+        case OP_INSTANCE_OF:
+        case OP_NEW_ARRAY:
+        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:
+            fmt = kFmt22c;
+            break;
+        case OP_ADD_INT_LIT16:
+        case OP_RSUB_INT:
+        case OP_MUL_INT_LIT16:
+        case OP_DIV_INT_LIT16:
+        case OP_REM_INT_LIT16:
+        case OP_AND_INT_LIT16:
+        case OP_OR_INT_LIT16:
+        case OP_XOR_INT_LIT16:
+            fmt = kFmt22s;
+            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:
+            fmt = kFmt22t;
+            break;
+        case OP_MOVE_FROM16:
+        case OP_MOVE_WIDE_FROM16:
+        case OP_MOVE_OBJECT_FROM16:
+            fmt = kFmt22x;
+            break;
+        case OP_CMPL_FLOAT:
+        case OP_CMPG_FLOAT:
+        case OP_CMPL_DOUBLE:
+        case OP_CMPG_DOUBLE:
+        case OP_CMP_LONG:
+        case OP_AGET:
+        case OP_AGET_WIDE:
+        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_WIDE:
+        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_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:
+            fmt = kFmt23x;
+            break;
+        case OP_CONST:
+        case OP_CONST_WIDE_32:
+            fmt = kFmt31i;
+            break;
+        case OP_CONST_STRING_JUMBO:
+            fmt = kFmt31c;
+            break;
+        case OP_MOVE_16:
+        case OP_MOVE_WIDE_16:
+        case OP_MOVE_OBJECT_16:
+            fmt = kFmt32x;
+            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:
+            fmt = kFmt35c;
+            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:
+            fmt = kFmt3rc;
+            break;
+        case OP_CONST_WIDE:
+            fmt = kFmt51l;
+            break;
+
+        /*
+         * Optimized instructions.
+         */
+        case OP_THROW_VERIFICATION_ERROR:
+            fmt = kFmt20bc;
+            break;
+        case OP_IGET_WIDE_VOLATILE:
+        case OP_IPUT_WIDE_VOLATILE:
+        case OP_IGET_VOLATILE:
+        case OP_IPUT_VOLATILE:
+        case OP_IGET_OBJECT_VOLATILE:
+        case OP_IPUT_OBJECT_VOLATILE:
+            fmt = kFmt22c;
+            break;
+        case OP_SGET_OBJECT_VOLATILE:
+        case OP_SPUT_OBJECT_VOLATILE:
+        case OP_SGET_VOLATILE:
+        case OP_SPUT_VOLATILE:
+        case OP_SGET_WIDE_VOLATILE:
+        case OP_SPUT_WIDE_VOLATILE:
+            fmt = kFmt21c;
+            break;
+        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:
+            fmt = kFmt22cs;
+            break;
+        case OP_INVOKE_VIRTUAL_QUICK:
+        case OP_INVOKE_SUPER_QUICK:
+            fmt = kFmt35ms;
+            break;
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_SUPER_QUICK_RANGE:
+            fmt = kFmt3rms;
+            break;
+        case OP_EXECUTE_INLINE:
+            fmt = kFmt3inline;
+            break;
+        case OP_EXECUTE_INLINE_RANGE:
+            fmt = kFmt3rinline;
+            break;
+        case OP_INVOKE_DIRECT_EMPTY:
+            fmt = kFmt35c;
+            break;
+
+        /* these should never appear when scanning code */
+        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_F1:
+        case OP_UNUSED_FF:
+            fmt = kFmtUnknown;
+            break;
+
+        /*
+         * DO NOT add a "default" clause here.  Without it the compiler will
+         * complain if an instruction is missing (which is desirable).
+         */
+        }
+
+        instFmt[opc] = fmt;
+    }
+
+    return instFmt;
+}
+
+/*
+ * Copied from InterpCore.h.  Used for instruction decoding.
+ */
+#define FETCH(_offset)      (insns[(_offset)])
+#define INST_INST(_inst)    ((_inst) & 0xff)
+#define INST_A(_inst)       (((u2)(_inst) >> 8) & 0x0f)
+#define INST_B(_inst)       ((u2)(_inst) >> 12)
+#define INST_AA(_inst)      ((_inst) >> 8)
+
+/*
+ * Decode the instruction pointed to by "insns".
+ *
+ * Fills out the pieces of "pDec" that are affected by the current
+ * instruction.  Does not touch anything else.
+ */
+void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns,
+    DecodedInstruction* pDec)
+{
+    u2 inst = *insns;
+
+    pDec->opCode = (OpCode) INST_INST(inst);
+
+    switch (dexGetInstrFormat(fmts, pDec->opCode)) {
+    case kFmt10x:       // op
+        /* nothing to do; copy the AA bits out for the verifier */
+        pDec->vA = INST_AA(inst);
+        break;
+    case kFmt12x:       // op vA, vB
+        pDec->vA = INST_A(inst);
+        pDec->vB = INST_B(inst);
+        break;
+    case kFmt11n:       // op vA, #+B
+        pDec->vA = INST_A(inst);
+        pDec->vB = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value
+        break;
+    case kFmt11x:       // op vAA
+        pDec->vA = INST_AA(inst);
+        break;
+    case kFmt10t:       // op +AA
+        pDec->vA = (s1) INST_AA(inst);              // sign-extend 8-bit value
+        break;
+    case kFmt20t:       // op +AAAA
+        pDec->vA = (s2) FETCH(1);                   // sign-extend 16-bit value
+        break;
+    case kFmt20bc:      // op AA, thing@BBBB
+    case kFmt21c:       // op vAA, thing@BBBB
+    case kFmt22x:       // op vAA, vBBBB
+        pDec->vA = INST_AA(inst);
+        pDec->vB = FETCH(1);
+        break;
+    case kFmt21s:       // op vAA, #+BBBB
+    case kFmt21t:       // op vAA, +BBBB
+        pDec->vA = INST_AA(inst);
+        pDec->vB = (s2) FETCH(1);                   // sign-extend 16-bit value
+        break;
+    case kFmt21h:       // op vAA, #+BBBB0000[00000000]
+        pDec->vA = INST_AA(inst);
+        /*
+         * The value should be treated as right-zero-extended, but we don't
+         * actually do that here. Among other things, we don't know if it's
+         * the top bits of a 32- or 64-bit value.
+         */
+        pDec->vB = FETCH(1);
+        break;
+    case kFmt23x:       // op vAA, vBB, vCC
+        pDec->vA = INST_AA(inst);
+        pDec->vB = FETCH(1) & 0xff;
+        pDec->vC = FETCH(1) >> 8;
+        break;
+    case kFmt22b:       // op vAA, vBB, #+CC
+        pDec->vA = INST_AA(inst);
+        pDec->vB = FETCH(1) & 0xff;
+        pDec->vC = (s1) (FETCH(1) >> 8);            // sign-extend 8-bit value
+        break;
+    case kFmt22s:       // op vA, vB, #+CCCC
+    case kFmt22t:       // op vA, vB, +CCCC
+        pDec->vA = INST_A(inst);
+        pDec->vB = INST_B(inst);
+        pDec->vC = (s2) FETCH(1);                   // sign-extend 16-bit value
+        break;
+    case kFmt22c:       // op vA, vB, thing@CCCC
+    case kFmt22cs:      // [opt] op vA, vB, field offset CCCC
+        pDec->vA = INST_A(inst);
+        pDec->vB = INST_B(inst);
+        pDec->vC = FETCH(1);
+        break;
+    case kFmt30t:        // op +AAAAAAAA
+        pDec->vA = FETCH(1) | ((u4) FETCH(2) << 16); // signed 32-bit value
+        break;
+    case kFmt31t:       // op vAA, +BBBBBBBB
+    case kFmt31c:       // op vAA, thing@BBBBBBBB
+        pDec->vA = INST_AA(inst);
+        pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16); // 32-bit value
+        break;
+    case kFmt32x:       // op vAAAA, vBBBB
+        pDec->vA = FETCH(1);
+        pDec->vB = FETCH(2);
+        break;
+    case kFmt31i:       // op vAA, #+BBBBBBBB
+        pDec->vA = INST_AA(inst);
+        pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16);
+        break;
+    case kFmt35c:       // op vB, {vD..vG,vA}, thing@CCCC
+    case kFmt35ms:      // [opt] invoke-virtual+super
+        {
+            /*
+             * The lettering changes that came about when we went from 4 args
+             * to 5 made the "range" versions of the calls different from
+             * the non-range versions.  We have the choice between decoding
+             * them the way the spec shows and having lots of conditionals
+             * in the verifier, or mapping the values onto their original
+             * registers and leaving the verifier intact.
+             *
+             * Current plan is to leave the verifier alone.  We can fix it
+             * later if it's architecturally unbearable.
+             *
+             * Bottom line: method constant is always in vB.
+             */
+            u2 regList;
+            int i, count;
+
+            pDec->vA = INST_B(inst);
+            pDec->vB = FETCH(1);
+            regList = FETCH(2);
+
+            if (pDec->vA > 5) {
+                LOGW("Invalid arg count in 35c/35ms (%d)\n", pDec->vA);
+                goto bail;
+            }
+            count = pDec->vA;
+            if (count == 5) {
+                /* 5th arg comes from A field in instruction */
+                pDec->arg[4] = INST_A(inst);
+                count--;
+            }
+            for (i = 0; i < count; i++) {
+                pDec->arg[i] = regList & 0x0f;
+                regList >>= 4;
+            }
+            /* copy arg[0] to vC; we don't have vD/vE/vF, so ignore those */
+            if (pDec->vA > 0)
+                pDec->vC = pDec->arg[0];
+        }
+        break;
+    case kFmt3inline:   // [opt] inline invoke
+        {
+            u2 regList;
+            int i;
+
+            pDec->vA = INST_B(inst);
+            pDec->vB = FETCH(1);
+            regList = FETCH(2);
+
+            if (pDec->vA > 4) {
+                LOGW("Invalid arg count in 3inline (%d)\n", pDec->vA);
+                goto bail;
+            }
+            for (i = 0; i < (int) pDec->vA; i++) {
+                pDec->arg[i] = regList & 0x0f;
+                regList >>= 4;
+            }
+            /* copy arg[0] to vC; we don't have vD/vE/vF, so ignore those */
+            if (pDec->vA > 0)
+                pDec->vC = pDec->arg[0];
+        }
+        break;
+    case kFmt35fs:      // [opt] invoke-interface
+        assert(false);  // TODO
+        break;
+    case kFmt3rc:       // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+    case kFmt3rms:      // [opt] invoke-virtual+super/range
+    case kFmt3rinline:  // [opt] execute-inline/range
+        pDec->vA = INST_AA(inst);
+        pDec->vB = FETCH(1);
+        pDec->vC = FETCH(2);
+        break;
+    case kFmt3rfs:      // [opt] invoke-interface/range
+        assert(false);  // TODO
+        break;
+    case kFmt51l:       // op vAA, #+BBBBBBBBBBBBBBBB
+        pDec->vA = INST_AA(inst);
+        pDec->vB_wide = FETCH(1);
+        pDec->vB_wide |= (u8)FETCH(2) << 16;
+        pDec->vB_wide |= (u8)FETCH(3) << 32;
+        pDec->vB_wide |= (u8)FETCH(4) << 48;
+        break;
+    default:
+        LOGW("Can't decode unexpected format %d (op=%d)\n",
+            dexGetInstrFormat(fmts, pDec->opCode), pDec->opCode);
+        assert(false);
+        break;
+    }
+
+bail:
+    ;
+}
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined.  Also
+ * works for special OP_NOP entries, including switch statement data tables
+ * and array data.
+ */
+size_t dexGetInstrOrTableWidthAbs(const InstructionWidth* widths,
+    const u2* insns)
+{
+    size_t width;
+
+    if (*insns == kPackedSwitchSignature) {
+        width = 4 + insns[1] * 2;
+    } else if (*insns == kSparseSwitchSignature) {
+        width = 2 + insns[1] * 4;
+    } else if (*insns == kArrayDataSignature) {
+        u2 elemWidth = insns[1];
+        u4 len = insns[2] | (((u4)insns[3]) << 16);
+        width = 4 + (elemWidth * len + 1) / 2;
+    } else {
+        width = dexGetInstrWidthAbs(widths, INST_INST(insns[0]));
+    }
+    return width;
+}
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
new file mode 100644
index 0000000..ad2bc34
--- /dev/null
+++ b/libdex/InstrUtils.h
@@ -0,0 +1,186 @@
+/*
+ * 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 instruction utility functions.
+ */
+#ifndef _LIBDEX_INSTRUTILS
+#define _LIBDEX_INSTRUTILS
+
+#include "DexFile.h"
+#include "OpCode.h"
+
+/*
+ * Dalvik-defined instruction formats.
+ *
+ * (This defines InstructionFormat as an unsigned char to reduce the size
+ * of the table.  This isn't necessary with some compilers, which use an
+ * integer width appropriate for the number of enum values.)
+ *
+ * If you add or delete a format, you have to change some or all of:
+ *  - this enum
+ *  - the switch inside dexDecodeInstruction() in InstrUtils.c
+ *  - the switch inside dumpInstruction() in DexDump.c
+ */
+typedef unsigned char InstructionFormat;
+enum InstructionFormat {
+    kFmtUnknown = 0,
+    kFmt10x,        // op
+    kFmt12x,        // op vA, vB
+    kFmt11n,        // op vA, #+B
+    kFmt11x,        // op vAA
+    kFmt10t,        // op +AA
+    kFmt20bc,       // op AA, thing@BBBB
+    kFmt20t,        // op +AAAA
+    kFmt22x,        // op vAA, vBBBB
+    kFmt21t,        // op vAA, +BBBB
+    kFmt21s,        // op vAA, #+BBBB
+    kFmt21h,        // op vAA, #+BBBB00000[00000000]
+    kFmt21c,        // op vAA, thing@BBBB
+    kFmt23x,        // op vAA, vBB, vCC
+    kFmt22b,        // op vAA, vBB, #+CC
+    kFmt22t,        // op vA, vB, +CCCC
+    kFmt22s,        // op vA, vB, #+CCCC
+    kFmt22c,        // op vA, vB, thing@CCCC
+    kFmt22cs,       // [opt] op vA, vB, field offset CCCC
+    kFmt32x,        // op vAAAA, vBBBB
+    kFmt30t,        // op +AAAAAAAA
+    kFmt31t,        // op vAA, +BBBBBBBB
+    kFmt31i,        // op vAA, #+BBBBBBBB
+    kFmt31c,        // op vAA, thing@BBBBBBBB
+    kFmt35c,        // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG)
+    kFmt35ms,       // [opt] invoke-virtual+super
+    kFmt35fs,       // [opt] invoke-interface
+    kFmt3rc,        // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+    kFmt3rms,       // [opt] invoke-virtual+super/range
+    kFmt3rfs,       // [opt] invoke-interface/range
+    kFmt3inline,    // [opt] inline invoke
+    kFmt3rinline,   // [opt] inline invoke/range
+    kFmt51l,        // op vAA, #+BBBBBBBBBBBBBBBB
+};
+
+/*
+ * Holds the contents of a decoded instruction.
+ */
+typedef struct DecodedInstruction {
+    u4      vA;
+    u4      vB;
+    u8      vB_wide;        /* for kFmt51l */
+    u4      vC;
+    u4      arg[5];         /* vC/D/E/F/G in invoke or filled-new-array */
+    OpCode  opCode;
+} DecodedInstruction;
+
+/*
+ * Instruction width, a value in the range -3 to 5.
+ */
+typedef signed char InstructionWidth;
+
+/*
+ * Instruction flags, used by the verifier and JIT to determine where
+ * control can flow to next.  Expected to fit in 8 bits.
+ */
+typedef unsigned char InstructionFlags;
+enum InstructionFlags {
+    kInstrCanBranch     = 1,        // conditional or unconditional branch
+    kInstrCanContinue   = 1 << 1,   // flow can continue to next statement
+    kInstrCanSwitch     = 1 << 2,   // switch statement
+    kInstrCanThrow      = 1 << 3,   // could cause an exception to be thrown
+    kInstrCanReturn     = 1 << 4,   // returns, no additional statements
+    kInstrInvoke        = 1 << 5,   // a flavor of invoke
+    kInstrUnconditional = 1 << 6,   // unconditional branch
+};
+
+
+/*
+ * Allocate and populate a 256-element array with instruction widths.  A
+ * width of zero means the entry does not exist.
+ */
+InstructionWidth* dexCreateInstrWidthTable(void);
+
+#if 0       // no longer used
+/*
+ * Returns the width of the specified instruction, or 0 if not defined.
+ * Optimized instructions use negative values.
+ */
+DEX_INLINE int dexGetInstrWidth(const InstructionWidth* widths, OpCode opCode)
+{
+   // assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+    return widths[opCode];
+}
+#endif
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined.
+ */
+DEX_INLINE size_t dexGetInstrWidthAbs(const InstructionWidth* widths,
+    OpCode opCode)
+{
+    //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+
+    int val = widths[opCode];
+    if (val < 0)
+        val = -val;
+    /* XXX - the no-compare trick may be a cycle slower on ARM */
+    return val;
+}
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined.  Also
+ * works for special OP_NOP entries, including switch statement data tables
+ * and array data.
+ */
+size_t dexGetInstrOrTableWidthAbs(const InstructionWidth* widths,
+    const u2* insns);
+
+
+/*
+ * Allocate and populate a 256-element array with instruction flags.
+ */
+InstructionFlags* dexCreateInstrFlagsTable(void);
+
+/*
+ * Returns the flags for the specified opcode.
+ */
+DEX_INLINE int dexGetInstrFlags(const InstructionFlags* flags, OpCode opCode)
+{
+    //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+    return flags[opCode];
+}
+
+
+/*
+ * Allocate and populate a 256-element array with instruction formats.
+ */
+InstructionFormat* dexCreateInstrFormatTable(void);
+
+/*
+ * Return the instruction format for the specified opcode.
+ */
+DEX_INLINE InstructionFormat dexGetInstrFormat(const InstructionFormat* fmts,
+    OpCode opCode)
+{
+    //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+    return fmts[opCode];
+}
+
+/*
+ * Decode the instruction pointed to by "insns".
+ */
+void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns,
+    DecodedInstruction* pDec);
+
+#endif /*_LIBDEX_INSTRUTILS*/
diff --git a/libdex/Leb128.c b/libdex/Leb128.c
new file mode 100644
index 0000000..ed09e19
--- /dev/null
+++ b/libdex/Leb128.c
@@ -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.
+ */
+
+/*
+ * Functions for interpreting LEB128 (little endian base 128) values
+ */
+
+#include "Leb128.h"
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifyUnsignedLeb128(const u1** pStream, const u1* limit,
+        bool* okay) {
+    const u1* ptr = *pStream;
+    int result = readUnsignedLeb128(pStream);
+
+    if (((limit != NULL) && (*pStream > limit))
+            || (((*pStream - ptr) == 5) && (ptr[4] > 0x0f))) {
+        *okay = false;
+    }
+
+    return result;
+}
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifySignedLeb128(const u1** pStream, const u1* limit,
+        bool* okay) {
+    const u1* ptr = *pStream;
+    int result = readSignedLeb128(pStream);
+
+    if (((limit != NULL) && (*pStream > limit))
+            || (((*pStream - ptr) == 5) && (ptr[4] > 0x0f))) {
+        *okay = false;
+    }
+
+    return result;
+}
diff --git a/libdex/Leb128.h b/libdex/Leb128.h
new file mode 100644
index 0000000..41799fe
--- /dev/null
+++ b/libdex/Leb128.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.
+ */
+
+/*
+ * Functions for interpreting LEB128 (little endian base 128) values
+ */
+
+#ifndef _LIBDEX_LEB128
+#define _LIBDEX_LEB128
+
+#include "DexFile.h"
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value. This function tolerates
+ * non-zero high-order bits in the fifth encoded byte.
+ */
+DEX_INLINE int readUnsignedLeb128(const u1** pStream) {
+    const u1* ptr = *pStream;
+    int result = *(ptr++);
+
+    if (result > 0x7f) {
+        int cur = *(ptr++);
+        result = (result & 0x7f) | ((cur & 0x7f) << 7);
+        if (cur > 0x7f) {
+            cur = *(ptr++);
+            result |= (cur & 0x7f) << 14;
+            if (cur > 0x7f) {
+                cur = *(ptr++);
+                result |= (cur & 0x7f) << 21;
+                if (cur > 0x7f) {
+                    /*
+                     * Note: We don't check to see if cur is out of
+                     * range here, meaning we tolerate garbage in the
+                     * high four-order bits.
+                     */
+                    cur = *(ptr++);
+                    result |= cur << 28;
+                }
+            }
+        }
+    }
+
+    *pStream = ptr;
+    return result;
+}
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value. This function tolerates
+ * non-zero high-order bits in the fifth encoded byte.
+ */
+DEX_INLINE int readSignedLeb128(const u1** pStream) {
+    const u1* ptr = *pStream;
+    int result = *(ptr++);
+
+    if (result <= 0x7f) {
+        result = (result << 25) >> 25;
+    } else {
+        int cur = *(ptr++);
+        result = (result & 0x7f) | ((cur & 0x7f) << 7);
+        if (cur <= 0x7f) {
+            result = (result << 18) >> 18;
+        } else {
+            cur = *(ptr++);
+            result |= (cur & 0x7f) << 14;
+            if (cur <= 0x7f) {
+                result = (result << 11) >> 11;
+            } else {
+                cur = *(ptr++);
+                result |= (cur & 0x7f) << 21;
+                if (cur <= 0x7f) {
+                    result = (result << 4) >> 4;
+                } else {
+                    /*
+                     * Note: We don't check to see if cur is out of
+                     * range here, meaning we tolerate garbage in the
+                     * high four-order bits.
+                     */
+                    cur = *(ptr++);
+                    result |= cur << 28;
+                }
+            }
+        }
+    }
+
+    *pStream = ptr;
+    return result;
+}
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifyUnsignedLeb128(const u1** pStream, const u1* limit,
+        bool* okay);
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifySignedLeb128(const u1** pStream, const u1* limit, bool* okay);
+
+
+/*
+ * Writes a 32-bit value in unsigned ULEB128 format.
+ *
+ * Returns the updated pointer.
+ */
+DEX_INLINE u1* writeUnsignedLeb128(u1* ptr, u4 data)
+{
+    while (true) {
+        u1 out = data & 0x7f;
+        if (out != data) {
+            *ptr++ = out | 0x80;
+            data >>= 7;
+        } else {
+            *ptr++ = out;
+            break;
+        }
+    }
+
+    return ptr;
+}
+
+/*
+ * Returns the number of bytes needed to encode "val" in ULEB128 form.
+ */
+DEX_INLINE int unsignedLeb128Size(u4 data)
+{
+    int count = 0;
+
+    do {
+        data >>= 7;
+        count++;
+    } while (data != 0);
+
+    return count;
+}
+
+#endif
diff --git a/libdex/OpCode.h b/libdex/OpCode.h
new file mode 100644
index 0000000..312ff01
--- /dev/null
+++ b/libdex/OpCode.h
@@ -0,0 +1,664 @@
+/*
+ * 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 opcode enumeration.
+ */
+#ifndef _LIBDEX_OPCODE
+#define _LIBDEX_OPCODE
+
+/*
+ * If you add, delete, or renumber instructions, you need to change things
+ * in various places.  Renumbering really only affects the "unused" opcodes,
+ * which are given explicit enumeration values to make it easier to find
+ * the places in the code that need to be updated when making changes --
+ * if you replace "OP_UNUSED_2D" and neglect to update a switch statement,
+ * the compiler will complain about an unknown value.
+ *
+ * Opcode definitions and attributes:
+ *  - update the OpCode enum below
+ *  - update the "goto table" definition macro, DEFINE_GOTO_TABLE(), below
+ *  - update the instruction info table generators and (if you changed an
+ *    instruction format) instruction decoder in InstrUtils.c
+ *  - update the instruction format list in InstrUtils.h, if necessary
+ *  - update the parallel definitions in the class dalvik.bytecode.Opcodes
+ *
+ * Interpreter:
+ *  - implement/update the instruction in C in mterp/c/...
+ *    - verify new code by running with "dalvik.vm.execution-mode =
+ *      int:portable" or "-Xint:portable"
+ *  - implement/update the instruction in ARM in mterp/armv5/...
+ *    - verify by enabling ARM handler for that instruction in mterp config
+ *      and running int:fast as above
+ *  - repeat for other platforms (x86, ...)
+ *  (see notes in mterp/ReadMe.txt for rebuilding instructions)
+ *
+ * Verifier / optimizer:
+ *  - update some stuff in analysis/DexOptimize.c, analysis/DexVerify.c,
+ *    and/or analysis/CodeVerify.c as needed
+ *    - verify by running with verifier enabled (it's on by default)
+ *
+ * Tools:
+ *  - update the OpCodeNames table in dexdump/OpCodeNames.c
+ *  - update dexdump/DexDump.c if an instruction format has changed
+ *
+ * Note: The Dalvik VM tests (in the tests subdirectory) provide a convenient
+ * way to test most of the above without doing any rebuilds.  In particular,
+ * test 003-omnibus-opcodes will exercise most of the opcodes.
+ */
+
+/*
+ * Dalvik opcode list.
+ */
+typedef enum OpCode {
+    OP_NOP                          = 0x00,
+
+    OP_MOVE                         = 0x01,
+    OP_MOVE_FROM16                  = 0x02,
+    OP_MOVE_16                      = 0x03,
+    OP_MOVE_WIDE                    = 0x04,
+    OP_MOVE_WIDE_FROM16             = 0x05,
+    OP_MOVE_WIDE_16                 = 0x06,
+    OP_MOVE_OBJECT                  = 0x07,
+    OP_MOVE_OBJECT_FROM16           = 0x08,
+    OP_MOVE_OBJECT_16               = 0x09,
+
+    OP_MOVE_RESULT                  = 0x0a,
+    OP_MOVE_RESULT_WIDE             = 0x0b,
+    OP_MOVE_RESULT_OBJECT           = 0x0c,
+    OP_MOVE_EXCEPTION               = 0x0d,
+
+    OP_RETURN_VOID                  = 0x0e,
+    OP_RETURN                       = 0x0f,
+    OP_RETURN_WIDE                  = 0x10,
+    OP_RETURN_OBJECT                = 0x11,
+
+    OP_CONST_4                      = 0x12,
+    OP_CONST_16                     = 0x13,
+    OP_CONST                        = 0x14,
+    OP_CONST_HIGH16                 = 0x15,
+    OP_CONST_WIDE_16                = 0x16,
+    OP_CONST_WIDE_32                = 0x17,
+    OP_CONST_WIDE                   = 0x18,
+    OP_CONST_WIDE_HIGH16            = 0x19,
+    OP_CONST_STRING                 = 0x1a,
+    OP_CONST_STRING_JUMBO           = 0x1b,
+    OP_CONST_CLASS                  = 0x1c,
+
+    OP_MONITOR_ENTER                = 0x1d,
+    OP_MONITOR_EXIT                 = 0x1e,
+
+    OP_CHECK_CAST                   = 0x1f,
+    OP_INSTANCE_OF                  = 0x20,
+
+    OP_ARRAY_LENGTH                 = 0x21,
+
+    OP_NEW_INSTANCE                 = 0x22,
+    OP_NEW_ARRAY                    = 0x23,
+
+    OP_FILLED_NEW_ARRAY             = 0x24,
+    OP_FILLED_NEW_ARRAY_RANGE       = 0x25,
+    OP_FILL_ARRAY_DATA              = 0x26,
+
+    OP_THROW                        = 0x27,
+    OP_GOTO                         = 0x28,
+    OP_GOTO_16                      = 0x29,
+    OP_GOTO_32                      = 0x2a,
+    OP_PACKED_SWITCH                = 0x2b,
+    OP_SPARSE_SWITCH                = 0x2c,
+
+    OP_CMPL_FLOAT                   = 0x2d,
+    OP_CMPG_FLOAT                   = 0x2e,
+    OP_CMPL_DOUBLE                  = 0x2f,
+    OP_CMPG_DOUBLE                  = 0x30,
+    OP_CMP_LONG                     = 0x31,
+
+    OP_IF_EQ                        = 0x32,
+    OP_IF_NE                        = 0x33,
+    OP_IF_LT                        = 0x34,
+    OP_IF_GE                        = 0x35,
+    OP_IF_GT                        = 0x36,
+    OP_IF_LE                        = 0x37,
+    OP_IF_EQZ                       = 0x38,
+    OP_IF_NEZ                       = 0x39,
+    OP_IF_LTZ                       = 0x3a,
+    OP_IF_GEZ                       = 0x3b,
+    OP_IF_GTZ                       = 0x3c,
+    OP_IF_LEZ                       = 0x3d,
+
+    OP_UNUSED_3E                    = 0x3e,
+    OP_UNUSED_3F                    = 0x3f,
+    OP_UNUSED_40                    = 0x40,
+    OP_UNUSED_41                    = 0x41,
+    OP_UNUSED_42                    = 0x42,
+    OP_UNUSED_43                    = 0x43,
+
+    OP_AGET                         = 0x44,
+    OP_AGET_WIDE                    = 0x45,
+    OP_AGET_OBJECT                  = 0x46,
+    OP_AGET_BOOLEAN                 = 0x47,
+    OP_AGET_BYTE                    = 0x48,
+    OP_AGET_CHAR                    = 0x49,
+    OP_AGET_SHORT                   = 0x4a,
+    OP_APUT                         = 0x4b,
+    OP_APUT_WIDE                    = 0x4c,
+    OP_APUT_OBJECT                  = 0x4d,
+    OP_APUT_BOOLEAN                 = 0x4e,
+    OP_APUT_BYTE                    = 0x4f,
+    OP_APUT_CHAR                    = 0x50,
+    OP_APUT_SHORT                   = 0x51,
+
+    OP_IGET                         = 0x52,
+    OP_IGET_WIDE                    = 0x53,
+    OP_IGET_OBJECT                  = 0x54,
+    OP_IGET_BOOLEAN                 = 0x55,
+    OP_IGET_BYTE                    = 0x56,
+    OP_IGET_CHAR                    = 0x57,
+    OP_IGET_SHORT                   = 0x58,
+    OP_IPUT                         = 0x59,
+    OP_IPUT_WIDE                    = 0x5a,
+    OP_IPUT_OBJECT                  = 0x5b,
+    OP_IPUT_BOOLEAN                 = 0x5c,
+    OP_IPUT_BYTE                    = 0x5d,
+    OP_IPUT_CHAR                    = 0x5e,
+    OP_IPUT_SHORT                   = 0x5f,
+
+    OP_SGET                         = 0x60,
+    OP_SGET_WIDE                    = 0x61,
+    OP_SGET_OBJECT                  = 0x62,
+    OP_SGET_BOOLEAN                 = 0x63,
+    OP_SGET_BYTE                    = 0x64,
+    OP_SGET_CHAR                    = 0x65,
+    OP_SGET_SHORT                   = 0x66,
+    OP_SPUT                         = 0x67,
+    OP_SPUT_WIDE                    = 0x68,
+    OP_SPUT_OBJECT                  = 0x69,
+    OP_SPUT_BOOLEAN                 = 0x6a,
+    OP_SPUT_BYTE                    = 0x6b,
+    OP_SPUT_CHAR                    = 0x6c,
+    OP_SPUT_SHORT                   = 0x6d,
+
+    OP_INVOKE_VIRTUAL               = 0x6e,
+    OP_INVOKE_SUPER                 = 0x6f,
+    OP_INVOKE_DIRECT                = 0x70,
+    OP_INVOKE_STATIC                = 0x71,
+    OP_INVOKE_INTERFACE             = 0x72,
+
+    OP_UNUSED_73                    = 0x73,
+
+    OP_INVOKE_VIRTUAL_RANGE         = 0x74,
+    OP_INVOKE_SUPER_RANGE           = 0x75,
+    OP_INVOKE_DIRECT_RANGE          = 0x76,
+    OP_INVOKE_STATIC_RANGE          = 0x77,
+    OP_INVOKE_INTERFACE_RANGE       = 0x78,
+
+    OP_UNUSED_79                    = 0x79,
+    OP_UNUSED_7A                    = 0x7a,
+
+    OP_NEG_INT                      = 0x7b,
+    OP_NOT_INT                      = 0x7c,
+    OP_NEG_LONG                     = 0x7d,
+    OP_NOT_LONG                     = 0x7e,
+    OP_NEG_FLOAT                    = 0x7f,
+    OP_NEG_DOUBLE                   = 0x80,
+    OP_INT_TO_LONG                  = 0x81,
+    OP_INT_TO_FLOAT                 = 0x82,
+    OP_INT_TO_DOUBLE                = 0x83,
+    OP_LONG_TO_INT                  = 0x84,
+    OP_LONG_TO_FLOAT                = 0x85,
+    OP_LONG_TO_DOUBLE               = 0x86,
+    OP_FLOAT_TO_INT                 = 0x87,
+    OP_FLOAT_TO_LONG                = 0x88,
+    OP_FLOAT_TO_DOUBLE              = 0x89,
+    OP_DOUBLE_TO_INT                = 0x8a,
+    OP_DOUBLE_TO_LONG               = 0x8b,
+    OP_DOUBLE_TO_FLOAT              = 0x8c,
+    OP_INT_TO_BYTE                  = 0x8d,
+    OP_INT_TO_CHAR                  = 0x8e,
+    OP_INT_TO_SHORT                 = 0x8f,
+
+    OP_ADD_INT                      = 0x90,
+    OP_SUB_INT                      = 0x91,
+    OP_MUL_INT                      = 0x92,
+    OP_DIV_INT                      = 0x93,
+    OP_REM_INT                      = 0x94,
+    OP_AND_INT                      = 0x95,
+    OP_OR_INT                       = 0x96,
+    OP_XOR_INT                      = 0x97,
+    OP_SHL_INT                      = 0x98,
+    OP_SHR_INT                      = 0x99,
+    OP_USHR_INT                     = 0x9a,
+
+    OP_ADD_LONG                     = 0x9b,
+    OP_SUB_LONG                     = 0x9c,
+    OP_MUL_LONG                     = 0x9d,
+    OP_DIV_LONG                     = 0x9e,
+    OP_REM_LONG                     = 0x9f,
+    OP_AND_LONG                     = 0xa0,
+    OP_OR_LONG                      = 0xa1,
+    OP_XOR_LONG                     = 0xa2,
+    OP_SHL_LONG                     = 0xa3,
+    OP_SHR_LONG                     = 0xa4,
+    OP_USHR_LONG                    = 0xa5,
+
+    OP_ADD_FLOAT                    = 0xa6,
+    OP_SUB_FLOAT                    = 0xa7,
+    OP_MUL_FLOAT                    = 0xa8,
+    OP_DIV_FLOAT                    = 0xa9,
+    OP_REM_FLOAT                    = 0xaa,
+    OP_ADD_DOUBLE                   = 0xab,
+    OP_SUB_DOUBLE                   = 0xac,
+    OP_MUL_DOUBLE                   = 0xad,
+    OP_DIV_DOUBLE                   = 0xae,
+    OP_REM_DOUBLE                   = 0xaf,
+
+    OP_ADD_INT_2ADDR                = 0xb0,
+    OP_SUB_INT_2ADDR                = 0xb1,
+    OP_MUL_INT_2ADDR                = 0xb2,
+    OP_DIV_INT_2ADDR                = 0xb3,
+    OP_REM_INT_2ADDR                = 0xb4,
+    OP_AND_INT_2ADDR                = 0xb5,
+    OP_OR_INT_2ADDR                 = 0xb6,
+    OP_XOR_INT_2ADDR                = 0xb7,
+    OP_SHL_INT_2ADDR                = 0xb8,
+    OP_SHR_INT_2ADDR                = 0xb9,
+    OP_USHR_INT_2ADDR               = 0xba,
+
+    OP_ADD_LONG_2ADDR               = 0xbb,
+    OP_SUB_LONG_2ADDR               = 0xbc,
+    OP_MUL_LONG_2ADDR               = 0xbd,
+    OP_DIV_LONG_2ADDR               = 0xbe,
+    OP_REM_LONG_2ADDR               = 0xbf,
+    OP_AND_LONG_2ADDR               = 0xc0,
+    OP_OR_LONG_2ADDR                = 0xc1,
+    OP_XOR_LONG_2ADDR               = 0xc2,
+    OP_SHL_LONG_2ADDR               = 0xc3,
+    OP_SHR_LONG_2ADDR               = 0xc4,
+    OP_USHR_LONG_2ADDR              = 0xc5,
+
+    OP_ADD_FLOAT_2ADDR              = 0xc6,
+    OP_SUB_FLOAT_2ADDR              = 0xc7,
+    OP_MUL_FLOAT_2ADDR              = 0xc8,
+    OP_DIV_FLOAT_2ADDR              = 0xc9,
+    OP_REM_FLOAT_2ADDR              = 0xca,
+    OP_ADD_DOUBLE_2ADDR             = 0xcb,
+    OP_SUB_DOUBLE_2ADDR             = 0xcc,
+    OP_MUL_DOUBLE_2ADDR             = 0xcd,
+    OP_DIV_DOUBLE_2ADDR             = 0xce,
+    OP_REM_DOUBLE_2ADDR             = 0xcf,
+
+    OP_ADD_INT_LIT16                = 0xd0,
+    OP_RSUB_INT                     = 0xd1, /* no _LIT16 suffix for this */
+    OP_MUL_INT_LIT16                = 0xd2,
+    OP_DIV_INT_LIT16                = 0xd3,
+    OP_REM_INT_LIT16                = 0xd4,
+    OP_AND_INT_LIT16                = 0xd5,
+    OP_OR_INT_LIT16                 = 0xd6,
+    OP_XOR_INT_LIT16                = 0xd7,
+
+    OP_ADD_INT_LIT8                 = 0xd8,
+    OP_RSUB_INT_LIT8                = 0xd9,
+    OP_MUL_INT_LIT8                 = 0xda,
+    OP_DIV_INT_LIT8                 = 0xdb,
+    OP_REM_INT_LIT8                 = 0xdc,
+    OP_AND_INT_LIT8                 = 0xdd,
+    OP_OR_INT_LIT8                  = 0xde,
+    OP_XOR_INT_LIT8                 = 0xdf,
+    OP_SHL_INT_LIT8                 = 0xe0,
+    OP_SHR_INT_LIT8                 = 0xe1,
+    OP_USHR_INT_LIT8                = 0xe2,
+
+    /* verifier/optimizer output -- nothing below here is generated by "dx" */
+    OP_IGET_VOLATILE                = 0xe3,
+    OP_IPUT_VOLATILE                = 0xe4,
+    OP_SGET_VOLATILE                = 0xe5,
+    OP_SPUT_VOLATILE                = 0xe6,
+    OP_IGET_OBJECT_VOLATILE         = 0xe7,
+
+    OP_IGET_WIDE_VOLATILE           = 0xe8,
+    OP_IPUT_WIDE_VOLATILE           = 0xe9,
+    OP_SGET_WIDE_VOLATILE           = 0xea,
+    OP_SPUT_WIDE_VOLATILE           = 0xeb,
+
+    /*
+     * The "breakpoint" instruction is special, in that it should never
+     * be seen by anything but the debug interpreter.  During debugging
+     * it takes the place of an arbitrary opcode, which means operations
+     * like "tell me the opcode width so I can find the next instruction"
+     * aren't possible.  (This is correctable, but probably not useful.)
+     */
+    OP_BREAKPOINT                   = 0xec,
+
+    OP_THROW_VERIFICATION_ERROR     = 0xed,
+    OP_EXECUTE_INLINE               = 0xee,
+    OP_EXECUTE_INLINE_RANGE         = 0xef,
+
+    OP_INVOKE_DIRECT_EMPTY          = 0xf0,
+    OP_UNUSED_F1                    = 0xf1, /* OP_INVOKE_DIRECT_EMPTY_RANGE? */
+    OP_IGET_QUICK                   = 0xf2,
+    OP_IGET_WIDE_QUICK              = 0xf3,
+    OP_IGET_OBJECT_QUICK            = 0xf4,
+    OP_IPUT_QUICK                   = 0xf5,
+    OP_IPUT_WIDE_QUICK              = 0xf6,
+    OP_IPUT_OBJECT_QUICK            = 0xf7,
+
+    OP_INVOKE_VIRTUAL_QUICK         = 0xf8,
+    OP_INVOKE_VIRTUAL_QUICK_RANGE   = 0xf9,
+    OP_INVOKE_SUPER_QUICK           = 0xfa,
+    OP_INVOKE_SUPER_QUICK_RANGE     = 0xfb,
+    OP_IPUT_OBJECT_VOLATILE         = 0xfc,
+    OP_SGET_OBJECT_VOLATILE         = 0xfd,
+    OP_SPUT_OBJECT_VOLATILE         = 0xfe,
+
+    OP_UNUSED_FF                    = 0xff, /* reserved for code expansion */
+} OpCode;
+
+#define kNumDalvikInstructions 256
+
+
+/*
+ * Switch-statement signatures are a "NOP" followed by a code.  (A true NOP
+ * is 0x0000.)
+ */
+#define kPackedSwitchSignature  0x0100
+#define kSparseSwitchSignature  0x0200
+#define kArrayDataSignature     0x0300
+
+/*
+ * Macro used to generate computed goto tables for the C interpreter.
+ *
+ * The labels here must match up with the labels in the interpreter
+ * implementation.  There is no direct connection between these and the
+ * numeric definitions above, but if the two get out of sync strange things
+ * will happen.
+ */
+#define DEFINE_GOTO_TABLE(_name) \
+    static const void* _name[kNumDalvikInstructions] = {                    \
+        /* 00..0f */                                                        \
+        H(OP_NOP),                                                          \
+        H(OP_MOVE),                                                         \
+        H(OP_MOVE_FROM16),                                                  \
+        H(OP_MOVE_16),                                                      \
+        H(OP_MOVE_WIDE),                                                    \
+        H(OP_MOVE_WIDE_FROM16),                                             \
+        H(OP_MOVE_WIDE_16),                                                 \
+        H(OP_MOVE_OBJECT),                                                  \
+        H(OP_MOVE_OBJECT_FROM16),                                           \
+        H(OP_MOVE_OBJECT_16),                                               \
+        H(OP_MOVE_RESULT),                                                  \
+        H(OP_MOVE_RESULT_WIDE),                                             \
+        H(OP_MOVE_RESULT_OBJECT),                                           \
+        H(OP_MOVE_EXCEPTION),                                               \
+        H(OP_RETURN_VOID),                                                  \
+        H(OP_RETURN),                                                       \
+        /* 10..1f */                                                        \
+        H(OP_RETURN_WIDE),                                                  \
+        H(OP_RETURN_OBJECT),                                                \
+        H(OP_CONST_4),                                                      \
+        H(OP_CONST_16),                                                     \
+        H(OP_CONST),                                                        \
+        H(OP_CONST_HIGH16),                                                 \
+        H(OP_CONST_WIDE_16),                                                \
+        H(OP_CONST_WIDE_32),                                                \
+        H(OP_CONST_WIDE),                                                   \
+        H(OP_CONST_WIDE_HIGH16),                                            \
+        H(OP_CONST_STRING),                                                 \
+        H(OP_CONST_STRING_JUMBO),                                           \
+        H(OP_CONST_CLASS),                                                  \
+        H(OP_MONITOR_ENTER),                                                \
+        H(OP_MONITOR_EXIT),                                                 \
+        H(OP_CHECK_CAST),                                                   \
+        /* 20..2f */                                                        \
+        H(OP_INSTANCE_OF),                                                  \
+        H(OP_ARRAY_LENGTH),                                                 \
+        H(OP_NEW_INSTANCE),                                                 \
+        H(OP_NEW_ARRAY),                                                    \
+        H(OP_FILLED_NEW_ARRAY),                                             \
+        H(OP_FILLED_NEW_ARRAY_RANGE),                                       \
+        H(OP_FILL_ARRAY_DATA),                                              \
+        H(OP_THROW),                                                        \
+        H(OP_GOTO),                                                         \
+        H(OP_GOTO_16),                                                      \
+        H(OP_GOTO_32),                                                      \
+        H(OP_PACKED_SWITCH),                                                \
+        H(OP_SPARSE_SWITCH),                                                \
+        H(OP_CMPL_FLOAT),                                                   \
+        H(OP_CMPG_FLOAT),                                                   \
+        H(OP_CMPL_DOUBLE),                                                  \
+        /* 30..3f */                                                        \
+        H(OP_CMPG_DOUBLE),                                                  \
+        H(OP_CMP_LONG),                                                     \
+        H(OP_IF_EQ),                                                        \
+        H(OP_IF_NE),                                                        \
+        H(OP_IF_LT),                                                        \
+        H(OP_IF_GE),                                                        \
+        H(OP_IF_GT),                                                        \
+        H(OP_IF_LE),                                                        \
+        H(OP_IF_EQZ),                                                       \
+        H(OP_IF_NEZ),                                                       \
+        H(OP_IF_LTZ),                                                       \
+        H(OP_IF_GEZ),                                                       \
+        H(OP_IF_GTZ),                                                       \
+        H(OP_IF_LEZ),                                                       \
+        H(OP_UNUSED_3E),                                                    \
+        H(OP_UNUSED_3F),                                                    \
+        /* 40..4f */                                                        \
+        H(OP_UNUSED_40),                                                    \
+        H(OP_UNUSED_41),                                                    \
+        H(OP_UNUSED_42),                                                    \
+        H(OP_UNUSED_43),                                                    \
+        H(OP_AGET),                                                         \
+        H(OP_AGET_WIDE),                                                    \
+        H(OP_AGET_OBJECT),                                                  \
+        H(OP_AGET_BOOLEAN),                                                 \
+        H(OP_AGET_BYTE),                                                    \
+        H(OP_AGET_CHAR),                                                    \
+        H(OP_AGET_SHORT),                                                   \
+        H(OP_APUT),                                                         \
+        H(OP_APUT_WIDE),                                                    \
+        H(OP_APUT_OBJECT),                                                  \
+        H(OP_APUT_BOOLEAN),                                                 \
+        H(OP_APUT_BYTE),                                                    \
+        /* 50..5f */                                                        \
+        H(OP_APUT_CHAR),                                                    \
+        H(OP_APUT_SHORT),                                                   \
+        H(OP_IGET),                                                         \
+        H(OP_IGET_WIDE),                                                    \
+        H(OP_IGET_OBJECT),                                                  \
+        H(OP_IGET_BOOLEAN),                                                 \
+        H(OP_IGET_BYTE),                                                    \
+        H(OP_IGET_CHAR),                                                    \
+        H(OP_IGET_SHORT),                                                   \
+        H(OP_IPUT),                                                         \
+        H(OP_IPUT_WIDE),                                                    \
+        H(OP_IPUT_OBJECT),                                                  \
+        H(OP_IPUT_BOOLEAN),                                                 \
+        H(OP_IPUT_BYTE),                                                    \
+        H(OP_IPUT_CHAR),                                                    \
+        H(OP_IPUT_SHORT),                                                   \
+        /* 60..6f */                                                        \
+        H(OP_SGET),                                                         \
+        H(OP_SGET_WIDE),                                                    \
+        H(OP_SGET_OBJECT),                                                  \
+        H(OP_SGET_BOOLEAN),                                                 \
+        H(OP_SGET_BYTE),                                                    \
+        H(OP_SGET_CHAR),                                                    \
+        H(OP_SGET_SHORT),                                                   \
+        H(OP_SPUT),                                                         \
+        H(OP_SPUT_WIDE),                                                    \
+        H(OP_SPUT_OBJECT),                                                  \
+        H(OP_SPUT_BOOLEAN),                                                 \
+        H(OP_SPUT_BYTE),                                                    \
+        H(OP_SPUT_CHAR),                                                    \
+        H(OP_SPUT_SHORT),                                                   \
+        H(OP_INVOKE_VIRTUAL),                                               \
+        H(OP_INVOKE_SUPER),                                                 \
+        /* 70..7f */                                                        \
+        H(OP_INVOKE_DIRECT),                                                \
+        H(OP_INVOKE_STATIC),                                                \
+        H(OP_INVOKE_INTERFACE),                                             \
+        H(OP_UNUSED_73),                                                    \
+        H(OP_INVOKE_VIRTUAL_RANGE),                                         \
+        H(OP_INVOKE_SUPER_RANGE),                                           \
+        H(OP_INVOKE_DIRECT_RANGE),                                          \
+        H(OP_INVOKE_STATIC_RANGE),                                          \
+        H(OP_INVOKE_INTERFACE_RANGE),                                       \
+        H(OP_UNUSED_79),                                                    \
+        H(OP_UNUSED_7A),                                                    \
+        H(OP_NEG_INT),                                                      \
+        H(OP_NOT_INT),                                                      \
+        H(OP_NEG_LONG),                                                     \
+        H(OP_NOT_LONG),                                                     \
+        H(OP_NEG_FLOAT),                                                    \
+        /* 80..8f */                                                        \
+        H(OP_NEG_DOUBLE),                                                   \
+        H(OP_INT_TO_LONG),                                                  \
+        H(OP_INT_TO_FLOAT),                                                 \
+        H(OP_INT_TO_DOUBLE),                                                \
+        H(OP_LONG_TO_INT),                                                  \
+        H(OP_LONG_TO_FLOAT),                                                \
+        H(OP_LONG_TO_DOUBLE),                                               \
+        H(OP_FLOAT_TO_INT),                                                 \
+        H(OP_FLOAT_TO_LONG),                                                \
+        H(OP_FLOAT_TO_DOUBLE),                                              \
+        H(OP_DOUBLE_TO_INT),                                                \
+        H(OP_DOUBLE_TO_LONG),                                               \
+        H(OP_DOUBLE_TO_FLOAT),                                              \
+        H(OP_INT_TO_BYTE),                                                  \
+        H(OP_INT_TO_CHAR),                                                  \
+        H(OP_INT_TO_SHORT),                                                 \
+        /* 90..9f */                                                        \
+        H(OP_ADD_INT),                                                      \
+        H(OP_SUB_INT),                                                      \
+        H(OP_MUL_INT),                                                      \
+        H(OP_DIV_INT),                                                      \
+        H(OP_REM_INT),                                                      \
+        H(OP_AND_INT),                                                      \
+        H(OP_OR_INT),                                                       \
+        H(OP_XOR_INT),                                                      \
+        H(OP_SHL_INT),                                                      \
+        H(OP_SHR_INT),                                                      \
+        H(OP_USHR_INT),                                                     \
+        H(OP_ADD_LONG),                                                     \
+        H(OP_SUB_LONG),                                                     \
+        H(OP_MUL_LONG),                                                     \
+        H(OP_DIV_LONG),                                                     \
+        H(OP_REM_LONG),                                                     \
+        /* a0..af */                                                        \
+        H(OP_AND_LONG),                                                     \
+        H(OP_OR_LONG),                                                      \
+        H(OP_XOR_LONG),                                                     \
+        H(OP_SHL_LONG),                                                     \
+        H(OP_SHR_LONG),                                                     \
+        H(OP_USHR_LONG),                                                    \
+        H(OP_ADD_FLOAT),                                                    \
+        H(OP_SUB_FLOAT),                                                    \
+        H(OP_MUL_FLOAT),                                                    \
+        H(OP_DIV_FLOAT),                                                    \
+        H(OP_REM_FLOAT),                                                    \
+        H(OP_ADD_DOUBLE),                                                   \
+        H(OP_SUB_DOUBLE),                                                   \
+        H(OP_MUL_DOUBLE),                                                   \
+        H(OP_DIV_DOUBLE),                                                   \
+        H(OP_REM_DOUBLE),                                                   \
+        /* b0..bf */                                                        \
+        H(OP_ADD_INT_2ADDR),                                                \
+        H(OP_SUB_INT_2ADDR),                                                \
+        H(OP_MUL_INT_2ADDR),                                                \
+        H(OP_DIV_INT_2ADDR),                                                \
+        H(OP_REM_INT_2ADDR),                                                \
+        H(OP_AND_INT_2ADDR),                                                \
+        H(OP_OR_INT_2ADDR),                                                 \
+        H(OP_XOR_INT_2ADDR),                                                \
+        H(OP_SHL_INT_2ADDR),                                                \
+        H(OP_SHR_INT_2ADDR),                                                \
+        H(OP_USHR_INT_2ADDR),                                               \
+        H(OP_ADD_LONG_2ADDR),                                               \
+        H(OP_SUB_LONG_2ADDR),                                               \
+        H(OP_MUL_LONG_2ADDR),                                               \
+        H(OP_DIV_LONG_2ADDR),                                               \
+        H(OP_REM_LONG_2ADDR),                                               \
+        /* c0..cf */                                                        \
+        H(OP_AND_LONG_2ADDR),                                               \
+        H(OP_OR_LONG_2ADDR),                                                \
+        H(OP_XOR_LONG_2ADDR),                                               \
+        H(OP_SHL_LONG_2ADDR),                                               \
+        H(OP_SHR_LONG_2ADDR),                                               \
+        H(OP_USHR_LONG_2ADDR),                                              \
+        H(OP_ADD_FLOAT_2ADDR),                                              \
+        H(OP_SUB_FLOAT_2ADDR),                                              \
+        H(OP_MUL_FLOAT_2ADDR),                                              \
+        H(OP_DIV_FLOAT_2ADDR),                                              \
+        H(OP_REM_FLOAT_2ADDR),                                              \
+        H(OP_ADD_DOUBLE_2ADDR),                                             \
+        H(OP_SUB_DOUBLE_2ADDR),                                             \
+        H(OP_MUL_DOUBLE_2ADDR),                                             \
+        H(OP_DIV_DOUBLE_2ADDR),                                             \
+        H(OP_REM_DOUBLE_2ADDR),                                             \
+        /* d0..df */                                                        \
+        H(OP_ADD_INT_LIT16),                                                \
+        H(OP_RSUB_INT),                                                     \
+        H(OP_MUL_INT_LIT16),                                                \
+        H(OP_DIV_INT_LIT16),                                                \
+        H(OP_REM_INT_LIT16),                                                \
+        H(OP_AND_INT_LIT16),                                                \
+        H(OP_OR_INT_LIT16),                                                 \
+        H(OP_XOR_INT_LIT16),                                                \
+        H(OP_ADD_INT_LIT8),                                                 \
+        H(OP_RSUB_INT_LIT8),                                                \
+        H(OP_MUL_INT_LIT8),                                                 \
+        H(OP_DIV_INT_LIT8),                                                 \
+        H(OP_REM_INT_LIT8),                                                 \
+        H(OP_AND_INT_LIT8),                                                 \
+        H(OP_OR_INT_LIT8),                                                  \
+        H(OP_XOR_INT_LIT8),                                                 \
+        /* e0..ef */                                                        \
+        H(OP_SHL_INT_LIT8),                                                 \
+        H(OP_SHR_INT_LIT8),                                                 \
+        H(OP_USHR_INT_LIT8),                                                \
+        H(OP_IGET_VOLATILE),                                                \
+        H(OP_IPUT_VOLATILE),                                                \
+        H(OP_SGET_VOLATILE),                                                \
+        H(OP_SPUT_VOLATILE),                                                \
+        H(OP_IGET_OBJECT_VOLATILE),                                         \
+        H(OP_IGET_WIDE_VOLATILE),                                           \
+        H(OP_IPUT_WIDE_VOLATILE),                                           \
+        H(OP_SGET_WIDE_VOLATILE),                                           \
+        H(OP_SPUT_WIDE_VOLATILE),                                           \
+        H(OP_BREAKPOINT),                                                   \
+        H(OP_THROW_VERIFICATION_ERROR),                                     \
+        H(OP_EXECUTE_INLINE),                                               \
+        H(OP_EXECUTE_INLINE_RANGE),                                         \
+        /* f0..ff */                                                        \
+        H(OP_INVOKE_DIRECT_EMPTY),                                          \
+        H(OP_UNUSED_F1),                                                    \
+        H(OP_IGET_QUICK),                                                   \
+        H(OP_IGET_WIDE_QUICK),                                              \
+        H(OP_IGET_OBJECT_QUICK),                                            \
+        H(OP_IPUT_QUICK),                                                   \
+        H(OP_IPUT_WIDE_QUICK),                                              \
+        H(OP_IPUT_OBJECT_QUICK),                                            \
+        H(OP_INVOKE_VIRTUAL_QUICK),                                         \
+        H(OP_INVOKE_VIRTUAL_QUICK_RANGE),                                   \
+        H(OP_INVOKE_SUPER_QUICK),                                           \
+        H(OP_INVOKE_SUPER_QUICK_RANGE),                                     \
+        H(OP_IPUT_OBJECT_VOLATILE),                                         \
+        H(OP_SGET_OBJECT_VOLATILE),                                         \
+        H(OP_SPUT_OBJECT_VOLATILE),                                         \
+        H(OP_UNUSED_FF),                                                    \
+    };
+
+#endif /*_LIBDEX_OPCODE*/
diff --git a/libdex/OpCodeNames.c b/libdex/OpCodeNames.c
new file mode 100644
index 0000000..c182d4e
--- /dev/null
+++ b/libdex/OpCodeNames.c
@@ -0,0 +1,333 @@
+/*
+ * 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.
+ */
+
+/*
+ * Table of Dalvik opcode names.
+ */
+#include "OpCodeNames.h"
+
+#include <assert.h>
+
+/*
+ * The following two lines work, but slashes and dashes both turn into
+ * underscores, and the strings are all upper case.  The output is easier
+ * to read if we do the strings by hand (could probably write a
+ * post-processing function easily enough if maintenance becomes annoying).
+ */
+//#define H(_op) #_op
+//DEFINE_GOTO_TABLE(gOpNames)
+
+/*
+ * Dalvik opcode names.
+ */
+static const char* gOpNames[256] = {
+    /* 0x00 */
+    "nop",
+    "move",
+    "move/from16",
+    "move/16",
+    "move-wide",
+    "move-wide/from16",
+    "move-wide/16",
+    "move-object",
+    "move-object/from16",
+    "move-object/16",
+    "move-result",
+    "move-result-wide",
+    "move-result-object",
+    "move-exception",
+    "return-void",
+    "return",
+
+    /* 0x10 */
+    "return-wide",
+    "return-object",
+    "const/4",
+    "const/16",
+    "const",
+    "const/high16",
+    "const-wide/16",
+    "const-wide/32",
+    "const-wide",
+    "const-wide/high16",
+    "const-string",
+    "const-string/jumbo",
+    "const-class",
+    "monitor-enter",
+    "monitor-exit",
+    "check-cast",
+
+    /* 0x20 */
+    "instance-of",
+    "array-length",
+    "new-instance",
+    "new-array",
+    "filled-new-array",
+    "filled-new-array/range",
+    "fill-array-data",
+    "throw",
+    "goto",
+    "goto/16",
+    "goto/32",
+    "packed-switch",
+    "sparse-switch",
+    "cmpl-float",
+    "cmpg-float",
+    "cmpl-double",
+
+    /* 0x30 */
+    "cmpg-double",
+    "cmp-long",
+    "if-eq",
+    "if-ne",
+    "if-lt",
+    "if-ge",
+    "if-gt",
+    "if-le",
+    "if-eqz",
+    "if-nez",
+    "if-ltz",
+    "if-gez",
+    "if-gtz",
+    "if-lez",
+    "UNUSED",
+    "UNUSED",
+
+    /* 0x40 */
+    "UNUSED",
+    "UNUSED",
+    "UNUSED",
+    "UNUSED",
+    "aget",
+    "aget-wide",
+    "aget-object",
+    "aget-boolean",
+    "aget-byte",
+    "aget-char",
+    "aget-short",
+    "aput",
+    "aput-wide",
+    "aput-object",
+    "aput-boolean",
+    "aput-byte",
+
+    /* 0x50 */
+    "aput-char",
+    "aput-short",
+    "iget",
+    "iget-wide",
+    "iget-object",
+    "iget-boolean",
+    "iget-byte",
+    "iget-char",
+    "iget-short",
+    "iput",
+    "iput-wide",
+    "iput-object",
+    "iput-boolean",
+    "iput-byte",
+    "iput-char",
+    "iput-short",
+
+    /* 0x60 */
+    "sget",
+    "sget-wide",
+    "sget-object",
+    "sget-boolean",
+    "sget-byte",
+    "sget-char",
+    "sget-short",
+    "sput",
+    "sput-wide",
+    "sput-object",
+    "sput-boolean",
+    "sput-byte",
+    "sput-char",
+    "sput-short",
+    "invoke-virtual",
+    "invoke-super",
+
+    /* 0x70 */
+    "invoke-direct",
+    "invoke-static",
+    "invoke-interface",
+    "UNUSED",
+    "invoke-virtual/range",
+    "invoke-super/range",
+    "invoke-direct/range",
+    "invoke-static/range",
+    "invoke-interface/range",
+    "UNUSED",
+    "UNUSED",
+    "neg-int",
+    "not-int",
+    "neg-long",
+    "not-long",
+    "neg-float",
+
+    /* 0x80 */
+    "neg-double",
+    "int-to-long",
+    "int-to-float",
+    "int-to-double",
+    "long-to-int",
+    "long-to-float",
+    "long-to-double",
+    "float-to-int",
+    "float-to-long",
+    "float-to-double",
+    "double-to-int",
+    "double-to-long",
+    "double-to-float",
+    "int-to-byte",
+    "int-to-char",
+    "int-to-short",
+
+    /* 0x90 */
+    "add-int",
+    "sub-int",
+    "mul-int",
+    "div-int",
+    "rem-int",
+    "and-int",
+    "or-int",
+    "xor-int",
+    "shl-int",
+    "shr-int",
+    "ushr-int",
+    "add-long",
+    "sub-long",
+    "mul-long",
+    "div-long",
+    "rem-long",
+
+    /* 0xa0 */
+    "and-long",
+    "or-long",
+    "xor-long",
+    "shl-long",
+    "shr-long",
+    "ushr-long",
+    "add-float",
+    "sub-float",
+    "mul-float",
+    "div-float",
+    "rem-float",
+    "add-double",
+    "sub-double",
+    "mul-double",
+    "div-double",
+    "rem-double",
+
+    /* 0xb0 */
+    "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-long/2addr",
+    "sub-long/2addr",
+    "mul-long/2addr",
+    "div-long/2addr",
+    "rem-long/2addr",
+
+    /* 0xc0 */
+    "and-long/2addr",
+    "or-long/2addr",
+    "xor-long/2addr",
+    "shl-long/2addr",
+    "shr-long/2addr",
+    "ushr-long/2addr",
+    "add-float/2addr",
+    "sub-float/2addr",
+    "mul-float/2addr",
+    "div-float/2addr",
+    "rem-float/2addr",
+    "add-double/2addr",
+    "sub-double/2addr",
+    "mul-double/2addr",
+    "div-double/2addr",
+    "rem-double/2addr",
+
+    /* 0xd0 */
+    "add-int/lit16",
+    "rsub-int",
+    "mul-int/lit16",
+    "div-int/lit16",
+    "rem-int/lit16",
+    "and-int/lit16",
+    "or-int/lit16",
+    "xor-int/lit16",
+    "add-int/lit8",
+    "rsub-int/lit8",
+    "mul-int/lit8",
+    "div-int/lit8",
+    "rem-int/lit8",
+    "and-int/lit8",
+    "or-int/lit8",
+    "xor-int/lit8",
+
+    /* 0xe0 */
+    "shl-int/lit8",
+    "shr-int/lit8",
+    "ushr-int/lit8",
+    "+iget-volatile",
+    "+iput-volatile",
+    "+sget-volatile",
+    "+sput-volatile",
+    "+iget-object-volatile",
+    "+iget-wide-volatile",
+    "+iput-wide-volatile",
+    "+sget-wide-volatile",
+    "+sput-wide-volatile",
+    "^breakpoint",                  // does not appear in DEX files
+    "^throw-verification-error",    // does not appear in DEX files
+    "+execute-inline",
+    "+execute-inline/range",
+
+    /* 0xf0 */
+    "+invoke-direct-empty",
+    "UNUSED",
+    "+iget-quick",
+    "+iget-wide-quick",
+    "+iget-object-quick",
+    "+iput-quick",
+    "+iput-wide-quick",
+    "+iput-object-quick",
+    "+invoke-virtual-quick",
+    "+invoke-virtual-quick/range",
+    "+invoke-super-quick",
+    "+invoke-super-quick/range",
+    "+iput-object-volatile",
+    "+sget-object-volatile",
+    "+sput-object-volatile",
+    "UNUSED",
+};
+
+/*
+ * Return the name of an opcode.
+ */
+const char* dexGetOpcodeName(OpCode op)
+{
+    assert(op >= 0 && op < kNumDalvikInstructions);
+    return gOpNames[op];
+}
diff --git a/libdex/OpCodeNames.h b/libdex/OpCodeNames.h
new file mode 100644
index 0000000..f81368d
--- /dev/null
+++ b/libdex/OpCodeNames.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.
+ */
+
+/*
+ * Dalvik opcode names.
+ */
+#ifndef _LIBDEX_OPCODENAMES
+#define _LIBDEX_OPCODENAMES
+
+#include "OpCode.h"
+
+const char* dexGetOpcodeName(OpCode op);
+
+#endif /*_LIBDEX_OPCODENAMES*/
diff --git a/libdex/OptInvocation.c b/libdex/OptInvocation.c
new file mode 100644
index 0000000..df5f8d9
--- /dev/null
+++ b/libdex/OptInvocation.c
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+/*
+ * Utility functions for managing an invocation of "dexopt".
+ */
+#include "vm/DalvikVersion.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <errno.h>
+
+#include "OptInvocation.h"
+#include "DexFile.h"
+
+static const char* kClassesDex = "classes.dex";
+
+
+/*
+ * Given the filename of a .jar or .dex file, construct the DEX file cache
+ * name.
+ *
+ * For a Jar, "subFileName" is the name of the entry (usually "classes.dex").
+ * For a DEX, it may be NULL.
+ *
+ * Returns a newly-allocated string, or NULL on failure.
+ */
+char* dexOptGenerateCacheFileName(const char* fileName, const char* subFileName)
+{
+    char nameBuf[512];
+    static const char kDexCachePath[] = "dalvik-cache";
+    char absoluteFile[sizeof(nameBuf)];
+    const size_t kBufLen = sizeof(nameBuf) - 1;
+    const char* dataRoot;
+    char* cp;
+
+    /*
+     * Get the absolute path of the Jar or DEX file.
+     */
+    absoluteFile[0] = '\0';
+    if (fileName[0] != '/') {
+        /*
+         * Generate the absolute path.  This doesn't do everything it
+         * should, e.g. if filename is "./out/whatever" it doesn't crunch
+         * the leading "./" out, but it'll do.
+         */
+        if (getcwd(absoluteFile, kBufLen) == NULL) {
+            LOGE("Can't get CWD while opening jar file\n");
+            return NULL;
+        }
+        strncat(absoluteFile, "/", kBufLen);
+    }
+    strncat(absoluteFile, fileName, kBufLen);
+
+    /*
+     * Append the name of the Jar file entry, if any.  This is not currently
+     * required, but will be if we start putting more than one DEX file
+     * in a Jar.
+     */
+    if (subFileName != NULL) {
+        strncat(absoluteFile, "/", kBufLen);
+        strncat(absoluteFile, subFileName, kBufLen);
+    }
+
+    /* Turn the path into a flat filename by replacing
+     * any slashes after the first one with '@' characters.
+     */
+    cp = absoluteFile + 1;
+    while (*cp != '\0') {
+        if (*cp == '/') {
+            *cp = '@';
+        }
+        cp++;
+    }
+
+    /* Build the name of the cache directory.
+     */
+    dataRoot = getenv("ANDROID_DATA");
+    if (dataRoot == NULL)
+        dataRoot = "/data";
+    snprintf(nameBuf, kBufLen, "%s/%s", dataRoot, kDexCachePath);
+
+    /* Tack on the file name for the actual cache file path.
+     */
+    strncat(nameBuf, absoluteFile, kBufLen);
+
+    LOGV("Cache file for '%s' '%s' is '%s'\n", fileName, subFileName, nameBuf);
+    return strdup(nameBuf);
+}
+
+/*
+ * Create a skeletal "opt" header in a new file.  Most of the fields are
+ * initialized to garbage, but we fill in "dexOffset" so others can
+ * see how large the header is.
+ *
+ * "fd" must be positioned at the start of the file.  On return, it will
+ * be positioned just past the header, and the place where the DEX data
+ * should go.
+ *
+ * Returns 0 on success, errno on failure.
+ */
+int dexOptCreateEmptyHeader(int fd)
+{
+    DexOptHeader optHdr;
+    ssize_t actual;
+
+    assert(lseek(fd, 0, SEEK_CUR) == 0);
+
+    /*
+     * The data is only expected to be readable on the current system, so
+     * we just write the structure.  We do need the file offset to be 64-bit
+     * aligned to fulfill a DEX requirement.
+     */
+    assert((sizeof(optHdr) & 0x07) == 0);
+    memset(&optHdr, 0xff, sizeof(optHdr));
+    optHdr.dexOffset = sizeof(optHdr);
+    actual = write(fd, &optHdr, sizeof(optHdr));
+    if (actual != sizeof(optHdr)) {
+        int err = errno ? errno : -1;
+        LOGE("opt header write failed: %s", strerror(errno));
+        return errno;
+    }
+
+    return 0;
+}
diff --git a/libdex/OptInvocation.h b/libdex/OptInvocation.h
new file mode 100644
index 0000000..0352eb4
--- /dev/null
+++ b/libdex/OptInvocation.h
@@ -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.
+ */
+
+/*
+ * Utility functions related to "dexopt".
+ */
+#ifndef _LIBDEX_OPTINVOCATION
+#define _LIBDEX_OPTINVOCATION
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Utility routines, used by the VM.
+ */
+char* dexOptGenerateCacheFileName(const char* fileName,
+    const char* subFileName);
+int dexOptCreateEmptyHeader(int fd);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*_LIBDEX_OPTINVOCATION*/
diff --git a/libdex/SysUtil.c b/libdex/SysUtil.c
new file mode 100644
index 0000000..e2b1fff
--- /dev/null
+++ b/libdex/SysUtil.c
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ */
+
+/*
+ * System utilities.
+ */
+#include "DexFile.h"
+#include "SysUtil.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef HAVE_POSIX_FILEMAP
+# include <sys/mman.h>
+#endif
+#include <limits.h>
+#include <errno.h>
+
+#include <JNIHelp.h>        // TEMP_FAILURE_RETRY may or may not be in unistd
+
+
+/*
+ * Create an anonymous shared memory segment large enough to hold "length"
+ * bytes.  The actual segment may be larger because mmap() operates on
+ * page boundaries (usually 4K).
+ */
+static void* sysCreateAnonShmem(size_t length)
+{
+#ifdef HAVE_POSIX_FILEMAP
+    void* ptr;
+
+    ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
+            MAP_SHARED | MAP_ANON, -1, 0);
+    if (ptr == MAP_FAILED) {
+        LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
+            strerror(errno));
+        return NULL;
+    }
+
+    return ptr;
+#else
+    LOGE("sysCreateAnonShmem not implemented.\n");
+    return NULL;
+#endif
+}
+
+/*
+ * Create a private anonymous storage area.
+ */
+int sysCreatePrivateMap(size_t length, MemMapping* pMap)
+{
+    void* memPtr;
+
+    memPtr = sysCreateAnonShmem(length);
+    if (memPtr == NULL)
+        return -1;
+
+    pMap->addr = pMap->baseAddr = memPtr;
+    pMap->length = pMap->baseLength = length;
+    return 0;
+}
+
+/*
+ * Determine the current offset and remaining length of the open file.
+ */
+static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
+{
+    off_t start, end;
+    size_t length;
+
+    assert(start_ != NULL);
+    assert(length_ != NULL);
+
+    start = lseek(fd, 0L, SEEK_CUR);
+    end = lseek(fd, 0L, SEEK_END);
+    (void) lseek(fd, start, SEEK_SET);
+
+    if (start == (off_t) -1 || end == (off_t) -1) {
+        LOGE("could not determine length of file\n");
+        return -1;
+    }
+
+    length = end - start;
+    if (length == 0) {
+        LOGE("file is empty\n");
+        return -1;
+    }
+
+    *start_ = start;
+    *length_ = length;
+
+    return 0;
+}
+
+/*
+ * Pull the contents of a file into an new shared memory segment.  We grab
+ * everything from fd's current offset on.
+ *
+ * We need to know the length ahead of time so we can allocate a segment
+ * of sufficient size.
+ */
+int sysLoadFileInShmem(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+    off_t start;
+    size_t length, actual;
+    void* memPtr;
+
+    assert(pMap != NULL);
+
+    if (getFileStartAndLength(fd, &start, &length) < 0)
+        return -1;
+
+    memPtr = sysCreateAnonShmem(length);
+    if (memPtr == NULL)
+        return -1;
+
+    actual = read(fd, memPtr, length);
+    if (actual != length) {
+        LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
+        sysReleaseShmem(pMap);
+        return -1;
+    }
+
+    pMap->baseAddr = pMap->addr = memPtr;
+    pMap->baseLength = pMap->length = length;
+
+    return 0;
+#else
+    LOGE("sysLoadFileInShmem not implemented.\n");
+    return -1;
+#endif
+}
+
+#ifndef HAVE_POSIX_FILEMAP
+int sysFakeMapFile(int fd, MemMapping* pMap)
+{
+    /* No MMAP, just fake it by copying the bits.
+       For Win32 we could use MapViewOfFile if really necessary
+       (see libs/utils/FileMap.cpp).
+    */
+    off_t start;
+    size_t length;
+    void* memPtr;
+
+    assert(pMap != NULL);
+
+    if (getFileStartAndLength(fd, &start, &length) < 0)
+        return -1;
+
+    memPtr = malloc(length);
+    if (read(fd, memPtr, length) < 0) {
+        LOGW("read(fd=%d, start=%d, length=%d) failed: %s\n", (int) length,
+            fd, (int) start, strerror(errno));
+        return -1;
+    }
+
+    pMap->baseAddr = pMap->addr = memPtr;
+    pMap->baseLength = pMap->length = length;
+
+    return 0;
+}
+#endif
+
+/*
+ * Map a file (from fd's current offset) into a shared, read-only memory
+ * segment.  The file offset must be a multiple of the system page size.
+ *
+ * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+    off_t start;
+    size_t length;
+    void* memPtr;
+
+    assert(pMap != NULL);
+
+    if (getFileStartAndLength(fd, &start, &length) < 0)
+        return -1;
+
+    memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
+    if (memPtr == MAP_FAILED) {
+        LOGW("mmap(%d, RO, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
+            fd, (int) start, strerror(errno));
+        return -1;
+    }
+
+    pMap->baseAddr = pMap->addr = memPtr;
+    pMap->baseLength = pMap->length = length;
+
+    return 0;
+#else
+    return sysFakeMapFile(fd, pMap);
+#endif
+}
+
+/*
+ * Map a file (from fd's current offset) into a private, read-write memory
+ * segment that will be marked read-only (a/k/a "writable read-only").  The
+ * file offset must be a multiple of the system page size.
+ *
+ * In some cases the mapping will be fully writable (e.g. for files on
+ * FAT filesystems).
+ *
+ * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+    off_t start;
+    size_t length;
+    void* memPtr;
+
+    assert(pMap != NULL);
+
+    if (getFileStartAndLength(fd, &start, &length) < 0)
+        return -1;
+
+    memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
+            fd, start);
+    if (memPtr == MAP_FAILED) {
+        LOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s\n", (int) length,
+            fd, (int) start, strerror(errno));
+        return -1;
+    }
+    if (mprotect(memPtr, length, PROT_READ) < 0) {
+        /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
+        int err = errno;
+        LOGV("mprotect(%p, %d, PROT_READ) failed: %s\n",
+            memPtr, length, strerror(err));
+        LOGD("mprotect(RO) failed (%d), file will remain read-write\n", err);
+    }
+
+    pMap->baseAddr = pMap->addr = memPtr;
+    pMap->baseLength = pMap->length = length;
+
+    return 0;
+#else
+    return sysFakeMapFile(fd, pMap);
+#endif
+}
+
+/*
+ * Map part of a file into a shared, read-only memory segment.  The "start"
+ * offset is absolute, not relative.
+ *
+ * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
+    MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+    size_t actualLength;
+    off_t actualStart;
+    int adjust;
+    void* memPtr;
+
+    assert(pMap != NULL);
+
+    /* adjust to be page-aligned */
+    adjust = start % SYSTEM_PAGE_SIZE;
+    actualStart = start - adjust;
+    actualLength = length + adjust;
+
+    memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
+                fd, actualStart);
+    if (memPtr == MAP_FAILED) {
+        LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
+            (int) actualLength, fd, (int) actualStart, strerror(errno));
+        return -1;
+    }
+
+    pMap->baseAddr = memPtr;
+    pMap->baseLength = actualLength;
+    pMap->addr = (char*)memPtr + adjust;
+    pMap->length = length;
+
+    LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
+        (int) start, (int) length,
+        pMap->baseAddr, (int) pMap->baseLength,
+        pMap->addr, (int) pMap->length);
+
+    return 0;
+#else
+    LOGE("sysMapFileSegmentInShmem not implemented.\n");
+    return -1;
+#endif
+}
+
+/*
+ * Change the access rights on one or more pages to read-only or read-write.
+ *
+ * Returns 0 on success.
+ */
+int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
+    MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+    /*
+     * Verify that "addr" is part of this mapping file.
+     */
+    if (addr < pMap->baseAddr ||
+        (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength)
+    {
+        LOGE("Attempted to change %p; map is %p - %p\n",
+            addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength);
+        return -1;
+    }
+
+    /*
+     * Align "addr" to a page boundary and adjust "length" appropriately.
+     * (The address must be page-aligned, the length doesn't need to be,
+     * but we do need to ensure we cover the same range.)
+     */
+    u1* alignAddr = (u1*) ((int) addr & ~(SYSTEM_PAGE_SIZE-1));
+    size_t alignLength = length + ((u1*) addr - alignAddr);
+
+    //LOGI("%p/%zd --> %p/%zd\n", addr, length, alignAddr, alignLength);
+    int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
+    if (mprotect(alignAddr, alignLength, prot) != 0) {
+        int err = errno;
+        LOGV("mprotect (%p,%zd,%d) failed: %s\n",
+            alignAddr, alignLength, prot, strerror(errno));
+        return (errno != 0) ? errno : -1;
+    }
+#endif
+
+    /* for "fake" mapping, no need to do anything */
+    return 0;
+}
+
+/*
+ * Release a memory mapping.
+ */
+void sysReleaseShmem(MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+    if (pMap->baseAddr == NULL && pMap->baseLength == 0)
+        return;
+
+    if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
+        LOGW("munmap(%p, %d) failed: %s\n",
+            pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
+    } else {
+        LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
+        pMap->baseAddr = NULL;
+        pMap->baseLength = 0;
+    }
+#else
+    /* Free the bits allocated by sysMapFileInShmem. */
+    if (pMap->baseAddr != NULL) {
+      free(pMap->baseAddr);
+      pMap->baseAddr = NULL;
+    }
+    pMap->baseLength = 0;
+#endif
+}
+
+/*
+ * Make a copy of a MemMapping.
+ */
+void sysCopyMap(MemMapping* dst, const MemMapping* src)
+{
+    memcpy(dst, src, sizeof(MemMapping));
+}
+
+/*
+ * Write until all bytes have been written.
+ *
+ * Returns 0 on success, or an errno value on failure.
+ */
+int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg)
+{
+    while (count != 0) {
+        ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
+        if (actual < 0) {
+            int err = errno;
+            LOGE("%s: write failed: %s\n", logMsg, strerror(err));
+            return err;
+        } else if (actual != (ssize_t) count) {
+            LOGD("%s: partial write (will retry): (%d of %zd)\n",
+                logMsg, (int) actual, count);
+            buf = (const void*) (((const u1*) buf) + actual);
+        }
+        count -= actual;
+    }
+
+    return 0;
+}
diff --git a/libdex/SysUtil.h b/libdex/SysUtil.h
new file mode 100644
index 0000000..01b4e1a
--- /dev/null
+++ b/libdex/SysUtil.h
@@ -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.
+ */
+
+/*
+ * System utilities.
+ */
+#ifndef _LIBDEX_SYSUTIL
+#define _LIBDEX_SYSUTIL
+
+#include <sys/types.h>
+
+/*
+ * System page size.  Normally you're expected to get this from
+ * sysconf(_SC_PAGESIZE) or some system-specific define (usually PAGESIZE
+ * or PAGE_SIZE).  If we use a simple #define the compiler can generate
+ * appropriate masks directly, so we define it here and verify it as the
+ * VM is starting up.
+ *
+ * Must be a power of 2.
+ */
+#define SYSTEM_PAGE_SIZE        4096
+
+/*
+ * Use this to keep track of mapped segments.
+ */
+typedef struct MemMapping {
+    void*   addr;           /* start of data */
+    size_t  length;         /* length of data */
+
+    void*   baseAddr;       /* page-aligned base address */
+    size_t  baseLength;     /* length of mapping */
+} MemMapping;
+
+/*
+ * Copy a map.
+ */
+void sysCopyMap(MemMapping* dst, const MemMapping* src);
+
+/*
+ * Load a file into a new shared memory segment.  All data from the current
+ * offset to the end of the file is pulled in.
+ *
+ * The segment is read-write, allowing VM fixups.  (It should be modified
+ * to support .gz/.zip compressed data.)
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysLoadFileInShmem(int fd, MemMapping* pMap);
+
+/*
+ * Map a file (from fd's current offset) into a shared,
+ * read-only memory segment.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap);
+
+/*
+ * Map a file (from fd's current offset) into a shared, read-only memory
+ * segment that can be made writable.  (In some cases, such as when
+ * mapping a file on a FAT filesystem, the result may be fully writable.)
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap);
+
+/*
+ * Like sysMapFileInShmemReadOnly, but on only part of a file.
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
+    MemMapping* pMap);
+
+/*
+ * Create a private anonymous mapping, useful for large allocations.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysCreatePrivateMap(size_t length, MemMapping* pMap);
+
+/*
+ * Change the access rights on one or more pages.  If "wantReadWrite" is
+ * zero, the pages will be made read-only; otherwise they will be read-write.
+ *
+ * Returns 0 on success.
+ */
+int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
+    MemMapping* pmap);
+
+/*
+ * Release the pages associated with a shared memory segment.
+ *
+ * This does not free "pMap"; it just releases the memory.
+ */
+void sysReleaseShmem(MemMapping* pMap);
+
+/*
+ * Write until all bytes have been written.
+ *
+ * Returns 0 on success, or an errno value on failure.
+ */
+int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg);
+
+#endif /*_DALVIK_SYSUTIL*/
diff --git a/libdex/ZipArchive.c b/libdex/ZipArchive.c
new file mode 100644
index 0000000..7feb417
--- /dev/null
+++ b/libdex/ZipArchive.c
@@ -0,0 +1,750 @@
+/*
+ * 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.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#include "ZipArchive.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <JNIHelp.h>        // TEMP_FAILURE_RETRY may or may not be in unistd
+
+
+/*
+ * Zip file constants.
+ */
+#define kEOCDSignature      0x06054b50
+#define kEOCDLen            22
+#define kEOCDNumEntries     8               // offset to #of entries in file
+#define kEOCDSize           12              // size of the central directory
+#define kEOCDFileOffset     16              // offset to central directory
+
+#define kMaxCommentLen      65535           // longest possible in ushort
+#define kMaxEOCDSearch      (kMaxCommentLen + kEOCDLen)
+
+#define kLFHSignature       0x04034b50
+#define kLFHLen             30              // excluding variable-len fields
+#define kLFHNameLen         26              // offset to filename length
+#define kLFHExtraLen        28              // offset to extra length
+
+#define kCDESignature       0x02014b50
+#define kCDELen             46              // excluding variable-len fields
+#define kCDEMethod          10              // offset to compression method
+#define kCDEModWhen         12              // offset to modification timestamp
+#define kCDECRC             16              // offset to entry CRC
+#define kCDECompLen         20              // offset to compressed length
+#define kCDEUncompLen       24              // offset to uncompressed length
+#define kCDENameLen         28              // offset to filename length
+#define kCDEExtraLen        30              // offset to extra length
+#define kCDECommentLen      32              // offset to comment length
+#define kCDELocalOffset     42              // offset to local hdr
+
+/*
+ * The values we return for ZipEntry use 0 as an invalid value, so we
+ * want to adjust the hash table index by a fixed amount.  Using a large
+ * value helps insure that people don't mix & match arguments, e.g. with
+ * entry indices.
+ */
+#define kZipEntryAdj        10000
+
+/*
+ * Convert a ZipEntry to a hash table index, verifying that it's in a
+ * valid range.
+ */
+static int entryToIndex(const ZipArchive* pArchive, const ZipEntry entry)
+{
+    long ent = ((long) entry) - kZipEntryAdj;
+    if (ent < 0 || ent >= pArchive->mHashTableSize ||
+        pArchive->mHashTable[ent].name == NULL)
+    {
+        LOGW("Zip: invalid ZipEntry %p (%ld)\n", entry, ent);
+        return -1;
+    }
+    return ent;
+}
+
+/*
+ * Simple string hash function for non-null-terminated strings.
+ */
+static unsigned int computeHash(const char* str, int len)
+{
+    unsigned int hash = 0;
+
+    while (len--)
+        hash = hash * 31 + *str++;
+
+    return hash;
+}
+
+/*
+ * Add a new entry to the hash table.
+ */
+static void addToHash(ZipArchive* pArchive, const char* str, int strLen,
+    unsigned int hash)
+{
+    const int hashTableSize = pArchive->mHashTableSize;
+    int ent = hash & (hashTableSize - 1);
+
+    /*
+     * We over-allocated the table, so we're guaranteed to find an empty slot.
+     */
+    while (pArchive->mHashTable[ent].name != NULL)
+        ent = (ent + 1) & (hashTableSize-1);
+
+    pArchive->mHashTable[ent].name = str;
+    pArchive->mHashTable[ent].nameLen = strLen;
+}
+
+/*
+ * Get 2 little-endian bytes.
+ */
+static u2 get2LE(unsigned char const* pSrc)
+{
+    return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+static 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;
+}
+
+/*
+ * Find the zip Central Directory and memory-map it.
+ *
+ * On success, returns 0 after populating fields from the EOCD area:
+ *   mDirectoryOffset
+ *   mDirectoryMap
+ *   mNumEntries
+ */
+static int mapCentralDirectory(int fd, const char* debugFileName,
+    ZipArchive* pArchive)
+{
+    u1* scanBuf = NULL;
+    int result = -1;
+
+    /*
+     * Get and test file length.
+     */
+    off_t fileLength = lseek(fd, 0, SEEK_END);
+    if (fileLength < kEOCDLen) {
+        LOGV("Zip: length %ld is too small to be zip\n", (long) fileLength);
+        goto bail;
+    }
+
+    /*
+     * Perform the traditional EOCD snipe hunt.
+     *
+     * We're searching for the End of Central Directory magic number,
+     * which appears at the start of the EOCD block.  It's followed by
+     * 18 bytes of EOCD stuff and up to 64KB of archive comment.  We
+     * need to read the last part of the file into a buffer, dig through
+     * it to find the magic number, parse some values out, and use those
+     * to determine the extent of the CD.
+     *
+     * We start by pulling in the last part of the file.
+     */
+    size_t readAmount = kMaxEOCDSearch;
+    if (readAmount > (size_t) fileLength)
+        readAmount = fileLength;
+    off_t searchStart = fileLength - readAmount;
+
+    scanBuf = (u1*) malloc(readAmount);
+    if (lseek(fd, searchStart, SEEK_SET) != searchStart) {
+        LOGW("Zip: seek %ld failed: %s\n", (long) searchStart, strerror(errno));
+        goto bail;
+    }
+    ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount));
+    if (actual != (ssize_t) readAmount) {
+        LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Scan backward for the EOCD magic.  In an archive without a trailing
+     * comment, we'll find it on the first try.  (We may want to consider
+     * doing an initial minimal read; if we don't find it, retry with a
+     * second read as above.)
+     */
+    int i;
+    for (i = readAmount - kEOCDLen; i >= 0; i--) {
+        if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
+            LOGV("+++ Found EOCD at buf+%d\n", i);
+            break;
+        }
+    }
+    if (i < 0) {
+        LOGD("Zip: EOCD not found, %s is not zip\n", debugFileName);
+        goto bail;
+    }
+
+    off_t eocdOffset = searchStart + i;
+    const u1* eocdPtr = scanBuf + i;
+
+    assert(eocdOffset < fileLength);
+
+    /*
+     * Grab the CD offset and size, and the number of entries in the
+     * archive.  Verify that they look reasonable.
+     */
+    u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries);
+    u4 dirSize = get4LE(eocdPtr + kEOCDSize);
+    u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset);
+
+    if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) {
+        LOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)\n",
+            (long) dirOffset, dirSize, (long) eocdOffset);
+        goto bail;
+    }
+    if (numEntries == 0) {
+        LOGW("Zip: empty archive?\n");
+        goto bail;
+    }
+
+    LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
+        numEntries, dirSize, dirOffset);
+
+    /*
+     * It all looks good.  Create a mapping for the CD, and set the fields
+     * in pArchive.
+     */
+    if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize,
+            &pArchive->mDirectoryMap) != 0)
+    {
+        LOGW("Zip: cd map failed\n");
+        goto bail;
+    }
+
+    pArchive->mNumEntries = numEntries;
+    pArchive->mDirectoryOffset = dirOffset;
+
+    result = 0;
+
+bail:
+    free(scanBuf);
+    return result;
+}
+
+/*
+ * Parses the Zip archive's Central Directory.  Allocates and populates the
+ * hash table.
+ *
+ * Returns 0 on success.
+ */
+static int parseZipArchive(ZipArchive* pArchive)
+{
+    int result = -1;
+    const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr;
+    size_t cdLength = pArchive->mDirectoryMap.length;
+    int numEntries = pArchive->mNumEntries;
+
+    /*
+     * Create hash table.  We have a minimum 75% load factor, possibly as
+     * low as 50% after we round off to a power of 2.  There must be at
+     * least one unused entry to avoid an infinite loop during creation.
+     */
+    pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3);
+    pArchive->mHashTable = (ZipHashEntry*)
+            calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry));
+
+    /*
+     * Walk through the central directory, adding entries to the hash
+     * table and verifying values.
+     */
+    const u1* ptr = cdPtr;
+    int i;
+    for (i = 0; i < numEntries; i++) {
+        if (get4LE(ptr) != kCDESignature) {
+            LOGW("Zip: missed a central dir sig (at %d)\n", i);
+            goto bail;
+        }
+        if (ptr + kCDELen > cdPtr + cdLength) {
+            LOGW("Zip: ran off the end (at %d)\n", i);
+            goto bail;
+        }
+
+        long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
+        if (localHdrOffset >= pArchive->mDirectoryOffset) {
+            LOGW("Zip: bad LFH offset %ld at entry %d\n", localHdrOffset, i);
+            goto bail;
+        }
+
+        unsigned int fileNameLen, extraLen, commentLen, hash;
+        fileNameLen = get2LE(ptr + kCDENameLen);
+        extraLen = get2LE(ptr + kCDEExtraLen);
+        commentLen = get2LE(ptr + kCDECommentLen);
+
+        /* add the CDE filename to the hash table */
+        hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
+        addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash);
+
+        ptr += kCDELen + fileNameLen + extraLen + commentLen;
+        if ((size_t)(ptr - cdPtr) > cdLength) {
+            LOGW("Zip: bad CD advance (%d vs %zd) at entry %d\n",
+                (int) (ptr - cdPtr), cdLength, i);
+            goto bail;
+        }
+    }
+    LOGV("+++ zip good scan %d entries\n", numEntries);
+
+    result = 0;
+
+bail:
+    return result;
+}
+
+/*
+ * Open the specified file read-only.  We examine the contents and verify
+ * that it appears to be a valid zip file.
+ *
+ * This will be called on non-Zip files, especially during VM startup, so
+ * we don't want to be too noisy about certain types of failure.  (Do
+ * we want a "quiet" flag?)
+ *
+ * On success, we fill out the contents of "pArchive" and return 0.  On
+ * failure we return the errno value.
+ */
+int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive)
+{
+    int fd, err;
+
+    LOGV("Opening as zip '%s' %p\n", fileName, pArchive);
+
+    memset(pArchive, 0, sizeof(ZipArchive));
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+    fd = open(fileName, O_RDONLY | O_BINARY, 0);
+    if (fd < 0) {
+        err = errno ? errno : -1;
+        LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
+        return err;
+    }
+
+    return dexZipPrepArchive(fd, fileName, pArchive);
+}
+
+/*
+ * Prepare to access a ZipArchive through an open file descriptor.
+ *
+ * On success, we fill out the contents of "pArchive" and return 0.
+ */
+int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive)
+{
+    int result = -1;
+
+    memset(pArchive, 0, sizeof(*pArchive));
+    pArchive->mFd = fd;
+
+    if (mapCentralDirectory(fd, debugFileName, pArchive) != 0)
+        goto bail;
+
+    if (parseZipArchive(pArchive) != 0) {
+        LOGV("Zip: parsing '%s' failed\n", debugFileName);
+        goto bail;
+    }
+
+    /* success */
+    result = 0;
+
+bail:
+    if (result != 0)
+        dexZipCloseArchive(pArchive);
+    return result;
+}
+
+
+/*
+ * Close a ZipArchive, closing the file and freeing the contents.
+ *
+ * NOTE: the ZipArchive may not have been fully created.
+ */
+void dexZipCloseArchive(ZipArchive* pArchive)
+{
+    LOGV("Closing archive %p\n", pArchive);
+
+    if (pArchive->mFd >= 0)
+        close(pArchive->mFd);
+
+    sysReleaseShmem(&pArchive->mDirectoryMap);
+
+    free(pArchive->mHashTable);
+
+    /* ensure nobody tries to use the ZipArchive after it's closed */
+    pArchive->mDirectoryOffset = -1;
+    pArchive->mFd = -1;
+    pArchive->mNumEntries = -1;
+    pArchive->mHashTableSize = -1;
+    pArchive->mHashTable = NULL;
+}
+
+
+/*
+ * Find a matching entry.
+ *
+ * Returns 0 if not found.
+ */
+ZipEntry dexZipFindEntry(const ZipArchive* pArchive, const char* entryName)
+{
+    int nameLen = strlen(entryName);
+    unsigned int hash = computeHash(entryName, nameLen);
+    const int hashTableSize = pArchive->mHashTableSize;
+    int ent = hash & (hashTableSize-1);
+
+    while (pArchive->mHashTable[ent].name != NULL) {
+        if (pArchive->mHashTable[ent].nameLen == nameLen &&
+            memcmp(pArchive->mHashTable[ent].name, entryName, nameLen) == 0)
+        {
+            /* match */
+            return (ZipEntry)(long)(ent + kZipEntryAdj);
+        }
+
+        ent = (ent + 1) & (hashTableSize-1);
+    }
+
+    return NULL;
+}
+
+#if 0
+/*
+ * Find the Nth entry.
+ *
+ * This currently involves walking through the sparse hash table, counting
+ * non-empty entries.  If we need to speed this up we can either allocate
+ * a parallel lookup table or (perhaps better) provide an iterator interface.
+ */
+ZipEntry findEntryByIndex(ZipArchive* pArchive, int idx)
+{
+    if (idx < 0 || idx >= pArchive->mNumEntries) {
+        LOGW("Invalid index %d\n", idx);
+        return NULL;
+    }
+
+    int ent;
+    for (ent = 0; ent < pArchive->mHashTableSize; ent++) {
+        if (pArchive->mHashTable[ent].name != NULL) {
+            if (idx-- == 0)
+                return (ZipEntry) (ent + kZipEntryAdj);
+        }
+    }
+
+    return NULL;
+}
+#endif
+
+/*
+ * Get the useful fields from the zip entry.
+ *
+ * Returns non-zero if the contents of the fields (particularly the data
+ * offset) appear to be bogus.
+ */
+int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry,
+    int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset,
+    long* pModWhen, long* pCrc32)
+{
+    int ent = entryToIndex(pArchive, entry);
+    if (ent < 0)
+        return -1;
+
+    /*
+     * Recover the start of the central directory entry from the filename
+     * pointer.  The filename is the first entry past the fixed-size data,
+     * so we can just subtract back from that.
+     */
+    const unsigned char* basePtr = (const unsigned char*)
+        pArchive->mDirectoryMap.addr;
+    const unsigned char* ptr = (const unsigned char*)
+        pArchive->mHashTable[ent].name;
+    off_t cdOffset = pArchive->mDirectoryOffset;
+
+    ptr -= kCDELen;
+
+    int method = get2LE(ptr + kCDEMethod);
+    if (pMethod != NULL)
+        *pMethod = method;
+
+    if (pModWhen != NULL)
+        *pModWhen = get4LE(ptr + kCDEModWhen);
+    if (pCrc32 != NULL)
+        *pCrc32 = get4LE(ptr + kCDECRC);
+
+    size_t compLen = get4LE(ptr + kCDECompLen);
+    if (pCompLen != NULL)
+        *pCompLen = compLen;
+    size_t uncompLen = get4LE(ptr + kCDEUncompLen);
+    if (pUncompLen != NULL)
+        *pUncompLen = uncompLen;
+
+    /*
+     * If requested, determine the offset of the start of the data.  All we
+     * have is the offset to the Local File Header, which is variable size,
+     * so we have to read the contents of the struct to figure out where
+     * the actual data starts.
+     *
+     * We also need to make sure that the lengths are not so large that
+     * somebody trying to map the compressed or uncompressed data runs
+     * off the end of the mapped region.
+     *
+     * Note we don't verify compLen/uncompLen if they don't request the
+     * dataOffset, because dataOffset is expensive to determine.  However,
+     * if they don't have the file offset, they're not likely to be doing
+     * anything with the contents.
+     */
+    if (pOffset != NULL) {
+        long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
+        if (localHdrOffset + kLFHLen >= cdOffset) {
+            LOGW("Zip: bad local hdr offset in zip\n");
+            return -1;
+        }
+
+        u1 lfhBuf[kLFHLen];
+        if (lseek(pArchive->mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+            LOGW("Zip: failed seeking to lfh at offset %ld\n", localHdrOffset);
+            return -1;
+        }
+        ssize_t actual =
+            TEMP_FAILURE_RETRY(read(pArchive->mFd, lfhBuf, sizeof(lfhBuf)));
+        if (actual != sizeof(lfhBuf)) {
+            LOGW("Zip: failed reading lfh from offset %ld\n", localHdrOffset);
+            return -1;
+        }
+
+        if (get4LE(lfhBuf) != kLFHSignature) {
+            LOGW("Zip: didn't find signature at start of lfh, offset=%ld\n",
+                localHdrOffset);
+            return -1;
+        }
+
+        off_t dataOffset = localHdrOffset + kLFHLen
+            + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
+        if (dataOffset >= cdOffset) {
+            LOGW("Zip: bad data offset %ld in zip\n", (long) dataOffset);
+            return -1;
+        }
+
+        /* check lengths */
+        if ((off_t)(dataOffset + compLen) > cdOffset) {
+            LOGW("Zip: bad compressed length in zip (%ld + %zd > %ld)\n",
+                (long) dataOffset, compLen, (long) cdOffset);
+            return -1;
+        }
+
+        if (method == kCompressStored &&
+            (off_t)(dataOffset + uncompLen) > cdOffset)
+        {
+            LOGW("Zip: bad uncompressed length in zip (%ld + %zd > %ld)\n",
+                (long) dataOffset, uncompLen, (long) cdOffset);
+            return -1;
+        }
+
+        *pOffset = dataOffset;
+    }
+    return 0;
+}
+
+/*
+ * Uncompress "deflate" data from the archive's file to an open file
+ * descriptor.
+ */
+static int inflateToFile(int inFd, int outFd, size_t uncompLen, size_t compLen)
+{
+    int result = -1;
+    const size_t kBufSize = 32768;
+    unsigned char* readBuf = (unsigned char*) malloc(kBufSize);
+    unsigned char* writeBuf = (unsigned char*) malloc(kBufSize);
+    z_stream zstream;
+    int zerr;
+
+    if (readBuf == NULL || writeBuf == NULL)
+        goto bail;
+
+    /*
+     * Initialize the zlib stream struct.
+     */
+    memset(&zstream, 0, sizeof(zstream));
+    zstream.zalloc = Z_NULL;
+    zstream.zfree = Z_NULL;
+    zstream.opaque = Z_NULL;
+    zstream.next_in = NULL;
+    zstream.avail_in = 0;
+    zstream.next_out = (Bytef*) writeBuf;
+    zstream.avail_out = kBufSize;
+    zstream.data_type = Z_UNKNOWN;
+
+    /*
+     * Use the undocumented "negative window bits" feature to tell zlib
+     * that there's no zlib header waiting for it.
+     */
+    zerr = inflateInit2(&zstream, -MAX_WBITS);
+    if (zerr != Z_OK) {
+        if (zerr == Z_VERSION_ERROR) {
+            LOGE("Installed zlib is not compatible with linked version (%s)\n",
+                ZLIB_VERSION);
+        } else {
+            LOGW("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+        }
+        goto bail;
+    }
+
+    /*
+     * Loop while we have more to do.
+     */
+    do {
+        /* read as much as we can */
+        if (zstream.avail_in == 0) {
+            size_t getSize = (compLen > kBufSize) ? kBufSize : compLen;
+
+            ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, readBuf, getSize));
+            if (actual != (ssize_t) getSize) {
+                LOGW("Zip: inflate read failed (%d vs %zd)\n",
+                    (int)actual, getSize);
+                goto z_bail;
+            }
+
+            compLen -= getSize;
+
+            zstream.next_in = readBuf;
+            zstream.avail_in = getSize;
+        }
+
+        /* uncompress the data */
+        zerr = inflate(&zstream, Z_NO_FLUSH);
+        if (zerr != Z_OK && zerr != Z_STREAM_END) {
+            LOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
+                zerr, zstream.next_in, zstream.avail_in,
+                zstream.next_out, zstream.avail_out);
+            goto z_bail;
+        }
+
+        /* write when we're full or when we're done */
+        if (zstream.avail_out == 0 ||
+            (zerr == Z_STREAM_END && zstream.avail_out != kBufSize))
+        {
+            size_t writeSize = zstream.next_out - writeBuf;
+            if (sysWriteFully(outFd, writeBuf, writeSize, "Zip inflate") != 0)
+                goto z_bail;
+
+            zstream.next_out = writeBuf;
+            zstream.avail_out = kBufSize;
+        }
+    } while (zerr == Z_OK);
+
+    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
+
+    /* paranoia */
+    if (zstream.total_out != uncompLen) {
+        LOGW("Zip: size mismatch on inflated file (%ld vs %zd)\n",
+            zstream.total_out, uncompLen);
+        goto z_bail;
+    }
+
+    result = 0;
+
+z_bail:
+    inflateEnd(&zstream);        /* free up any allocated structures */
+
+bail:
+    free(readBuf);
+    free(writeBuf);
+    return result;
+}
+
+/*
+ * Copy bytes from input to output.
+ */
+static int copyFileToFile(int inFd, int outFd, size_t uncompLen)
+{
+    const size_t kBufSize = 32768;
+    unsigned char buf[kBufSize];
+
+    while (uncompLen != 0) {
+        size_t getSize = (uncompLen > kBufSize) ? kBufSize : uncompLen;
+
+        ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize));
+        if (actual != (ssize_t) getSize) {
+            LOGW("Zip: copy read failed (%d vs %zd)\n", (int)actual, getSize);
+            return -1;
+        }
+
+        if (sysWriteFully(outFd, buf, getSize, "Zip copy") != 0)
+            return -1;
+
+        uncompLen -= getSize;
+    }
+
+    return 0;
+}
+
+/*
+ * Uncompress an entry, in its entirety, to an open file descriptor.
+ *
+ * TODO: this doesn't verify the data's CRC, but probably should (especially
+ * for uncompressed data).
+ */
+int dexZipExtractEntryToFile(const ZipArchive* pArchive,
+    const ZipEntry entry, int fd)
+{
+    int result = -1;
+    int ent = entryToIndex(pArchive, entry);
+    if (ent < 0) {
+        LOGW("Zip: extract can't find entry %p\n", entry);
+        goto bail;
+    }
+
+    int method;
+    size_t uncompLen, compLen;
+    off_t dataOffset;
+
+    if (dexZipGetEntryInfo(pArchive, entry, &method, &uncompLen, &compLen,
+            &dataOffset, NULL, NULL) != 0)
+    {
+        goto bail;
+    }
+    if (lseek(pArchive->mFd, dataOffset, SEEK_SET) != dataOffset) {
+        LOGW("Zip: lseek to data at %ld failed\n", (long) dataOffset);
+        goto bail;
+    }
+
+    if (method == kCompressStored) {
+        if (copyFileToFile(pArchive->mFd, fd, uncompLen) != 0)
+            goto bail;
+    } else {
+        if (inflateToFile(pArchive->mFd, fd, uncompLen, compLen) != 0)
+            goto bail;
+    }
+
+    result = 0;
+
+bail:
+    return result;
+}
diff --git a/libdex/ZipArchive.h b/libdex/ZipArchive.h
new file mode 100644
index 0000000..bf4edf9
--- /dev/null
+++ b/libdex/ZipArchive.h
@@ -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.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#ifndef _LIBDEX_ZIPARCHIVE
+#define _LIBDEX_ZIPARCHIVE
+
+#include "SysUtil.h"
+#include "DexFile.h"            // need DEX_INLINE
+
+
+/*
+ * Trivial typedef to ensure that ZipEntry is not treated as a simple
+ * integer.  We use NULL to indicate an invalid value.
+ */
+typedef void* ZipEntry;
+
+/*
+ * One entry in the hash table.
+ */
+typedef struct ZipHashEntry {
+    const char*     name;
+    unsigned short  nameLen;
+    //unsigned int    hash;
+} ZipHashEntry;
+
+/*
+ * Read-only Zip archive.
+ *
+ * We want "open" and "find entry by name" to be fast operations, and
+ * we want to use as little memory as possible.  We memory-map the zip
+ * central directory, and load a hash table with pointers to the filenames
+ * (which aren't null-terminated).  The other fields are at a fixed offset
+ * from the filename, so we don't need to extract those (but we do need
+ * to byte-read and endian-swap them every time we want them).
+ *
+ * It's possible that somebody has handed us a massive (~1GB) zip archive,
+ * so we can't expect to mmap the entire file.
+ *
+ * To speed comparisons when doing a lookup by name, we could make the mapping
+ * "private" (copy-on-write) and null-terminate the filenames after verifying
+ * the record structure.  However, this requires a private mapping of
+ * every page that the Central Directory touches.  Easier to tuck a copy
+ * of the string length into the hash table entry.
+ */
+typedef struct ZipArchive {
+    /* open Zip archive */
+    int         mFd;
+
+    /* mapped central directory area */
+    off_t       mDirectoryOffset;
+    MemMapping  mDirectoryMap;
+
+    /* number of entries in the Zip archive */
+    int         mNumEntries;
+
+    /*
+     * We know how many entries are in the Zip archive, so we can have a
+     * fixed-size hash table.  We probe on collisions.
+     */
+    int         mHashTableSize;
+    ZipHashEntry* mHashTable;
+} ZipArchive;
+
+/* Zip compression methods we support */
+enum {
+    kCompressStored     = 0,        // no compression
+    kCompressDeflated   = 8,        // standard deflate
+};
+
+
+/*
+ * Open a Zip archive.
+ *
+ * On success, returns 0 and populates "pArchive".  Returns nonzero errno
+ * value on failure.
+ */
+int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive);
+
+/*
+ * Like dexZipOpenArchive, but takes a file descriptor open for reading
+ * at the start of the file.  The descriptor must be mappable (this does
+ * not allow access to a stream).
+ *
+ * "debugFileName" will appear in error messages, but is not otherwise used.
+ */
+int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive);
+
+/*
+ * Close archive, releasing resources associated with it.
+ *
+ * Depending on the implementation this could unmap pages used by classes
+ * stored in a Jar.  This should only be done after unloading classes.
+ */
+void dexZipCloseArchive(ZipArchive* pArchive);
+
+/*
+ * Return the archive's file descriptor.
+ */
+DEX_INLINE int dexZipGetArchiveFd(const ZipArchive* pArchive) {
+    return pArchive->mFd;
+}
+
+/*
+ * Find an entry in the Zip archive, by name.  Returns NULL if the entry
+ * was not found.
+ */
+ZipEntry dexZipFindEntry(const ZipArchive* pArchive,
+    const char* entryName);
+
+/*
+ * Retrieve one or more of the "interesting" fields.  Non-NULL pointers
+ * are filled in.
+ *
+ * Returns 0 on success.
+ */
+int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry,
+    int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset,
+    long* pModWhen, long* pCrc32);
+
+/*
+ * Simple accessors.
+ */
+DEX_INLINE long dexGetZipEntryOffset(const ZipArchive* pArchive,
+    const ZipEntry entry)
+{
+    off_t val = 0;
+    dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, &val, NULL, NULL);
+    return (long) val;
+}
+DEX_INLINE size_t dexGetZipEntryUncompLen(const ZipArchive* pArchive,
+    const ZipEntry entry)
+{
+    size_t val = 0;
+    dexZipGetEntryInfo(pArchive, entry, NULL, &val, NULL, NULL, NULL, NULL);
+    return val;
+}
+DEX_INLINE long dexGetZipEntryModTime(const ZipArchive* pArchive,
+    const ZipEntry entry)
+{
+    long val = 0;
+    dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, NULL, &val, NULL);
+    return val;
+}
+DEX_INLINE long dexGetZipEntryCrc32(const ZipArchive* pArchive,
+    const ZipEntry entry)
+{
+    long val = 0;
+    dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, NULL, NULL, &val);
+    return val;
+}
+
+/*
+ * Uncompress and write an entry to a file descriptor.
+ *
+ * Returns 0 on success.
+ */
+int dexZipExtractEntryToFile(const ZipArchive* pArchive,
+    const ZipEntry entry, int fd);
+
+/*
+ * Utility function to compute a CRC-32.
+ */
+u4 dexInitCrc32(void);
+u4 dexComputeCrc32(u4 crc, const void* buf, size_t len);
+
+#endif /*_LIBDEX_ZIPARCHIVE*/
diff --git a/libdex/sha1.c b/libdex/sha1.c
new file mode 100644
index 0000000..dc7e30a
--- /dev/null
+++ b/libdex/sha1.c
@@ -0,0 +1,514 @@
+/*
+ * Tweaked in various ways for Google/Android:
+ *  - Changed from .cpp to .c.
+ *  - Made argument to SHA1Update a const pointer, and enabled
+ *    SHA1HANDSOFF.  This incurs a speed penalty but prevents us from
+ *    trashing the input.
+ *  - Include <endian.h> to get endian info.
+ *  - Split a small piece into a header file.
+ */
+
+/*
+sha1sum: inspired by md5sum.
+
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+bit machines
+Routine SHA1Update changed from
+    void SHA1Update(SHA1_CTX* context, unsigned char* data,
+      unsigned int len)
+to
+    void SHA1Update(SHA1_CTX* context, unsigned char* data,
+      unsigned long len)
+
+The 'len' parameter was declared an int which works fine on 32
+bit machines. However, on 16 bit machines an int is too small
+for the shifts being done against it.  This caused the hash
+function to generate incorrect values if len was greater than
+8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or
+larger would be guaranteed to generate the wrong hash (e.g.
+Test Vector #3, a million "a"s).
+
+I also changed the declaration of variables i & j in SHA1Update
+to unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit
+implementations since an int and a long are the same size in
+those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland
+C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments
+containing 'JHB'
+
+-----------------
+Modified 13 August 2000
+By Michael Paul Johnson <mpj@cryptography.org>
+Still 100% Public Domain
+
+Changed command line syntax, added feature to automatically
+check files against their previous SHA-1 check values, kind of
+like md5sum does. Added functions hexval, verifyfile,
+and sha1file. Rewrote main().
+-----------------
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF    /*Copies data before messing with it.*/
+
+/*#define CMDLINE        * include main() and file processing */
+
+#include "sha1.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef __BORLANDC__
+#include <dir.h>
+#include <dos.h>
+#include <process.h>   /*  prototype for exit() - JHB
+               needed for Win32, but chokes Linux - MPJ */
+#define X_LITTLE_ENDIAN /* This should be #define'd if true.*/
+#else
+# include <unistd.h>
+# include <stdlib.h>
+//# include <endian.h>
+
+#include "DexFile.h"    // want common byte ordering def
+
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+#  define X_LITTLE_ENDIAN
+# endif
+#endif
+#include <ctype.h>
+
+#define LINESIZE 2048
+
+static void SHA1Transform(unsigned long state[5],
+    const unsigned char buffer[64]);
+
+#define rol(value,bits) \
+ (((value)<<(bits))|((value)>>(32-(bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from
+   SSLeay */
+#ifdef X_LITTLE_ENDIAN
+#define blk0(i) (block->l[i]=(rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void SHA1Transform(unsigned long state[5],
+    const unsigned char buffer[64])
+{
+unsigned long a, b, c, d, e;
+typedef union {
+    unsigned char c[64];
+    unsigned long l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+static unsigned char workspace[64];
+    block = (CHAR64LONG16*)workspace;
+    memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16*)buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2);
+    R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5);
+    R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8);
+    R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14);
+    R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17);
+    R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20);
+    R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26);
+    R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29);
+    R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32);
+    R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38);
+    R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41);
+    R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44);
+    R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50);
+    R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53);
+    R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56);
+    R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62);
+    R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65);
+    R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68);
+    R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74);
+    R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77);
+    R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+/*    a = b = c = d = e = 0; Nice try, but the compiler
+optimizes this out, anyway, and it produces an annoying
+warning. */
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const unsigned char* data,
+    unsigned long len)  /* JHB */
+{
+    unsigned long i, j; /* JHB */
+
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3))
+        context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63)
+    {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1Transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else
+        i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[HASHSIZE], SHA1_CTX*
+context)
+{
+unsigned long i;    /* JHB */
+unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++)
+    {
+        finalcount[i] = (unsigned char)((context->count[(i>=4?
+            0:1)]>>((3-(i&3))*8))&255);
+        /* Endian independent */
+    }
+    SHA1Update(context, (unsigned char *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+        SHA1Update(context, (unsigned char *)"\0", 1);
+    }
+    SHA1Update(context, finalcount, 8);
+    /* Should cause a SHA1Transform() */
+    for (i = 0; i < HASHSIZE; i++) {
+        digest[i] = (unsigned char)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, HASHSIZE);
+    memset(context->count, 0, 8);
+    memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF
+    /* make SHA1Transform overwrite it's own static vars */
+    SHA1Transform(context->state, context->buffer);
+#endif
+}
+
+
+
+#ifdef CMDLINE
+
+/* sha1file computes the SHA-1 hash of the named file and puts
+   it in the 20-byte array digest. If fname is NULL, stdin is
+   assumed.
+*/
+void sha1file(char *fname, unsigned char* digest)
+{
+    int bytesread;
+    SHA1_CTX context;
+    unsigned char buffer[16384];
+    FILE* f;
+
+    if (fname)
+    {
+        f = fopen(fname, "rb");
+        if (!f)
+        {
+            fprintf(stderr, "Can't open %s\n", fname);
+            memset(digest, 0, HASHSIZE);
+            return;
+        }
+    }
+    else
+    {
+        f = stdin;
+    }
+    SHA1Init(&context);
+    while (!feof(f))
+    {
+        bytesread = fread(buffer, 1, 16384, f);
+        SHA1Update(&context, buffer, bytesread);
+    }
+    SHA1Final(digest, &context);
+    if (fname)
+        fclose(f);
+}
+
+/* Convert ASCII hexidecimal digit to 4-bit value. */
+unsigned char hexval(char c)
+{
+    unsigned char h;
+
+    c = toupper(c);
+    if (c >= 'A')
+        h = c - 'A' + 10;
+    else
+        h = c - '0';
+    return h;
+}
+
+/* Verify a file created with sha1sum by redirecting output
+   to a file. */
+int verifyfile(char *fname)
+{
+    int j, k;
+    int found = 0;
+    unsigned char digest[HASHSIZE];
+    unsigned char expected_digest[HASHSIZE];
+    FILE *checkfile;
+    char checkline[LINESIZE];
+    char *s;
+    unsigned char err;
+
+    checkfile = fopen(fname, "rt");
+    if (!checkfile)
+    {
+        fprintf(stderr, "Can't open %s\n", fname);
+        return(0);
+    }
+    do
+    {
+        s = fgets(checkline, LINESIZE, checkfile);
+        if (s)
+        {
+            if ((strlen(checkline)>26)&&
+                1 /*(!strncmp(checkline,"SHA1=", 5))*/)
+            {
+                /* Overwrite newline. */
+                checkline[strlen(checkline)-1]=0;
+                found = 1;
+
+                /* Read expected check value. */
+                for (k=0, j=5; k < HASHSIZE; k++)
+                {
+                    expected_digest[k]=hexval(checkline[j++]);
+                    expected_digest[k]=(expected_digest[k]<<4)
+                        +hexval(checkline[j++]);
+                }
+
+                /* Compute fingerprints */
+                s = checkline+46;
+                sha1file(s, digest);
+
+                /* Compare fingerprints */
+                err = 0;
+                for (k=0; k<HASHSIZE; k++)
+                    err |= digest[k]-
+                        expected_digest[k];
+                if (err)
+                {
+                    fprintf(stderr, "FAILED: %s\n"
+                        " EXPECTED: ", s);
+                    for (k=0; k<HASHSIZE; k++)
+                        fprintf(stderr, "%02X",
+                            expected_digest[k]);
+                    fprintf(stderr,"\n    FOUND: ");
+                    for (k=0; k<HASHSIZE; k++)
+                        fprintf(stderr, "%02X", digest[k]);
+                    fprintf(stderr, "\n");
+                }
+                else
+                {
+                    printf("OK: %s\n", s);
+                }
+            }
+        }
+    } while (s);
+    return found;
+}
+
+
+
+void syntax(char *progname)
+{
+    printf("\nsyntax:\n"
+     "%s [-c|-h][-q] file name[s]\n"
+     "    -c = check files against previous check values\n"
+     "    -g = generate SHA-1 check values (default action)\n"
+     "    -h = display this help\n"
+     "For example,\n"
+     "sha1sum test.txt > check.txt\n"
+     "generates check value for test.txt in check.txt, and\n"
+     "sha1sum -c check.txt\n"
+     "checks test.txt against the check value in check.txt\n",
+     progname);
+    exit(1);
+}
+
+
+/**********************************************************/
+
+int main(int argc, char** argv)
+{
+    int i, j, k;
+    int check = 0;
+    int found = 0;
+    unsigned char digest[HASHSIZE];
+    unsigned char expected_digest[HASHSIZE];
+    FILE *checkfile;
+    char checkline[LINESIZE];
+    char *s;
+#ifdef __BORLANDC__
+    struct ffblk f;
+    int done;
+    char path[MAXPATH];
+    char drive[MAXDRIVE];
+    char dir[MAXDIR];
+    char name[MAXFILE];
+    char ext[MAXEXT];
+#endif
+    unsigned char err;
+
+    for (i = 1; i < argc; i++)
+    {
+        if (argv[i][0] == '-')
+        {
+            switch (argv[i][1])
+            {
+                case 'c':
+                case 'C':
+                    check = 1;
+                    break;
+                case 'g':
+                case 'G':
+                    check = 0;
+                    break;
+                default:
+                    syntax(argv[0]);
+            }
+        }
+    }
+
+    for (i=1; i<argc; i++)
+    {
+        if (argv[i][0] != '-')
+        {
+#ifdef __BORLANDC__
+            fnsplit(argv[i], drive, dir, name, ext);
+            done = findfirst(argv[i], &f, FA_RDONLY |
+                FA_HIDDEN|FA_SYSTEM|FA_ARCH);
+             while (!done)
+            {
+                sprintf(path, "%s%s%s", drive, dir, f.ff_name);
+                s = path;
+#else
+                s = argv[i];
+#endif
+
+                if (check)
+                {   /* Check fingerprint file. */
+                    found |= verifyfile(s);
+                }
+                else
+                {   /* Generate fingerprints & write to
+                       stdout. */
+                    sha1file(s, digest);
+                    //printf("SHA1=");
+                    for (j=0; j<HASHSIZE; j++)
+                        printf("%02x", digest[j]);
+                    printf("  %s\n", s);
+                    found = 1;
+                }
+
+#ifdef __BORLANDC__
+                done = findnext(&f);
+            }
+#endif
+
+        }
+    }
+    if (!found)
+    {
+        if (check)
+        {
+            fprintf(stderr,
+                "No SHA1 lines found in %s\n",
+                argv[i]);
+        }
+        else
+        {
+            fprintf(stderr, "No files checked.\n");
+            syntax(argv[0]);
+        }
+    }
+    return(0);  /* JHB */
+}
+
+#endif  /*CMDLINE*/
diff --git a/libdex/sha1.h b/libdex/sha1.h
new file mode 100644
index 0000000..65cf667
--- /dev/null
+++ b/libdex/sha1.h
@@ -0,0 +1,20 @@
+/*
+ * See "sha1.c" for author info.
+ */
+#ifndef _DALVIK_SHA1
+#define _DALVIK_SHA1
+
+typedef struct {
+    unsigned long state[5];
+    unsigned long count[2];
+    unsigned char buffer[64];
+} SHA1_CTX;
+
+#define HASHSIZE 20
+
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data,
+    unsigned long len);
+void SHA1Final(unsigned char digest[HASHSIZE], SHA1_CTX* context);
+
+#endif /*_DALVIK_SHA1*/
diff --git a/libnativehelper/Android.mk b/libnativehelper/Android.mk
new file mode 100644
index 0000000..630eec3
--- /dev/null
+++ b/libnativehelper/Android.mk
@@ -0,0 +1,88 @@
+# 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.
+
+
+LOCAL_PATH := $(call my-dir)
+
+#
+# Common definitions for host and device.
+#
+
+src_files := \
+    JNIHelp.c \
+    Register.c
+
+c_includes := \
+    $(JNI_H_INCLUDE)
+
+# Any shared/static libs required by libjavacore
+# need to be mentioned here as well.
+# TODO: fix this requirement
+
+shared_libraries := \
+    libcrypto  \
+    libicui18n \
+    libicuuc   \
+    libsqlite \
+    libssl
+
+static_libraries := \
+    libjavacore \
+    libfdlibm
+
+
+
+#
+# Build for the target (device).
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(src_files)
+LOCAL_C_INCLUDES := $(c_includes)
+LOCAL_STATIC_LIBRARIES := $(static_libraries)
+LOCAL_SHARED_LIBRARIES := $(shared_libraries) libcutils libexpat liblog libutils libz
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libnativehelper
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+    include $(CLEAR_VARS)
+
+    LOCAL_SRC_FILES := $(src_files)
+    LOCAL_C_INCLUDES := $(c_includes)
+    LOCAL_WHOLE_STATIC_LIBRARIES := $(static_libraries:%=%-host)
+
+    ifeq ($(HOST_OS)-$(HOST_ARCH),darwin-x86)
+        # OSX has a lot of libraries built in, which we don't have to
+        # bother building; just include them on the ld line.
+        LOCAL_LDLIBS := -lexpat -lssl -lz -lcrypto -licucore -lsqlite3
+        LOCAL_WHOLE_STATIC_LIBRARIES += libutils
+    else
+        LOCAL_SHARED_LIBRARIES := $(shared_libraries)
+        LOCAL_STATIC_LIBRARIES := libcutils libexpat liblog libutils libz
+    endif
+
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := libnativehelper
+    include $(BUILD_HOST_STATIC_LIBRARY)
+
+endif
diff --git a/libnativehelper/JNIHelp.c b/libnativehelper/JNIHelp.c
new file mode 100644
index 0000000..59d457b
--- /dev/null
+++ b/libnativehelper/JNIHelp.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 helper functions.
+ */
+#define LOG_TAG "JNIHelp"
+#include "JNIHelp.h"
+#include "utils/Log.h"
+
+#include <string.h>
+#include <assert.h>
+
+/*
+ * Register native JNI-callable methods.
+ *
+ * "className" looks like "java/lang/String".
+ */
+int jniRegisterNativeMethods(JNIEnv* env, const char* className,
+    const JNINativeMethod* gMethods, int numMethods)
+{
+    jclass clazz;
+
+    LOGV("Registering %s natives\n", className);
+    clazz = (*env)->FindClass(env, className);
+    if (clazz == NULL) {
+        LOGE("Native registration unable to find class '%s'\n", className);
+        return -1;
+    }
+
+    int result = 0;
+    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
+        LOGE("RegisterNatives failed for '%s'\n", className);
+        result = -1;
+    }
+
+    (*env)->DeleteLocalRef(env, clazz);
+    return result;
+}
+
+/*
+ * Get a human-readable summary of an exception object.  The buffer will
+ * be populated with the "binary" class name and, if present, the
+ * exception message.
+ */
+static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
+{
+    int success = 0;
+
+    /* get the name of the exception's class */
+    jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
+    jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail
+    jmethodID classGetNameMethod = (*env)->GetMethodID(
+            env, classClazz, "getName", "()Ljava/lang/String;");
+    jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod);
+    if (classNameStr != NULL) {
+        /* get printable string */
+        const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL);
+        if (classNameChars != NULL) {
+            /* if the exception has a message string, get that */
+            jmethodID throwableGetMessageMethod = (*env)->GetMethodID(
+                    env, exceptionClazz, "getMessage", "()Ljava/lang/String;");
+            jstring messageStr = (*env)->CallObjectMethod(
+                    env, exception, throwableGetMessageMethod);
+
+            if (messageStr != NULL) {
+                const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL);
+                if (messageChars != NULL) {
+                    snprintf(buf, bufLen, "%s: %s", classNameChars, messageChars);
+                    (*env)->ReleaseStringUTFChars(env, messageStr, messageChars);
+                } else {
+                    (*env)->ExceptionClear(env); // clear OOM
+                    snprintf(buf, bufLen, "%s: <error getting message>", classNameChars);
+                }
+                (*env)->DeleteLocalRef(env, messageStr);
+            } else {
+                strncpy(buf, classNameChars, bufLen);
+                buf[bufLen - 1] = '\0';
+            }
+
+            (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars);
+            success = 1;
+        }
+        (*env)->DeleteLocalRef(env, classNameStr);
+    }
+    (*env)->DeleteLocalRef(env, classClazz);
+    (*env)->DeleteLocalRef(env, exceptionClazz);
+
+    if (! success) {
+        (*env)->ExceptionClear(env);
+        snprintf(buf, bufLen, "%s", "<error getting class name>");
+    }
+}
+
+/*
+ * Formats an exception as a string with its stack trace.
+ */
+static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
+{
+    int success = 0;
+
+    jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter");
+    if (stringWriterClazz != NULL) {
+        jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz,
+                "<init>", "()V");
+        jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz,
+                "toString", "()Ljava/lang/String;");
+
+        jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter");
+        if (printWriterClazz != NULL) {
+            jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz,
+                    "<init>", "(Ljava/io/Writer;)V");
+
+            jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor);
+            if (stringWriterObj != NULL) {
+                jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor,
+                        stringWriterObj);
+                if (printWriterObj != NULL) {
+                    jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
+                    jmethodID printStackTraceMethod = (*env)->GetMethodID(
+                            env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V");
+
+                    (*env)->CallVoidMethod(
+                            env, exception, printStackTraceMethod, printWriterObj);
+                    if (! (*env)->ExceptionCheck(env)) {
+                        jstring messageStr = (*env)->CallObjectMethod(
+                                env, stringWriterObj, stringWriterToStringMethod);
+                        if (messageStr != NULL) {
+                            jsize messageStrLength = (*env)->GetStringLength(env, messageStr);
+                            if (messageStrLength >= (jsize) bufLen) {
+                                messageStrLength = bufLen - 1;
+                            }
+                            (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf);
+                            (*env)->DeleteLocalRef(env, messageStr);
+                            buf[messageStrLength] = '\0';
+                            success = 1;
+                        }
+                    }
+                    (*env)->DeleteLocalRef(env, exceptionClazz);
+                    (*env)->DeleteLocalRef(env, printWriterObj);
+                }
+                (*env)->DeleteLocalRef(env, stringWriterObj);
+            }
+            (*env)->DeleteLocalRef(env, printWriterClazz);
+        }
+        (*env)->DeleteLocalRef(env, stringWriterClazz);
+    }
+
+    if (! success) {
+        (*env)->ExceptionClear(env);
+        getExceptionSummary(env, exception, buf, bufLen);
+    }
+}
+
+/*
+ * Throw an exception with the specified class and an optional message.
+ *
+ * If an exception is currently pending, we log a warning message and
+ * clear it.
+ *
+ * Returns 0 if the specified exception was successfully thrown.  (Some
+ * sort of exception will always be pending when this returns.)
+ */
+int jniThrowException(JNIEnv* env, const char* className, const char* msg)
+{
+    jclass exceptionClass;
+
+    if ((*env)->ExceptionCheck(env)) {
+        /* TODO: consider creating the new exception with this as "cause" */
+        char buf[256];
+
+        jthrowable exception = (*env)->ExceptionOccurred(env);
+        (*env)->ExceptionClear(env);
+
+        if (exception != NULL) {
+            getExceptionSummary(env, exception, buf, sizeof(buf));
+            LOGW("Discarding pending exception (%s) to throw %s\n", buf, className);
+            (*env)->DeleteLocalRef(env, exception);
+        }
+    }
+
+    exceptionClass = (*env)->FindClass(env, className);
+    if (exceptionClass == NULL) {
+        LOGE("Unable to find exception class %s\n", className);
+        /* ClassNotFoundException now pending */
+        return -1;
+    }
+
+    int result = 0;
+    if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
+        LOGE("Failed throwing '%s' '%s'\n", className, msg);
+        /* an exception, most likely OOM, will now be pending */
+        result = -1;
+    }
+
+    (*env)->DeleteLocalRef(env, exceptionClass);
+    return result;
+}
+
+/*
+ * Throw a java.lang.NullPointerException, with an optional message.
+ */
+int jniThrowNullPointerException(JNIEnv* env, const char* msg)
+{
+    return jniThrowException(env, "java/lang/NullPointerException", msg);
+}
+
+/*
+ * Throw a java.lang.RuntimeException, with an optional message.
+ */
+int jniThrowRuntimeException(JNIEnv* env, const char* msg)
+{
+    return jniThrowException(env, "java/lang/RuntimeException", msg);
+}
+
+/*
+ * Throw a java.io.IOException, generating the message from errno.
+ */
+int jniThrowIOException(JNIEnv* env, int errnum)
+{
+    char buffer[80];
+    const char* message = jniStrError(errnum, buffer, sizeof(buffer));
+    return jniThrowException(env, "java/io/IOException", message);
+}
+
+/*
+ * Log an exception.
+ * If exception is NULL, logs the current exception in the JNI environment, if any.
+ */
+void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception)
+{
+    int currentException = 0;
+    if (exception == NULL) {
+        exception = (*env)->ExceptionOccurred(env);
+        if (exception == NULL) {
+            return;
+        }
+
+        (*env)->ExceptionClear(env);
+        currentException = 1;
+    }
+
+    char buffer[1024];
+    printStackTrace(env, exception, buffer, sizeof(buffer));
+    __android_log_write(priority, tag, buffer);
+
+    if (currentException) {
+        (*env)->Throw(env, exception); // rethrow
+        (*env)->DeleteLocalRef(env, exception);
+    }
+}
+
+const char* jniStrError(int errnum, char* buf, size_t buflen)
+{
+    // note: glibc has a nonstandard strerror_r that returns char* rather
+    // than POSIX's int.
+    // char *strerror_r(int errnum, char *buf, size_t n);
+    char* ret = (char*) strerror_r(errnum, buf, buflen);
+    if (((int)ret) == 0) {
+        //POSIX strerror_r, success
+        return buf;
+    } else if (((int)ret) == -1) {
+        //POSIX strerror_r, failure
+        // (Strictly, POSIX only guarantees a value other than 0. The safest
+        // way to implement this function is to use C++ and overload on the
+        // type of strerror_r to accurately distinguish GNU from POSIX. But
+        // realistic implementations will always return -1.)
+        snprintf(buf, buflen, "errno %d", errnum);
+        return buf;
+    } else {
+        //glibc strerror_r returning a string
+        return ret;
+    }
+}
+
+static struct CachedFields {
+    jclass fileDescriptorClass;
+    jmethodID fileDescriptorCtor;
+    jfieldID descriptorField;
+} gCachedFields;
+
+int registerJniHelp(JNIEnv* env) {
+    gCachedFields.fileDescriptorClass =
+            (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor"));
+    if (gCachedFields.fileDescriptorClass == NULL) {
+        return -1;
+    }
+
+    gCachedFields.fileDescriptorCtor =
+            (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V");
+    if (gCachedFields.fileDescriptorCtor == NULL) {
+        return -1;
+    }
+
+    gCachedFields.descriptorField =
+            (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I");
+    if (gCachedFields.descriptorField == NULL) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Create a java.io.FileDescriptor given an integer fd
+ */
+jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
+    jobject fileDescriptor = (*env)->NewObject(env,
+            gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
+    jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
+    return fileDescriptor;
+}
+
+/*
+ * Get an int file descriptor from a java.io.FileDescriptor
+ */
+int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
+    return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField);
+}
+
+/*
+ * Set the descriptor of a java.io.FileDescriptor
+ */
+void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
+    (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
+}
diff --git a/libnativehelper/MODULE_LICENSE_APACHE2 b/libnativehelper/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libnativehelper/MODULE_LICENSE_APACHE2
diff --git a/libnativehelper/NOTICE b/libnativehelper/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libnativehelper/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libnativehelper/README b/libnativehelper/README
new file mode 100644
index 0000000..5a5f5d4
--- /dev/null
+++ b/libnativehelper/README
@@ -0,0 +1,11 @@
+Support functions for Android's class libraries
+
+
+These are VM-agnostic native functions that implement methods for system
+class libraries.  All code here:
+
+ - MUST not be associated with an android.* class (that code lives in
+   frameworks/base/).
+ - SHOULD be written in C rather than C++ where possible.
+
+Some helper functions are defined in include/nativehelper/JNIHelp.h.
diff --git a/libnativehelper/Register.c b/libnativehelper/Register.c
new file mode 100644
index 0000000..87017ac
--- /dev/null
+++ b/libnativehelper/Register.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 helper functions.
+ */
+
+#include "jni.h"
+
+extern int registerCoreLibrariesJni(JNIEnv* env);
+extern int registerJniHelp(JNIEnv* env);
+
+/*
+ * Register all methods for system classes.
+ */
+int jniRegisterSystemMethods(JNIEnv* env)
+{
+    // We initialize JNIHelp.c first so that the core libraries can safely rely on it.
+    return registerJniHelp(env) != -1 && registerCoreLibrariesJni(env) != -1;
+}
diff --git a/libnativehelper/include/nativehelper/JNIHelp.h b/libnativehelper/include/nativehelper/JNIHelp.h
new file mode 100644
index 0000000..1c9f933
--- /dev/null
+++ b/libnativehelper/include/nativehelper/JNIHelp.h
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+/*
+ * JNI helper functions.
+ *
+ * This file may be included by C or C++ code, which is trouble because jni.h
+ * uses different typedefs for JNIEnv in each language.
+ */
+#ifndef _NATIVEHELPER_JNIHELP_H
+#define _NATIVEHELPER_JNIHELP_H
+
+#include "jni.h"
+#include "utils/Log.h"
+#include <unistd.h>
+
+#ifndef NELEM
+# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Register one or more native methods with a particular class.
+ */
+int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
+    const JNINativeMethod* gMethods, int numMethods);
+
+/*
+ * Throw an exception with the specified class and an optional message.
+ * The "className" argument will be passed directly to FindClass, which
+ * takes strings with slashes (e.g. "java/lang/Object").
+ *
+ * Returns 0 on success, nonzero if something failed (e.g. the exception
+ * class couldn't be found).
+ *
+ * Currently aborts the VM if it can't throw the exception.
+ */
+int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
+
+/*
+ * Throw a java.lang.NullPointerException, with an optional message.
+ */
+int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.lang.RuntimeException, with an optional message.
+ */
+int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.io.IOException, generating the message from errno.
+ */
+int jniThrowIOException(C_JNIEnv* env, int errnum);
+
+/*
+ * Return a pointer to a locale-dependent error string explaining errno
+ * value 'errnum'. The returned pointer may or may not be equal to 'buf'.
+ * This function is thread-safe (unlike strerror) and portable (unlike
+ * strerror_r).
+ */
+const char* jniStrError(int errnum, char* buf, size_t buflen);
+
+/*
+ * Create a java.io.FileDescriptor given an integer fd
+ */
+jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
+
+/*
+ * Get an int file descriptor from a java.io.FileDescriptor
+ */
+int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
+
+/*
+ * Set an int file descriptor to a java.io.FileDescriptor
+ */
+void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value);
+
+/*
+ * Log a message and an exception.
+ * If exception is NULL, logs the current exception in the JNI environment.
+ */
+void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*
+ * For C++ code, we provide inlines that map to the C functions.  g++ always
+ * inlines these, even on non-optimized builds.
+ */
+#if defined(__cplusplus) && !defined(JNI_FORCE_C)
+inline int jniRegisterNativeMethods(JNIEnv* env, const char* className,
+    const JNINativeMethod* gMethods, int numMethods)
+{
+    return jniRegisterNativeMethods(&env->functions, className, gMethods,
+        numMethods);
+}
+inline int jniThrowException(JNIEnv* env, const char* className,
+    const char* msg)
+{
+    return jniThrowException(&env->functions, className, msg);
+}
+inline int jniThrowNullPointerException(JNIEnv* env, const char* msg)
+{
+    return jniThrowNullPointerException(&env->functions, msg);
+}
+inline int jniThrowRuntimeException(JNIEnv* env, const char* msg)
+{
+    return jniThrowRuntimeException(&env->functions, msg);
+}
+inline int jniThrowIOException(JNIEnv* env, int errnum)
+{
+    return jniThrowIOException(&env->functions, errnum);
+}
+inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd)
+{
+    return jniCreateFileDescriptor(&env->functions, fd);
+}
+inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor)
+{
+    return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor);
+}
+inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor,
+    int value)
+{
+    jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value);
+}
+inline void jniLogException(JNIEnv* env, int priority, const char* tag,
+        jthrowable exception = NULL)
+{
+    jniLogException(&env->functions, priority, tag, exception);
+}
+#endif
+
+/* Logging macros.
+ *
+ * Logs an exception.  If the exception is omitted or NULL, logs the current exception
+ * from the JNI environment, if any.
+ */
+#define LOG_EX(env, priority, tag, ...) \
+    IF_LOG(priority, tag) jniLogException(env, ANDROID_##priority, tag, ##__VA_ARGS__)
+#define LOGV_EX(env, ...) LOG_EX(env, LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+#define LOGD_EX(env, ...) LOG_EX(env, LOG_DEBUG, LOG_TAG, ##__VA_ARGS__)
+#define LOGI_EX(env, ...) LOG_EX(env, LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define LOGW_EX(env, ...) LOG_EX(env, LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define LOGE_EX(env, ...) LOG_EX(env, LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({         \
+    typeof (exp) _rc;                      \
+    do {                                   \
+        _rc = (exp);                       \
+    } while (_rc == -1 && errno == EINTR); \
+    _rc; })
+#endif
+
+#endif /*_NATIVEHELPER_JNIHELP_H*/
diff --git a/libnativehelper/include/nativehelper/jni.h b/libnativehelper/include/nativehelper/jni.h
new file mode 100644
index 0000000..22b1d88
--- /dev/null
+++ b/libnativehelper/include/nativehelper/jni.h
@@ -0,0 +1,1155 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 specification, as defined by Sun:
+ * http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html
+ *
+ * Everything here is expected to be VM-neutral.
+ */
+
+#ifndef _JNI_H
+#define _JNI_H
+
+#include <stdarg.h>
+
+/*
+ * Primitive types that match up with Java equivalents.
+ */
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>      /* C99 */
+typedef uint8_t         jboolean;       /* unsigned 8 bits */
+typedef int8_t          jbyte;          /* signed 8 bits */
+typedef uint16_t        jchar;          /* unsigned 16 bits */
+typedef int16_t         jshort;         /* signed 16 bits */
+typedef int32_t         jint;           /* signed 32 bits */
+typedef int64_t         jlong;          /* signed 64 bits */
+typedef float           jfloat;         /* 32-bit IEEE 754 */
+typedef double          jdouble;        /* 64-bit IEEE 754 */
+#else
+typedef unsigned char   jboolean;       /* unsigned 8 bits */
+typedef signed char     jbyte;          /* signed 8 bits */
+typedef unsigned short  jchar;          /* unsigned 16 bits */
+typedef short           jshort;         /* signed 16 bits */
+typedef int             jint;           /* signed 32 bits */
+typedef long long       jlong;          /* signed 64 bits */
+typedef float           jfloat;         /* 32-bit IEEE 754 */
+typedef double          jdouble;        /* 64-bit IEEE 754 */
+#endif
+
+/* "cardinal indices and sizes" */
+typedef jint            jsize;
+
+#ifdef __cplusplus
+/*
+ * Reference types, in C++
+ */
+class _jobject {};
+class _jclass : public _jobject {};
+class _jstring : public _jobject {};
+class _jarray : public _jobject {};
+class _jobjectArray : public _jarray {};
+class _jbooleanArray : public _jarray {};
+class _jbyteArray : public _jarray {};
+class _jcharArray : public _jarray {};
+class _jshortArray : public _jarray {};
+class _jintArray : public _jarray {};
+class _jlongArray : public _jarray {};
+class _jfloatArray : public _jarray {};
+class _jdoubleArray : public _jarray {};
+class _jthrowable : public _jobject {};
+
+typedef _jobject*       jobject;
+typedef _jclass*        jclass;
+typedef _jstring*       jstring;
+typedef _jarray*        jarray;
+typedef _jobjectArray*  jobjectArray;
+typedef _jbooleanArray* jbooleanArray;
+typedef _jbyteArray*    jbyteArray;
+typedef _jcharArray*    jcharArray;
+typedef _jshortArray*   jshortArray;
+typedef _jintArray*     jintArray;
+typedef _jlongArray*    jlongArray;
+typedef _jfloatArray*   jfloatArray;
+typedef _jdoubleArray*  jdoubleArray;
+typedef _jthrowable*    jthrowable;
+typedef _jobject*       jweak;
+
+
+#else /* not __cplusplus */
+
+/*
+ * Reference types, in C.
+ */
+typedef void*           jobject;
+typedef jobject         jclass;
+typedef jobject         jstring;
+typedef jobject         jarray;
+typedef jarray          jobjectArray;
+typedef jarray          jbooleanArray;
+typedef jarray          jbyteArray;
+typedef jarray          jcharArray;
+typedef jarray          jshortArray;
+typedef jarray          jintArray;
+typedef jarray          jlongArray;
+typedef jarray          jfloatArray;
+typedef jarray          jdoubleArray;
+typedef jobject         jthrowable;
+typedef jobject         jweak;
+
+#endif /* not __cplusplus */
+
+struct _jfieldID;                       /* opaque structure */
+typedef struct _jfieldID* jfieldID;     /* field IDs */
+
+struct _jmethodID;                      /* opaque structure */
+typedef struct _jmethodID* jmethodID;   /* method IDs */
+
+struct JNIInvokeInterface;
+
+typedef union jvalue {
+    jboolean    z;
+    jbyte       b;
+    jchar       c;
+    jshort      s;
+    jint        i;
+    jlong       j;
+    jfloat      f;
+    jdouble     d;
+    jobject     l;
+} jvalue;
+
+typedef enum jobjectRefType {
+    JNIInvalidRefType = 0,
+    JNILocalRefType = 1,
+    JNIGlobalRefType = 2,
+    JNIWeakGlobalRefType = 3
+} jobjectRefType;
+
+typedef struct {
+    const char* name;
+    const char* signature;
+    void*       fnPtr;
+} JNINativeMethod;
+
+struct _JNIEnv;
+struct _JavaVM;
+typedef const struct JNINativeInterface* C_JNIEnv;
+
+#if defined(__cplusplus)
+typedef _JNIEnv JNIEnv;
+typedef _JavaVM JavaVM;
+#else
+typedef const struct JNINativeInterface* JNIEnv;
+typedef const struct JNIInvokeInterface* JavaVM;
+#endif
+
+/*
+ * Table of interface function pointers.
+ */
+struct JNINativeInterface {
+    void*       reserved0;
+    void*       reserved1;
+    void*       reserved2;
+    void*       reserved3;
+
+    jint        (*GetVersion)(JNIEnv *);
+
+    jclass      (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
+                        jsize);
+    jclass      (*FindClass)(JNIEnv*, const char*);
+
+    jmethodID   (*FromReflectedMethod)(JNIEnv*, jobject);
+    jfieldID    (*FromReflectedField)(JNIEnv*, jobject);
+    /* spec doesn't show jboolean parameter */
+    jobject     (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
+
+    jclass      (*GetSuperclass)(JNIEnv*, jclass);
+    jboolean    (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
+
+    /* spec doesn't show jboolean parameter */
+    jobject     (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
+
+    jint        (*Throw)(JNIEnv*, jthrowable);
+    jint        (*ThrowNew)(JNIEnv *, jclass, const char *);
+    jthrowable  (*ExceptionOccurred)(JNIEnv*);
+    void        (*ExceptionDescribe)(JNIEnv*);
+    void        (*ExceptionClear)(JNIEnv*);
+    void        (*FatalError)(JNIEnv*, const char*);
+
+    jint        (*PushLocalFrame)(JNIEnv*, jint);
+    jobject     (*PopLocalFrame)(JNIEnv*, jobject);
+
+    jobject     (*NewGlobalRef)(JNIEnv*, jobject);
+    void        (*DeleteGlobalRef)(JNIEnv*, jobject);
+    void        (*DeleteLocalRef)(JNIEnv*, jobject);
+    jboolean    (*IsSameObject)(JNIEnv*, jobject, jobject);
+
+    jobject     (*NewLocalRef)(JNIEnv*, jobject);
+    jint        (*EnsureLocalCapacity)(JNIEnv*, jint);
+
+    jobject     (*AllocObject)(JNIEnv*, jclass);
+    jobject     (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
+    jobject     (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list);
+    jobject     (*NewObjectA)(JNIEnv*, jclass, jmethodID, jvalue*);
+
+    jclass      (*GetObjectClass)(JNIEnv*, jobject);
+    jboolean    (*IsInstanceOf)(JNIEnv*, jobject, jclass);
+    jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
+
+    jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jobject     (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jobject     (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jboolean    (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jboolean    (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jboolean    (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jbyte       (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jbyte       (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jbyte       (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jchar       (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jchar       (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jchar       (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jshort      (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jshort      (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jshort      (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jint        (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jint        (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jlong       (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jlong       (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jlong       (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jfloat      (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jfloat      (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jfloat      (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jdouble     (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jdouble     (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jdouble     (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
+    void        (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    void        (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+
+    jobject     (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jobject     (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jobject     (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jboolean    (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jboolean    (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass,
+                         jmethodID, va_list);
+    jboolean    (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass,
+                         jmethodID, jvalue*);
+    jbyte       (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jbyte       (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jbyte       (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jchar       (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jchar       (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jchar       (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jshort      (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jshort      (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jshort      (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jint        (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jint        (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jint        (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jlong       (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jlong       (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jlong       (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jfloat      (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jfloat      (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jfloat      (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jdouble     (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jdouble     (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jdouble     (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    void        (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    void        (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    void        (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+
+    jfieldID    (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
+
+    jobject     (*GetObjectField)(JNIEnv*, jobject, jfieldID);
+    jboolean    (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
+    jbyte       (*GetByteField)(JNIEnv*, jobject, jfieldID);
+    jchar       (*GetCharField)(JNIEnv*, jobject, jfieldID);
+    jshort      (*GetShortField)(JNIEnv*, jobject, jfieldID);
+    jint        (*GetIntField)(JNIEnv*, jobject, jfieldID);
+    jlong       (*GetLongField)(JNIEnv*, jobject, jfieldID);
+    jfloat      (*GetFloatField)(JNIEnv*, jobject, jfieldID);
+    jdouble     (*GetDoubleField)(JNIEnv*, jobject, jfieldID);
+
+    void        (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
+    void        (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
+    void        (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
+    void        (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
+    void        (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
+    void        (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
+    void        (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
+    void        (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat);
+    void        (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble);
+
+    jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
+
+    jobject     (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jobject     (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jobject     (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jboolean    (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jboolean    (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
+                        va_list);
+    jboolean    (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID,
+                        jvalue*);
+    jbyte       (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jbyte       (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jbyte       (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jchar       (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jchar       (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jchar       (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jshort      (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jshort      (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jshort      (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jint        (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jint        (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jint        (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jlong       (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jlong       (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jlong       (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jfloat      (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jfloat      (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jfloat      (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jdouble     (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jdouble     (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jdouble     (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
+    void        (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    void        (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+
+    jfieldID    (*GetStaticFieldID)(JNIEnv*, jclass, const char*,
+                        const char*);
+
+    jobject     (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
+    jboolean    (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID);
+    jbyte       (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
+    jchar       (*GetStaticCharField)(JNIEnv*, jclass, jfieldID);
+    jshort      (*GetStaticShortField)(JNIEnv*, jclass, jfieldID);
+    jint        (*GetStaticIntField)(JNIEnv*, jclass, jfieldID);
+    jlong       (*GetStaticLongField)(JNIEnv*, jclass, jfieldID);
+    jfloat      (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID);
+    jdouble     (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID);
+
+    void        (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
+    void        (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean);
+    void        (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
+    void        (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar);
+    void        (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort);
+    void        (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint);
+    void        (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong);
+    void        (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat);
+    void        (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble);
+
+    jstring     (*NewString)(JNIEnv*, const jchar*, jsize);
+    jsize       (*GetStringLength)(JNIEnv*, jstring);
+    const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
+    void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
+    jstring     (*NewStringUTF)(JNIEnv*, const char*);
+    jsize       (*GetStringUTFLength)(JNIEnv*, jstring);
+    /* JNI spec says this returns const jbyte*, but that's inconsistent */
+    const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
+    void        (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
+    jsize       (*GetArrayLength)(JNIEnv*, jarray);
+    jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject);
+    jobject     (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
+    void        (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);
+
+    jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
+    jbyteArray    (*NewByteArray)(JNIEnv*, jsize);
+    jcharArray    (*NewCharArray)(JNIEnv*, jsize);
+    jshortArray   (*NewShortArray)(JNIEnv*, jsize);
+    jintArray     (*NewIntArray)(JNIEnv*, jsize);
+    jlongArray    (*NewLongArray)(JNIEnv*, jsize);
+    jfloatArray   (*NewFloatArray)(JNIEnv*, jsize);
+    jdoubleArray  (*NewDoubleArray)(JNIEnv*, jsize);
+
+    jboolean*   (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
+    jbyte*      (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
+    jchar*      (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
+    jshort*     (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
+    jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
+    jlong*      (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
+    jfloat*     (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
+    jdouble*    (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
+
+    void        (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,
+                        jboolean*, jint);
+    void        (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,
+                        jbyte*, jint);
+    void        (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,
+                        jchar*, jint);
+    void        (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,
+                        jshort*, jint);
+    void        (*ReleaseIntArrayElements)(JNIEnv*, jintArray,
+                        jint*, jint);
+    void        (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,
+                        jlong*, jint);
+    void        (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,
+                        jfloat*, jint);
+    void        (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,
+                        jdouble*, jint);
+
+    void        (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
+                        jsize, jsize, jboolean*);
+    void        (*GetByteArrayRegion)(JNIEnv*, jbyteArray,
+                        jsize, jsize, jbyte*);
+    void        (*GetCharArrayRegion)(JNIEnv*, jcharArray,
+                        jsize, jsize, jchar*);
+    void        (*GetShortArrayRegion)(JNIEnv*, jshortArray,
+                        jsize, jsize, jshort*);
+    void        (*GetIntArrayRegion)(JNIEnv*, jintArray,
+                        jsize, jsize, jint*);
+    void        (*GetLongArrayRegion)(JNIEnv*, jlongArray,
+                        jsize, jsize, jlong*);
+    void        (*GetFloatArrayRegion)(JNIEnv*, jfloatArray,
+                        jsize, jsize, jfloat*);
+    void        (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
+                        jsize, jsize, jdouble*);
+
+    /* spec shows these without const; some jni.h do, some don't */
+    void        (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
+                        jsize, jsize, const jboolean*);
+    void        (*SetByteArrayRegion)(JNIEnv*, jbyteArray,
+                        jsize, jsize, const jbyte*);
+    void        (*SetCharArrayRegion)(JNIEnv*, jcharArray,
+                        jsize, jsize, const jchar*);
+    void        (*SetShortArrayRegion)(JNIEnv*, jshortArray,
+                        jsize, jsize, const jshort*);
+    void        (*SetIntArrayRegion)(JNIEnv*, jintArray,
+                        jsize, jsize, const jint*);
+    void        (*SetLongArrayRegion)(JNIEnv*, jlongArray,
+                        jsize, jsize, const jlong*);
+    void        (*SetFloatArrayRegion)(JNIEnv*, jfloatArray,
+                        jsize, jsize, const jfloat*);
+    void        (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
+                        jsize, jsize, const jdouble*);
+
+    jint        (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,
+                        jint);
+    jint        (*UnregisterNatives)(JNIEnv*, jclass);
+    jint        (*MonitorEnter)(JNIEnv*, jobject);
+    jint        (*MonitorExit)(JNIEnv*, jobject);
+    jint        (*GetJavaVM)(JNIEnv*, JavaVM**);
+
+    void        (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*);
+    void        (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*);
+
+    void*       (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*);
+    void        (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint);
+
+    const jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*);
+    void        (*ReleaseStringCritical)(JNIEnv*, jstring, const jchar*);
+
+    jweak       (*NewWeakGlobalRef)(JNIEnv*, jobject);
+    void        (*DeleteWeakGlobalRef)(JNIEnv*, jweak);
+
+    jboolean    (*ExceptionCheck)(JNIEnv*);
+
+    jobject     (*NewDirectByteBuffer)(JNIEnv*, void*, jlong);
+    void*       (*GetDirectBufferAddress)(JNIEnv*, jobject);
+    jlong       (*GetDirectBufferCapacity)(JNIEnv*, jobject);
+
+    /* added in JNI 1.6 */
+    jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
+};
+
+/*
+ * C++ object wrapper.
+ *
+ * This is usually overlaid on a C struct whose first element is a
+ * JNINativeInterface*.  We rely somewhat on compiler behavior.
+ */
+struct _JNIEnv {
+    /* do not rename this; it does not seem to be entirely opaque */
+    const struct JNINativeInterface* functions;
+
+#if defined(__cplusplus)
+
+    jint GetVersion()
+    { return functions->GetVersion(this); }
+
+    jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
+        jsize bufLen)
+    { return functions->DefineClass(this, name, loader, buf, bufLen); }
+
+    jclass FindClass(const char* name)
+    { return functions->FindClass(this, name); }
+
+    jmethodID FromReflectedMethod(jobject method)
+    { return functions->FromReflectedMethod(this, method); }
+
+    jfieldID FromReflectedField(jobject field)
+    { return functions->FromReflectedField(this, field); }
+
+    jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic)
+    { return functions->ToReflectedMethod(this, cls, methodID, isStatic); }
+
+    jclass GetSuperclass(jclass clazz)
+    { return functions->GetSuperclass(this, clazz); }
+
+    jboolean IsAssignableFrom(jclass clazz1, jclass clazz2)
+    { return functions->IsAssignableFrom(this, clazz1, clazz2); }
+
+    jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic)
+    { return functions->ToReflectedField(this, cls, fieldID, isStatic); }
+
+    jint Throw(jthrowable obj)
+    { return functions->Throw(this, obj); }
+
+    jint ThrowNew(jclass clazz, const char* message)
+    { return functions->ThrowNew(this, clazz, message); }
+
+    jthrowable ExceptionOccurred()
+    { return functions->ExceptionOccurred(this); }
+
+    void ExceptionDescribe()
+    { functions->ExceptionDescribe(this); }
+
+    void ExceptionClear()
+    { functions->ExceptionClear(this); }
+
+    void FatalError(const char* msg)
+    { functions->FatalError(this, msg); }
+
+    jint PushLocalFrame(jint capacity)
+    { return functions->PushLocalFrame(this, capacity); }
+
+    jobject PopLocalFrame(jobject result)
+    { return functions->PopLocalFrame(this, result); }
+
+    jobject NewGlobalRef(jobject obj)
+    { return functions->NewGlobalRef(this, obj); }
+
+    void DeleteGlobalRef(jobject globalRef)
+    { functions->DeleteGlobalRef(this, globalRef); }
+
+    void DeleteLocalRef(jobject localRef)
+    { functions->DeleteLocalRef(this, localRef); }
+
+    jboolean IsSameObject(jobject ref1, jobject ref2)
+    { return functions->IsSameObject(this, ref1, ref2); }
+
+    jobject NewLocalRef(jobject ref)
+    { return functions->NewLocalRef(this, ref); }
+
+    jint EnsureLocalCapacity(jint capacity)
+    { return functions->EnsureLocalCapacity(this, capacity); }
+
+    jobject AllocObject(jclass clazz)
+    { return functions->AllocObject(this, clazz); }
+
+    jobject NewObject(jclass clazz, jmethodID methodID, ...)
+    {
+        va_list args;
+        va_start(args, methodID);
+        jobject result = functions->NewObjectV(this, clazz, methodID, args);
+        va_end(args);
+        return result;
+    }
+
+    jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
+    { return functions->NewObjectV(this, clazz, methodID, args); }
+
+    jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue* args)
+    { return functions->NewObjectA(this, clazz, methodID, args); }
+
+    jclass GetObjectClass(jobject obj)
+    { return functions->GetObjectClass(this, obj); }
+
+    jboolean IsInstanceOf(jobject obj, jclass clazz)
+    { return functions->IsInstanceOf(this, obj, clazz); }
+
+    jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
+    { return functions->GetMethodID(this, clazz, name, sig); }
+
+#define CALL_TYPE_METHOD(_jtype, _jname)                                    \
+    _jtype Call##_jname##Method(jobject obj, jmethodID methodID, ...)       \
+    {                                                                       \
+        _jtype result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        result = functions->Call##_jname##MethodV(this, obj, methodID,      \
+                    args);                                                  \
+        va_end(args);                                                       \
+        return result;                                                      \
+    }
+#define CALL_TYPE_METHODV(_jtype, _jname)                                   \
+    _jtype Call##_jname##MethodV(jobject obj, jmethodID methodID,           \
+        va_list args)                                                       \
+    { return functions->Call##_jname##MethodV(this, obj, methodID, args); }
+#define CALL_TYPE_METHODA(_jtype, _jname)                                   \
+    _jtype Call##_jname##MethodA(jobject obj, jmethodID methodID,           \
+        jvalue* args)                                                       \
+    { return functions->Call##_jname##MethodA(this, obj, methodID, args); }
+
+#define CALL_TYPE(_jtype, _jname)                                           \
+    CALL_TYPE_METHOD(_jtype, _jname)                                        \
+    CALL_TYPE_METHODV(_jtype, _jname)                                       \
+    CALL_TYPE_METHODA(_jtype, _jname)
+
+    CALL_TYPE(jobject, Object)
+    CALL_TYPE(jboolean, Boolean)
+    CALL_TYPE(jbyte, Byte)
+    CALL_TYPE(jchar, Char)
+    CALL_TYPE(jshort, Short)
+    CALL_TYPE(jint, Int)
+    CALL_TYPE(jlong, Long)
+    CALL_TYPE(jfloat, Float)
+    CALL_TYPE(jdouble, Double)
+
+    void CallVoidMethod(jobject obj, jmethodID methodID, ...)
+    {
+        va_list args;
+        va_start(args, methodID);
+        functions->CallVoidMethodV(this, obj, methodID, args);
+        va_end(args);
+    }
+    void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
+    { functions->CallVoidMethodV(this, obj, methodID, args); }
+    void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args)
+    { functions->CallVoidMethodA(this, obj, methodID, args); }
+
+#define CALL_NONVIRT_TYPE_METHOD(_jtype, _jname)                            \
+    _jtype CallNonvirtual##_jname##Method(jobject obj, jclass clazz,        \
+        jmethodID methodID, ...)                                            \
+    {                                                                       \
+        _jtype result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        result = functions->CallNonvirtual##_jname##MethodV(this, obj,      \
+                    clazz, methodID, args);                                 \
+        va_end(args);                                                       \
+        return result;                                                      \
+    }
+#define CALL_NONVIRT_TYPE_METHODV(_jtype, _jname)                           \
+    _jtype CallNonvirtual##_jname##MethodV(jobject obj, jclass clazz,       \
+        jmethodID methodID, va_list args)                                   \
+    { return functions->CallNonvirtual##_jname##MethodV(this, obj, clazz,   \
+        methodID, args); }
+#define CALL_NONVIRT_TYPE_METHODA(_jtype, _jname)                           \
+    _jtype CallNonvirtual##_jname##MethodA(jobject obj, jclass clazz,       \
+        jmethodID methodID, jvalue* args)                                   \
+    { return functions->CallNonvirtual##_jname##MethodA(this, obj, clazz,   \
+        methodID, args); }
+
+#define CALL_NONVIRT_TYPE(_jtype, _jname)                                   \
+    CALL_NONVIRT_TYPE_METHOD(_jtype, _jname)                                \
+    CALL_NONVIRT_TYPE_METHODV(_jtype, _jname)                               \
+    CALL_NONVIRT_TYPE_METHODA(_jtype, _jname)
+
+    CALL_NONVIRT_TYPE(jobject, Object)
+    CALL_NONVIRT_TYPE(jboolean, Boolean)
+    CALL_NONVIRT_TYPE(jbyte, Byte)
+    CALL_NONVIRT_TYPE(jchar, Char)
+    CALL_NONVIRT_TYPE(jshort, Short)
+    CALL_NONVIRT_TYPE(jint, Int)
+    CALL_NONVIRT_TYPE(jlong, Long)
+    CALL_NONVIRT_TYPE(jfloat, Float)
+    CALL_NONVIRT_TYPE(jdouble, Double)
+
+    void CallNonvirtualVoidMethod(jobject obj, jclass clazz,
+        jmethodID methodID, ...)
+    {
+        va_list args;
+        va_start(args, methodID);
+        functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args);
+        va_end(args);
+    }
+    void CallNonvirtualVoidMethodV(jobject obj, jclass clazz,
+        jmethodID methodID, va_list args)
+    { functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); }
+    void CallNonvirtualVoidMethodA(jobject obj, jclass clazz,
+        jmethodID methodID, jvalue* args)
+    { functions->CallNonvirtualVoidMethodA(this, obj, clazz, methodID, args); }
+
+    jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
+    { return functions->GetFieldID(this, clazz, name, sig); }
+
+    jobject GetObjectField(jobject obj, jfieldID fieldID)
+    { return functions->GetObjectField(this, obj, fieldID); }
+    jboolean GetBooleanField(jobject obj, jfieldID fieldID)
+    { return functions->GetBooleanField(this, obj, fieldID); }
+    jbyte GetByteField(jobject obj, jfieldID fieldID)
+    { return functions->GetByteField(this, obj, fieldID); }
+    jchar GetCharField(jobject obj, jfieldID fieldID)
+    { return functions->GetCharField(this, obj, fieldID); }
+    jshort GetShortField(jobject obj, jfieldID fieldID)
+    { return functions->GetShortField(this, obj, fieldID); }
+    jint GetIntField(jobject obj, jfieldID fieldID)
+    { return functions->GetIntField(this, obj, fieldID); }
+    jlong GetLongField(jobject obj, jfieldID fieldID)
+    { return functions->GetLongField(this, obj, fieldID); }
+    jfloat GetFloatField(jobject obj, jfieldID fieldID)
+    { return functions->GetFloatField(this, obj, fieldID); }
+    jdouble GetDoubleField(jobject obj, jfieldID fieldID)
+    { return functions->GetDoubleField(this, obj, fieldID); }
+
+    void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
+    { functions->SetObjectField(this, obj, fieldID, value); }
+    void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
+    { functions->SetBooleanField(this, obj, fieldID, value); }
+    void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
+    { functions->SetByteField(this, obj, fieldID, value); }
+    void SetCharField(jobject obj, jfieldID fieldID, jchar value)
+    { functions->SetCharField(this, obj, fieldID, value); }
+    void SetShortField(jobject obj, jfieldID fieldID, jshort value)
+    { functions->SetShortField(this, obj, fieldID, value); }
+    void SetIntField(jobject obj, jfieldID fieldID, jint value)
+    { functions->SetIntField(this, obj, fieldID, value); }
+    void SetLongField(jobject obj, jfieldID fieldID, jlong value)
+    { functions->SetLongField(this, obj, fieldID, value); }
+    void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
+    { functions->SetFloatField(this, obj, fieldID, value); }
+    void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
+    { functions->SetDoubleField(this, obj, fieldID, value); }
+
+    jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
+    { return functions->GetStaticMethodID(this, clazz, name, sig); }
+
+#define CALL_STATIC_TYPE_METHOD(_jtype, _jname)                             \
+    _jtype CallStatic##_jname##Method(jclass clazz, jmethodID methodID,     \
+        ...)                                                                \
+    {                                                                       \
+        _jtype result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        result = functions->CallStatic##_jname##MethodV(this, clazz,        \
+                    methodID, args);                                        \
+        va_end(args);                                                       \
+        return result;                                                      \
+    }
+#define CALL_STATIC_TYPE_METHODV(_jtype, _jname)                            \
+    _jtype CallStatic##_jname##MethodV(jclass clazz, jmethodID methodID,    \
+        va_list args)                                                       \
+    { return functions->CallStatic##_jname##MethodV(this, clazz, methodID,  \
+        args); }
+#define CALL_STATIC_TYPE_METHODA(_jtype, _jname)                            \
+    _jtype CallStatic##_jname##MethodA(jclass clazz, jmethodID methodID,    \
+        jvalue* args)                                                       \
+    { return functions->CallStatic##_jname##MethodA(this, clazz, methodID,  \
+        args); }
+
+#define CALL_STATIC_TYPE(_jtype, _jname)                                    \
+    CALL_STATIC_TYPE_METHOD(_jtype, _jname)                                 \
+    CALL_STATIC_TYPE_METHODV(_jtype, _jname)                                \
+    CALL_STATIC_TYPE_METHODA(_jtype, _jname)
+
+    CALL_STATIC_TYPE(jobject, Object)
+    CALL_STATIC_TYPE(jboolean, Boolean)
+    CALL_STATIC_TYPE(jbyte, Byte)
+    CALL_STATIC_TYPE(jchar, Char)
+    CALL_STATIC_TYPE(jshort, Short)
+    CALL_STATIC_TYPE(jint, Int)
+    CALL_STATIC_TYPE(jlong, Long)
+    CALL_STATIC_TYPE(jfloat, Float)
+    CALL_STATIC_TYPE(jdouble, Double)
+
+    void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
+    {
+        va_list args;
+        va_start(args, methodID);
+        functions->CallStaticVoidMethodV(this, clazz, methodID, args);
+        va_end(args);
+    }
+    void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args)
+    { functions->CallStaticVoidMethodV(this, clazz, methodID, args); }
+    void CallStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue* args)
+    { functions->CallStaticVoidMethodA(this, clazz, methodID, args); }
+
+    jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
+    { return functions->GetStaticFieldID(this, clazz, name, sig); }
+
+    jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticObjectField(this, clazz, fieldID); }
+    jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticBooleanField(this, clazz, fieldID); }
+    jbyte GetStaticByteField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticByteField(this, clazz, fieldID); }
+    jchar GetStaticCharField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticCharField(this, clazz, fieldID); }
+    jshort GetStaticShortField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticShortField(this, clazz, fieldID); }
+    jint GetStaticIntField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticIntField(this, clazz, fieldID); }
+    jlong GetStaticLongField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticLongField(this, clazz, fieldID); }
+    jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticFloatField(this, clazz, fieldID); }
+    jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticDoubleField(this, clazz, fieldID); }
+
+    void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
+    { functions->SetStaticObjectField(this, clazz, fieldID, value); }
+    void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)
+    { functions->SetStaticBooleanField(this, clazz, fieldID, value); }
+    void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value)
+    { functions->SetStaticByteField(this, clazz, fieldID, value); }
+    void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value)
+    { functions->SetStaticCharField(this, clazz, fieldID, value); }
+    void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value)
+    { functions->SetStaticShortField(this, clazz, fieldID, value); }
+    void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
+    { functions->SetStaticIntField(this, clazz, fieldID, value); }
+    void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value)
+    { functions->SetStaticLongField(this, clazz, fieldID, value); }
+    void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value)
+    { functions->SetStaticFloatField(this, clazz, fieldID, value); }
+    void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
+    { functions->SetStaticDoubleField(this, clazz, fieldID, value); }
+
+    jstring NewString(const jchar* unicodeChars, jsize len)
+    { return functions->NewString(this, unicodeChars, len); }
+
+    jsize GetStringLength(jstring string)
+    { return functions->GetStringLength(this, string); }
+
+    const jchar* GetStringChars(jstring string, jboolean* isCopy)
+    { return functions->GetStringChars(this, string, isCopy); }
+
+    void ReleaseStringChars(jstring string, const jchar* chars)
+    { functions->ReleaseStringChars(this, string, chars); }
+
+    jstring NewStringUTF(const char* bytes)
+    { return functions->NewStringUTF(this, bytes); }
+
+    jsize GetStringUTFLength(jstring string)
+    { return functions->GetStringUTFLength(this, string); }
+
+    const char* GetStringUTFChars(jstring string, jboolean* isCopy)
+    { return functions->GetStringUTFChars(this, string, isCopy); }
+
+    void ReleaseStringUTFChars(jstring string, const char* utf)
+    { functions->ReleaseStringUTFChars(this, string, utf); }
+
+    jsize GetArrayLength(jarray array)
+    { return functions->GetArrayLength(this, array); }
+
+    jobjectArray NewObjectArray(jsize length, jclass elementClass,
+        jobject initialElement)
+    { return functions->NewObjectArray(this, length, elementClass,
+        initialElement); }
+
+    jobject GetObjectArrayElement(jobjectArray array, jsize index)
+    { return functions->GetObjectArrayElement(this, array, index); }
+
+    void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
+    { functions->SetObjectArrayElement(this, array, index, value); }
+
+    jbooleanArray NewBooleanArray(jsize length)
+    { return functions->NewBooleanArray(this, length); }
+    jbyteArray NewByteArray(jsize length)
+    { return functions->NewByteArray(this, length); }
+    jcharArray NewCharArray(jsize length)
+    { return functions->NewCharArray(this, length); }
+    jshortArray NewShortArray(jsize length)
+    { return functions->NewShortArray(this, length); }
+    jintArray NewIntArray(jsize length)
+    { return functions->NewIntArray(this, length); }
+    jlongArray NewLongArray(jsize length)
+    { return functions->NewLongArray(this, length); }
+    jfloatArray NewFloatArray(jsize length)
+    { return functions->NewFloatArray(this, length); }
+    jdoubleArray NewDoubleArray(jsize length)
+    { return functions->NewDoubleArray(this, length); }
+
+    jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy)
+    { return functions->GetBooleanArrayElements(this, array, isCopy); }
+    jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy)
+    { return functions->GetByteArrayElements(this, array, isCopy); }
+    jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy)
+    { return functions->GetCharArrayElements(this, array, isCopy); }
+    jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy)
+    { return functions->GetShortArrayElements(this, array, isCopy); }
+    jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
+    { return functions->GetIntArrayElements(this, array, isCopy); }
+    jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy)
+    { return functions->GetLongArrayElements(this, array, isCopy); }
+    jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy)
+    { return functions->GetFloatArrayElements(this, array, isCopy); }
+    jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy)
+    { return functions->GetDoubleArrayElements(this, array, isCopy); }
+
+    void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems,
+        jint mode)
+    { functions->ReleaseBooleanArrayElements(this, array, elems, mode); }
+    void ReleaseByteArrayElements(jbyteArray array, jbyte* elems,
+        jint mode)
+    { functions->ReleaseByteArrayElements(this, array, elems, mode); }
+    void ReleaseCharArrayElements(jcharArray array, jchar* elems,
+        jint mode)
+    { functions->ReleaseCharArrayElements(this, array, elems, mode); }
+    void ReleaseShortArrayElements(jshortArray array, jshort* elems,
+        jint mode)
+    { functions->ReleaseShortArrayElements(this, array, elems, mode); }
+    void ReleaseIntArrayElements(jintArray array, jint* elems,
+        jint mode)
+    { functions->ReleaseIntArrayElements(this, array, elems, mode); }
+    void ReleaseLongArrayElements(jlongArray array, jlong* elems,
+        jint mode)
+    { functions->ReleaseLongArrayElements(this, array, elems, mode); }
+    void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems,
+        jint mode)
+    { functions->ReleaseFloatArrayElements(this, array, elems, mode); }
+    void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems,
+        jint mode)
+    { functions->ReleaseDoubleArrayElements(this, array, elems, mode); }
+
+    void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+        jboolean* buf)
+    { functions->GetBooleanArrayRegion(this, array, start, len, buf); }
+    void GetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+        jbyte* buf)
+    { functions->GetByteArrayRegion(this, array, start, len, buf); }
+    void GetCharArrayRegion(jcharArray array, jsize start, jsize len,
+        jchar* buf)
+    { functions->GetCharArrayRegion(this, array, start, len, buf); }
+    void GetShortArrayRegion(jshortArray array, jsize start, jsize len,
+        jshort* buf)
+    { functions->GetShortArrayRegion(this, array, start, len, buf); }
+    void GetIntArrayRegion(jintArray array, jsize start, jsize len,
+        jint* buf)
+    { functions->GetIntArrayRegion(this, array, start, len, buf); }
+    void GetLongArrayRegion(jlongArray array, jsize start, jsize len,
+        jlong* buf)
+    { functions->GetLongArrayRegion(this, array, start, len, buf); }
+    void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+        jfloat* buf)
+    { functions->GetFloatArrayRegion(this, array, start, len, buf); }
+    void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+        jdouble* buf)
+    { functions->GetDoubleArrayRegion(this, array, start, len, buf); }
+
+    void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+        const jboolean* buf)
+    { functions->SetBooleanArrayRegion(this, array, start, len, buf); }
+    void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+        const jbyte* buf)
+    { functions->SetByteArrayRegion(this, array, start, len, buf); }
+    void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
+        const jchar* buf)
+    { functions->SetCharArrayRegion(this, array, start, len, buf); }
+    void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
+        const jshort* buf)
+    { functions->SetShortArrayRegion(this, array, start, len, buf); }
+    void SetIntArrayRegion(jintArray array, jsize start, jsize len,
+        const jint* buf)
+    { functions->SetIntArrayRegion(this, array, start, len, buf); }
+    void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
+        const jlong* buf)
+    { functions->SetLongArrayRegion(this, array, start, len, buf); }
+    void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+        const jfloat* buf)
+    { functions->SetFloatArrayRegion(this, array, start, len, buf); }
+    void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+        const jdouble* buf)
+    { functions->SetDoubleArrayRegion(this, array, start, len, buf); }
+
+    jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
+        jint nMethods)
+    { return functions->RegisterNatives(this, clazz, methods, nMethods); }
+
+    jint UnregisterNatives(jclass clazz)
+    { return functions->UnregisterNatives(this, clazz); }
+
+    jint MonitorEnter(jobject obj)
+    { return functions->MonitorEnter(this, obj); }
+
+    jint MonitorExit(jobject obj)
+    { return functions->MonitorExit(this, obj); }
+
+    jint GetJavaVM(JavaVM** vm)
+    { return functions->GetJavaVM(this, vm); }
+
+    void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)
+    { functions->GetStringRegion(this, str, start, len, buf); }
+
+    void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf)
+    { return functions->GetStringUTFRegion(this, str, start, len, buf); }
+
+    void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy)
+    { return functions->GetPrimitiveArrayCritical(this, array, isCopy); }
+
+    void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode)
+    { functions->ReleasePrimitiveArrayCritical(this, array, carray, mode); }
+
+    const jchar* GetStringCritical(jstring string, jboolean* isCopy)
+    { return functions->GetStringCritical(this, string, isCopy); }
+
+    void ReleaseStringCritical(jstring string, const jchar* carray)
+    { functions->ReleaseStringCritical(this, string, carray); }
+
+    jweak NewWeakGlobalRef(jobject obj)
+    { return functions->NewWeakGlobalRef(this, obj); }
+
+    void DeleteWeakGlobalRef(jweak obj)
+    { functions->DeleteWeakGlobalRef(this, obj); }
+
+    jboolean ExceptionCheck()
+    { return functions->ExceptionCheck(this); }
+
+    jobject NewDirectByteBuffer(void* address, jlong capacity)
+    { return functions->NewDirectByteBuffer(this, address, capacity); }
+
+    void* GetDirectBufferAddress(jobject buf)
+    { return functions->GetDirectBufferAddress(this, buf); }
+
+    jlong GetDirectBufferCapacity(jobject buf)
+    { return functions->GetDirectBufferCapacity(this, buf); }
+
+    /* added in JNI 1.6 */
+    jobjectRefType GetObjectRefType(jobject obj)
+    { return functions->GetObjectRefType(this, obj); }
+#endif /*__cplusplus*/
+};
+
+
+/*
+ * JNI invocation interface.
+ */
+struct JNIInvokeInterface {
+    void*       reserved0;
+    void*       reserved1;
+    void*       reserved2;
+
+    jint        (*DestroyJavaVM)(JavaVM*);
+    jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
+    jint        (*DetachCurrentThread)(JavaVM*);
+    jint        (*GetEnv)(JavaVM*, void**, jint);
+    jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
+};
+
+/*
+ * C++ version.
+ */
+struct _JavaVM {
+    const struct JNIInvokeInterface* functions;
+
+#if defined(__cplusplus)
+    jint DestroyJavaVM()
+    { return functions->DestroyJavaVM(this); }
+    jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
+    { return functions->AttachCurrentThread(this, p_env, thr_args); }
+    jint DetachCurrentThread()
+    { return functions->DetachCurrentThread(this); }
+    jint GetEnv(void** env, jint version)
+    { return functions->GetEnv(this, env, version); }
+    jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
+    { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
+#endif /*__cplusplus*/
+};
+
+struct JavaVMAttachArgs {
+    jint        version;    /* must be >= JNI_VERSION_1_2 */
+    const char* name;       /* NULL or name of thread as modified UTF-8 str */
+    jobject     group;      /* global ref of a ThreadGroup object, or NULL */
+};
+typedef struct JavaVMAttachArgs JavaVMAttachArgs;
+
+/*
+ * JNI 1.2+ initialization.  (As of 1.6, the pre-1.2 structures are no
+ * longer supported.)
+ */
+typedef struct JavaVMOption {
+    const char* optionString;
+    void*       extraInfo;
+} JavaVMOption;
+
+typedef struct JavaVMInitArgs {
+    jint        version;    /* use JNI_VERSION_1_2 or later */
+
+    jint        nOptions;
+    JavaVMOption* options;
+    jboolean    ignoreUnrecognized;
+} JavaVMInitArgs;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * VM initialization functions.
+ *
+ * Note these are the only symbols exported for JNI by the VM.
+ */
+jint JNI_GetDefaultJavaVMInitArgs(void*);
+jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
+jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
+
+/*
+ * Prototypes for functions exported by loadable shared libs.  These are
+ * called by JNI, not provided by JNI.
+ */
+jint JNI_OnLoad(JavaVM* vm, void* reserved);
+void JNI_OnUnload(JavaVM* vm, void* reserved);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*
+ * Manifest constants.
+ */
+#define JNI_FALSE   0
+#define JNI_TRUE    1
+
+#define JNI_VERSION_1_1 0x00010001
+#define JNI_VERSION_1_2 0x00010002
+#define JNI_VERSION_1_4 0x00010004
+#define JNI_VERSION_1_6 0x00010006
+
+#define JNI_OK          (0)         /* no error */
+#define JNI_ERR         (-1)        /* generic error */
+#define JNI_EDETACHED   (-2)        /* thread detached from the VM */
+#define JNI_EVERSION    (-3)        /* JNI version error */
+
+#define JNI_COMMIT      1           /* copy content, do not free buffer */
+#define JNI_ABORT       2           /* free buffer w/o copying back */
+
+/* need these for Windows-aware headers */
+#define JNIIMPORT
+#define JNIEXPORT
+#define JNICALL
+
+#endif /*_JNI_H*/
diff --git a/tests/001-nop/build b/tests/001-nop/build
new file mode 100644
index 0000000..5233a2d
--- /dev/null
+++ b/tests/001-nop/build
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+# Nothing to do here.
diff --git a/tests/001-nop/expected.txt b/tests/001-nop/expected.txt
new file mode 100644
index 0000000..80a233e
--- /dev/null
+++ b/tests/001-nop/expected.txt
@@ -0,0 +1 @@
+Blort.
diff --git a/tests/001-nop/info.txt b/tests/001-nop/info.txt
new file mode 100644
index 0000000..9942f10
--- /dev/null
+++ b/tests/001-nop/info.txt
@@ -0,0 +1,2 @@
+This is a sample no-op test, which does at least serve to verify that the
+test harness is working.
diff --git a/tests/001-nop/run b/tests/001-nop/run
new file mode 100644
index 0000000..210296b
--- /dev/null
+++ b/tests/001-nop/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "Blort."
diff --git a/tests/002-sleep/expected.txt b/tests/002-sleep/expected.txt
new file mode 100644
index 0000000..f994ce5
--- /dev/null
+++ b/tests/002-sleep/expected.txt
@@ -0,0 +1,2 @@
+Sleeping 1000 msec...
+Done sleeping
diff --git a/tests/002-sleep/info.txt b/tests/002-sleep/info.txt
new file mode 100644
index 0000000..9a0afe9
--- /dev/null
+++ b/tests/002-sleep/info.txt
@@ -0,0 +1,3 @@
+Test that Thread.sleep() operates reasonably. This test is actually
+mostly meant as an easy thing to modify in order to test other things
+in an ad-hoc way.
diff --git a/tests/002-sleep/src/Main.java b/tests/002-sleep/src/Main.java
new file mode 100644
index 0000000..c1a2d83
--- /dev/null
+++ b/tests/002-sleep/src/Main.java
@@ -0,0 +1,22 @@
+public class Main {
+    static public void main(String[] args) throws Exception {
+        int millis = 1000;
+
+        if (args.length != 0) {
+            millis = Integer.parseInt(args[0]);
+        }
+
+        System.out.println("Sleeping " + millis + " msec...");
+
+        long start = System.currentTimeMillis();
+        Thread.sleep(millis);
+        long elapsed = System.currentTimeMillis() - start;
+        long offBy = Math.abs(elapsed - millis);
+
+        System.out.println("Done sleeping");
+
+        if (offBy > 250) {
+            System.out.println("Actually slept about " + elapsed + " msec...");
+        }
+    }
+}
diff --git a/tests/003-omnibus-opcodes/build b/tests/003-omnibus-opcodes/build
new file mode 100644
index 0000000..9eb5ed3
--- /dev/null
+++ b/tests/003-omnibus-opcodes/build
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+rm classes/UnresClass.class
+${JAVAC} -d classes `find src2 -name '*.java'`
+
+dx -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
+zip test.jar classes.dex
diff --git a/tests/003-omnibus-opcodes/expected.txt b/tests/003-omnibus-opcodes/expected.txt
new file mode 100644
index 0000000..4895dc3
--- /dev/null
+++ b/tests/003-omnibus-opcodes/expected.txt
@@ -0,0 +1,74 @@
+(assertions are enabled)
+InstField assign...
+InstField check...
+InstField.nullCheck
+StaticField assign...
+StaticField check...
+IntMath.shiftTest1
+IntMath.shiftTest2
+IntMath.unsignedShiftTest
+IntMath.convTest
+IntMath.charSubTest
+IntMath.intOperTest
+IntMath.intOperCheck
+IntMath.longOperTest
+IntMath.longOperCheck
+IntMath.lit16Test
+IntMath.lit8Test
+IntMath.intShiftTest
+IntMath.intShiftCheck
+IntMath.longShiftTest
+IntMath.longShiftCheck
+IntMath.truncateTest
+IntMath.divideByZero
+IntMath.bigDivideOverflow
+IntMath.checkConsts
+IntMath.jlmTests
+FloatMath.convTest
+FloatMath.floatOperTest
+FloatMath.doubleOperTest
+FloatMath.checkConvI
+FloatMath.checkConvL
+FloatMath.checkConvF
+ 0: -2.0054409E9
+ 1: -8.613303E18
+ 2: -3.1415927
+-2.0054409E9, -8.6133031E18, -3.1415927
+FloatMath.checkConvD
+ 0: -2.005440939E9
+ 1: -8.613303245920329E18
+ 2: 123.45600128173828
+-2.005440939E9, -8.6133032459203287E18, 123.4560012817382
+FloatMath.checkConsts
+FloatMath.jlmTests
+IntMath.testIntCompare
+IntMath.testLongCompare
+IntMath.testFloatCompare
+IntMath.testDoubleCompare
+Monitor.run
+Switch.testSwitch
+Array check...
+Array.checkRange32
+Array.checkRange64
+Array.checkNegAlloc
+Classes.checkCast
+Classes.arrayInstance
+Goto.smallGoto
+Goto.smallGoto
+Goto.mediumGoto
+Goto.mediumGoto
+Goto.bigGoto
+Goto.bigGoto
+  MethodCallBase ctor
+  MethodCall ctor
+MethodCalls.manyArgs
+Throw.one
+Throw.twoA
+Throw.twoN
+Throw.rethrow
+UnresTest1...
+UnresTest1...
+UnresTest2...
+UnresTest2 done
+InternedString.run
+Done!
diff --git a/tests/003-omnibus-opcodes/info.txt b/tests/003-omnibus-opcodes/info.txt
new file mode 100644
index 0000000..6c0fbda
--- /dev/null
+++ b/tests/003-omnibus-opcodes/info.txt
@@ -0,0 +1 @@
+This is a smoke test of many Dalvik opcodes.
diff --git a/tests/003-omnibus-opcodes/src/Array.java b/tests/003-omnibus-opcodes/src/Array.java
new file mode 100644
index 0000000..f385dd8
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Array.java
@@ -0,0 +1,224 @@
+// Copyright 2008 The Android Open Source Project
+
+
+/**
+ * Exercise arrays.
+ */
+public class Array {
+
+    /*
+     * Verify array contents.
+     */
+    static void checkBytes(byte[] bytes) {
+        assert(bytes[0] == 0);
+        assert(bytes[1] == -1);
+        assert(bytes[2] == -2);
+        assert(bytes[3] == -3);
+        assert(bytes[4] == -4);
+    }
+    static void checkShorts(short[] shorts) {
+        assert(shorts[0] == 20);
+        assert(shorts[1] == 10);
+        assert(shorts[2] == 0);
+        assert(shorts[3] == -10);
+        assert(shorts[4] == -20);
+    }
+    static void checkChars(char[] chars) {
+        assert(chars[0] == 40000);
+        assert(chars[1] == 40001);
+        assert(chars[2] == 40002);
+        assert(chars[3] == 40003);
+        assert(chars[4] == 40004);
+    }
+    static void checkInts(int[] ints) {
+        assert(ints[0] == 70000);
+        assert(ints[1] == 70001);
+        assert(ints[2] == 70002);
+        assert(ints[3] == 70003);
+        assert(ints[4] == 70004);
+    }
+    static void checkBooleans(boolean[] booleans) {
+        assert(booleans[0]);
+        assert(booleans[1]);
+        assert(!booleans[2]);
+        assert(booleans[3]);
+        assert(!booleans[4]);
+    }
+    static void checkFloats(float[] floats) {
+        assert(floats[0] == -1.5);
+        assert(floats[1] == -0.5);
+        assert(floats[2] == 0.0);
+        assert(floats[3] == 0.5);
+        assert(floats[4] == 1.5);
+    }
+    static void checkLongs(long[] longs) {
+        assert(longs[0] == 0x1122334455667788L);
+        assert(longs[1] == 0x8877665544332211L);
+        assert(longs[2] == 0L);
+        assert(longs[3] == 1L);
+        assert(longs[4] == -1L);
+    }
+    static void checkStrings(String[] strings) {
+        assert(strings[0].equals("zero"));
+        assert(strings[1].equals("one"));
+        assert(strings[2].equals("two"));
+        assert(strings[3].equals("three"));
+        assert(strings[4].equals("four"));
+    }
+
+    /*
+     * Try bad range values, 32 bit get/put.
+     */
+    static void checkRange32(int[] ints, int[] empty, int negVal1, int negVal2){
+        System.out.println("Array.checkRange32");
+        int i = 0;
+
+        assert(ints.length == 5);
+
+        try {
+            i = ints[5];            // exact bound
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            ints[5] = i;            // exact bound
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            i = ints[6];            // one past
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            i = ints[negVal1];      // -1
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            ints[negVal1] = i;      // -1
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            i = ints[negVal2];      // min int
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+
+
+        try {
+            i = empty[1];
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+    }
+
+    /*
+     * Try bad range values, 64 bit get/put.
+     */
+    static void checkRange64(long[] longs, int negVal1, int negVal2) {
+        System.out.println("Array.checkRange64");
+        long l = 0L;
+
+        assert(longs.length == 5);
+
+        try {
+            l = longs[5];            // exact bound
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            longs[5] = l;            // exact bound
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            l = longs[6];            // one past
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            l = longs[negVal1];      // -1
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            longs[negVal1] = l;      // -1
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+        try {
+            l = longs[negVal2];      // min int
+            assert(false);
+        } catch (ArrayIndexOutOfBoundsException aioobe) {
+            // good
+        }
+    }
+
+    /*
+     * Test negative allocations of object and primitive arrays.
+     */
+    static void checkNegAlloc(int count) {
+        System.out.println("Array.checkNegAlloc");
+        String[] strings;
+        int[] ints;
+
+        try {
+            ints = new int[count];
+            assert(false);
+        } catch (NegativeArraySizeException nase) {
+            // good
+        }
+
+        try {
+            strings = new String[count];
+            assert(false);
+        } catch (NegativeArraySizeException nase) {
+            // good
+        }
+    }
+
+    public static void run() {
+        System.out.println("Array check...");
+
+        byte[] xBytes = new byte[] { 0, -1, -2, -3, -4 };
+        short[] xShorts = new short[] { 20, 10, 0, -10, -20 };
+        char[] xChars = new char[] { 40000, 40001, 40002, 40003, 40004 };
+        int[] xInts = new int[] { 70000, 70001, 70002, 70003, 70004 };
+        boolean[] xBooleans = new boolean[] { true, true, false, true, false };
+        float[] xFloats = new float[] { -1.5f, -0.5f, 0.0f, 0.5f, 1.5f };
+        long[] xLongs = new long[] {
+            0x1122334455667788L, 0x8877665544332211L, 0L, 1L, -1l };
+        String[] xStrings = new String[] {
+            "zero", "one", "two", "three", "four" };
+
+        int[] xEmpty = new int[0];
+
+        checkBytes(xBytes);
+        checkShorts(xShorts);
+        checkChars(xChars);
+        checkInts(xInts);
+        checkBooleans(xBooleans);
+        checkFloats(xFloats);
+        checkLongs(xLongs);
+        checkStrings(xStrings);
+
+        checkRange32(xInts, xEmpty, -1, (int) 0x80000000);
+        checkRange64(xLongs, -1, (int) 0x80000000);
+
+        checkNegAlloc(-1);
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/Classes.java b/tests/003-omnibus-opcodes/src/Classes.java
new file mode 100644
index 0000000..c89ff3e
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Classes.java
@@ -0,0 +1,219 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Exercise some class-related instructions.
+ */
+public class Classes {
+    int mSome;
+
+    public void subFunc(boolean wantSub) {
+        assert(!wantSub);
+    }
+
+    void checkCast(Object thisRef, Object moreRef, Object nullRef) {
+        System.out.println("Classes.checkCast");
+
+        Classes classes;
+        MoreClasses more;
+
+        classes = (Classes) thisRef;
+        assert(thisRef instanceof Classes);
+        classes = (Classes) moreRef;
+        assert(moreRef instanceof Classes);
+
+        more = (MoreClasses) moreRef;
+        assert(moreRef instanceof MoreClasses);
+        assert(!(thisRef instanceof MoreClasses));
+
+        try {
+            more = (MoreClasses) thisRef;
+            assert(false);
+        } catch (ClassCastException cce) {
+            //System.out.println("  class cast msg: " + cce.getMessage());
+            //Dalvik throws terser message than Hotspot VM
+            assert(cce.getMessage().regionMatches(false, 0, "Classes", 0, 7));
+        }
+        assert(!(thisRef instanceof MoreClasses));
+
+        /* hopefully these classes cause a resolve */
+        try {
+            java.math.RoundingMode mode = (java.math.RoundingMode) thisRef;
+            assert(false);
+        } catch (ClassCastException cce) {
+            //System.out.println("  class cast msg: " + cce.getMessage());
+            //Dalvik throws terser message than Hotspot VM
+            assert(cce.getMessage().regionMatches(false, 0, "Classes", 0, 7));
+        }
+        assert(!(thisRef instanceof java.math.BigDecimal));
+
+        /* try some stuff with a null reference */
+        classes = (Classes) nullRef;
+        classes = (MoreClasses) nullRef;
+        more = (MoreClasses) nullRef;
+        assert(!(nullRef instanceof Classes));
+
+    }
+
+
+    static void xTests(Object x) {
+        assert(  x instanceof Classes);
+        assert(!(x instanceof MoreClasses));
+    }
+    static void yTests(Object y) {
+        assert(  y instanceof Classes);
+        assert(  y instanceof MoreClasses);
+    }
+    static void xarTests(Object xar) {
+        assert(  xar instanceof Object);
+        assert(!(xar instanceof Classes));
+        assert(  xar instanceof Classes[]);
+        assert(!(xar instanceof MoreClasses[]));
+        assert(  xar instanceof Object[]);
+        assert(!(xar instanceof Object[][]));
+    }
+    static void yarTests(Object yar) {
+        assert(  yar instanceof Classes[]);
+        assert(  yar instanceof MoreClasses[]);
+    }
+    static void xarararTests(Object xararar) {
+        assert(  xararar instanceof Object);
+        assert(  xararar instanceof Object[]);
+        assert(!(xararar instanceof Classes));
+        assert(!(xararar instanceof Classes[]));
+        assert(!(xararar instanceof Classes[][]));
+        assert(  xararar instanceof Classes[][][]);
+        assert(!(xararar instanceof MoreClasses[][][]));
+        assert(  xararar instanceof Object[][][]);
+        assert(  xararar instanceof Serializable);
+        assert(  xararar instanceof Serializable[]);
+        assert(  xararar instanceof Serializable[][]);
+        assert(!(xararar instanceof Serializable[][][]));
+    }
+    static void yarararTests(Object yararar) {
+        assert(  yararar instanceof Classes[][][]);
+        assert(  yararar instanceof MoreClasses[][][]);
+    }
+    static void iarTests(Object iar) {
+        assert(  iar instanceof Object);
+        assert(!(iar instanceof Object[]));
+    }
+    static void iararTests(Object iarar) {
+        assert(  iarar instanceof Object);
+        assert(  iarar instanceof Object[]);
+        assert(!(iarar instanceof Object[][]));
+    }
+
+    /*
+     * Exercise filled-new-array and test instanceof on arrays.
+     *
+     * We call out instead of using "instanceof" directly to avoid
+     * compiler optimizations.
+     */
+    static void arrayInstance() {
+        System.out.println("Classes.arrayInstance");
+
+        Classes x = new Classes();
+        Classes[] xar = new Classes[1];
+        Classes[][] xarar = new Classes[1][1];
+        Classes[][][] xararar = new Classes[1][2][3];
+        MoreClasses y = new MoreClasses();
+        MoreClasses[] yar = new MoreClasses[3];
+        MoreClasses[][] yarar = new MoreClasses[2][3];
+        MoreClasses[][][] yararar = new MoreClasses[1][2][3];
+        int[] iar = new int[1];
+        int[][] iarar = new int[1][1];
+        Object test;
+
+        xTests(x);
+        yTests(y);
+        xarTests(xar);
+        yarTests(yar);
+        xarararTests(xararar);
+        yarararTests(yararar);
+        iarTests(iar);
+        iararTests(iarar);
+
+        yararar[0] = yarar;
+        yararar[0][0] = yar;
+        yararar[0][1] = yar;
+        yararar[0][0][0] = y;
+        yararar[0][0][1] = y;
+        yararar[0][0][2] = y;
+        yararar[0][1][0] = y;
+        yararar[0][1][1] = y;
+        yararar[0][1][2] = y;
+
+        String strForm;
+
+        String[][][][] multi1 = new String[2][3][2][1];
+        multi1[0] = new String[2][3][2];
+        multi1[0][1] = new String[3][2];
+        multi1[0][1][2] = new String[2];
+        multi1[0][1][2][1] = "HELLO-1";
+        strForm = Arrays.deepToString(multi1);
+
+        String[][][][][] multi2 = new String[5][2][3][2][1];
+        multi2[0] = new String[5][2][3][2];
+        multi2[0][1] = new String[5][2][3];
+        multi2[0][1][2] = new String[5][2];
+        multi2[0][1][2][1] = new String[5];
+        multi2[0][1][2][1][4] = "HELLO-2";
+        strForm = Arrays.deepToString(multi2);
+
+
+        String[][][][][][] multi3 = new String[2][5][2][3][2][1];
+        multi3[0] = new String[2][][][][];
+        multi3[0][1] = new String[3][][][];
+        multi3[0][1][2] = new String[2][][];
+        multi3[0][1][2][1] = new String[5][];
+        multi3[0][1][2][1][4] = new String[2];
+        multi3[0][1][2][1][4][1] = "HELLO-3";
+        strForm = Arrays.deepToString(multi3);
+
+        // build up pieces
+        String[][][][][][] multi4 = new String[1][][][][][];
+        multi4[0] = new String[2][][][][];
+        multi4[0][1] = new String[3][][][];
+        multi4[0][1][2] = new String[2][][];
+        multi4[0][1][2][1] = new String[5][];
+        multi4[0][1][2][1][4] = new String[2];
+        multi4[0][1][2][1][4][1] = "HELLO-4";
+        strForm = Arrays.deepToString(multi4);
+
+        /* this is expected to fail; 1073921584 * 4 overflows 32 bits */
+        try {
+            String[][][][][] multiX = new String[5][2][3][2][1073921584];
+            assert(false);
+        } catch (Error e) {
+            //System.out.println("  Got expected failure: " + e);
+        }
+
+    }
+
+    public static void run() {
+        Classes classes = new Classes();
+        MoreClasses more = new MoreClasses();
+        classes.checkCast(classes, more, null);
+
+        more.subFunc(true);
+        more.superFunc(false);
+        arrayInstance();
+    }
+}
+
+class MoreClasses extends Classes {
+    int mMore;
+
+    public MoreClasses() {}
+
+    public void subFunc(boolean wantSub) {
+        assert(wantSub);
+    }
+
+    public void superFunc(boolean wantSub) {
+        super.subFunc(wantSub);
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/Compare.java b/tests/003-omnibus-opcodes/src/Compare.java
new file mode 100644
index 0000000..43a708a
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Compare.java
@@ -0,0 +1,171 @@
+// Copyright 2008 The Android Open Source Project
+
+
+
+/**
+ * Test comparison operators.
+ */
+public class Compare {
+
+    /*
+     * Test the integer comparisons in various ways.
+     */
+    static void testIntCompare(int minus, int plus, int plus2, int zero) {
+        System.out.println("IntMath.testIntCompare");
+
+        if (minus > plus)
+            assert(false);
+        if (minus >= plus)
+            assert(false);
+        if (plus < minus)
+            assert(false);
+        if (plus <= minus)
+            assert(false);
+        if (plus == minus)
+            assert(false);
+        if (plus != plus2)
+            assert(false);
+
+        /* try a branch-taken */
+        if (plus != minus) {
+            assert(true);
+        } else {
+            assert(false);
+        }
+
+        if (minus > 0)
+            assert(false);
+        if (minus >= 0)
+            assert(false);
+        if (plus < 0)
+            assert(false);
+        if (plus <= 0)
+            assert(false);
+        if (plus == 0)
+            assert(false);
+        if (zero != 0)
+            assert(false);
+
+        if (zero == 0) {
+            assert(true);
+        } else {
+            assert(false);
+        }
+    }
+
+    /*
+     * Test cmp-long.
+     *
+     * minus=-5, alsoMinus=0xFFFFFFFF00000009, plus=4, alsoPlus=8
+     */
+    static void testLongCompare(long minus, long alsoMinus, long plus,
+        long alsoPlus) {
+
+        System.out.println("IntMath.testLongCompare");
+        if (minus > plus)
+            assert(false);
+        if (plus < minus)
+            assert(false);
+        if (plus == minus)
+            assert(false);
+
+        if (plus >= plus+1)
+            assert(false);
+        if (minus >= minus+1)
+            assert(false);
+
+        /* try a branch-taken */
+        if (plus != minus) {
+            assert(true);
+        } else {
+            assert(false);
+        }
+
+        /* compare when high words are equal but low words differ */
+        if (plus > alsoPlus)
+            assert(false);
+        if (alsoPlus < plus)
+            assert(false);
+        if (alsoPlus == plus)
+            assert(false);
+
+        /* high words are equal, low words have apparently different signs */
+        if (minus < alsoMinus)      // bug!
+            assert(false);
+        if (alsoMinus > minus)
+            assert(false);
+        if (alsoMinus == minus)
+            assert(false);
+    }
+
+    /*
+     * Test cmpl-float and cmpg-float.
+     */
+    static void testFloatCompare(float minus, float plus, float plus2,
+        float nan) {
+
+        System.out.println("IntMath.testFloatCompare");
+        if (minus > plus)
+            assert(false);
+        if (plus < minus)
+            assert(false);
+        if (plus == minus)
+            assert(false);
+        if (plus != plus2)
+            assert(false);
+
+        if (plus <= nan)
+            assert(false);
+        if (plus >= nan)
+            assert(false);
+        if (minus <= nan)
+            assert(false);
+        if (minus >= nan)
+            assert(false);
+        if (nan >= plus)
+            assert(false);
+        if (nan <= plus)
+            assert(false);
+
+        if (nan == nan)
+            assert(false);
+    }
+
+    static void testDoubleCompare(double minus, double plus, double plus2,
+        double nan) {
+
+        System.out.println("IntMath.testDoubleCompare");
+        if (minus > plus)
+            assert(false);
+        if (plus < minus)
+            assert(false);
+        if (plus == minus)
+            assert(false);
+        if (plus != plus2)
+            assert(false);
+
+        if (plus <= nan)
+            assert(false);
+        if (plus >= nan)
+            assert(false);
+        if (minus <= nan)
+            assert(false);
+        if (minus >= nan)
+            assert(false);
+        if (nan >= plus)
+            assert(false);
+        if (nan <= plus)
+            assert(false);
+
+        if (nan == nan)
+            assert(false);
+    }
+
+    public static void run() {
+        testIntCompare(-5, 4, 4, 0);
+        testLongCompare(-5L, -4294967287L, 4L, 8L);
+
+        testFloatCompare(-5.0f, 4.0f, 4.0f, (1.0f/0.0f) / (1.0f/0.0f));
+        testDoubleCompare(-5.0, 4.0, 4.0, (1.0/0.0) / (1.0/0.0));
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/FloatMath.java b/tests/003-omnibus-opcodes/src/FloatMath.java
new file mode 100644
index 0000000..3c49402
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/FloatMath.java
@@ -0,0 +1,337 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test arithmetic operations.
+ */
+public class FloatMath {
+
+    static void convTest() {
+        System.out.println("FloatMath.convTest");
+
+        float f;
+        double d;
+        int i;
+        long l;
+
+        /* float --> int */
+        f = 1234.5678f;
+        i = (int) f;
+        assert(i == 1234);
+
+        f = -1234.5678f;
+        i = (int) f;
+        assert(i == -1234);
+
+        /* float --> long */
+        f = 1238.5678f;
+        l = (long) f;
+        assert(l == 1238);
+
+        f = -1238.5678f;
+        l = (long) f;
+        assert(l == -1238);
+
+        /* float --> double */
+        f = 1238.5678f;
+        d = (double) f;
+        assert(d > 1238.567 && d < 1238.568);
+
+        /* double --> int */
+        d = 1234.5678;
+        i = (int) d;
+        assert(i == 1234);
+
+        d = -1234.5678;
+        i = (int) d;
+        assert(i == -1234);
+
+        /* double --> long */
+        d = 5678956789.0123;
+        l = (long) d;
+        assert(l == 5678956789L);
+
+        d = -5678956789.0123;
+        l = (long) d;
+        assert(l == -5678956789L);
+
+        /* double --> float */
+        d = 1238.5678;
+        f = (float) d;
+        assert(f > 1238.567 && f < 1238.568);
+
+        /* int --> long */
+        i = 7654;
+        l = (long) i;
+        assert(l == 7654L);
+
+        i = -7654;
+        l = (long) i;
+        assert(l == -7654L);
+
+        /* int --> float */
+        i = 1234;
+        f = (float) i;
+        assert(f > 1233.9f && f < 1234.1f);
+
+        i = -1234;
+        f = (float) i;
+        assert(f < -1233.9f && f > -1234.1f);
+
+        /* int --> double */
+        i = 1238;
+        d = (double) i;
+        assert(d > 1237.9f && d < 1238.1f);
+
+        i = -1238;
+        d = (double) i;
+        assert(d < -1237.9f && d > -1238.1f);
+
+        /* long --> int (with truncation) */
+        l = 5678956789L;
+        i = (int) l;
+        assert(i == 1383989493);
+
+        l = -5678956789L;
+        i = (int) l;
+        assert(i == -1383989493);
+
+        /* long --> float */
+        l = 5678956789L;
+        f = (float) l;
+        assert(f > 5.6789564E9 && f < 5.6789566E9);
+
+        l = -5678956789L;
+        f = (float) l;
+        assert(f < -5.6789564E9 && f > -5.6789566E9);
+
+        /* long --> double */
+        l = 6678956789L;
+        d = (double) l;
+        assert(d > 6.6789567E9 && d < 6.6789568E9);
+
+        l = -6678956789L;
+        d = (double) l;
+        assert(d < -6.6789567E9 && d > -6.6789568E9);
+    }
+
+    /*
+     * We pass in the arguments and return the results so the compiler
+     * doesn't do the math for us.
+     */
+    static float[] floatOperTest(float x, float y) {
+        System.out.println("FloatMath.floatOperTest");
+
+        float[] results = new float[9];
+
+        /* this seems to generate "op-float" instructions */
+        results[0] = x + y;
+        results[1] = x - y;
+        results[2] = x * y;
+        results[3] = x / y;
+        results[4] = x % -y;
+
+        /* this seems to generate "op-float/2addr" instructions */
+        results[8] = x + (((((x + y) - y) * y) / y) % y);
+
+        return results;
+    }
+    static void floatOperCheck(float[] results) {
+        assert(results[0] > 69996.99f && results[0] < 69997.01f);
+        assert(results[1] > 70002.99f && results[1] < 70003.01f);
+        assert(results[2] > -210000.01f && results[2] < -209999.99f);
+        assert(results[3] > -23333.34f && results[3] < -23333.32f);
+        assert(results[4] > 0.999f && results[4] < 1.001f);
+        assert(results[8] > 70000.99f && results[8] < 70001.01f);
+    }
+
+    /*
+     * We pass in the arguments and return the results so the compiler
+     * doesn't do the math for us.
+     */
+    static double[] doubleOperTest(double x, double y) {
+        System.out.println("FloatMath.doubleOperTest");
+
+        double[] results = new double[9];
+
+        /* this seems to generate "op-double" instructions */
+        results[0] = x + y;
+        results[1] = x - y;
+        results[2] = x * y;
+        results[3] = x / y;
+        results[4] = x % -y;
+
+        /* this seems to generate "op-double/2addr" instructions */
+        results[8] = x + (((((x + y) - y) * y) / y) % y);
+
+        return results;
+    }
+    static void doubleOperCheck(double[] results) {
+        assert(results[0] > 69996.99 && results[0] < 69997.01);
+        assert(results[1] > 70002.99 && results[1] < 70003.01);
+        assert(results[2] > -210000.01 && results[2] < -209999.99);
+        assert(results[3] > -23333.34 && results[3] < -23333.32);
+        assert(results[4] > 0.999 && results[4] < 1.001);
+        assert(results[8] > 70000.99 && results[8] < 70001.01);
+    }
+
+    /*
+     * Try to cause some unary operations.
+     */
+    static float unopTest(float f) {
+        f = -f;
+        return f;
+    }
+
+    static int[] convI(long l, float f, double d, float zero) {
+        int[] results = new int[6];
+        results[0] = (int) l;
+        results[1] = (int) f;
+        results[2] = (int) d;
+        results[3] = (int) (1.0f / zero);       // +inf
+        results[4] = (int) (-1.0f / zero);      // -inf
+        results[5] = (int) ((1.0f / zero) / (1.0f / zero)); // NaN
+        return results;
+    }
+    static void checkConvI(int[] results) {
+        System.out.println("FloatMath.checkConvI");
+        assert(results[0] == 0x44332211);
+        assert(results[1] == 123);
+        assert(results[2] == -3);
+        assert(results[3] == 0x7fffffff);
+        assert(results[4] == 0x80000000);
+        assert(results[5] == 0);
+    }
+
+    static long[] convL(int i, float f, double d, double zero) {
+        long[] results = new long[6];
+        results[0] = (long) i;
+        results[1] = (long) f;
+        results[2] = (long) d;
+        results[3] = (long) (1.0 / zero);       // +inf
+        results[4] = (long) (-1.0 / zero);      // -inf
+        results[5] = (long) ((1.0 / zero) / (1.0 / zero));  // NaN
+        return results;
+    }
+    static void checkConvL(long[] results) {
+        System.out.println("FloatMath.checkConvL");
+        assert(results[0] == 0xFFFFFFFF88776655L);
+        assert(results[1] == 123);
+        assert(results[2] == -3);
+        assert(results[3] == 0x7fffffffffffffffL);
+        assert(results[4] == 0x8000000000000000L);
+        assert(results[5] == 0);
+    }
+
+    static float[] convF(int i, long l, double d) {
+        float[] results = new float[3];
+        results[0] = (float) i;
+        results[1] = (float) l;
+        results[2] = (float) d;
+        return results;
+    }
+    static void checkConvF(float[] results) {
+        System.out.println("FloatMath.checkConvF");
+        // TODO: assert values
+        for (int i = 0; i < results.length; i++)
+            System.out.println(" " + i + ": " + results[i]);
+        System.out.println("-2.0054409E9, -8.6133031E18, -3.1415927");
+    }
+
+    static double[] convD(int i, long l, float f) {
+        double[] results = new double[3];
+        results[0] = (double) i;
+        results[1] = (double) l;
+        results[2] = (double) f;
+        return results;
+    }
+    static void checkConvD(double[] results) {
+        System.out.println("FloatMath.checkConvD");
+        // TODO: assert values
+        for (int i = 0; i < results.length; i++)
+            System.out.println(" " + i + ": " + results[i]);
+        System.out.println("-2.005440939E9, -8.6133032459203287E18, 123.4560012817382");
+    }
+
+    static void checkConsts() {
+        System.out.println("FloatMath.checkConsts");
+
+        float f = 10.0f;        // const/special
+        assert(f > 9.9 && f < 10.1);
+
+        double d = 10.0;        // const-wide/special
+        assert(d > 9.9 && d < 10.1);
+    }
+
+    /*
+     * Determine if two floating point numbers are approximately equal.
+     *
+     * (Assumes that floating point is generally working, so we can't use
+     * this for the first set of tests.)
+     */
+    static boolean approxEqual(float a, float b, float maxDelta) {
+        if (a > b)
+            return (a - b) < maxDelta;
+        else
+            return (b - a) < maxDelta;
+    }
+    static boolean approxEqual(double a, double b, double maxDelta) {
+        if (a > b)
+            return (a - b) < maxDelta;
+        else
+            return (b - a) < maxDelta;
+    }
+
+    /*
+     * Test some java.lang.Math functions.
+     *
+     * The method arguments are positive values.
+     */
+    static void jlmTests(float ff, double dd) {
+        System.out.println("FloatMath.jlmTests");
+
+        assert(approxEqual(Math.abs(ff), ff, 0.001f));
+        assert(approxEqual(Math.abs(-ff), ff, 0.001f));
+        assert(approxEqual(Math.min(ff, -5.0f), -5.0f, 0.001f));
+        assert(approxEqual(Math.max(ff, -5.0f), ff, 0.001f));
+
+        assert(approxEqual(Math.abs(dd), dd, 0.001));
+        assert(approxEqual(Math.abs(-dd), dd, 0.001));
+        assert(approxEqual(Math.min(dd, -5.0), -5.0, 0.001));
+        assert(approxEqual(Math.max(dd, -5.0), dd, 0.001));
+
+        double sq = Math.sqrt(dd);
+        assert(approxEqual(sq*sq, dd, 0.001));
+
+        assert(approxEqual(0.5403023058681398, Math.cos(1.0), 0.00000001));
+        assert(approxEqual(0.8414709848078965, Math.sin(1.0), 0.00000001));
+    }
+
+    public static void run() {
+        convTest();
+
+        float[] floatResults;
+        double[] doubleResults;
+        int[] intResults;
+        long[] longResults;
+
+        floatResults = floatOperTest(70000.0f, -3.0f);
+        floatOperCheck(floatResults);
+        doubleResults = doubleOperTest(70000.0, -3.0);
+        doubleOperCheck(doubleResults);
+
+        intResults = convI(0x8877665544332211L, 123.456f, -3.1415926535, 0.0f);
+        checkConvI(intResults);
+        longResults = convL(0x88776655, 123.456f, -3.1415926535, 0.0);
+        checkConvL(longResults);
+        floatResults = convF(0x88776655, 0x8877665544332211L, -3.1415926535);
+        checkConvF(floatResults);
+        doubleResults = convD(0x88776655, 0x8877665544332211L, 123.456f);
+        checkConvD(doubleResults);
+
+        unopTest(123.456f);
+
+        checkConsts();
+
+        jlmTests(3.14159f, 123456.78987654321);
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/Goto.java b/tests/003-omnibus-opcodes/src/Goto.java
new file mode 100644
index 0000000..d56ceae
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Goto.java
@@ -0,0 +1,2408 @@
+/*
+ * 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.
+ */
+
+/**
+ * Try to cause some gotos.
+ */
+class Goto {
+    static int filler(int i) {
+        return i+1;
+    }
+
+    static int smallGoto(boolean which) {
+        System.out.println("Goto.smallGoto");
+
+        int i = 0;
+
+        if (which) {
+            i += filler(i);
+        } else {
+            i -= filler(i);
+        }
+
+        return i;
+    }
+
+    static int mediumGoto(boolean which) {
+        System.out.println("Goto.mediumGoto");
+
+        int i = 0;
+
+        if (which) {
+            i += filler(i);
+        } else {
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+        }
+
+        return i;
+    }
+
+    static int bigGoto(boolean which) {
+        System.out.println("Goto.bigGoto");
+
+        int i = 0;
+
+        if (which) {
+            i += filler(i);
+        } else {
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+            i -= filler(i);  i -= filler(i);  i -= filler(i);  i -= filler(i);
+        }
+
+        return i;
+    }
+
+    public static void run() {
+        smallGoto(false);
+        smallGoto(true);
+        mediumGoto(false);
+        mediumGoto(true);
+        bigGoto(false);
+        bigGoto(true);
+    }
+};
diff --git a/tests/003-omnibus-opcodes/src/InstField.java b/tests/003-omnibus-opcodes/src/InstField.java
new file mode 100644
index 0000000..80b95ab
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/InstField.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+public class InstField {
+    public boolean mBoolean1, mBoolean2;
+    public byte mByte1, mByte2;
+    public char mChar1, mChar2;
+    public short mShort1, mShort2;
+    public int mInt1, mInt2;
+    public float mFloat1, mFloat2;
+    public long mLong1, mLong2;
+    public double mDouble1, mDouble2;
+    public volatile long mVolatileLong1, mVolatileLong2;
+
+    public void run() {
+        assignFields();
+        checkFields();
+        InstField.nullCheck(null);
+    }
+
+    /*
+     * Check access to instance fields through a null pointer.
+     */
+    static public void nullCheck(InstField nully) {
+        System.out.println("InstField.nullCheck");
+        try {
+            int x = nully.mInt1;
+            assert(false);
+        } catch (NullPointerException npe) {
+            // good
+        }
+        try {
+            long l = nully.mLong1;
+            assert(false);
+        } catch (NullPointerException npe) {
+            // good
+        }
+        try {
+            nully.mInt1 = 5;
+            assert(false);
+        } catch (NullPointerException npe) {
+            // good
+        }
+        try {
+            nully.mLong1 = 17L;
+            assert(false);
+        } catch (NullPointerException npe) {
+            // good
+        }
+    }
+
+    public void assignFields() {
+        System.out.println("InstField assign...");
+        mBoolean1 = true;
+        mBoolean2 = false;
+        mByte1 = 127;
+        mByte2 = -128;
+        mChar1 = 32767;
+        mChar2 = 65535;
+        mShort1 = 32767;
+        mShort2 = -32768;
+        mInt1 = 65537;
+        mInt2 = -65537;
+        mFloat1 = 3.1415f;
+        mFloat2 = -1.0f / 0.0f;                // -inf
+        mLong1 = 1234605616436508552L;     // 0x1122334455667788
+        mLong2 = -1234605616436508552L;
+        mDouble1 = 3.1415926535;
+        mDouble2 = 1.0 / 0.0;               // +inf
+        mVolatileLong1 = mLong1 - 1;
+        mVolatileLong2 = mLong2 + 1;
+    }
+
+    public void checkFields() {
+        System.out.println("InstField check...");
+        assert(mBoolean1);
+        assert(!mBoolean2);
+        assert(mByte1 == 127);
+        assert(mByte2 == -128);
+        assert(mChar1 == 32767);
+        assert(mChar2 == 65535);
+        assert(mShort1 == 32767);
+        assert(mShort2 == -32768);
+        assert(mInt1 == 65537);
+        assert(mInt2 == -65537);
+        assert(mFloat1 > 3.141f && mFloat1 < 3.142f);
+        assert(mFloat2 < mFloat1);
+        assert(mLong1 == 1234605616436508552L);
+        assert(mLong2 == -1234605616436508552L);
+        assert(mDouble1 > 3.141592653 && mDouble1 < 3.141592654);
+        assert(mDouble2 > mDouble1);
+        assert(mVolatileLong1 == 1234605616436508551L);
+        assert(mVolatileLong2 == -1234605616436508551L);
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/IntMath.java b/tests/003-omnibus-opcodes/src/IntMath.java
new file mode 100644
index 0000000..89194de
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/IntMath.java
@@ -0,0 +1,492 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test arithmetic operations.
+ */
+public class IntMath {
+
+    static void shiftTest1() {
+        System.out.println("IntMath.shiftTest1");
+
+        final int[] mBytes = {
+            0x11, 0x22, 0x33, 0x44, 0x88, 0x99, 0xaa, 0xbb
+        };
+        long l;
+        int i1, i2;
+
+        i1 = mBytes[0] | mBytes[1] << 8 | mBytes[2] << 16 | mBytes[3] << 24;
+        i2 = mBytes[4] | mBytes[5] << 8 | mBytes[6] << 16 | mBytes[7] << 24;
+        l = i1 | ((long)i2 << 32);
+
+        assert(i1 == 0x44332211);
+        assert(i2 == 0xbbaa9988);
+        assert(l == 0xbbaa998844332211L);
+
+        l = (long)mBytes[0]
+            | (long)mBytes[1] << 8
+            | (long)mBytes[2] << 16
+            | (long)mBytes[3] << 24
+            | (long)mBytes[4] << 32
+            | (long)mBytes[5] << 40
+            | (long)mBytes[6] << 48
+            | (long)mBytes[7] << 56;
+
+        assert(l == 0xbbaa998844332211L);
+    }
+
+    static void shiftTest2() {
+        System.out.println("IntMath.shiftTest2");
+
+        long    a = 0x11;
+        long    b = 0x22;
+        long    c = 0x33;
+        long    d = 0x44;
+        long    e = 0x55;
+        long    f = 0x66;
+        long    g = 0x77;
+        long    h = 0x88;
+
+        long    result = ((a << 56) | (b << 48) | (c << 40) | (d << 32) |
+                         (e << 24) | (f << 16) | (g <<  8) | h);
+
+        assert(result == 0x1122334455667788L);
+    }
+
+    static void unsignedShiftTest() {
+        System.out.println("IntMath.unsignedShiftTest");
+
+        byte b = -4;
+        short s = -4;
+        char c = 0xfffc;
+        int i = -4;
+
+        b >>>= 4;
+        s >>>= 4;
+        c >>>= 4;
+        i >>>= 4;
+
+        assert((int) b == -1);
+        assert((int) s == -1);
+        assert((int) c == 0x0fff);
+        assert(i == 268435455);
+    }
+
+    static void convTest() {
+        System.out.println("IntMath.convTest");
+
+        float f;
+        double d;
+        int i;
+        long l;
+
+        /* int --> long */
+        i = 7654;
+        l = (long) i;
+        assert(l == 7654L);
+
+        i = -7654;
+        l = (long) i;
+        assert(l == -7654L);
+
+        /* long --> int (with truncation) */
+        l = 5678956789L;
+        i = (int) l;
+        assert(i == 1383989493);
+
+        l = -5678956789L;
+        i = (int) l;
+        assert(i == -1383989493);
+    }
+
+    static void charSubTest() {
+        System.out.println("IntMath.charSubTest");
+
+        char char1 = 0x00e9;
+        char char2 = 0xffff;
+        int i;
+
+        /* chars are unsigned-expanded to ints before subtraction */
+        i = char1 - char2;
+        assert(i == 0xffff00ea);
+    }
+
+    /*
+     * We pass in the arguments and return the results so the compiler
+     * doesn't do the math for us.  (x=70000, y=-3)
+     */
+    static int[] intOperTest(int x, int y) {
+        System.out.println("IntMath.intOperTest");
+
+        int[] results = new int[10];
+
+        /* this seems to generate "op-int" instructions */
+        results[0] = x + y;
+        results[1] = x - y;
+        results[2] = x * y;
+        results[3] = x * x;
+        results[4] = x / y;
+        results[5] = x % -y;
+        results[6] = x & y;
+        results[7] = x | y;
+        results[8] = x ^ y;
+
+        /* this seems to generate "op-int/2addr" instructions */
+        results[9] = x + ((((((((x + y) - y) * y) / y) % y) & y) | y) ^ y);
+
+        return results;
+    }
+    static void intOperCheck(int[] results) {
+        System.out.println("IntMath.intOperCheck");
+
+        /* check this edge case while we're here (div-int/2addr) */
+        int minInt = -2147483648;
+        int negOne = -results[5];
+        int plusOne = 1;
+        int result = (((minInt + plusOne) - plusOne) / negOne) / negOne;
+        assert(result == minInt);
+
+        assert(results[0] == 69997);
+        assert(results[1] == 70003);
+        assert(results[2] == -210000);
+        assert(results[3] == 605032704);    // overflow / truncate
+        assert(results[4] == -23333);
+        assert(results[5] == 1);
+        assert(results[6] == 70000);
+        assert(results[7] == -3);
+        assert(results[8] == -70003);
+        assert(results[9] == 70000);
+    }
+
+    /*
+     * More operations, this time with 16-bit constants.  (x=77777)
+     */
+    static int[] lit16Test(int x) {
+        System.out.println("IntMath.lit16Test");
+
+        int[] results = new int[8];
+
+        /* try to generate op-int/lit16" instructions */
+        results[0] = x + 1000;
+        results[1] = 1000 - x;
+        results[2] = x * 1000;
+        results[3] = x / 1000;
+        results[4] = x % 1000;
+        results[5] = x & 1000;
+        results[6] = x | -1000;
+        results[7] = x ^ -1000;
+        return results;
+    }
+    static void lit16Check(int[] results) {
+        assert(results[0] == 78777);
+        assert(results[1] == -76777);
+        assert(results[2] == 77777000);
+        assert(results[3] == 77);
+        assert(results[4] == 777);
+        assert(results[5] == 960);
+        assert(results[6] == -39);
+        assert(results[7] == -76855);
+    }
+
+    /*
+     * More operations, this time with 8-bit constants.  (x=-55555)
+     */
+    static int[] lit8Test(int x) {
+        System.out.println("IntMath.lit8Test");
+
+        int[] results = new int[8];
+
+        /* try to generate op-int/lit8" instructions */
+        results[0] = x + 10;
+        results[1] = 10 - x;
+        results[2] = x * 10;
+        results[3] = x / 10;
+        results[4] = x % 10;
+        results[5] = x & 10;
+        results[6] = x | -10;
+        results[7] = x ^ -10;
+        return results;
+    }
+    static void lit8Check(int[] results) {
+        //for (int i = 0; i < results.length; i++)
+        //    System.out.println(" " + i + ": " + results[i]);
+
+        /* check this edge case while we're here (div-int/lit8) */
+        int minInt = -2147483648;
+        int result = minInt / -1;
+        assert(result == minInt);
+
+        assert(results[0] == -55545);
+        assert(results[1] == 55565);
+        assert(results[2] == -555550);
+        assert(results[3] == -5555);
+        assert(results[4] == -5);
+        assert(results[5] == 8);
+        assert(results[6] == -1);
+        assert(results[7] == 55563);
+    }
+
+
+    /*
+     * Shift some data.  (value=0xff00aa01, dist=8)
+     */
+    static int[] intShiftTest(int value, int dist) {
+        System.out.println("IntMath.intShiftTest");
+
+        int results[] = new int[4];
+
+        results[0] = value << dist;
+        results[1] = value >> dist;
+        results[2] = value >>> dist;
+
+        results[3] = (((value << dist) >> dist) >>> dist) << dist;
+        return results;
+    }
+    static void intShiftCheck(int[] results) {
+        System.out.println("IntMath.intShiftCheck");
+
+        assert(results[0] == 0x00aa0100);
+        assert(results[1] == 0xffff00aa);
+        assert(results[2] == 0x00ff00aa);
+        assert(results[3] == 0xaa00);
+    }
+
+    /*
+     * We pass in the arguments and return the results so the compiler
+     * doesn't do the math for us.  (x=70000000000, y=-3)
+     */
+    static long[] longOperTest(long x, long y) {
+        System.out.println("IntMath.longOperTest");
+
+        long[] results = new long[10];
+
+        /* this seems to generate "op-long" instructions */
+        results[0] = x + y;
+        results[1] = x - y;
+        results[2] = x * y;
+        results[3] = x * x;
+        results[4] = x / y;
+        results[5] = x % -y;
+        results[6] = x & y;
+        results[7] = x | y;
+        results[8] = x ^ y;
+
+        /* this seems to generate "op-long/2addr" instructions */
+        results[9] = x + ((((((((x + y) - y) * y) / y) % y) & y) | y) ^ y);
+
+        return results;
+    }
+    static void longOperCheck(long[] results) {
+        System.out.println("IntMath.longOperCheck");
+
+        /* check this edge case while we're here (div-long/2addr) */
+        long minLong = -9223372036854775808L;
+        long negOne = -results[5];
+        long plusOne = 1;
+        long result = (((minLong + plusOne) - plusOne) / negOne) / negOne;
+        assert(result == minLong);
+
+        assert(results[0] == 69999999997L);
+        assert(results[1] == 70000000003L);
+        assert(results[2] == -210000000000L);
+        assert(results[3] == -6833923606740729856L);    // overflow
+        assert(results[4] == -23333333333L);
+        assert(results[5] == 1);
+        assert(results[6] == 70000000000L);
+        assert(results[7] == -3);
+        assert(results[8] == -70000000003L);
+        assert(results[9] == 70000000000L);
+
+        assert(results.length == 10);
+    }
+
+    /*
+     * Shift some data.  (value=0xd5aa96deff00aa01, dist=8)
+     */
+    static long[] longShiftTest(long value, int dist) {
+        System.out.println("IntMath.longShiftTest");
+
+        long results[] = new long[4];
+
+        results[0] = value << dist;
+        results[1] = value >> dist;
+        results[2] = value >>> dist;
+
+        results[3] = (((value << dist) >> dist) >>> dist) << dist;
+        return results;
+    }
+    static long longShiftCheck(long[] results) {
+        System.out.println("IntMath.longShiftCheck");
+
+        assert(results[0] == 0x96deff00aa010000L);
+        assert(results[1] == 0xffffd5aa96deff00L);
+        assert(results[2] == 0x0000d5aa96deff00L);
+        assert(results[3] == 0xffff96deff000000L);
+
+        assert(results.length == 4);
+
+        return results[0];      // test return-long
+    }
+
+
+    /*
+     * Try to cause some unary operations.
+     */
+    static int unopTest(int x) {
+        x = -x;
+        x ^= 0xffffffff;
+        return x;
+    }
+    static void unopCheck(int result) {
+        assert(result == 37);
+    }
+
+    static class Shorty {
+        public short mShort;
+        public char mChar;
+        public byte mByte;
+    };
+
+    /*
+     * Truncate an int.
+     */
+    static Shorty truncateTest(int x) {
+        System.out.println("IntMath.truncateTest");
+        Shorty shorts = new Shorty();
+
+        shorts.mShort = (short) x;
+        shorts.mChar = (char) x;
+        shorts.mByte = (byte) x;
+        return shorts;
+    }
+    static void truncateCheck(Shorty shorts) {
+        assert(shorts.mShort == -5597);     // 0xea23
+        assert(shorts.mChar == 59939);      // 0xea23
+        assert(shorts.mByte == 35);         // 0x23
+    }
+
+    /*
+     * Verify that we get a divide-by-zero exception.
+     */
+    static void divideByZero(int z) {
+        System.out.println("IntMath.divideByZero");
+
+        try {
+            int x = 100 / z;
+            assert(false);
+        } catch (ArithmeticException ae) {
+        }
+
+        try {
+            int x = 100 % z;
+            assert(false);
+        } catch (ArithmeticException ae) {
+        }
+
+        try {
+            long x = 100L / z;
+            assert(false);
+        } catch (ArithmeticException ae) {
+        }
+
+        try {
+            long x = 100L % z;
+            assert(false);
+        } catch (ArithmeticException ae) {
+        }
+    }
+
+    /*
+     * Check an edge condition: dividing the most-negative integer by -1
+     * returns the most-negative integer, and doesn't cause an exception.
+     *
+     * Pass in -1, -1L.
+     */
+    static void bigDivideOverflow(int idiv, long ldiv) {
+        System.out.println("IntMath.bigDivideOverflow");
+        int mostNegInt = (int) 0x80000000;
+        long mostNegLong = (long) 0x8000000000000000L;
+
+        int intDivResult = mostNegInt / idiv;
+        int intModResult = mostNegInt % idiv;
+        long longDivResult = mostNegLong / ldiv;
+        long longModResult = mostNegLong % ldiv;
+
+        assert(intDivResult == mostNegInt);
+        assert(intModResult == 0);
+        assert(longDivResult == mostNegLong);
+        assert(longModResult == 0);
+    }
+
+    /*
+     * Check "const" instructions.  We use negative values to ensure that
+     * sign-extension is happening.
+     */
+    static void checkConsts(byte small, short medium, int large, long huge) {
+        System.out.println("IntMath.checkConsts");
+
+        assert(small == 1);                     // const/4
+        assert(medium == -256);                 // const/16
+        assert(medium == -256L);                // const-wide/16
+        assert(large == -88888);                // const
+        assert(large == -88888L);               // const-wide/32
+        assert(huge == 0x9922334455667788L);    // const-wide
+    }
+
+    /*
+     * Test some java.lang.Math functions.
+     *
+     * The method arguments are positive values.
+     */
+    static void jlmTests(int ii, long ll) {
+        System.out.println("IntMath.jlmTests");
+
+        assert(Math.abs(ii) == ii);
+        assert(Math.abs(-ii) == ii);
+        assert(Math.min(ii, -5) == -5);
+        assert(Math.max(ii, -5) == ii);
+
+        assert(Math.abs(ll) == ll);
+        assert(Math.abs(-ll) == ll);
+        assert(Math.min(ll, -5L) == -5L);
+        assert(Math.max(ll, -5L) == ll);
+    }
+
+    public static void run() {
+        shiftTest1();
+        shiftTest2();
+        unsignedShiftTest();
+        convTest();
+        charSubTest();
+
+        int[] intResults;
+        long[] longResults;
+
+        intResults = intOperTest(70000, -3);
+        intOperCheck(intResults);
+        longResults = longOperTest(70000000000L, -3L);
+        longOperCheck(longResults);
+
+        intResults = lit16Test(77777);
+        lit16Check(intResults);
+        intResults = lit8Test(-55555);
+        lit8Check(intResults);
+
+        intResults = intShiftTest(0xff00aa01, 8);
+        intShiftCheck(intResults);
+        longResults = longShiftTest(0xd5aa96deff00aa01L, 16);
+        long longRet = longShiftCheck(longResults);
+        assert(longRet == 0x96deff00aa010000L);
+
+        Shorty shorts = truncateTest(-16717277);    // 0xff00ea23
+        truncateCheck(shorts);
+
+        divideByZero(0);
+        bigDivideOverflow(-1, -1L);
+
+        checkConsts((byte) 1, (short) -256, -88888, 0x9922334455667788L);
+
+        unopCheck(unopTest(38));
+
+        jlmTests(12345, 0x1122334455667788L);
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/InternedString.java b/tests/003-omnibus-opcodes/src/InternedString.java
new file mode 100644
index 0000000..4baab0c
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/InternedString.java
@@ -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.
+ */
+
+import java.lang.ref.*;
+
+public class InternedString {
+    public static final String CONST = "Class InternedString";
+
+    public static void run() {
+        System.out.println("InternedString.run");
+        testImmortalInternedString();
+        testDeadInternedString();
+    }
+
+    private static void testDeadInternedString() {
+        String s = "blah";
+        s = s + s;
+        WeakReference strRef = new WeakReference<String>(s.intern());
+        // Kill s, otherwise the string object is still accessible from root set
+        s = CONST;
+        System.gc();
+        // "blahblah" should disappear from the intern list
+        assert(strRef.get() == null);
+    }
+
+    private static void testImmortalInternedString() {
+        WeakReference strRef = new WeakReference<String>(CONST.intern());
+        System.gc();
+        // Class constant string should be entered to the interned table when
+        // loaded
+        assert(CONST == CONST.intern());
+        // and it should survive the gc
+        assert(strRef.get() != null);
+
+        String s = CONST;
+        // "Class InternedString" should remain on the intern list
+        strRef = new WeakReference<String>(s.intern());
+        // Kill s, otherwise the string object is still accessible from root set
+        s = "";
+        System.gc();
+        assert(strRef.get() == CONST);
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/Main.java b/tests/003-omnibus-opcodes/src/Main.java
new file mode 100644
index 0000000..fb39d76
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Main.java
@@ -0,0 +1,82 @@
+/*
+ * 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 instruction exerciser.
+ */
+public class Main {
+    /*
+     * Start up.
+     */
+    public static void main(String[] args) {
+        boolean assertEnabled = false;
+        assert assertEnabled = true;
+        if (!assertEnabled) {
+            System.out.println("FAIL: assert doesn't work (specify '-ea')\n");
+            throw new RuntimeException();
+        } else {
+            System.out.println("(assertions are enabled)");
+        }
+
+        Main main = new Main();
+        main.run();
+
+        /* run through the heap to see if we trashed something */
+        System.gc();
+
+        System.out.println("Done!");
+    }
+
+    public void run() {
+        InstField instField = new InstField();
+        instField.run();
+
+        StaticField.run();
+
+        IntMath.run();
+        FloatMath.run();
+        Compare.run();
+
+        Monitor.run();
+        Switch.run();
+        Array.run();
+        Classes.run();
+        Goto.run();
+        MethodCall.run();
+        Throw.run();
+
+        try {
+            UnresTest1.run();
+        } catch (VerifyError ve) {
+            System.out.println("Caught: " + ve);
+        }
+        try {
+            UnresTest1.run();
+        } catch (VerifyError ve) {
+            System.out.println("Caught (retry): " + ve);
+        }
+
+        try {
+            UnresTest2.run();
+        } catch (VerifyError ve) {
+            System.out.println("Caught: " + ve);
+        } catch (NoClassDefFoundError ncdfe) {
+            /* UnresClass can cause desktop Java to freak out */
+            System.out.println("NOTE: UnresTest2 not available");
+        }
+        InternedString.run();
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/MethodCall.java b/tests/003-omnibus-opcodes/src/MethodCall.java
new file mode 100644
index 0000000..f89194b
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/MethodCall.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+/**
+ * Try different kinds of method calls.
+ */
+public class MethodCall extends MethodCallBase {
+    MethodCall() {
+        super();
+        System.out.println("  MethodCall ctor");
+    }
+
+    /* overridden method */
+    int tryThing() {
+        int val = super.tryThing();
+        assert(val == 7);
+        return val;
+    }
+
+    /* do-nothing private instance method */
+    private void directly() {}
+
+    /*
+     * Function with many arguments.
+     */
+    static void manyArgs(int a0, long a1, int a2, long a3, int a4, long a5,
+        int a6, int a7, double a8, float a9, double a10, short a11, int a12,
+        char a13, int a14, int a15, byte a16, boolean a17, int a18, int a19,
+        long a20, long a21, int a22, int a23, int a24, int a25, int a26,
+        String[][] a27, String[] a28, String a29)
+    {
+        System.out.println("MethodCalls.manyArgs");
+        assert(a0 == 0);
+        assert(a9 > 8.99 && a9 < 9.01);
+        assert(a16 == -16);
+        assert(a25 == 25);
+        assert(a29.equals("twenty nine"));
+    }
+
+    public static void run() {
+        MethodCall inst = new MethodCall();
+
+        MethodCallBase base = inst;
+        base.tryThing();
+        inst.tryThing();
+
+        inst = null;
+        try {
+            inst.directly();
+            assert(false);
+        } catch (NullPointerException npe) {
+            // good
+        }
+
+        manyArgs(0, 1L, 2, 3L, 4, 5L, 6, 7, 8.0, 9.0f, 10.0, (short)11, 12,
+            (char)13, 14, 15, (byte)-16, true, 18, 19, 20L, 21L, 22, 23, 24,
+            25, 26, null, null, "twenty nine");
+    }
+}
+
+class MethodCallBase {
+    MethodCallBase() {
+        System.out.println("  MethodCallBase ctor");
+    }
+
+    int tryThing() {
+        return 7;
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/Monitor.java b/tests/003-omnibus-opcodes/src/Monitor.java
new file mode 100644
index 0000000..66d7c65
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Monitor.java
@@ -0,0 +1,44 @@
+// Copyright 2008 The Android Open Source Project
+
+
+
+/**
+ * Exercise monitors.
+ */
+public class Monitor {
+    public static int mVal = 0;
+
+    public synchronized void subTest() {
+        Object obj = new Object();
+        synchronized (obj) {
+            mVal++;
+            obj = null;     // does NOT cause a failure on exit
+            assert(obj == null);
+        }
+    }
+
+
+    public static void run() {
+        System.out.println("Monitor.run");
+
+        Object obj = null;
+
+        try {
+            synchronized (obj) {
+                mVal++;
+            }
+            assert(false);
+        } catch (NullPointerException npe) {
+            /* expected */
+        }
+
+        obj = new Object();
+        synchronized (obj) {
+            mVal++;
+        }
+
+        new Monitor().subTest();
+
+        assert(mVal == 2);
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/StaticField.java b/tests/003-omnibus-opcodes/src/StaticField.java
new file mode 100644
index 0000000..7ccdd7e
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/StaticField.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+public class StaticField {
+    public static boolean mBoolean1, mBoolean2;
+    public static byte mByte1, mByte2;
+    public static char mChar1, mChar2;
+    public static short mShort1, mShort2;
+    public static int mInt1, mInt2;
+    public static float mFloat1, mFloat2;
+    public static long mLong1, mLong2;
+    public static double mDouble1, mDouble2;
+    public static volatile long mVolatileLong1, mVolatileLong2;
+
+    public static void run() {
+        assignFields();
+        checkFields();
+    }
+
+    public static void assignFields() {
+        System.out.println("StaticField assign...");
+        mBoolean1 = true;
+        mBoolean2 = false;
+        mByte1 = 127;
+        mByte2 = -128;
+        mChar1 = 32767;
+        mChar2 = 65535;
+        mShort1 = 32767;
+        mShort2 = -32768;
+        mInt1 = 65537;
+        mInt2 = -65537;
+        mFloat1 = 3.1415f;
+        mFloat2 = -1.0f / 0.0f;                // -inf
+        mLong1 = 1234605616436508552L;     // 0x1122334455667788
+        mLong2 = -1234605616436508552L;
+        mDouble1 = 3.1415926535;
+        mDouble2 = 1.0 / 0.0;               // +inf
+        mVolatileLong1 = mLong1 - 1;
+        mVolatileLong2 = mLong2 + 1;
+    }
+
+    public static void checkFields() {
+        System.out.println("StaticField check...");
+        assert(mBoolean1);
+        assert(!mBoolean2);
+        assert(mByte1 == 127);
+        assert(mByte2 == -128);
+        assert(mChar1 == 32767);
+        assert(mChar2 == 65535);
+        assert(mShort1 == 32767);
+        assert(mShort2 == -32768);
+        assert(mInt1 == 65537);
+        assert(mInt2 == -65537);
+        assert(mFloat1 > 3.141f && mFloat2 < 3.142f);
+        assert(mFloat2 < mFloat1);
+        assert(mLong1 == 1234605616436508552L);
+        assert(mLong2 == -1234605616436508552L);
+        assert(mDouble1 > 3.141592653 && mDouble1 < 3.141592654);
+        assert(mDouble2 > mDouble1);
+        assert(mVolatileLong1 == 1234605616436508551L);
+        assert(mVolatileLong2 == -1234605616436508551L);
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/Switch.java b/tests/003-omnibus-opcodes/src/Switch.java
new file mode 100644
index 0000000..67c82b0
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Switch.java
@@ -0,0 +1,62 @@
+public class Switch {
+    /**
+     * Test switch() blocks
+     */
+    private static void testSwitch() {
+        System.out.println("Switch.testSwitch");
+
+        int a = 1;
+
+        switch (a) {
+            case -1: assert(false); break;
+            case 0: assert(false); break;
+            case 1: /*correct*/ break;
+            case 2: assert(false); break;
+            case 3: assert(false); break;
+            case 4: assert(false); break;
+            default: assert(false); break;
+        }
+        switch (a) {
+            case 3: assert(false); break;
+            case 4: assert(false); break;
+            default: /*correct*/ break;
+        }
+
+        a = 0x12345678;
+
+        switch (a) {
+            case 0x12345678: /*correct*/ break;
+            case 0x12345679: assert(false); break;
+            default: assert(false); break;
+        }
+        switch (a) {
+            case 57: assert(false); break;
+            case -6: assert(false); break;
+            case 0x12345678: /*correct*/ break;
+            case 22: assert(false); break;
+            case 3: assert(false); break;
+            default: assert(false); break;
+        }
+        switch (a) {
+            case -6: assert(false); break;
+            case 3: assert(false); break;
+            default: /*correct*/ break;
+        }
+
+        a = -5;
+        switch (a) {
+            case 12: assert(false); break;
+            case -5: /*correct*/ break;
+            case 0: assert(false); break;
+            default: assert(false); break;
+        }
+
+        switch (a) {
+            default: /*correct*/ break;
+        }
+    }
+
+    public static void run() {
+        testSwitch();
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/Throw.java b/tests/003-omnibus-opcodes/src/Throw.java
new file mode 100644
index 0000000..91ee6dd
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Throw.java
@@ -0,0 +1,124 @@
+/*
+ * 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 exception throwing.
+ */
+public class Throw {
+    public void throwNullPointerException() {
+        throw new NullPointerException("npe!");
+    }
+
+    public void throwArithmeticException() {
+        throw new ArithmeticException();
+    }
+
+    public void one() {
+        System.out.println("Throw.one");
+        try {
+            throwNullPointerException();
+            assert(false);
+        } catch (Exception ex) {
+            // good
+            return;
+        }
+
+        assert(false);
+    }
+
+    public void twoA() {
+        System.out.println("Throw.twoA");
+        boolean gotN = false;
+        boolean gotA = false;
+        boolean gotWeird = false;
+
+        try {
+            try {
+                throwArithmeticException();
+                gotWeird = true;
+            } catch (ArithmeticException ae) {
+                gotA = true;
+            }
+        } catch (NullPointerException npe) {
+            gotN = true;
+        }
+
+        assert(gotA);
+        assert(!gotN);
+        assert(!gotWeird);
+    }
+
+    public void twoN() {
+        System.out.println("Throw.twoN");
+        boolean gotN = false;
+        boolean gotA = false;
+        boolean gotWeird = false;
+
+        try {
+            try {
+                throwNullPointerException();
+                gotWeird = true;
+            } catch (ArithmeticException ae) {
+                gotA = true;
+            }
+        } catch (NullPointerException npe) {
+            gotN = true;
+        }
+
+        assert(!gotA);
+        assert(gotN);
+        assert(!gotWeird);
+    }
+
+    public void rethrow() {
+        System.out.println("Throw.rethrow");
+        boolean caught = false;
+        boolean lly = false;
+        boolean second = false;
+
+        try {
+            try {
+                throwNullPointerException();
+                assert(false);
+            } catch (Exception ex) {
+                if (ex instanceof ArithmeticException) {
+                    assert(false);
+                }
+                if (ex instanceof NullPointerException) {
+                    caught = true;
+                    throw (NullPointerException) ex;
+                }
+            } finally {
+                lly = true;
+            }
+        } catch (Exception ex) {
+            second = true;
+        }
+
+        assert(caught);
+        assert(lly);
+        assert(second);
+    }
+
+    public static void run() {
+        Throw th = new Throw();
+
+        th.one();
+        th.twoA();
+        th.twoN();
+        th.rethrow();
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/UnresClass.java b/tests/003-omnibus-opcodes/src/UnresClass.java
new file mode 100644
index 0000000..52b3d4f
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/UnresClass.java
@@ -0,0 +1,9 @@
+/*
+ * Unresolved class.
+ *
+ * "happy" version.
+ */
+
+public class UnresClass {
+    int foo;
+}
diff --git a/tests/003-omnibus-opcodes/src/UnresStuff.java b/tests/003-omnibus-opcodes/src/UnresStuff.java
new file mode 100644
index 0000000..1d2a556
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/UnresStuff.java
@@ -0,0 +1,22 @@
+/*
+ * Unresolved classes / fields / methods in a resolved class.
+ *
+ * "happy" version.
+ */
+
+public class UnresStuff {
+    public int instField;
+
+    public static int staticField;
+
+    public double wideInstField;
+    public static double wideStaticField;
+
+    public void virtualMethod() {
+        System.out.println("unres!");
+    }
+
+    public static void staticMethod() {
+        System.out.println("unres!");
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/UnresTest1.java b/tests/003-omnibus-opcodes/src/UnresTest1.java
new file mode 100644
index 0000000..5a80a7a
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/UnresTest1.java
@@ -0,0 +1,80 @@
+/*
+ * Test failure to resolve class members.
+ */
+class UnresTest1 {
+    public static void run() {
+        System.out.println("UnresTest1...");
+
+        UnresStuff stuff = new UnresStuff();
+        try {
+            int x = stuff.instField;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+        try {       // hit the same one a second time
+            int x = stuff.instField;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+        try {
+            stuff.instField = 5;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+
+        try {
+            double d = stuff.wideInstField;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+        try {
+            stuff.wideInstField = 0.0;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+
+        try {
+            int y = UnresStuff.staticField;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+        try {
+            UnresStuff.staticField = 17;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+
+        try {
+            double d = UnresStuff.wideStaticField;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+        try {
+            UnresStuff.wideStaticField = 1.0;
+            assert(false);
+        } catch (NoSuchFieldError nsfe) {
+            // good
+        }
+
+        try {
+            stuff.virtualMethod();
+            assert(false);
+        } catch (NoSuchMethodError nsfe) {
+            // good
+        }
+        try {
+            UnresStuff.staticMethod();
+            assert(false);
+        } catch (NoSuchMethodError nsfe) {
+            // good
+        }
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src/UnresTest2.java b/tests/003-omnibus-opcodes/src/UnresTest2.java
new file mode 100644
index 0000000..768be8f
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/UnresTest2.java
@@ -0,0 +1,49 @@
+/*
+ * Test failure to resolve classes.
+ */
+class UnresTest2 {
+    /*
+     * Try check-cast and instance-of.
+     */
+    static boolean checkCasts(Object obj) {
+        boolean foo = false;
+
+        try {
+            UnresClass un = (UnresClass) obj;
+            assert(false);
+        } catch (NoClassDefFoundError ncdfe) {
+            // good
+        }
+        try {
+            foo = obj instanceof UnresClass;
+            assert(false);
+        } catch (NoClassDefFoundError ncdfe) {
+            // good
+        }
+
+        return foo;
+    }
+
+    public static void run() {
+        System.out.println("UnresTest2...");
+        UnresClass un;
+        UnresStuff stuff = new UnresStuff();
+
+        try {
+            un = new UnresClass();
+            assert(false);
+        } catch (NoClassDefFoundError ncdfe) {
+            // good
+        }
+
+        try {
+            UnresClass[] uar = new UnresClass[3];
+            assert(false);
+        } catch (NoClassDefFoundError ncdfe) {
+            // good
+        }
+
+        checkCasts(stuff);
+        System.out.println("UnresTest2 done");
+    }
+}
diff --git a/tests/003-omnibus-opcodes/src2/UnresStuff.java b/tests/003-omnibus-opcodes/src2/UnresStuff.java
new file mode 100644
index 0000000..56f43af
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src2/UnresStuff.java
@@ -0,0 +1,9 @@
+/*
+ * Unresolved classes / fields / methods in a resolved class.
+ *
+ * "happy" version.
+ */
+
+public class UnresStuff {
+    public int x;
+}
diff --git a/tests/004-annotations/expected.txt b/tests/004-annotations/expected.txt
new file mode 100644
index 0000000..063b3ed
--- /dev/null
+++ b/tests/004-annotations/expected.txt
@@ -0,0 +1,96 @@
+TestAnnotations...
+java.lang.String android.test.anno.TestAnnotations.thing1: @android.test.anno.AnnoArrayField(bb=[], cc=[a, b], dd=[0.987654321], ff=[3.1415927], ii=[], jj=[], ss=[], str=[], zz=[])
+java.lang.String android.test.anno.TestAnnotations.thing2: @android.test.anno.AnnoArrayField(bb=[-1, 0, 1], cc=[Q], dd=[0.3, 0.6, 0.9], ff=[1.1, 1.2, 1.3], ii=[1, 2, 3, 4], jj=[-5, 0, 5], ss=[12, 13, 14, 15, 16, 17], str=[hickory, dickory, dock], zz=[true, false, true])
+mapping is class [Landroid.test.anno.IntToString;
+  0='@android.test.anno.IntToString(from=0, to=NORMAL_FOCUS)'
+  1='@android.test.anno.IntToString(from=2, to=WEAK_FOCUS)'
+present(getFocusType, ExportedProperty): true
+present(getFocusType, AnnoSimpleType): false
+
+AnnoSimpleField true, SimplyNoted false
+annotations on TYPE class android.test.anno.SimplyNoted(2):
+  @android.test.anno.AnnoSimpleType()
+    interface android.test.anno.AnnoSimpleType
+  @android.test.anno.AnnoSimpleType2()
+    interface android.test.anno.AnnoSimpleType2
+
+  annotations on CTOR android.test.anno.SimplyNoted():
+    @android.test.anno.AnnoSimpleConstructor()
+      interface android.test.anno.AnnoSimpleConstructor
+    constructor parameter annotations:
+  annotations on CTOR android.test.anno.SimplyNoted(int):
+    @android.test.anno.AnnoSimpleConstructor()
+      interface android.test.anno.AnnoSimpleConstructor
+    constructor parameter annotations:
+      @android.test.anno.AnnoSimpleParameter()
+        interface android.test.anno.AnnoSimpleParameter
+  annotations on METH public int android.test.anno.SimplyNoted.foo():
+    @android.test.anno.AnnoSimpleMethod()
+      interface android.test.anno.AnnoSimpleMethod
+    method parameter annotations:
+  annotations on FIELD public static int android.test.anno.SimplyNoted.mOneFoo:
+    @android.test.anno.AnnoSimpleField()
+      interface android.test.anno.AnnoSimpleField
+  annotations on FIELD public int android.test.anno.SimplyNoted.mFoo:
+    @android.test.anno.AnnoSimpleField()
+      interface android.test.anno.AnnoSimpleField
+
+annotations on TYPE interface android.test.anno.INoted(1):
+  @android.test.anno.AnnoSimpleType2()
+    interface android.test.anno.AnnoSimpleType2
+
+  annotations on METH public abstract int android.test.anno.INoted.bar():
+    @android.test.anno.AnnoSimpleMethod()
+      interface android.test.anno.AnnoSimpleMethod
+    method parameter annotations:
+
+annotations on TYPE class android.test.anno.SubNoted(3):
+  @android.test.anno.AnnoFancyType(name=unknown, num=5)
+    interface android.test.anno.AnnoFancyType
+  @android.test.anno.AnnoSimpleType()
+    interface android.test.anno.AnnoSimpleType
+  @android.test.anno.AnnoSimpleType2()
+    interface android.test.anno.AnnoSimpleType2
+
+  annotations on CTOR public android.test.anno.SubNoted():
+    constructor parameter annotations:
+  annotations on METH public int android.test.anno.SubNoted.bar():
+    method parameter annotations:
+  annotations on FIELD int android.test.anno.SubNoted.mBar:
+
+annotations on TYPE class android.test.anno.FullyNoted(1):
+  @android.test.anno.AnnoFancyType(name=full, num=5)
+    interface android.test.anno.AnnoFancyType
+
+  annotations on CTOR android.test.anno.FullyNoted(int):
+    @android.test.anno.AnnoFancyConstructor(numArgs=1)
+      interface android.test.anno.AnnoFancyConstructor
+    constructor parameter annotations:
+      @android.test.anno.AnnoFancyParameter(factor=0.5)
+        interface android.test.anno.AnnoFancyParameter
+  annotations on METH public int android.test.anno.FullyNoted.bar(int,long) throws java.io.IOException,java.io.EOFException:
+    @android.test.anno.AnnoFancyMethod(biteMe=false, callMe=true, enumerated=FOO, someClass=class android.test.anno.SomeClass)
+      interface android.test.anno.AnnoFancyMethod
+    method parameter annotations:
+      @android.test.anno.AnnoSimpleParameter()
+        interface android.test.anno.AnnoSimpleParameter
+      @android.test.anno.AnnoFancyParameter(factor=3.7879912899761)
+        interface android.test.anno.AnnoFancyParameter
+  annotations on METH public int android.test.anno.FullyNoted.bar1(int,long) throws java.io.IOException:
+    @android.test.anno.AnnoFancyMethod(biteMe=true, callMe=false, enumerated=BAR, someClass=class android.test.anno.SomeClass)
+      interface android.test.anno.AnnoFancyMethod
+    method parameter annotations:
+      @android.test.anno.AnnoSimpleParameter()
+        interface android.test.anno.AnnoSimpleParameter
+      @android.test.anno.AnnoFancyParameter(factor=3.7879912899761)
+        interface android.test.anno.AnnoFancyParameter
+  annotations on METH public int android.test.anno.FullyNoted.notAnnotated():
+    method parameter annotations:
+  annotations on FIELD int android.test.anno.FullyNoted.mBar:
+    @android.test.anno.AnnoFancyField(nombre=fubar)
+      interface android.test.anno.AnnoFancyField
+    aff: @android.test.anno.AnnoFancyField(nombre=fubar) / class $Proxy17
+    --> nombre is 'fubar'
+
+SimplyNoted.get(AnnoSimpleType) = @android.test.anno.AnnoSimpleType()
+SubNoted.get(AnnoSimpleType) = @android.test.anno.AnnoSimpleType()
diff --git a/tests/004-annotations/info.txt b/tests/004-annotations/info.txt
new file mode 100644
index 0000000..c8c9280
--- /dev/null
+++ b/tests/004-annotations/info.txt
@@ -0,0 +1 @@
+Test a bunch of uses of annotations.
diff --git a/tests/004-annotations/src/Main.java b/tests/004-annotations/src/Main.java
new file mode 100644
index 0000000..e44722f
--- /dev/null
+++ b/tests/004-annotations/src/Main.java
@@ -0,0 +1,7 @@
+import android.test.anno.TestAnnotations;
+
+public class Main {
+    static public void main(String[] args) {
+        TestAnnotations.main(args);
+    }
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoArrayField.java b/tests/004-annotations/src/android/test/anno/AnnoArrayField.java
new file mode 100644
index 0000000..681045c
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoArrayField.java
@@ -0,0 +1,19 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Documented
+@Inherited
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AnnoArrayField {
+    boolean[] zz() default {};
+    byte[] bb() default {};
+    char[] cc() default {'a', 'b'};
+    short[] ss() default {};
+    int[] ii() default {};
+    float[] ff() default {3.141592654f};
+    long[] jj() default {};
+    double[] dd() default {0.987654321};
+    String[] str() default {};
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyConstructor.java b/tests/004-annotations/src/android/test/anno/AnnoFancyConstructor.java
new file mode 100644
index 0000000..19dadec
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyConstructor.java
@@ -0,0 +1,10 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.CONSTRUCTOR)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoFancyConstructor {
+    public int numArgs() default 0;
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyField.java b/tests/004-annotations/src/android/test/anno/AnnoFancyField.java
new file mode 100644
index 0000000..855ba56
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyField.java
@@ -0,0 +1,12 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited      // should have no effect
+@Documented
+
+public @interface AnnoFancyField {
+    public String nombre() default "no se";
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyMethod.java b/tests/004-annotations/src/android/test/anno/AnnoFancyMethod.java
new file mode 100644
index 0000000..3088866
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyMethod.java
@@ -0,0 +1,14 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoFancyMethod {
+    enum AnnoFancyMethodEnum { FOO, BAR };
+    boolean callMe() default false;
+    boolean biteMe();
+    AnnoFancyMethodEnum enumerated() default AnnoFancyMethodEnum.FOO;
+    Class someClass() default SomeClass.class;
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyParameter.java b/tests/004-annotations/src/android/test/anno/AnnoFancyParameter.java
new file mode 100644
index 0000000..bc2ba7c
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyParameter.java
@@ -0,0 +1,10 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoFancyParameter {
+    double factor();
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyType.java b/tests/004-annotations/src/android/test/anno/AnnoFancyType.java
new file mode 100644
index 0000000..745f838
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyType.java
@@ -0,0 +1,11 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoFancyType {
+    public int num();
+    public String name() default "unknown";
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java
new file mode 100644
index 0000000..d66b9ae
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.CONSTRUCTOR)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleConstructor {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleField.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleField.java
new file mode 100644
index 0000000..04193d2
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleField.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleField {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java
new file mode 100644
index 0000000..a839fa2
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.LOCAL_VARIABLE)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleLocalVariable {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleMethod.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleMethod.java
new file mode 100644
index 0000000..fcd9c0f
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleMethod.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleMethod {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimplePackage.java b/tests/004-annotations/src/android/test/anno/AnnoSimplePackage.java
new file mode 100644
index 0000000..4aadfe7
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimplePackage.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.PACKAGE)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimplePackage {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleParameter.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleParameter.java
new file mode 100644
index 0000000..6e26ca3
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleParameter.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleParameter {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleType.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleType.java
new file mode 100644
index 0000000..3bba3db
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleType.java
@@ -0,0 +1,9 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface AnnoSimpleType {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleType2.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleType2.java
new file mode 100644
index 0000000..f74b291
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleType2.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface AnnoSimpleType2 {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java
new file mode 100644
index 0000000..541d82e
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+
+public @interface AnnoSimpleTypeInvis {}
diff --git a/tests/004-annotations/src/android/test/anno/ExportedProperty.java b/tests/004-annotations/src/android/test/anno/ExportedProperty.java
new file mode 100644
index 0000000..810d28f
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/ExportedProperty.java
@@ -0,0 +1,12 @@
+/* part of test for array problem */
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface ExportedProperty {
+    boolean resolveId() default false;
+    IntToString[] mapping() default { @IntToString(from = -1, to = "-1") };
+}
diff --git a/tests/004-annotations/src/android/test/anno/FullyNoted.java b/tests/004-annotations/src/android/test/anno/FullyNoted.java
new file mode 100644
index 0000000..76a7b04
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/FullyNoted.java
@@ -0,0 +1,39 @@
+
+package android.test.anno;
+
+import java.io.IOException;
+import java.io.EOFException;
+
+@AnnoFancyType(num=5, name="full")
+public class FullyNoted {
+    @AnnoFancyField(nombre="fubar")
+    int mBar;
+
+    @AnnoFancyConstructor(numArgs=1)
+    FullyNoted(@AnnoFancyParameter(factor=0.5) int bar)
+    {
+        mBar = bar;
+    }
+
+    @AnnoFancyMethod(callMe=true, biteMe=false)
+    public int bar(
+        @AnnoSimpleParameter int one,
+        @AnnoFancyParameter(factor=3.7879912899761) long two)
+        throws IOException, EOFException {
+
+        return 0;
+    }
+
+    @AnnoFancyMethod(biteMe=true, enumerated=AnnoFancyMethod.AnnoFancyMethodEnum.BAR)
+    public int bar1(
+        @AnnoSimpleParameter int one,
+        @AnnoFancyParameter(factor=3.7879912899761) long two)
+        throws IOException {
+
+        return 0;
+    }
+
+    public int notAnnotated() {
+        return 0;
+    }
+}
diff --git a/tests/004-annotations/src/android/test/anno/INoted.java b/tests/004-annotations/src/android/test/anno/INoted.java
new file mode 100644
index 0000000..b2cd007
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/INoted.java
@@ -0,0 +1,7 @@
+package android.test.anno;
+
+@AnnoSimpleType2
+public interface INoted {
+    @AnnoSimpleMethod
+    public int bar();
+}
diff --git a/tests/004-annotations/src/android/test/anno/IntToString.java b/tests/004-annotations/src/android/test/anno/IntToString.java
new file mode 100644
index 0000000..e84fcfa
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/IntToString.java
@@ -0,0 +1,12 @@
+/* part of test for array problem */
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface IntToString {
+    int from();
+    String to();
+}
diff --git a/tests/004-annotations/src/android/test/anno/SimplyNoted.java b/tests/004-annotations/src/android/test/anno/SimplyNoted.java
new file mode 100644
index 0000000..95a3d24
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/SimplyNoted.java
@@ -0,0 +1,30 @@
+package android.test.anno;
+
+@AnnoSimpleType
+@AnnoSimpleType2
+@AnnoSimpleTypeInvis
+public class SimplyNoted {
+    @AnnoSimpleField
+    public int mFoo;
+
+    @AnnoSimpleField
+    public static int mOneFoo;
+
+    @AnnoSimpleConstructor
+    SimplyNoted() {
+        mFoo = 0;
+    }
+
+    @AnnoSimpleConstructor
+    SimplyNoted(@AnnoSimpleParameter int xyzzy) {
+        mFoo = xyzzy;
+    }
+
+    @AnnoSimpleMethod
+    public int foo() {
+        @AnnoSimpleLocalVariable
+        int bar = 5;
+
+        return bar;
+    }
+}
diff --git a/tests/004-annotations/src/android/test/anno/SomeClass.java b/tests/004-annotations/src/android/test/anno/SomeClass.java
new file mode 100644
index 0000000..c21d68d
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/SomeClass.java
@@ -0,0 +1,4 @@
+package android.test.anno;
+
+public class SomeClass {
+}
diff --git a/tests/004-annotations/src/android/test/anno/SubNoted.java b/tests/004-annotations/src/android/test/anno/SubNoted.java
new file mode 100644
index 0000000..2530346
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/SubNoted.java
@@ -0,0 +1,12 @@
+package android.test.anno;
+
+@AnnoFancyType(num=5)       // first occurrence of AnnoFancyType
+                            // we inherit @AnnoSimpleType
+@AnnoSimpleType2            // AnnoSimpleType2 here *and* inherited from parent
+public class SubNoted extends SimplyNoted implements INoted {
+    int mBar;
+
+    public int bar() {
+        return 0;
+    }
+}
diff --git a/tests/004-annotations/src/android/test/anno/TestAnnotations.java b/tests/004-annotations/src/android/test/anno/TestAnnotations.java
new file mode 100644
index 0000000..4ad32d5
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/TestAnnotations.java
@@ -0,0 +1,177 @@
+package android.test.anno;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.TreeMap;
+
+public class TestAnnotations {
+    /**
+     * Print the annotations in sorted order, so as to avoid
+     * any (legitimate) non-determinism with regard to the iteration order.
+     */
+    static private void printAnnotationArray(String prefix, Annotation[] arr) {
+        TreeMap<String, Annotation> sorted =
+            new TreeMap<String, Annotation>();
+
+        for (Annotation a : arr) {
+            sorted.put(a.annotationType().getName(), a);
+        }
+
+        for (Annotation a : sorted.values()) {
+            System.out.println(prefix + "  " + a);
+            System.out.println(prefix + "    " + a.annotationType());
+        }
+    }
+
+    static void printAnnotations(Class clazz) {
+        Annotation[] annos;
+        Annotation[][] parAnnos;
+
+        annos = clazz.getAnnotations();
+        System.out.println("annotations on TYPE " + clazz +
+            "(" + annos.length + "):");
+        printAnnotationArray("", annos);
+        System.out.println();
+
+        for (Constructor c: clazz.getDeclaredConstructors()) {
+            annos = c.getDeclaredAnnotations();
+            System.out.println("  annotations on CTOR " + c + ":");
+            printAnnotationArray("  ", annos);
+
+            System.out.println("    constructor parameter annotations:");
+            for (Annotation[] pannos: c.getParameterAnnotations()) {
+                printAnnotationArray("    ", pannos);
+            }
+        }
+
+        for (Method m: clazz.getDeclaredMethods()) {
+            annos = m.getDeclaredAnnotations();
+            System.out.println("  annotations on METH " + m + ":");
+            printAnnotationArray("  ", annos);
+
+            System.out.println("    method parameter annotations:");
+            for (Annotation[] pannos: m.getParameterAnnotations()) {
+                printAnnotationArray("    ", pannos);
+            }
+        }
+
+        for (Field f: clazz.getDeclaredFields()) {
+            annos = f.getDeclaredAnnotations();
+            System.out.println("  annotations on FIELD " + f + ":");
+            printAnnotationArray("  ", annos);
+
+            AnnoFancyField aff;
+            aff = (AnnoFancyField) f.getAnnotation(AnnoFancyField.class);
+            if (aff != null) {
+                System.out.println("    aff: " + aff + " / " + aff.getClass());
+                System.out.println("    --> nombre is '" + aff.nombre() + "'");
+            }
+        }
+        System.out.println();
+    }
+
+
+    @ExportedProperty(mapping = {
+        @IntToString(from = 0, to = "NORMAL_FOCUS"),
+        @IntToString(from = 2, to = "WEAK_FOCUS")
+    })
+    public int getFocusType() {
+        return 2;
+    }
+
+
+    @AnnoArrayField
+    String thing1;
+
+    @AnnoArrayField(
+            zz = {true,false,true},
+            bb = {-1,0,1},
+            cc = {'Q'},
+            ss = {12,13,14,15,16,17},
+            ii = {1,2,3,4},
+            ff = {1.1f,1.2f,1.3f},
+            jj = {-5,0,5},
+            dd = {0.3,0.6,0.9},
+            str = {"hickory","dickory","dock"}
+            )
+    String thing2;
+
+    public static void testArrays() {
+        TestAnnotations ta = new TestAnnotations();
+        Field field;
+        Annotation[] annotations;
+
+        try {
+            field = TestAnnotations.class.getDeclaredField("thing1");
+            annotations = field.getAnnotations();
+            System.out.println(field + ": " + annotations[0].toString());
+
+            field = TestAnnotations.class.getDeclaredField("thing2");
+            annotations = field.getAnnotations();
+            System.out.println(field + ": " + annotations[0].toString());
+        } catch (NoSuchFieldException nsfe) {
+            throw new RuntimeException(nsfe);
+        }
+    }
+
+    public static void testArrayProblem() {
+        Method meth;
+        ExportedProperty property;
+        final IntToString[] mapping;
+
+        try {
+            meth = TestAnnotations.class.getMethod("getFocusType",
+                    (Class[])null);
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException(nsme);
+        }
+        property = meth.getAnnotation(ExportedProperty.class);
+        mapping = property.mapping();
+
+        System.out.println("mapping is " + mapping.getClass() +
+            "\n  0='" + mapping[0] + "'\n  1='" + mapping[1] + "'");
+
+        /* while we're here, check isAnnotationPresent on Method */
+        System.out.println("present(getFocusType, ExportedProperty): " +
+            meth.isAnnotationPresent(ExportedProperty.class));
+        System.out.println("present(getFocusType, AnnoSimpleType): " +
+            meth.isAnnotationPresent(AnnoSimpleType.class));
+
+        System.out.println("");
+    }
+
+
+
+    public static void main(String[] args) {
+        System.out.println("TestAnnotations...");
+
+        testArrays();
+        testArrayProblem();
+        //System.exit(0);
+
+        System.out.println(
+            "AnnoSimpleField " + AnnoSimpleField.class.isAnnotation() +
+            ", SimplyNoted " + SimplyNoted.class.isAnnotation());
+
+        Class clazz;
+        clazz = SimplyNoted.class;
+        printAnnotations(clazz);
+        clazz = INoted.class;
+        printAnnotations(clazz);
+        clazz = SubNoted.class;
+        printAnnotations(clazz);
+        clazz = FullyNoted.class;
+        printAnnotations(clazz);
+
+        Annotation anno;
+
+        // this is expected to be non-null
+        anno = SimplyNoted.class.getAnnotation(AnnoSimpleType.class);
+        System.out.println("SimplyNoted.get(AnnoSimpleType) = " + anno);
+        // this is non-null if the @Inherited tag is present
+        anno = SubNoted.class.getAnnotation(AnnoSimpleType.class);
+        System.out.println("SubNoted.get(AnnoSimpleType) = " + anno);
+    }
+}
diff --git a/tests/004-annotations/src/android/test/anno/package-info.java b/tests/004-annotations/src/android/test/anno/package-info.java
new file mode 100644
index 0000000..74b11f9
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/package-info.java
@@ -0,0 +1,2 @@
+@AnnoSimplePackage
+package android.test.anno;
diff --git a/tests/005-args/expected.txt b/tests/005-args/expected.txt
new file mode 100644
index 0000000..094fbbb
--- /dev/null
+++ b/tests/005-args/expected.txt
@@ -0,0 +1,5 @@
+VALUES: 1122334455667788 9887766554433221 1122334455667788
+VALUES: 1234605616436508552 -7455860480511102431 1234605616436508552
+1234605616436508552
+j = 1234605616436508552
+a=123 c=q d=3.343434 j=1234605616436508552 f=0.12345
diff --git a/tests/005-args/info.txt b/tests/005-args/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/005-args/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/005-args/src/ArgsTest.java b/tests/005-args/src/ArgsTest.java
new file mode 100644
index 0000000..2b874cd
--- /dev/null
+++ b/tests/005-args/src/ArgsTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+class ArgsTest{
+    public ArgsTest() {
+
+    }
+
+    private long mLongArray[] = new long[] {
+        0x1122334455667788L, 0x9887766554433221L };
+
+    /**
+     *
+     * @param a
+     * @param c
+     * @param d
+     * @param j
+     * @param f
+     */
+    void argTest(int a, char c, double d, long j, float f) {
+        System.out.println("VALUES: " + Long.toHexString(mLongArray[0]) + " "
+            + Long.toHexString(mLongArray[1]) + " " + Long.toHexString(j));
+        System.out.println("VALUES: " + mLongArray[0] + " "
+            + mLongArray[1] + " " + j);
+
+        System.out.println(j);
+        System.out.println("j = " + j);
+        System.out.println("a=" + a + " c=" + c + " d=" + d
+            + " j=" + j + " f=" + f);
+    }
+}
diff --git a/tests/005-args/src/Main.java b/tests/005-args/src/Main.java
new file mode 100644
index 0000000..1240ec5
--- /dev/null
+++ b/tests/005-args/src/Main.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test passing arguments of various sizes
+ */
+public class Main {
+    public static void main (String args[]) {
+        new ArgsTest()
+                .argTest(123, 'q', 3.343434, 0x1122334455667788L, 0.12345f);
+    }
+}
diff --git a/tests/006-count10/expected.txt b/tests/006-count10/expected.txt
new file mode 100644
index 0000000..8b1acc1
--- /dev/null
+++ b/tests/006-count10/expected.txt
@@ -0,0 +1,10 @@
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
diff --git a/tests/006-count10/info.txt b/tests/006-count10/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/006-count10/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/006-count10/src/Main.java b/tests/006-count10/src/Main.java
new file mode 100644
index 0000000..650d053
--- /dev/null
+++ b/tests/006-count10/src/Main.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Simple loop-and-print
+ */
+public class Main {
+    public static void main(String args[]) {
+        for (int i = 0; i < 10; i++) {
+            System.out.println(i);
+        }
+    }
+}
diff --git a/tests/007-exceptions/expected.txt b/tests/007-exceptions/expected.txt
new file mode 100644
index 0000000..2a31636
--- /dev/null
+++ b/tests/007-exceptions/expected.txt
@@ -0,0 +1,9 @@
+Got an NPE: second throw
+java.lang.NullPointerException: second throw
+	at Main.catchAndRethrow(Main.java:36)
+	at Main.main(Main.java:23)
+	at dalvik.system.NativeStart.main(Native Method)
+Caused by: java.lang.NullPointerException: first throw
+	at Main.throwNullPointerException(Main.java:43)
+	at Main.catchAndRethrow(Main.java:33)
+	... 2 more
diff --git a/tests/007-exceptions/info.txt b/tests/007-exceptions/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/007-exceptions/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/007-exceptions/src/Main.java b/tests/007-exceptions/src/Main.java
new file mode 100644
index 0000000..c7da215
--- /dev/null
+++ b/tests/007-exceptions/src/Main.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/**
+ * Exceptions across method calls
+ */
+public class Main {
+    public static void main (String args[]) {
+        try {
+            catchAndRethrow();
+        } catch (NullPointerException npe) {
+            System.out.print("Got an NPE: ");
+            System.out.println(npe.getMessage());
+            npe.printStackTrace();
+        }
+    }
+
+    private static void catchAndRethrow() {
+        try {
+            throwNullPointerException();
+        } catch (NullPointerException npe) {
+            NullPointerException npe2;
+            npe2 = new NullPointerException("second throw");
+            npe2.initCause(npe);
+            throw npe2;
+        }
+    }
+
+    private static void throwNullPointerException() {
+        throw new NullPointerException("first throw");
+    }
+}
diff --git a/tests/008-instanceof/expected.txt b/tests/008-instanceof/expected.txt
new file mode 100644
index 0000000..77fd0cb
--- /dev/null
+++ b/tests/008-instanceof/expected.txt
@@ -0,0 +1,6 @@
+iface1.mFloaty = 5.0 wahoo
+aa.mFloaty = 5.0 wahoo
+bb.mWhoami = ImplB!
+aaOkay (false) = false
+bbOkay (true) = true
+Caught a ClassCastException (expected)
diff --git a/tests/008-instanceof/info.txt b/tests/008-instanceof/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/008-instanceof/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/008-instanceof/src/Iface1.java b/tests/008-instanceof/src/Iface1.java
new file mode 100644
index 0000000..ba17d45
--- /dev/null
+++ b/tests/008-instanceof/src/Iface1.java
@@ -0,0 +1,13 @@
+// Copyright 2005 The Android Open Source Project
+
+/**
+ * Test stuff.
+ */
+public interface Iface1 {
+
+    public int iFunc1(int ii);
+
+    public float mFloaty = 5.0f;
+
+    public String mWahoo = new String("wahoo");
+}
diff --git a/tests/008-instanceof/src/Iface2.java b/tests/008-instanceof/src/Iface2.java
new file mode 100644
index 0000000..83fe650
--- /dev/null
+++ b/tests/008-instanceof/src/Iface2.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2 {
+
+    public int iFunc2(int ii);
+}
diff --git a/tests/008-instanceof/src/Iface2Sub1.java b/tests/008-instanceof/src/Iface2Sub1.java
new file mode 100644
index 0000000..db3e905
--- /dev/null
+++ b/tests/008-instanceof/src/Iface2Sub1.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2Sub1 extends Iface2, Cloneable {
+
+    //public int iFunc2(int ii);
+}
diff --git a/tests/008-instanceof/src/ImplA.java b/tests/008-instanceof/src/ImplA.java
new file mode 100644
index 0000000..9007001
--- /dev/null
+++ b/tests/008-instanceof/src/ImplA.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplA implements Iface1, Iface2 {
+
+    public int iFunc1(int ii) {
+        return ii+1;
+    }
+    public int iFunc2(int ii) {
+        return ii+2;
+    }
+}
diff --git a/tests/008-instanceof/src/ImplB.java b/tests/008-instanceof/src/ImplB.java
new file mode 100644
index 0000000..619fa00
--- /dev/null
+++ b/tests/008-instanceof/src/ImplB.java
@@ -0,0 +1,16 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplB implements Iface1, Iface2 {
+
+    public int iFunc1(int ii) {
+        return ii+10;
+    }
+    public int iFunc2(int ii) {
+        return ii+20;
+    }
+
+    public static String mWhoami = new String("ImplB!");
+}
diff --git a/tests/008-instanceof/src/ImplBSub.java b/tests/008-instanceof/src/ImplBSub.java
new file mode 100644
index 0000000..f3a7714
--- /dev/null
+++ b/tests/008-instanceof/src/ImplBSub.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Interface test.
+ */
+public class ImplBSub extends ImplB implements /*Iface2,*/ Iface2Sub1 {
+
+    public int iFunc1(int ii) {
+        return ii+100;
+    }
+    public int iFunc2(int ii) {
+        return ii+200;
+    }
+}
diff --git a/tests/008-instanceof/src/Main.java b/tests/008-instanceof/src/Main.java
new file mode 100644
index 0000000..77f3f51
--- /dev/null
+++ b/tests/008-instanceof/src/Main.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test instanceof
+ */
+public class Main {
+    public static void main(String args[]) {
+        Iface1 face1;
+        ImplA aa = new ImplA();
+        ImplBSub bb = new ImplBSub();
+        boolean aaOkay, bbOkay;
+
+        face1 = aa;
+        face1 = bb;
+
+        System.out.println("iface1.mFloaty = " + face1.mFloaty + " " + face1.mWahoo);
+        System.out.println("aa.mFloaty = " + aa.mFloaty + " " + aa.mWahoo);
+        System.out.println("bb.mWhoami = " + bb.mWhoami);
+
+        aaOkay = face1 instanceof ImplA;
+        System.out.print("aaOkay (false) = ");
+        System.out.println(aaOkay);
+        bbOkay = face1 instanceof ImplB;
+        System.out.print("bbOkay (true) = ");
+        System.out.println(bbOkay);
+
+        bb = (ImplBSub) face1;
+        try {
+            aa = (ImplA) face1;
+        }
+        catch (ClassCastException cce) {
+            System.out.println("Caught a ClassCastException (expected)");
+        }
+    }
+}
diff --git a/tests/009-instanceof2/expected.txt b/tests/009-instanceof2/expected.txt
new file mode 100644
index 0000000..74ad202
--- /dev/null
+++ b/tests/009-instanceof2/expected.txt
@@ -0,0 +1,5 @@
+instanceof Serializable = true
+instanceof Cloneable = true
+instanceof Runnable = false
+aaOkay (false) = false
+bbOkay (true) = true
diff --git a/tests/009-instanceof2/info.txt b/tests/009-instanceof2/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/009-instanceof2/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/009-instanceof2/src/Iface1.java b/tests/009-instanceof2/src/Iface1.java
new file mode 100644
index 0000000..ba17d45
--- /dev/null
+++ b/tests/009-instanceof2/src/Iface1.java
@@ -0,0 +1,13 @@
+// Copyright 2005 The Android Open Source Project
+
+/**
+ * Test stuff.
+ */
+public interface Iface1 {
+
+    public int iFunc1(int ii);
+
+    public float mFloaty = 5.0f;
+
+    public String mWahoo = new String("wahoo");
+}
diff --git a/tests/009-instanceof2/src/Iface2.java b/tests/009-instanceof2/src/Iface2.java
new file mode 100644
index 0000000..83fe650
--- /dev/null
+++ b/tests/009-instanceof2/src/Iface2.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2 {
+
+    public int iFunc2(int ii);
+}
diff --git a/tests/009-instanceof2/src/Iface2Sub1.java b/tests/009-instanceof2/src/Iface2Sub1.java
new file mode 100644
index 0000000..db3e905
--- /dev/null
+++ b/tests/009-instanceof2/src/Iface2Sub1.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2Sub1 extends Iface2, Cloneable {
+
+    //public int iFunc2(int ii);
+}
diff --git a/tests/009-instanceof2/src/ImplA.java b/tests/009-instanceof2/src/ImplA.java
new file mode 100644
index 0000000..9007001
--- /dev/null
+++ b/tests/009-instanceof2/src/ImplA.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplA implements Iface1, Iface2 {
+
+    public int iFunc1(int ii) {
+        return ii+1;
+    }
+    public int iFunc2(int ii) {
+        return ii+2;
+    }
+}
diff --git a/tests/009-instanceof2/src/ImplB.java b/tests/009-instanceof2/src/ImplB.java
new file mode 100644
index 0000000..619fa00
--- /dev/null
+++ b/tests/009-instanceof2/src/ImplB.java
@@ -0,0 +1,16 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplB implements Iface1, Iface2 {
+
+    public int iFunc1(int ii) {
+        return ii+10;
+    }
+    public int iFunc2(int ii) {
+        return ii+20;
+    }
+
+    public static String mWhoami = new String("ImplB!");
+}
diff --git a/tests/009-instanceof2/src/ImplBSub.java b/tests/009-instanceof2/src/ImplBSub.java
new file mode 100644
index 0000000..f3a7714
--- /dev/null
+++ b/tests/009-instanceof2/src/ImplBSub.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Interface test.
+ */
+public class ImplBSub extends ImplB implements /*Iface2,*/ Iface2Sub1 {
+
+    public int iFunc1(int ii) {
+        return ii+100;
+    }
+    public int iFunc2(int ii) {
+        return ii+200;
+    }
+}
diff --git a/tests/009-instanceof2/src/Main.java b/tests/009-instanceof2/src/Main.java
new file mode 100644
index 0000000..15a1e50
--- /dev/null
+++ b/tests/009-instanceof2/src/Main.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+/**
+ * more instanceof cases
+ */
+public class Main {
+    public static void main(String args[]) {
+        Iface1[] faceArray;
+        ImplA[] aaArray = new ImplA[5];
+        ImplBSub[] bbArray = new ImplBSub[5];
+        boolean aaOkay, bbOkay;
+
+        faceArray = aaArray;
+        faceArray = bbArray;
+
+        System.out.print("instanceof Serializable = ");
+        System.out.println((Object)aaArray instanceof java.io.Serializable);
+        System.out.print("instanceof Cloneable = ");
+        System.out.println((Object)aaArray instanceof java.lang.Cloneable);
+        System.out.print("instanceof Runnable = ");
+        System.out.println((Object)aaArray instanceof java.lang.Runnable);
+
+        aaOkay = faceArray instanceof ImplA[];
+        System.out.print("aaOkay (false) = ");
+        System.out.println(aaOkay);
+        bbOkay = faceArray instanceof ImplB[];
+        System.out.print("bbOkay (true) = ");
+        System.out.println(bbOkay);
+    }
+}
diff --git a/tests/010-instance/expected.txt b/tests/010-instance/expected.txt
new file mode 100644
index 0000000..219dd06
--- /dev/null
+++ b/tests/010-instance/expected.txt
@@ -0,0 +1,30 @@
+instance begin
+x instanceof X (true): true
+x instanceof Y (false): false
+y instanceof X (true): true
+y instanceof Y (true): true
+xar instanceof Object (true): true
+xar instanceof X (false): false
+xar instanceof X[] (true): true
+xar instanceof Y[] (false): false
+xar instanceof Object[] (true): true
+xar instanceof X[][] (false): false
+yar instanceof X[] (true): true
+xararar instanceof Object (true): true
+xararar instanceof Object[] (true): true
+xararar instanceof X (false): false
+xararar instanceof X[] (false): false
+xararar instanceof X[][] (false): false
+xararar instanceof X[][][] (true): true
+xararar instanceof Object[][][] (true): true
+xararar instanceof Serializable (true): true
+xararar instanceof Serializable[] (true): true
+xararar instanceof Serializable[][] (true): true
+xararar instanceof Serializable[][][] (false): false
+yararar instanceof X[][][] (true): true
+iar instanceof Object (true): true
+iar instanceof Object[] (false): false
+iarar instanceof Object (true): true
+iarar instanceof Object[] (true): true
+iarar instanceof Object[][] (false): false
+instanceof end
diff --git a/tests/010-instance/info.txt b/tests/010-instance/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/010-instance/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/010-instance/src/InstanceTest.java b/tests/010-instance/src/InstanceTest.java
new file mode 100644
index 0000000..78384ff
--- /dev/null
+++ b/tests/010-instance/src/InstanceTest.java
@@ -0,0 +1,93 @@
+// Copyright 2006 The Android Open Source Project
+
+import java.io.Serializable;
+
+/**
+ * Test some instanceof stuff.
+ */
+public class InstanceTest {
+    public static void main(String[] args) {
+        System.out.println("instance begin");
+
+        X x = new X();
+        X[] xar = new X[1];
+        X[][] xarar = new X[1][1];
+        X[][][] xararar = new X[1][1][1];
+        Y y = new Y();
+        Y[] yar = new Y[1];
+        Y[][] yarar = new Y[1][1];
+        Y[][][] yararar = new Y[1][1][1];
+        int[] iar = new int[1];
+        int[][] iarar = new int[1][1];
+        Object test;
+
+        test = x;
+        System.out.println("x instanceof X (true): " + (test instanceof X));
+        System.out.println("x instanceof Y (false): " + (test instanceof Y));
+        test = y;
+        System.out.println("y instanceof X (true): " + (test instanceof X));
+        System.out.println("y instanceof Y (true): " + (test instanceof Y));
+
+        test = xar;
+        System.out.println("xar instanceof Object (true): "
+            + (test instanceof Object));
+        System.out.println("xar instanceof X (false): "
+            + (test instanceof X));
+        System.out.println("xar instanceof X[] (true): "
+            + (test instanceof X[]));
+        System.out.println("xar instanceof Y[] (false): "
+            + (test instanceof Y[]));
+        System.out.println("xar instanceof Object[] (true): "
+            + (test instanceof Object[]));
+        System.out.println("xar instanceof X[][] (false): "
+            + (test instanceof X[][]));
+        test = yar;
+        System.out.println("yar instanceof X[] (true): "
+            + (test instanceof X[]));
+
+        test = xararar;
+        System.out.println("xararar instanceof Object (true): "
+            + (test instanceof Object));
+        System.out.println("xararar instanceof Object[] (true): "
+            + (test instanceof Object[]));
+        System.out.println("xararar instanceof X (false): "
+            + (test instanceof X));
+        System.out.println("xararar instanceof X[] (false): "
+            + (test instanceof X[]));
+        System.out.println("xararar instanceof X[][] (false): "
+            + (test instanceof X[][]));
+        System.out.println("xararar instanceof X[][][] (true): "
+            + (test instanceof X[][][]));
+        System.out.println("xararar instanceof Object[][][] (true): "
+            + (test instanceof Object[][][]));
+
+        System.out.println("xararar instanceof Serializable (true): "
+            + (test instanceof Serializable));
+        System.out.println("xararar instanceof Serializable[] (true): "
+            + (test instanceof Serializable[]));
+        System.out.println("xararar instanceof Serializable[][] (true): "
+            + (test instanceof Serializable[][]));
+        System.out.println("xararar instanceof Serializable[][][] (false): "
+            + (test instanceof Serializable[][][]));
+
+        test = yararar;
+        System.out.println("yararar instanceof X[][][] (true): "
+            + (test instanceof X[][][]));
+
+        test = iar;
+        System.out.println("iar instanceof Object (true): "
+            + (test instanceof Object));
+        System.out.println("iar instanceof Object[] (false): "
+            + (test instanceof Object[]));
+
+        test = iarar;
+        System.out.println("iarar instanceof Object (true): "
+            + (test instanceof Object));
+        System.out.println("iarar instanceof Object[] (true): "
+            + (test instanceof Object[]));
+        System.out.println("iarar instanceof Object[][] (false): "
+            + (test instanceof Object[][]));
+
+        System.out.println("instanceof end");
+    }
+}
diff --git a/tests/010-instance/src/Main.java b/tests/010-instance/src/Main.java
new file mode 100644
index 0000000..ab0ab5e
--- /dev/null
+++ b/tests/010-instance/src/Main.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+/**
+ * even more instanceof cases
+ */
+public class Main {
+    public static void main(String args[]) {
+        InstanceTest.main(null);
+    }
+}
diff --git a/tests/010-instance/src/X.java b/tests/010-instance/src/X.java
new file mode 100644
index 0000000..d664d48
--- /dev/null
+++ b/tests/010-instance/src/X.java
@@ -0,0 +1,8 @@
+class X {
+    public X() {
+    }
+
+    int foo() {
+        return 0;
+    }
+}
diff --git a/tests/010-instance/src/Y.java b/tests/010-instance/src/Y.java
new file mode 100644
index 0000000..2b2c371
--- /dev/null
+++ b/tests/010-instance/src/Y.java
@@ -0,0 +1,8 @@
+class Y extends X {
+    public Y() {
+    }
+
+    int bar() {
+        return 1;
+    }
+}
diff --git a/tests/011-array-copy/expected.txt b/tests/011-array-copy/expected.txt
new file mode 100644
index 0000000..0ef2029
--- /dev/null
+++ b/tests/011-array-copy/expected.txt
@@ -0,0 +1,4 @@
+string -> object
+object -> string
+object -> string (modified)
+caught ArrayStoreException (expected)
diff --git a/tests/011-array-copy/info.txt b/tests/011-array-copy/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/011-array-copy/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/011-array-copy/src/Iface1.java b/tests/011-array-copy/src/Iface1.java
new file mode 100644
index 0000000..ba17d45
--- /dev/null
+++ b/tests/011-array-copy/src/Iface1.java
@@ -0,0 +1,13 @@
+// Copyright 2005 The Android Open Source Project
+
+/**
+ * Test stuff.
+ */
+public interface Iface1 {
+
+    public int iFunc1(int ii);
+
+    public float mFloaty = 5.0f;
+
+    public String mWahoo = new String("wahoo");
+}
diff --git a/tests/011-array-copy/src/Iface2.java b/tests/011-array-copy/src/Iface2.java
new file mode 100644
index 0000000..83fe650
--- /dev/null
+++ b/tests/011-array-copy/src/Iface2.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2 {
+
+    public int iFunc2(int ii);
+}
diff --git a/tests/011-array-copy/src/ImplA.java b/tests/011-array-copy/src/ImplA.java
new file mode 100644
index 0000000..9007001
--- /dev/null
+++ b/tests/011-array-copy/src/ImplA.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplA implements Iface1, Iface2 {
+
+    public int iFunc1(int ii) {
+        return ii+1;
+    }
+    public int iFunc2(int ii) {
+        return ii+2;
+    }
+}
diff --git a/tests/011-array-copy/src/Main.java b/tests/011-array-copy/src/Main.java
new file mode 100644
index 0000000..9cb8238
--- /dev/null
+++ b/tests/011-array-copy/src/Main.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+/**
+ * System.arraycopy cases
+ */
+public class Main {
+    public static void main(String args[]) {
+        String[] stringArray = new String[8];
+        Object[] objectArray = new Object[8];
+
+        for (int i = 0; i < stringArray.length; i++)
+            stringArray[i] = new String(Integer.toString(i));
+
+        System.out.println("string -> object");
+        System.arraycopy(stringArray, 0, objectArray, 0, stringArray.length);
+        System.out.println("object -> string");
+        System.arraycopy(objectArray, 0, stringArray, 0, stringArray.length);
+        System.out.println("object -> string (modified)");
+        objectArray[4] = new ImplA();
+        try {
+            System.arraycopy(objectArray, 0, stringArray, 0,stringArray.length);
+        }
+        catch (ArrayStoreException ase) {
+            System.out.println("caught ArrayStoreException (expected)");
+        }
+    }
+}
diff --git a/tests/012-math/expected.txt b/tests/012-math/expected.txt
new file mode 100644
index 0000000..af9708e
--- /dev/null
+++ b/tests/012-math/expected.txt
@@ -0,0 +1,32 @@
+res:10
+res:-4
+res:2
+res:-2
+res:21
+res:0
+res:3
+res:4
+res:384
+res:0
+res:0
+a:10
+a:3
+a:2
+a:-3
+a:-21
+a:-3
+a:-3
+a:-6
+a:-768
+a:-6
+a:33554431
+fres:10.0
+fres:-4.0
+fres:21.0
+fres:0.42857142857142855
+fres:3.0
+f:10.0
+f:3.0
+f:21.0
+f:3.0
+f:3.0
diff --git a/tests/012-math/info.txt b/tests/012-math/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/012-math/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/012-math/src/Main.java b/tests/012-math/src/Main.java
new file mode 100644
index 0000000..87ea40b
--- /dev/null
+++ b/tests/012-math/src/Main.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+/**
+ * test simple math opers
+ */
+public class Main {
+    public static void main(String args[]) {
+        int pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
+        int pad8, pad9, pad10, pad11, pad12, pad13, pad14, pad15;
+        int a, b, res;
+        //long a, b, res;
+
+        a = 3;
+        b = 7;
+
+        res = a + b;
+        System.out.println("res:" +res);
+        res = a - b;
+        System.out.println("res:" +res);
+        res = 5 - a;
+        System.out.println("res:" +res);
+        res = a - 5;
+        System.out.println("res:" +res);
+        res = a * b;
+        System.out.println("res:" +res);
+        res = a / b;
+        System.out.println("res:" +res);
+        res = a % b;
+        System.out.println("res:" +res);
+        res = a ^ b;
+        System.out.println("res:" +res);
+        res = a << b;
+        System.out.println("res:" +res);
+        res = a >> b;
+        System.out.println("res:" +res);
+        res = a >>> b;
+        System.out.println("res:" +res);
+
+        a += b;
+        System.out.println("a:" +a);
+        a -= b;
+        System.out.println("a:" +a);
+        a = 5 - a;
+        System.out.println("a:" +a);
+        a -= 5;
+        System.out.println("a:" +a);
+        a *= b;
+        System.out.println("a:" +a);
+        a /= b;
+        System.out.println("a:" +a);
+        a %= b;
+        System.out.println("a:" +a);
+        a ^= b;
+        System.out.println("a:" +a);
+        a <<= b;
+        System.out.println("a:" +a);
+        a >>= b;
+        System.out.println("a:" +a);
+        a >>>= b;
+        System.out.println("a:" +a);
+
+        double f, g, fres;
+
+        f = 3.0f;
+        g = 7.0f;
+
+        fres = f + g;
+        System.out.println("fres:" +fres);
+        fres = f - g;
+        System.out.println("fres:" +fres);
+        fres = f * g;
+        System.out.println("fres:" +fres);
+        fres = f / g;
+        System.out.println("fres:" +fres);
+        fres = f % g;
+        System.out.println("fres:" +fres);
+        f += g;
+        System.out.println("f:" +f);
+        f -= g;
+        System.out.println("f:" +f);
+        f *= g;
+        System.out.println("f:" +f);
+        f /= g;
+        System.out.println("f:" +f);
+        f %= g;
+        System.out.println("f:" +f);
+    }
+}
diff --git a/tests/013-math2/expected.txt b/tests/013-math2/expected.txt
new file mode 100644
index 0000000..d36c468
--- /dev/null
+++ b/tests/013-math2/expected.txt
@@ -0,0 +1 @@
+a:32003
diff --git a/tests/013-math2/info.txt b/tests/013-math2/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/013-math2/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/013-math2/src/Main.java b/tests/013-math2/src/Main.java
new file mode 100644
index 0000000..819571d
--- /dev/null
+++ b/tests/013-math2/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/**
+ * test add by a 16-bit constant
+ */
+public class Main {
+    public static void main(String args[]) {
+        int a, b, res;
+
+        a = 3;
+        b = 7;
+
+        // a 16-bit constant
+        a += 32000;
+        System.out.println("a:" +a);
+    }
+}
diff --git a/tests/014-math3/expected.txt b/tests/014-math3/expected.txt
new file mode 100644
index 0000000..bda3dc7
--- /dev/null
+++ b/tests/014-math3/expected.txt
@@ -0,0 +1 @@
+testMath3 success
diff --git a/tests/014-math3/info.txt b/tests/014-math3/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/014-math3/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/014-math3/src/Main.java b/tests/014-math3/src/Main.java
new file mode 100644
index 0000000..f55b17a
--- /dev/null
+++ b/tests/014-math3/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test math exceptions
+ */
+public class Main {
+    public static void main(String args[]) {
+        int expectedThrows = 2;
+        int i;
+        long j;
+        float f = 0.0f;
+        double d = 0.0;
+
+        try { i = 10 / 0; }
+        catch (ArithmeticException ae) {
+            expectedThrows--;
+        }
+
+        try { j = 10L / 0L; }
+        catch (ArithmeticException ae) {
+            expectedThrows--;
+        }
+
+        /*
+         * Floating point divide by zero doesn't throw an exception -- the
+         * result is just NaN.
+         */
+        try { f = 10.0f / f; }
+        catch (ArithmeticException ae) {
+            expectedThrows--;
+        }
+
+        try { d = 10.0 / d; }
+        catch (ArithmeticException ae) {
+            expectedThrows--;
+        }
+
+        if (expectedThrows != 0)
+            System.out.println("HEY: expected throws is " + expectedThrows);
+        else
+            System.out.println("testMath3 success");
+    }
+}
diff --git a/tests/015-switch/expected.txt b/tests/015-switch/expected.txt
new file mode 100644
index 0000000..ca3b518
--- /dev/null
+++ b/tests/015-switch/expected.txt
@@ -0,0 +1,10 @@
+CORRECT (one)
+CORRECT (not found)
+CORRECT (large)
+CORRECT (large2)
+CORRECT (large3)
+CORRECT (not found)
+CORRECT (not found)
+CORRECT (default only)
+CORRECT big sparse / first
+CORRECT big sparse / last
diff --git a/tests/015-switch/info.txt b/tests/015-switch/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/015-switch/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/015-switch/src/Main.java b/tests/015-switch/src/Main.java
new file mode 100644
index 0000000..7198e2b
--- /dev/null
+++ b/tests/015-switch/src/Main.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test switch() blocks
+ */
+public class Main {
+    public static void main(String args[]) {
+        int a = 1;
+
+        switch (a) {
+            case -1: System.out.print("neg one\n"); break;
+            case 0: System.out.print("zero\n"); break;
+            case 1: System.out.print("CORRECT (one)\n"); break;
+            case 2: System.out.print("two\n"); break;
+            case 3: System.out.print("three\n"); break;
+            case 4: System.out.print("four\n"); break;
+            default: System.out.print("???\n"); break;
+        }
+        switch (a) {
+            case 3: System.out.print("three\n"); break;
+            case 4: System.out.print("four\n"); break;
+            default: System.out.print("CORRECT (not found)\n"); break;
+        }
+
+        a = 0x12345678;
+
+        switch (a) {
+            case 0x12345678: System.out.print("CORRECT (large)\n"); break;
+            case 0x12345679: System.out.print("large+1\n"); break;
+            default: System.out.print("nuts\n"); break;
+        }
+        switch (a) {
+            case 0x12345678: System.out.print("CORRECT (large2)\n"); break;
+            case 0x12345700: System.out.print("large+many\n"); break;
+            default: System.out.print("nuts\n"); break;
+        }
+        switch (a) {
+            case 57: System.out.print("fifty-seven!\n"); break;
+            case -6: System.out.print("neg six!\n"); break;
+            case 0x12345678: System.out.print("CORRECT (large3)\n"); break;
+            case 22: System.out.print("twenty-two!\n"); break;
+            case 3: System.out.print("three!\n"); break;
+            default: System.out.print("huh?\n"); break;
+        }
+        switch (a) {
+            case -6: System.out.print("neg six!\n"); break;
+            case 3: System.out.print("three!\n"); break;
+            default: System.out.print("CORRECT (not found)\n"); break;
+        }
+
+        a = -5;
+        switch (a) {
+            case 12: System.out.print("twelve\n"); break;
+            case -5: System.out.print("CORRECT (not found)\n"); break;
+            case 0: System.out.print("zero\n"); break;
+            default: System.out.print("wah?\n"); break;
+        }
+
+        switch (a) {
+            default: System.out.print("CORRECT (default only)\n"); break;
+        }
+
+        a = -10;
+        switch (a) {
+            case -10: System.out.print("CORRECT big sparse / first\n"); break;
+            case -5: System.out.print("neg five\n"); break;
+            case 0: System.out.print("zero\n"); break;
+            case 5: System.out.print("five\n"); break;
+            case 10: System.out.print("ten\n"); break;
+            case 15: System.out.print("fifteen\n"); break;
+            case 20: System.out.print("twenty\n"); break;
+            case 50: System.out.print("fifty\n"); break;
+            case 100: System.out.print("hundred\n"); break;
+            default: System.out.print("blah!\n"); break;
+        }
+
+        a = 100;
+        switch (a) {
+            case -10: System.out.print("neg ten\n"); break;
+            case -5: System.out.print("neg five\n"); break;
+            case 0: System.out.print("zero\n"); break;
+            case 5: System.out.print("five\n"); break;
+            case 10: System.out.print("ten\n"); break;
+            case 15: System.out.print("fifteen\n"); break;
+            case 20: System.out.print("twenty\n"); break;
+            case 50: System.out.print("fifty\n"); break;
+            case 100: System.out.print("CORRECT big sparse / last\n"); break;
+            default: System.out.print("blah!\n"); break;
+        }
+    }
+}
diff --git a/tests/016-intern/expected.txt b/tests/016-intern/expected.txt
new file mode 100644
index 0000000..7d91963
--- /dev/null
+++ b/tests/016-intern/expected.txt
@@ -0,0 +1 @@
+good! foobar
diff --git a/tests/016-intern/info.txt b/tests/016-intern/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/016-intern/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/016-intern/src/Main.java b/tests/016-intern/src/Main.java
new file mode 100644
index 0000000..4306863
--- /dev/null
+++ b/tests/016-intern/src/Main.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/**
+ * Interned strings
+ */
+public class Main {
+    public static void main(String args[]) {
+        String a, b;
+        String foo = "foo";
+        String bar = "bar";
+
+        a = foo.concat(bar).intern();
+        b = foo.concat(bar).intern();
+        if (a == b && foo != bar) {
+            System.out.println("good! " + a);
+        } else {
+            System.out.println("bad!");
+        }
+    }
+}
diff --git a/tests/017-float/expected.txt b/tests/017-float/expected.txt
new file mode 100644
index 0000000..2062f9e
--- /dev/null
+++ b/tests/017-float/expected.txt
@@ -0,0 +1,3 @@
+base values: d=3.1415926535 f=3.1415927
+base values: d=3.1415926535 f=3.1415927
+base values: f=3.1415927 d=3.1415926535
diff --git a/tests/017-float/info.txt b/tests/017-float/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/017-float/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/017-float/src/Main.java b/tests/017-float/src/Main.java
new file mode 100644
index 0000000..a5dbe1e
--- /dev/null
+++ b/tests/017-float/src/Main.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+/**
+ * I dont know what this test does.
+ */
+public class Main {
+    public static void main(String args[]) {
+        float f = 3.1415926535f;
+        double d = 3.1415926535;
+        //float fd = (float) d;
+        //Float off = new Float(f);
+        //Float ofd = new Float(d);
+        System.out.println("base values: d=" + d + " f=" + f);
+        System.out.println("base values: d=" + d + " f=" + f);
+        System.out.println("base values: f=" + f + " d=" + d);
+        //System.out.println("object values: off="
+        //    + off.floatValue() + " ofd=" + ofd.floatValue());
+    }
+}
diff --git a/tests/018-stack-overflow/expected.txt b/tests/018-stack-overflow/expected.txt
new file mode 100644
index 0000000..7797816
--- /dev/null
+++ b/tests/018-stack-overflow/expected.txt
@@ -0,0 +1,2 @@
+caught SOE
+SOE test done
diff --git a/tests/018-stack-overflow/info.txt b/tests/018-stack-overflow/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/018-stack-overflow/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/018-stack-overflow/src/Main.java b/tests/018-stack-overflow/src/Main.java
new file mode 100644
index 0000000..f79c269
--- /dev/null
+++ b/tests/018-stack-overflow/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+/**
+ * generate a stack overflow condition and catch it
+ */
+public class Main {
+    public static void main(String args[]) {
+        try {
+            stackOverflowTestSub(0.0, 0.0, 0.0);
+        }
+        catch (StackOverflowError soe) {
+            System.out.println("caught SOE");
+        }
+        System.out.println("SOE test done");
+    }
+
+    private static void stackOverflowTestSub(double pad1, double pad2,
+            double pad3) {
+        stackOverflowTestSub(pad1, pad2, pad3);
+    }
+}
diff --git a/tests/019-wrong-array-type/expected.txt b/tests/019-wrong-array-type/expected.txt
new file mode 100644
index 0000000..c0ed716
--- /dev/null
+++ b/tests/019-wrong-array-type/expected.txt
@@ -0,0 +1 @@
+Got correct array store exception
diff --git a/tests/019-wrong-array-type/info.txt b/tests/019-wrong-array-type/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/019-wrong-array-type/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/019-wrong-array-type/src/Main.java b/tests/019-wrong-array-type/src/Main.java
new file mode 100644
index 0000000..c424ae9
--- /dev/null
+++ b/tests/019-wrong-array-type/src/Main.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+/**
+ * Stuff the wrong type object into an array.
+ */
+public class Main {
+    public static void main(String args[]) {
+        String[] strArray = new String[1];
+
+        Object[] objArray = strArray;
+
+        try {
+            objArray[0] = new Integer(1);
+            System.out.println("Array store succeeded?!");
+        } catch (ArrayStoreException ase) {
+            System.out.println("Got correct array store exception");
+        }
+    }
+}
diff --git a/tests/020-string/expected.txt b/tests/020-string/expected.txt
new file mode 100644
index 0000000..081fea3
--- /dev/null
+++ b/tests/020-string/expected.txt
@@ -0,0 +1,7 @@
+testStr is 'This is a very nice string'
+This is a very nice string
+Compare result is 32
+Compare unicode: -65302
+Got expected exception
+subStr is 'uick brown fox jumps over the lazy '
+Indexes are: 0:-1:0:43:33:-1:18:13:13:-1:18:18:-1:13:-1:-1:-1
diff --git a/tests/020-string/info.txt b/tests/020-string/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/020-string/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/020-string/src/Main.java b/tests/020-string/src/Main.java
new file mode 100644
index 0000000..bb8ce1f
--- /dev/null
+++ b/tests/020-string/src/Main.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+/**
+ * Simple string test.
+ */
+public class Main {
+    public static void main(String args[]) {
+        basicTest();
+        indexTest();
+    }
+
+    public static void basicTest() {
+        String baseStr = "*** This is a very nice string!!!";
+        String testStr;
+        int i;
+
+        testStr = baseStr.substring(4, baseStr.length() - 3);
+        System.out.println("testStr is '" + testStr + "'");
+
+        /* sloppy for loop */
+        for (i = 0; i < testStr.length(); i++)
+            System.out.print(testStr.charAt(i));
+        System.out.print("\n");
+
+        String testStr2 = "This is a very nice strinG";
+        if (testStr.length() != testStr2.length())
+            System.out.println("WARNING: stringTest length mismatch");
+
+        System.out.println("Compare result is " + testStr.compareTo(testStr2));
+
+        // expected: -65302
+        String s1 = "\u0c6d\u0cb6\u0d00\u0000\u0080\u0080\u0080\u0000\u0002\u0002\u0002\u0000\u00e9\u00e9\u00e9";
+        String s2 = "\u0c6d\u0cb6\u0d00\u0000\u0080\u0080\u0080\u0000\u0002\u0002\u0002\u0000\uffff\uffff\uffff\u00e9\u00e9\u00e9";
+        System.out.println("Compare unicode: " + s1.compareTo(s2));
+
+        try {
+            testStr.charAt(500);
+            System.out.println("GLITCH: expected exception");
+        } catch (StringIndexOutOfBoundsException sioobe) {
+            System.out.println("Got expected exception");
+        }
+    }
+
+    public static void indexTest() {
+        String baseStr = "The quick brown fox jumps over the lazy dog!";
+        String subStr;
+
+        subStr = baseStr.substring(5, baseStr.length() - 4);
+        System.out.println("subStr is '" + subStr + "'");
+
+        System.out.println("Indexes are: " +
+            baseStr.indexOf('T') + ":" +
+            subStr.indexOf('T') + ":" +
+            subStr.indexOf('u') + ":" +
+            baseStr.indexOf('!') + ":" +
+            subStr.indexOf('y') + ":" +
+            subStr.indexOf('d') + ":" +
+            baseStr.indexOf('x') + ":" +
+            subStr.indexOf('x', 0) + ":" +
+            subStr.indexOf('x', -1) + ":" +
+            subStr.indexOf('x', 200) + ":" +
+            baseStr.indexOf('x', 17) + ":" +
+            baseStr.indexOf('x', 18) + ":" +
+            baseStr.indexOf('x', 19) + ":" +
+            subStr.indexOf('x', 13) + ":" +
+            subStr.indexOf('x', 14) + ":" +
+            subStr.indexOf('&') + ":" +
+            baseStr.indexOf(0x12341234));
+    }
+}
diff --git a/tests/021-string2/expected.txt b/tests/021-string2/expected.txt
new file mode 100644
index 0000000..bd7f049
--- /dev/null
+++ b/tests/021-string2/expected.txt
@@ -0,0 +1 @@
+Got expected npe
diff --git a/tests/021-string2/info.txt b/tests/021-string2/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/021-string2/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/021-string2/src/Main.java b/tests/021-string2/src/Main.java
new file mode 100644
index 0000000..87e4baf
--- /dev/null
+++ b/tests/021-string2/src/Main.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+import junit.framework.Assert;
+
+/**
+ * more string tests
+ */
+public class Main {
+    public static void main(String args[]) {
+        String test = "0123456789";
+        String test1 = new String("0123456789");    // different object
+        String test2 = new String("0123456780");    // different value
+        String offset = new String("xxx0123456789yyy");
+        String sub = offset.substring(3, 13);
+        Object blah = new Object();
+
+        Assert.assertTrue(test.equals(test));
+        Assert.assertTrue(test.equals(test1));
+        Assert.assertFalse(test.equals(test2));
+
+        Assert.assertEquals(test.compareTo(test1), 0);
+        Assert.assertTrue(test1.compareTo(test2) > 0);
+        Assert.assertTrue(test2.compareTo(test1) < 0);
+
+        /* compare string with a nonzero offset, in left/right side */
+        Assert.assertEquals(test.compareTo(sub), 0);
+        Assert.assertEquals(sub.compareTo(test), 0);
+        Assert.assertTrue(test.equals(sub));
+        Assert.assertTrue(sub.equals(test));
+        /* same base, one is a substring */
+        Assert.assertFalse(offset.equals(sub));
+        Assert.assertFalse(sub.equals(offset));
+        /* wrong class */
+        Assert.assertFalse(test.equals(blah));
+
+        /* null ptr - throw */
+        try {
+            test.compareTo(null);
+            Assert.fail("didn't get expected npe");
+        } catch (NullPointerException npe) {
+            System.out.println("Got expected npe");
+        }
+        /* null ptr - ok */
+        Assert.assertFalse(test.equals(null));
+
+        test = test.substring(1);
+        Assert.assertTrue(test.equals("123456789"));
+        Assert.assertFalse(test.equals(test1));
+
+        test = test.substring(1);
+        Assert.assertTrue(test.equals("23456789"));
+
+        test = test.substring(1);
+        Assert.assertTrue(test.equals("3456789"));
+
+        test = test.substring(1);
+        Assert.assertTrue(test.equals("456789"));
+
+        test = test.substring(3,5);
+        Assert.assertTrue(test.equals("78"));
+
+        test = "this/is/a/path";
+        String[] strings = test.split("/");
+        Assert.assertEquals(4, strings.length);
+
+        Assert.assertEquals("this is a path", test.replaceAll("/", " "));
+        Assert.assertEquals("this is a path", test.replace("/", " "));
+    }
+}
diff --git a/tests/021-string2/src/junit/framework/Assert.java b/tests/021-string2/src/junit/framework/Assert.java
new file mode 100644
index 0000000..364e646
--- /dev/null
+++ b/tests/021-string2/src/junit/framework/Assert.java
@@ -0,0 +1,291 @@
+package junit.framework;
+
+/**
+ * A set of assert methods.  Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+    /**
+     * Protect constructor since it is a static only class
+     */
+    protected Assert() {
+    }
+
+    /**
+     * Asserts that a condition is true. If it isn't it throws
+     * an AssertionFailedError with the given message.
+     */
+    static public void assertTrue(String message, boolean condition) {
+        if (!condition)
+            fail(message);
+    }
+    /**
+     * Asserts that a condition is true. If it isn't it throws
+     * an AssertionFailedError.
+     */
+    static public void assertTrue(boolean condition) {
+        assertTrue(null, condition);
+    }
+    /**
+     * Asserts that a condition is false. If it isn't it throws
+     * an AssertionFailedError with the given message.
+     */
+    static public void assertFalse(String message, boolean condition) {
+        assertTrue(message, !condition);
+    }
+    /**
+     * Asserts that a condition is false. If it isn't it throws
+     * an AssertionFailedError.
+     */
+    static public void assertFalse(boolean condition) {
+        assertFalse(null, condition);
+    }
+    /**
+     * Fails a test with the given message.
+     */
+    static public void fail(String message) {
+        throw new AssertionFailedError(message);
+    }
+    /**
+     * Fails a test with no message.
+     */
+    static public void fail() {
+        fail(null);
+    }
+    /**
+     * Asserts that two objects are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, Object expected, Object actual) {
+        if (expected == null && actual == null)
+            return;
+        if (expected != null && expected.equals(actual))
+            return;
+        failNotEquals(message, expected, actual);
+    }
+    /**
+     * Asserts that two objects are equal. If they are not
+     * an AssertionFailedError is thrown.
+     */
+    static public void assertEquals(Object expected, Object actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two Strings are equal.
+     */
+    static public void assertEquals(String message, String expected, String actual) {
+        if (expected == null && actual == null)
+            return;
+        if (expected != null && expected.equals(actual))
+            return;
+        throw new ComparisonFailure(message, expected, actual);
+    }
+    /**
+     * Asserts that two Strings are equal.
+     */
+    static public void assertEquals(String expected, String actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two doubles are equal concerning a delta.  If they are not
+     * an AssertionFailedError is thrown with the given message.  If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(String message, double expected, double actual, double delta) {
+        // handle infinity specially since subtracting to infinite values gives NaN and the
+        // the following test fails
+        if (Double.isInfinite(expected)) {
+            if (!(expected == actual))
+                failNotEquals(message, new Double(expected), new Double(actual));
+        } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
+            failNotEquals(message, new Double(expected), new Double(actual));
+    }
+    /**
+     * Asserts that two doubles are equal concerning a delta. If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(double expected, double actual, double delta) {
+        assertEquals(null, expected, actual, delta);
+    }
+    /**
+     * Asserts that two floats are equal concerning a delta. If they are not
+     * an AssertionFailedError is thrown with the given message.  If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(String message, float expected, float actual, float delta) {
+         // handle infinity specially since subtracting to infinite values gives NaN and the
+        // the following test fails
+        if (Float.isInfinite(expected)) {
+            if (!(expected == actual))
+                failNotEquals(message, new Float(expected), new Float(actual));
+        } else if (!(Math.abs(expected-actual) <= delta))
+              failNotEquals(message, new Float(expected), new Float(actual));
+    }
+    /**
+     * Asserts that two floats are equal concerning a delta. If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(float expected, float actual, float delta) {
+        assertEquals(null, expected, actual, delta);
+    }
+    /**
+     * Asserts that two longs are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, long expected, long actual) {
+        assertEquals(message, new Long(expected), new Long(actual));
+    }
+    /**
+     * Asserts that two longs are equal.
+     */
+    static public void assertEquals(long expected, long actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two booleans are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, boolean expected, boolean actual) {
+            assertEquals(message, new Boolean(expected), new Boolean(actual));
+      }
+    /**
+     * Asserts that two booleans are equal.
+      */
+    static public void assertEquals(boolean expected, boolean actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two bytes are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+      static public void assertEquals(String message, byte expected, byte actual) {
+        assertEquals(message, new Byte(expected), new Byte(actual));
+    }
+    /**
+        * Asserts that two bytes are equal.
+     */
+    static public void assertEquals(byte expected, byte actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two chars are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+      static public void assertEquals(String message, char expected, char actual) {
+            assertEquals(message, new Character(expected), new Character(actual));
+      }
+    /**
+     * Asserts that two chars are equal.
+     */
+      static public void assertEquals(char expected, char actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two shorts are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, short expected, short actual) {
+            assertEquals(message, new Short(expected), new Short(actual));
+    }
+      /**
+     * Asserts that two shorts are equal.
+     */
+    static public void assertEquals(short expected, short actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two ints are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+      static public void assertEquals(String message, int expected, int actual) {
+        assertEquals(message, new Integer(expected), new Integer(actual));
+      }
+      /**
+        * Asserts that two ints are equal.
+     */
+      static public void assertEquals(int expected, int actual) {
+          assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that an object isn't null.
+     */
+    static public void assertNotNull(Object object) {
+        assertNotNull(null, object);
+    }
+    /**
+     * Asserts that an object isn't null. If it is
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertNotNull(String message, Object object) {
+        assertTrue(message, object != null);
+    }
+    /**
+     * Asserts that an object is null.
+     */
+    static public void assertNull(Object object) {
+        assertNull(null, object);
+    }
+    /**
+     * Asserts that an object is null.  If it is not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertNull(String message, Object object) {
+        assertTrue(message, object == null);
+    }
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertSame(String message, Object expected, Object actual) {
+        if (expected == actual)
+            return;
+        failNotSame(message, expected, actual);
+    }
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * the same an AssertionFailedError is thrown.
+     */
+    static public void assertSame(Object expected, Object actual) {
+        assertSame(null, expected, actual);
+    }
+     /**
+      * Asserts that two objects refer to the same object. If they are not
+      * an AssertionFailedError is thrown with the given message.
+      */
+    static public void assertNotSame(String message, Object expected, Object actual) {
+        if (expected == actual)
+            failSame(message);
+    }
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * the same an AssertionFailedError is thrown.
+     */
+    static public void assertNotSame(Object expected, Object actual) {
+        assertNotSame(null, expected, actual);
+    }
+
+    static private void failSame(String message) {
+        String formatted= "";
+         if (message != null)
+             formatted= message+" ";
+         fail(formatted+"expected not same");
+    }
+
+    static private void failNotSame(String message, Object expected, Object actual) {
+        String formatted= "";
+        if (message != null)
+            formatted= message+" ";
+        fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+    }
+
+    static private void failNotEquals(String message, Object expected, Object actual) {
+        fail(format(message, expected, actual));
+    }
+
+    static String format(String message, Object expected, Object actual) {
+        String formatted= "";
+        if (message != null)
+            formatted= message+" ";
+        return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+    }
+}
diff --git a/tests/021-string2/src/junit/framework/AssertionFailedError.java b/tests/021-string2/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..e9cb3a3
--- /dev/null
+++ b/tests/021-string2/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,13 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends Error {
+
+    public AssertionFailedError () {
+    }
+    public AssertionFailedError (String message) {
+        super (message);
+    }
+}
diff --git a/tests/021-string2/src/junit/framework/ComparisonFailure.java b/tests/021-string2/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..0cb2cee
--- /dev/null
+++ b/tests/021-string2/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,68 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ *
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+    private String fExpected;
+    private String fActual;
+
+    /**
+     * Constructs a comparison failure.
+     * @param message the identifying message or null
+     * @param expected the expected string value
+     * @param actual the actual string value
+     */
+    public ComparisonFailure (String message, String expected, String actual) {
+        super (message);
+        fExpected= expected;
+        fActual= actual;
+    }
+
+    /**
+     * Returns "..." in place of common prefix and "..." in
+     * place of common suffix between expected and actual.
+     *
+     * @see java.lang.Throwable#getMessage()
+     */
+    public String getMessage() {
+        if (fExpected == null || fActual == null)
+            return Assert.format(super.getMessage(), fExpected, fActual);
+
+        int end= Math.min(fExpected.length(), fActual.length());
+
+        int i= 0;
+        for(; i < end; i++) {
+            if (fExpected.charAt(i) != fActual.charAt(i))
+                break;
+        }
+        int j= fExpected.length()-1;
+        int k= fActual.length()-1;
+        for (; k >= i && j >= i; k--,j--) {
+            if (fExpected.charAt(j) != fActual.charAt(k))
+                break;
+        }
+        String actual, expected;
+
+        // equal strings
+        if (j < i && k < i) {
+            expected= fExpected;
+            actual= fActual;
+        } else {
+            expected= fExpected.substring(i, j+1);
+            actual= fActual.substring(i, k+1);
+            if (i <= end && i > 0) {
+                expected= "..."+expected;
+                actual= "..."+actual;
+            }
+
+            if (j < fExpected.length()-1)
+                expected= expected+"...";
+            if (k < fActual.length()-1)
+                actual= actual+"...";
+        }
+        return Assert.format(super.getMessage(), expected, actual);
+    }
+}
diff --git a/tests/022-interface/expected.txt b/tests/022-interface/expected.txt
new file mode 100644
index 0000000..1212663
--- /dev/null
+++ b/tests/022-interface/expected.txt
@@ -0,0 +1,2 @@
+ImplBSub intf: 205
+ImplA: 7
diff --git a/tests/022-interface/info.txt b/tests/022-interface/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/022-interface/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/022-interface/src/Iface1.java b/tests/022-interface/src/Iface1.java
new file mode 100644
index 0000000..ba17d45
--- /dev/null
+++ b/tests/022-interface/src/Iface1.java
@@ -0,0 +1,13 @@
+// Copyright 2005 The Android Open Source Project
+
+/**
+ * Test stuff.
+ */
+public interface Iface1 {
+
+    public int iFunc1(int ii);
+
+    public float mFloaty = 5.0f;
+
+    public String mWahoo = new String("wahoo");
+}
diff --git a/tests/022-interface/src/Iface2.java b/tests/022-interface/src/Iface2.java
new file mode 100644
index 0000000..83fe650
--- /dev/null
+++ b/tests/022-interface/src/Iface2.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2 {
+
+    public int iFunc2(int ii);
+}
diff --git a/tests/022-interface/src/Iface2Sub1.java b/tests/022-interface/src/Iface2Sub1.java
new file mode 100644
index 0000000..db3e905
--- /dev/null
+++ b/tests/022-interface/src/Iface2Sub1.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2Sub1 extends Iface2, Cloneable {
+
+    //public int iFunc2(int ii);
+}
diff --git a/tests/022-interface/src/ImplA.java b/tests/022-interface/src/ImplA.java
new file mode 100644
index 0000000..9007001
--- /dev/null
+++ b/tests/022-interface/src/ImplA.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplA implements Iface1, Iface2 {
+
+    public int iFunc1(int ii) {
+        return ii+1;
+    }
+    public int iFunc2(int ii) {
+        return ii+2;
+    }
+}
diff --git a/tests/022-interface/src/ImplB.java b/tests/022-interface/src/ImplB.java
new file mode 100644
index 0000000..619fa00
--- /dev/null
+++ b/tests/022-interface/src/ImplB.java
@@ -0,0 +1,16 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplB implements Iface1, Iface2 {
+
+    public int iFunc1(int ii) {
+        return ii+10;
+    }
+    public int iFunc2(int ii) {
+        return ii+20;
+    }
+
+    public static String mWhoami = new String("ImplB!");
+}
diff --git a/tests/022-interface/src/ImplBSub.java b/tests/022-interface/src/ImplBSub.java
new file mode 100644
index 0000000..f3a7714
--- /dev/null
+++ b/tests/022-interface/src/ImplBSub.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Interface test.
+ */
+public class ImplBSub extends ImplB implements /*Iface2,*/ Iface2Sub1 {
+
+    public int iFunc1(int ii) {
+        return ii+100;
+    }
+    public int iFunc2(int ii) {
+        return ii+200;
+    }
+}
diff --git a/tests/022-interface/src/Main.java b/tests/022-interface/src/Main.java
new file mode 100644
index 0000000..9151e89
--- /dev/null
+++ b/tests/022-interface/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+/**
+ * test calling through an interface
+ */
+public class Main {
+    public static void main(String args[]) {
+        int result = 0;
+        Iface2Sub1 faceObj;
+        ImplA faceObj2;
+
+        faceObj = new ImplBSub();
+
+        result = faceObj.iFunc2(5);
+        System.out.print("ImplBSub intf: ");
+        System.out.println(result);
+
+        faceObj2 = new ImplA();
+        result = faceObj2.iFunc2(5);
+        System.out.print("ImplA: ");
+        System.out.println(result);
+    }
+}
diff --git a/tests/023-many-interfaces/build b/tests/023-many-interfaces/build
new file mode 100644
index 0000000..fc81d62
--- /dev/null
+++ b/tests/023-many-interfaces/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# Write out a bunch of interface source files.
+gcc -o iface-gen iface-gen.c
+./iface-gen
+
+mkdir classes
+${JAVAC} -d classes src/*.java
+
+dx --debug --dex --dump-to=classes.lst --output=classes.dex classes
+zip test.jar classes.dex
diff --git a/tests/023-many-interfaces/expected.txt b/tests/023-many-interfaces/expected.txt
new file mode 100644
index 0000000..c6a0c61
--- /dev/null
+++ b/tests/023-many-interfaces/expected.txt
@@ -0,0 +1,9 @@
+testIface001: done
+testIface049: done
+testIface099: done
+testVirt001: done
+testVirt049: done
+testVirt099: done
+testInst001: done
+testInst049: done
+testInst099: done
diff --git a/tests/023-many-interfaces/iface-gen.c b/tests/023-many-interfaces/iface-gen.c
new file mode 100644
index 0000000..1e3284a
--- /dev/null
+++ b/tests/023-many-interfaces/iface-gen.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Generate a big pile of interface classes.
+ */
+#include <stdio.h>
+
+/*
+ * Create N interface files.
+ */
+static int createFiles(int count)
+{
+    FILE* fp;
+    int i;
+
+    for (i = 0; i < count; i++) {
+        char nameBuf[32];
+
+        sprintf(nameBuf, "src/Interface%03d.java", i);
+        fp = fopen(nameBuf, "w");
+        if (fp == NULL) {
+            fprintf(stderr, "ERROR: unable to open %s\n", nameBuf);
+            return -1;
+        }
+
+        fprintf(fp, "interface Interface%03d {\n", i);
+        if ((i & 0x01) != 0)
+            fprintf(fp, "    int func%03d();\n", i);
+        fprintf(fp, "}\n");
+        fclose(fp);
+    }
+
+    fp = fopen("func-decl", "w");
+    fprintf(fp, "    implements\n");
+    for (i = 0; i < count; i++) {
+        fprintf(fp, "        Interface%03d%s\n", i, (i == count-1) ? "" : ",");
+    }
+    fprintf(fp, "\n");
+    for (i = 1; i < count; i += 2) {
+        fprintf(fp, "    public int func%03d() { return %d; }\n", i, i);
+    }
+    fclose(fp);
+
+    return 0;
+}
+
+int main()
+{
+    int result;
+
+    result = createFiles(100);
+
+    return (result != 0);
+}
diff --git a/tests/023-many-interfaces/info.txt b/tests/023-many-interfaces/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/023-many-interfaces/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/023-many-interfaces/src/Main.java b/tests/023-many-interfaces/src/Main.java
new file mode 100644
index 0000000..666a41c
--- /dev/null
+++ b/tests/023-many-interfaces/src/Main.java
@@ -0,0 +1,6 @@
+public class Main {
+    static public void main(String[] args) throws Exception {
+        boolean timing = (args.length >= 1) && args[0].equals("--timing");
+        ManyInterfaces.run(timing);
+    }
+}
diff --git a/tests/023-many-interfaces/src/ManyInterfaces.java b/tests/023-many-interfaces/src/ManyInterfaces.java
new file mode 100644
index 0000000..375938a
--- /dev/null
+++ b/tests/023-many-interfaces/src/ManyInterfaces.java
@@ -0,0 +1,413 @@
+// Copyright 2007 The Android Open Source Project
+
+/*
+Initial:
+test001: 2039901us  (4079ns per call)
+test049: 3346619us  (6693ns per call)
+test099: 4687402us  (9374ns per call)
+testInst001: 1327216us  (2654ns per use)
+testInst049: 1326995us  (2653ns per use)
+testInst099: 1327735us  (2655ns per use)
+
+After refactoring cache code: 2871ns per use
+After re-refactoring cache code: 2797ns per use
+
+After de-inlining invoke-interface:
+test001: 2164873us  (4329ns per call)
+test049: 3303884us  (6607ns per call)
+test099: 4656718us  (9313ns per call)
+testInst001: 1401731us  (2803ns per use)
+testInst049: 1401120us  (2802ns per use)
+testInst099: 1401298us  (2802ns per use)
+
+After adding caching for invoke-interface:
+testIface001: 1909330us  (3818ns per call)
+testIface049: 1905204us  (3810ns per call)
+testIface099: 1899012us  (3798ns per call)
+testVirt001: 1825001us  (3650ns per call)
+testVirt049: 1826161us  (3652ns per call)
+testVirt099: 1823915us  (3647ns per call)
+testInst001: 1393963us  (2787ns per use)
+testInst049: 1393163us  (2786ns per use)
+testInst099: 1390496us  (2780ns per use)
+
+After repeating each operation 16 times inside the inner loop:
+testIface001: 1429472us  (2726ns per call)      * 2382ns
+testIface049: 1427847us  (2723ns per call)      * 2396ns
+testIface099: 1423707us  (2715ns per call)      * 2387ns
+testVirt001: 1277790us  (2437ns per call)       * 2118ns
+testVirt049: 1280276us  (2441ns per call)       * 2119ns
+testVirt099: 1272640us  (2427ns per call)       * 2118ns
+testInst001: 844694us  (1611ns per use)         * 1396ns
+testInst049: 845619us  (1612ns per use)         * 1395ns
+testInst099: 845526us  (1612ns per use)         * 1394ns
+('*' is with dx optimizations enabled)
+*/
+
+/**
+ * Semi-generated class with many interfaces.
+ */
+public class ManyInterfaces
+    implements
+        Interface000,
+        Interface001,
+        Interface002,
+        Interface003,
+        Interface004,
+        Interface005,
+        Interface006,
+        Interface007,
+        Interface008,
+        Interface009,
+        Interface010,
+        Interface011,
+        Interface012,
+        Interface013,
+        Interface014,
+        Interface015,
+        Interface016,
+        Interface017,
+        Interface018,
+        Interface019,
+        Interface020,
+        Interface021,
+        Interface022,
+        Interface023,
+        Interface024,
+        Interface025,
+        Interface026,
+        Interface027,
+        Interface028,
+        Interface029,
+        Interface030,
+        Interface031,
+        Interface032,
+        Interface033,
+        Interface034,
+        Interface035,
+        Interface036,
+        Interface037,
+        Interface038,
+        Interface039,
+        Interface040,
+        Interface041,
+        Interface042,
+        Interface043,
+        Interface044,
+        Interface045,
+        Interface046,
+        Interface047,
+        Interface048,
+        Interface049,
+        Interface050,
+        Interface051,
+        Interface052,
+        Interface053,
+        Interface054,
+        Interface055,
+        Interface056,
+        Interface057,
+        Interface058,
+        Interface059,
+        Interface060,
+        Interface061,
+        Interface062,
+        Interface063,
+        Interface064,
+        Interface065,
+        Interface066,
+        Interface067,
+        Interface068,
+        Interface069,
+        Interface070,
+        Interface071,
+        Interface072,
+        Interface073,
+        Interface074,
+        Interface075,
+        Interface076,
+        Interface077,
+        Interface078,
+        Interface079,
+        Interface080,
+        Interface081,
+        Interface082,
+        Interface083,
+        Interface084,
+        Interface085,
+        Interface086,
+        Interface087,
+        Interface088,
+        Interface089,
+        Interface090,
+        Interface091,
+        Interface092,
+        Interface093,
+        Interface094,
+        Interface095,
+        Interface096,
+        Interface097,
+        Interface098,
+        Interface099
+{
+    /** whether to report timing information */
+    private static boolean timing = false;
+
+    /**
+     * Report on a section.
+     */
+    private static void report(String label, long start, long end, int iter,
+            int rept) {
+        if (timing) {
+            System.out.println(label + ": " + (end - start) / 1000 + "us"
+                    + "  (" + (end - start) / (iter*rept) + "ns per call)");
+        } else {
+            System.out.println(label + ": done");
+        }
+    }
+
+    /**
+     * Run tests.
+     *
+     * @param timing whether to print out timing info
+     */
+    public static void run(boolean timing) {
+        ManyInterfaces.timing = timing;
+        ManyInterfaces obj = new ManyInterfaces();
+        Interface001 one;
+        Interface049 forty;
+        Interface099 ninety;
+        long start, end;
+        int iter = 32768;
+        int rept = 16;
+        int i;
+
+        /*
+         * Clear the heap.  The various classes involved should already
+         * be loaded and ready as a result of instantiating ManyInterfaces.
+         */
+        System.gc();
+
+        start = System.nanoTime();
+        testIface001(obj, iter);
+        end = System.nanoTime();
+        report("testIface001", start, end, iter, rept);
+
+        start = System.nanoTime();
+        testIface049(obj, iter);
+        end = System.nanoTime();
+        report("testIface049", start, end, iter, rept);
+
+        start = System.nanoTime();
+        testIface099(obj, iter);
+        end = System.nanoTime();
+        report("testIface099", start, end, iter, rept);
+
+        start = System.nanoTime();
+        testVirt001(obj, iter);
+        end = System.nanoTime();
+        report("testVirt001", start, end, iter, rept);
+
+        start = System.nanoTime();
+        testVirt049(obj, iter);
+        end = System.nanoTime();
+        report("testVirt049", start, end, iter, rept);
+
+        start = System.nanoTime();
+        testVirt099(obj, iter);
+        end = System.nanoTime();
+        report("testVirt099", start, end, iter, rept);
+
+        start = System.nanoTime();
+        testInstance001(obj, iter);
+        end = System.nanoTime();
+        report("testInst001", start, end, iter, rept);
+
+        start = System.nanoTime();
+        testInstance049(obj, iter);
+        end = System.nanoTime();
+        report("testInst049", start, end, iter, rept);
+
+        start = System.nanoTime();
+        testInstance099(obj, iter);
+        end = System.nanoTime();
+        report("testInst099", start, end, iter, rept);
+    }
+
+    public int func001() { return 1; }
+    public int func003() { return 3; }
+    public int func005() { return 5; }
+    public int func007() { return 7; }
+    public int func009() { return 9; }
+    public int func011() { return 11; }
+    public int func013() { return 13; }
+    public int func015() { return 15; }
+    public int func017() { return 17; }
+    public int func019() { return 19; }
+    public int func021() { return 21; }
+    public int func023() { return 23; }
+    public int func025() { return 25; }
+    public int func027() { return 27; }
+    public int func029() { return 29; }
+    public int func031() { return 31; }
+    public int func033() { return 33; }
+    public int func035() { return 35; }
+    public int func037() { return 37; }
+    public int func039() { return 39; }
+    public int func041() { return 41; }
+    public int func043() { return 43; }
+    public int func045() { return 45; }
+    public int func047() { return 47; }
+    public int func049() { return 49; }
+    public int func051() { return 51; }
+    public int func053() { return 53; }
+    public int func055() { return 55; }
+    public int func057() { return 57; }
+    public int func059() { return 59; }
+    public int func061() { return 61; }
+    public int func063() { return 63; }
+    public int func065() { return 65; }
+    public int func067() { return 67; }
+    public int func069() { return 69; }
+    public int func071() { return 71; }
+    public int func073() { return 73; }
+    public int func075() { return 75; }
+    public int func077() { return 77; }
+    public int func079() { return 79; }
+    public int func081() { return 81; }
+    public int func083() { return 83; }
+    public int func085() { return 85; }
+    public int func087() { return 87; }
+    public int func089() { return 89; }
+    public int func091() { return 91; }
+    public int func093() { return 93; }
+    public int func095() { return 95; }
+    public int func097() { return 97; }
+    public int func099() { return 99; }
+
+    static void testIface001(Interface001 iface, int count) {
+        while (count-- != 0) {
+            iface.func001(); iface.func001(); iface.func001(); iface.func001();
+            iface.func001(); iface.func001(); iface.func001(); iface.func001();
+            iface.func001(); iface.func001(); iface.func001(); iface.func001();
+            iface.func001(); iface.func001(); iface.func001(); iface.func001();
+        }
+    }
+
+    static void testIface049(Interface049 iface, int count) {
+        while (count-- != 0) {
+            iface.func049(); iface.func049(); iface.func049(); iface.func049();
+            iface.func049(); iface.func049(); iface.func049(); iface.func049();
+            iface.func049(); iface.func049(); iface.func049(); iface.func049();
+            iface.func049(); iface.func049(); iface.func049(); iface.func049();
+        }
+    }
+
+    static void testIface099(Interface099 iface, int count) {
+        while (count-- != 0) {
+            iface.func099(); iface.func099(); iface.func099(); iface.func099();
+            iface.func099(); iface.func099(); iface.func099(); iface.func099();
+            iface.func099(); iface.func099(); iface.func099(); iface.func099();
+            iface.func099(); iface.func099(); iface.func099(); iface.func099();
+        }
+    }
+
+    static void testVirt001(ManyInterfaces obj, int count) {
+        while (count-- != 0) {
+            obj.func001(); obj.func001(); obj.func001(); obj.func001();
+            obj.func001(); obj.func001(); obj.func001(); obj.func001();
+            obj.func001(); obj.func001(); obj.func001(); obj.func001();
+            obj.func001(); obj.func001(); obj.func001(); obj.func001();
+        }
+    }
+
+    static void testVirt049(ManyInterfaces obj, int count) {
+        while (count-- != 0) {
+            obj.func049(); obj.func049(); obj.func049(); obj.func049();
+            obj.func049(); obj.func049(); obj.func049(); obj.func049();
+            obj.func049(); obj.func049(); obj.func049(); obj.func049();
+            obj.func049(); obj.func049(); obj.func049(); obj.func049();
+        }
+    }
+
+    static void testVirt099(ManyInterfaces obj, int count) {
+        while (count-- != 0) {
+            obj.func099(); obj.func099(); obj.func099(); obj.func099();
+            obj.func099(); obj.func099(); obj.func099(); obj.func099();
+            obj.func099(); obj.func099(); obj.func099(); obj.func099();
+            obj.func099(); obj.func099(); obj.func099(); obj.func099();
+        }
+    }
+
+    static void testInstance001(Object obj, int count) {
+        if (!(obj instanceof Interface001))
+            System.err.println("BAD");
+        while (count-- != 0) {
+            boolean is;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+            is = obj instanceof Interface001;
+        }
+    }
+
+    static void testInstance049(Object obj, int count) {
+        if (!(obj instanceof Interface049))
+            System.err.println("BAD");
+        while (count-- != 0) {
+            boolean is;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+            is = obj instanceof Interface049;
+        }
+    }
+
+    static void testInstance099(Object obj, int count) {
+        if (!(obj instanceof Interface099))
+            System.err.println("BAD");
+        while (count-- != 0) {
+            boolean is;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+            is = obj instanceof Interface099;
+        }
+    }
+}
diff --git a/tests/024-illegal-access/expected.txt b/tests/024-illegal-access/expected.txt
new file mode 100644
index 0000000..5f951f4
--- /dev/null
+++ b/tests/024-illegal-access/expected.txt
@@ -0,0 +1,2 @@
+Got expected failure 1
+Got expected failure 2
diff --git a/tests/024-illegal-access/info.txt b/tests/024-illegal-access/info.txt
new file mode 100644
index 0000000..16a284d
--- /dev/null
+++ b/tests/024-illegal-access/info.txt
@@ -0,0 +1,3 @@
+Test that an attempt to access a private field results in a verification
+error.  Also try to access a non-public class in a different package with
+"instanceof".
diff --git a/tests/024-illegal-access/src/CheckInstanceof.java b/tests/024-illegal-access/src/CheckInstanceof.java
new file mode 100644
index 0000000..de48cd2
--- /dev/null
+++ b/tests/024-illegal-access/src/CheckInstanceof.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Make sure we're performing access checks on classes used in "instanceof".
+ */
+public class CheckInstanceof {
+    public static void main(Object obj) {
+        if (obj instanceof otherpkg.Package)
+            System.out.println("yes!");
+        else
+            System.out.println("no!");
+    }
+}
diff --git a/tests/024-illegal-access/src/Main.java b/tests/024-illegal-access/src/Main.java
new file mode 100644
index 0000000..bde73e9
--- /dev/null
+++ b/tests/024-illegal-access/src/Main.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+public class Main {
+    static public void main(String[] args) {
+        try {
+            PublicAccess.main();
+            System.err.println("ERROR: call 1 not expected to succeed");
+        } catch (VerifyError ve) {
+            // dalvik
+            System.out.println("Got expected failure 1");
+        } catch (IllegalAccessError iae) {
+            // reference
+            System.out.println("Got expected failure 1");
+        }
+
+        try {
+            CheckInstanceof.main(new Object());
+            System.err.println("ERROR: call 2 not expected to succeed");
+        } catch (VerifyError ve) {
+            // dalvik
+            System.out.println("Got expected failure 2");
+        } catch (IllegalAccessError iae) {
+            // reference
+            System.out.println("Got expected failure 2");
+        }
+    }
+}
diff --git a/tests/024-illegal-access/src/PublicAccess.java b/tests/024-illegal-access/src/PublicAccess.java
new file mode 100644
index 0000000..fdc0e9e
--- /dev/null
+++ b/tests/024-illegal-access/src/PublicAccess.java
@@ -0,0 +1,11 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Some stuff for access checks.
+ */
+public class PublicAccess {
+    public static void main() {
+        String shouldFail = SemiPrivate.mPrivvy;
+        System.out.println("Got " + shouldFail);
+    }
+}
diff --git a/tests/024-illegal-access/src/SemiPrivate.java b/tests/024-illegal-access/src/SemiPrivate.java
new file mode 100644
index 0000000..17b2ac0
--- /dev/null
+++ b/tests/024-illegal-access/src/SemiPrivate.java
@@ -0,0 +1,8 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Version with package scope access.
+ */
+public class SemiPrivate {
+    /* not private */ static String mPrivvy = "stuff";
+}
diff --git a/tests/024-illegal-access/src/otherpkg/Package.java b/tests/024-illegal-access/src/otherpkg/Package.java
new file mode 100644
index 0000000..bc295b5
--- /dev/null
+++ b/tests/024-illegal-access/src/otherpkg/Package.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package otherpkg;
+
+/*
+ * Package-scope class (public here).
+ */
+public class Package {
+}
diff --git a/tests/024-illegal-access/src2/SemiPrivate.java b/tests/024-illegal-access/src2/SemiPrivate.java
new file mode 100644
index 0000000..cf6f8e6
--- /dev/null
+++ b/tests/024-illegal-access/src2/SemiPrivate.java
@@ -0,0 +1,8 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Version with private access.
+ */
+public class SemiPrivate {
+    private static String mPrivvy = "stuff";
+}
diff --git a/tests/024-illegal-access/src2/otherpkg/Package.java b/tests/024-illegal-access/src2/otherpkg/Package.java
new file mode 100644
index 0000000..54d8341
--- /dev/null
+++ b/tests/024-illegal-access/src2/otherpkg/Package.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package otherpkg;
+
+/*
+ * Package-scope class.
+ */
+class Package {
+}
diff --git a/tests/025-access-controller/expected.txt b/tests/025-access-controller/expected.txt
new file mode 100644
index 0000000..75cfc99
--- /dev/null
+++ b/tests/025-access-controller/expected.txt
@@ -0,0 +1 @@
+AccessControllerTest: got 39
diff --git a/tests/025-access-controller/info.txt b/tests/025-access-controller/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/025-access-controller/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/025-access-controller/src/Main.java b/tests/025-access-controller/src/Main.java
new file mode 100644
index 0000000..84dc057
--- /dev/null
+++ b/tests/025-access-controller/src/Main.java
@@ -0,0 +1,14 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.security.AccessController;
+
+/**
+ * Test java.security.AccessController.
+ */
+public class Main {
+    public static void main(String[] args) {
+        Privvy priv = new Privvy(38);
+        Integer result = AccessController.doPrivileged(priv);
+        System.out.println("AccessControllerTest: got " + result);
+    }
+}
diff --git a/tests/025-access-controller/src/Privvy.java b/tests/025-access-controller/src/Privvy.java
new file mode 100644
index 0000000..07a0678
--- /dev/null
+++ b/tests/025-access-controller/src/Privvy.java
@@ -0,0 +1,18 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+
+class Privvy implements PrivilegedAction<Integer> {
+
+    private Integer mValue;
+
+    public Privvy(int val) {
+        mValue = new Integer(val + 1);
+    }
+
+    public Integer run() {
+        return mValue;
+    }
+}
diff --git a/tests/026-access/expected.txt b/tests/026-access/expected.txt
new file mode 100644
index 0000000..dabfb37
--- /dev/null
+++ b/tests/026-access/expected.txt
@@ -0,0 +1,2 @@
+access test
+Blort.
diff --git a/tests/026-access/info.txt b/tests/026-access/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/026-access/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/026-access/src/Main.java b/tests/026-access/src/Main.java
new file mode 100644
index 0000000..9628259
--- /dev/null
+++ b/tests/026-access/src/Main.java
@@ -0,0 +1,12 @@
+// Copyright 2006 The Android Open Source Project
+
+import otherpackage.PublicAccess;
+
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("access test");
+
+        PublicAccess pa = new PublicAccess();
+        pa.main();
+    }
+}
diff --git a/tests/026-access/src/otherpackage/PublicAccess.java b/tests/026-access/src/otherpackage/PublicAccess.java
new file mode 100644
index 0000000..996fa76
--- /dev/null
+++ b/tests/026-access/src/otherpackage/PublicAccess.java
@@ -0,0 +1,7 @@
+package otherpackage;
+
+public class PublicAccess {
+    static public void main() {
+        System.out.println("Blort.");
+    }
+}
diff --git a/tests/027-arithmetic/expected.txt b/tests/027-arithmetic/expected.txt
new file mode 100644
index 0000000..2dadf10
--- /dev/null
+++ b/tests/027-arithmetic/expected.txt
@@ -0,0 +1,18 @@
+f=1234.5677 --> i=1234
+f=-1234.5677 --> i=-1234
+d=1234.5678 --> i=1234
+d=-1234.5678 --> i=-1234
+d=5.6789567890123E9 --> l=5678956789
+d=-5.6789567890123E9 --> l=-5678956789
+i=7654 --> l=7654
+i=-7654 --> l=-7654
+l=5678956789 --> i=1383989493
+l=-5678956789 --> i=-1383989493
+i=1234 --> f=1234.0
+i=-1234 --> f=-1234.0
+values are 44332211 and bbaa9988
+First l is bbaa998844332211
+Second l is bbaa998844332211
+shiftTest2 l is 1122334455667788
+b=-1, s=-1, c=4095, i=268435455
+b=0xffffffff, s=0xffffffff, c=0xfff, i=0xfffffff
diff --git a/tests/027-arithmetic/info.txt b/tests/027-arithmetic/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/027-arithmetic/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/027-arithmetic/src/Main.java b/tests/027-arithmetic/src/Main.java
new file mode 100644
index 0000000..4d0f74e
--- /dev/null
+++ b/tests/027-arithmetic/src/Main.java
@@ -0,0 +1,141 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test arithmetic operations.
+ */
+public class Main {
+
+    static void shiftTest1()
+    {
+        final int[] mBytes = {
+            0x11, 0x22, 0x33, 0x44, 0x88, 0x99, 0xaa, 0xbb
+        };
+        long l;
+        int i1, i2;
+
+        i1 = mBytes[0] | mBytes[1] << 8 | mBytes[2] << 16 | mBytes[3] << 24;
+        i2 = mBytes[4] | mBytes[5] << 8 | mBytes[6] << 16 | mBytes[7] << 24;
+        l = i1 | ((long)i2 << 32);
+
+	System.out.println("values are " + Integer.toHexString(i1)
+	    + " and " + Integer.toHexString(i2));
+
+        System.out.println("First l is " + Long.toHexString(l));
+
+        l = (long)mBytes[0]
+            | (long)mBytes[1] << 8
+            | (long)mBytes[2] << 16
+            | (long)mBytes[3] << 24
+            | (long)mBytes[4] << 32
+            | (long)mBytes[5] << 40
+            | (long)mBytes[6] << 48
+            | (long)mBytes[7] << 56;
+
+        System.out.println("Second l is " + Long.toHexString(l));
+    }
+
+    static void shiftTest2()
+    {
+        long    a = 0x11;
+        long    b = 0x22;
+        long    c = 0x33;
+        long    d = 0x44;
+        long    e = 0x55;
+        long    f = 0x66;
+        long    g = 0x77;
+        long    h = 0x88;
+
+        long    result = ((a << 56) | (b << 48) | (c << 40) | (d << 32) |
+                         (e << 24) | (f << 16) | (g << 8) | h);
+
+        System.out.println("shiftTest2 l is " + Long.toHexString(result));
+    }
+
+    static void convTest()
+    {
+        float f;
+        double d;
+        int i;
+        long l;
+
+        /* float --> int */
+        f = 1234.5678f;
+        i = (int) f;
+        System.out.println("f=" + f + " --> i=" + i);
+
+        f = -1234.5678f;
+        i = (int) f;
+        System.out.println("f=" + f + " --> i=" + i);
+
+        /* double --> int */
+        d = 1234.5678;
+        i = (int) d;
+        System.out.println("d=" + d + " --> i=" + i);
+
+        d = -1234.5678;
+        i = (int) d;
+        System.out.println("d=" + d + " --> i=" + i);
+
+        /* double --> long */
+        d = 5678956789.0123;
+        l = (long) d;
+        System.out.println("d=" + d + " --> l=" + l);
+
+        d = -5678956789.0123;
+        l = (long) d;
+        System.out.println("d=" + d + " --> l=" + l);
+
+        /* int --> long */
+        i = 7654;
+        l = (long) i;
+        System.out.println("i=" + i + " --> l=" + l);
+
+        i = -7654;
+        l = (long) i;
+        System.out.println("i=" + i + " --> l=" + l);
+
+        /* long --> int (with truncation) */
+        l = 5678956789L;
+        i = (int) l;
+        System.out.println("l=" + l + " --> i=" + i);
+
+        l = -5678956789L;
+        i = (int) l;
+        System.out.println("l=" + l + " --> i=" + i);
+
+        /* int --> float */
+        i = 1234;
+        f = (float) i;
+        System.out.println("i=" + i + " --> f=" + f);
+
+        i = -1234;
+        f = (float) i;
+        System.out.println("i=" + i + " --> f=" + f);
+    }
+
+    static void unsignedShiftTest()
+    {
+        byte b = -4;
+        short s = -4;
+        char c = 0xfffc;
+        int i = -4;
+
+        b >>>= 4;
+        s >>>= 4;
+        c >>>= 4;
+        i >>>= 4;
+
+        System.out.println("b=" + b + ", s=" + s + ", c=" + (int)c + ", i=" +i);
+        System.out.println("b=0x" + Integer.toHexString((int)b)
+            + ", s=0x" + Integer.toHexString((int)s)
+            + ", c=0x" + Integer.toHexString((int)c)
+            + ", i=0x" + Integer.toHexString(i));
+    }
+
+    public static void main(String[] args) {
+        convTest();
+        shiftTest1();
+        shiftTest2();
+        unsignedShiftTest();
+    }
+}
diff --git a/tests/028-array-write/expected.txt b/tests/028-array-write/expected.txt
new file mode 100644
index 0000000..85986b5
--- /dev/null
+++ b/tests/028-array-write/expected.txt
@@ -0,0 +1,3 @@
+Running writeTest...
+Running copyTest...
+Done!
diff --git a/tests/028-array-write/info.txt b/tests/028-array-write/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/028-array-write/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/028-array-write/src/Main.java b/tests/028-array-write/src/Main.java
new file mode 100644
index 0000000..6f36f84
--- /dev/null
+++ b/tests/028-array-write/src/Main.java
@@ -0,0 +1,68 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Array write speed test.
+ */
+public class Main {
+    /** whether to report times */
+    static boolean timing = false;
+
+    static final int STORAGE_SIZE = 128*1024;
+    static int[] mStorage = new int[STORAGE_SIZE];
+
+    static public void report(long start, long end) {
+        if (! timing) {
+            return;
+        }
+
+        System.out.println("Finished in " + ((end - start) / 1000000.0)
+            + " msec");
+    }
+
+    static void writeArray(int val) {
+        for (int i = STORAGE_SIZE-1; i >= 0; i--)
+            mStorage[i] = val;
+    }
+
+    static void writeTest() {
+        long start, end;
+
+        writeArray(0);  // touch all the memory
+
+        System.out.println("Running writeTest...");
+        start = System.nanoTime();
+        for (int i = 1; i < 20; i++)
+            writeArray(i);
+        end = System.nanoTime();
+
+        report(start, end);
+    }
+
+    static void copyTest() {
+        long start, end;
+
+        // touch once
+        System.arraycopy(mStorage, 0, mStorage,
+            STORAGE_SIZE/2, STORAGE_SIZE/2);
+
+        System.out.println("Running copyTest...");
+        start = System.nanoTime();
+        for (int i = 1; i < 35; i++) {
+            System.arraycopy(mStorage, 0, mStorage,
+                STORAGE_SIZE/2, STORAGE_SIZE/2);
+        }
+        end = System.nanoTime();
+
+        report(start, end);
+    }
+
+    public static void main(String[] args) {
+        if ((args.length >= 1) && args[0].equals("--timing")) {
+            timing = true;
+        }
+
+        writeTest();
+        copyTest();
+        System.out.println("Done!");
+    }
+}
diff --git a/tests/029-assert/expected.txt b/tests/029-assert/expected.txt
new file mode 100644
index 0000000..bf0efec
--- /dev/null
+++ b/tests/029-assert/expected.txt
@@ -0,0 +1 @@
+caught expected assert exception
diff --git a/tests/029-assert/info.txt b/tests/029-assert/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/029-assert/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/029-assert/src/Main.java b/tests/029-assert/src/Main.java
new file mode 100644
index 0000000..1e5cc7c
--- /dev/null
+++ b/tests/029-assert/src/Main.java
@@ -0,0 +1,16 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Test Java language asserts.
+ */
+public class Main {
+    public static void main(String[] args) {
+        assert true;
+        try {
+            assert false;
+            System.out.println("GLITCH: didn't assert (is '-ea' set?)");
+        } catch (AssertionError ae) {
+            System.out.println("caught expected assert exception");
+        }
+    }
+}
diff --git a/tests/030-bad-finalizer/expected.txt b/tests/030-bad-finalizer/expected.txt
new file mode 100644
index 0000000..6e91dcc
--- /dev/null
+++ b/tests/030-bad-finalizer/expected.txt
@@ -0,0 +1,8 @@
+Constructed object.
+Nulled. Requestion gc.
+Finalizer started and spinning...
+Finalizer done spinning.
+Finalizer sleeping forever now.
+Requesting another GC.
+Requesting another GC.
+(segfault)
diff --git a/tests/030-bad-finalizer/info.txt b/tests/030-bad-finalizer/info.txt
new file mode 100644
index 0000000..0f76ad6
--- /dev/null
+++ b/tests/030-bad-finalizer/info.txt
@@ -0,0 +1,3 @@
+The finalizer for this class never finishes.  Dalvik is expected to detect
+this situation and abort the VM (so you will likely see a "deadd00d"
+crash in the log output).
diff --git a/tests/030-bad-finalizer/run b/tests/030-bad-finalizer/run
new file mode 100644
index 0000000..8e03cd3
--- /dev/null
+++ b/tests/030-bad-finalizer/run
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# 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.
+
+${RUN} "$@" > original-output.txt
+
+cat original-output.txt | awk '
+/Segmentation fault/ {
+    # ignore the details of the line
+    print "(segfault)"
+    next;
+}
+{
+    print;
+}'
diff --git a/tests/030-bad-finalizer/src/BadFinalizer.java b/tests/030-bad-finalizer/src/BadFinalizer.java
new file mode 100644
index 0000000..3ff422b
--- /dev/null
+++ b/tests/030-bad-finalizer/src/BadFinalizer.java
@@ -0,0 +1,32 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Class with a bad finalizer.
+ */
+public class BadFinalizer {
+    public static void snooze(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ie) {
+            System.out.println("Snooze: " + ie.getMessage());
+        }
+    }
+
+    protected void finalize() {
+        System.out.println("Finalizer started and spinning...");
+        int j = 0;
+
+        /* spin for a bit */
+        long start, end;
+        start = System.nanoTime();
+        for (int i = 0; i < 1000000; i++)
+            j++;
+        end = System.nanoTime();
+        System.out.println("Finalizer done spinning.");
+
+        System.out.println("Finalizer sleeping forever now.");
+        while (true) {
+            snooze(10000);
+        }
+    }
+}
diff --git a/tests/030-bad-finalizer/src/Main.java b/tests/030-bad-finalizer/src/Main.java
new file mode 100644
index 0000000..c063476
--- /dev/null
+++ b/tests/030-bad-finalizer/src/Main.java
@@ -0,0 +1,25 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Test a class with a bad finalizer.
+ */
+public class Main {
+    public static void main(String[] args) {
+        BadFinalizer bf = new BadFinalizer();
+
+        System.out.println("Constructed object.");
+        bf = null;
+
+        System.out.println("Nulled. Requestion gc.");
+        System.gc();
+
+        for (int i = 0; i < 8; i++) {
+            BadFinalizer.snooze(5000);
+            System.out.println("Requesting another GC.");
+            System.gc();
+        }
+
+        System.out.println("Done waiting.");
+        System.exit(0);
+    }
+}
diff --git a/tests/031-class-attributes/expected.txt b/tests/031-class-attributes/expected.txt
new file mode 100644
index 0000000..47eaeee
--- /dev/null
+++ b/tests/031-class-attributes/expected.txt
@@ -0,0 +1,164 @@
+***** class ClassAttrs:
+  name: ClassAttrs
+  canonical: ClassAttrs
+  simple: ClassAttrs
+  genericSignature: null
+  super: class java.lang.Object
+  declaring: null
+  enclosing: null
+  enclosingCon: null
+  enclosingMeth: null
+  modifiers: 1
+  package: null
+  declaredClasses: [2] class ClassAttrs$PublicMemberClass, class ClassAttrs$MemberClass
+  member classes: [1] class ClassAttrs$PublicMemberClass
+  isAnnotation: false
+  isAnonymous: false
+  isArray: false
+  isEnum: false
+  isInterface: false
+  isLocalClass: false
+  isMemberClass: false
+  isPrimitive: false
+  isSynthetic: false
+***** class OtherClass:
+  name: OtherClass
+  canonical: OtherClass
+  simple: OtherClass
+  genericSignature: null
+  super: class java.lang.Object
+  declaring: null
+  enclosing: null
+  enclosingCon: null
+  enclosingMeth: null
+  modifiers: 0
+  package: null
+  declaredClasses: [0]
+  member classes: [0]
+  isAnnotation: false
+  isAnonymous: false
+  isArray: false
+  isEnum: false
+  isInterface: false
+  isLocalClass: false
+  isMemberClass: false
+  isPrimitive: false
+  isSynthetic: false
+***** class otherpackage.OtherPackageClass:
+  name: otherpackage.OtherPackageClass
+  canonical: otherpackage.OtherPackageClass
+  simple: OtherPackageClass
+  genericSignature: null
+  super: class java.lang.Object
+  declaring: null
+  enclosing: null
+  enclosingCon: null
+  enclosingMeth: null
+  modifiers: 1
+  package: package otherpackage
+  declaredClasses: [0]
+  member classes: [0]
+  isAnnotation: false
+  isAnonymous: false
+  isArray: false
+  isEnum: false
+  isInterface: false
+  isLocalClass: false
+  isMemberClass: false
+  isPrimitive: false
+  isSynthetic: false
+***** class ClassAttrs$1InnerNamed:
+  name: ClassAttrs$1InnerNamed
+  canonical: null
+  simple: InnerNamed
+  genericSignature: null
+  super: class java.lang.Object
+  declaring: null
+  enclosing: class ClassAttrs
+  enclosingCon: null
+  enclosingMeth: public static void ClassAttrs.main()
+  modifiers: 0
+  package: null
+  declaredClasses: [0]
+  member classes: [0]
+  isAnnotation: false
+  isAnonymous: false
+  isArray: false
+  isEnum: false
+  isInterface: false
+  isLocalClass: true
+  isMemberClass: false
+  isPrimitive: false
+  isSynthetic: false
+***** class ClassAttrs$1ConsInnerNamed:
+  name: ClassAttrs$1ConsInnerNamed
+  canonical: null
+  simple: ConsInnerNamed
+  genericSignature: null
+  super: class java.lang.Object
+  declaring: null
+  enclosing: class ClassAttrs
+  enclosingCon: ClassAttrs()
+  enclosingMeth: null
+  modifiers: 0
+  package: null
+  declaredClasses: [0]
+  member classes: [0]
+  isAnnotation: false
+  isAnonymous: false
+  isArray: false
+  isEnum: false
+  isInterface: false
+  isLocalClass: true
+  isMemberClass: false
+  isPrimitive: false
+  isSynthetic: false
+***** class ClassAttrs$1:
+  name: ClassAttrs$1
+  canonical: null
+  simple: 
+  genericSignature: null
+  super: class OtherClass
+  declaring: null
+  enclosing: class ClassAttrs
+  enclosingCon: null
+  enclosingMeth: public static void ClassAttrs.main()
+  modifiers: 8
+  package: null
+  declaredClasses: [0]
+  member classes: [0]
+  isAnnotation: false
+  isAnonymous: true
+  isArray: false
+  isEnum: false
+  isInterface: false
+  isLocalClass: false
+  isMemberClass: false
+  isPrimitive: false
+  isSynthetic: false
+***** class ClassAttrs$MemberClass:
+  name: ClassAttrs$MemberClass
+  canonical: ClassAttrs.MemberClass
+  simple: MemberClass
+  genericSignature: <XYZ:Ljava/lang/Object;>Ljava/lang/Object;
+  super: class java.lang.Object
+  declaring: class ClassAttrs
+  enclosing: class ClassAttrs
+  enclosingCon: null
+  enclosingMeth: null
+  modifiers: 8
+  package: null
+  declaredClasses: [0]
+  member classes: [0]
+  isAnnotation: false
+  isAnonymous: false
+  isArray: false
+  isEnum: false
+  isInterface: false
+  isLocalClass: false
+  isMemberClass: true
+  isPrimitive: false
+  isSynthetic: false
+constructor signature: (LClassAttrs$MemberClass<TXYZ;>;)V
+method signature: ()Ljava/lang/Class<TXYZ;>;
+field signature: LClassAttrs$MemberClass<TXYZ;>;
diff --git a/tests/031-class-attributes/info.txt b/tests/031-class-attributes/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/031-class-attributes/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/031-class-attributes/src/ClassAttrs.java b/tests/031-class-attributes/src/ClassAttrs.java
new file mode 100644
index 0000000..c1407bd
--- /dev/null
+++ b/tests/031-class-attributes/src/ClassAttrs.java
@@ -0,0 +1,201 @@
+import otherpackage.OtherPackageClass;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+public class ClassAttrs {
+    ClassAttrs() {
+        /* local, not anonymous, not member */
+        class ConsInnerNamed {
+            public void showMe() {
+                printClassAttrs(this.getClass());
+            }
+        }
+
+        ConsInnerNamed cinner = new ConsInnerNamed();
+        cinner.showMe();
+    }
+
+    public static void main() {
+        printClassAttrs(ClassAttrs.class);
+        printClassAttrs(OtherClass.class);
+        printClassAttrs(OtherPackageClass.class);
+
+        /* local, not anonymous, not member */
+        class InnerNamed {
+            public void showMe() {
+                printClassAttrs(this.getClass());
+            }
+        }
+        InnerNamed inner = new InnerNamed();
+        inner.showMe();
+
+        ClassAttrs attrs = new ClassAttrs();
+
+        /* anonymous, not local, not member */
+        printClassAttrs((new OtherClass() { int i = 5; }).getClass());
+
+        /* member, not anonymous, not local */
+        printClassAttrs(MemberClass.class);
+
+        try {
+            Constructor cons;
+            cons = MemberClass.class.getConstructor(
+                    new Class[] { MemberClass.class });
+            System.out.println("constructor signature: "
+                    + getSignatureAttribute(cons));
+
+            Method meth;
+            meth = MemberClass.class.getMethod("foo", (Class[]) null);
+            System.out.println("method signature: "
+                    + getSignatureAttribute(meth));
+
+            Field field;
+            field = MemberClass.class.getField("mWha");
+            System.out.println("field signature: "
+                    + getSignatureAttribute(field));
+        } catch (NoSuchMethodException nsme) {
+            System.err.println("FAILED: " + nsme);
+        } catch (NoSuchFieldException nsfe) {
+            System.err.println("FAILED: " + nsfe);
+        } catch (RuntimeException re) {
+            System.err.println("FAILED: " + re);
+            re.printStackTrace();
+        }
+    }
+
+    /* to call the (out-of-scope) <code>getSignatureAttribute</code> methods */
+    public static String getSignatureAttribute(Object obj) {
+        Method method;
+        try {
+            if (obj instanceof AccessibleObject) {
+                method = AccessibleObject.class.getDeclaredMethod(
+                        "getSignatureAttribute");
+            } else {
+                // Should be a Class.
+                method = Class.class.getDeclaredMethod(
+                        "getSignatureAttribute");
+            }
+            method.setAccessible(true);
+        } catch (NoSuchMethodException ex) {
+            System.err.println("getSignatureAttribute() not defined.");
+            ex.printStackTrace();
+            return "<unknown>";
+        }
+
+        try {
+            return (String) method.invoke(obj);
+        } catch (IllegalAccessException ex) {
+            throw new RuntimeException(ex);
+        } catch (InvocationTargetException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /* for reflection testing */
+    static class MemberClass<XYZ> {
+        public MemberClass<XYZ> mWha;
+
+        public MemberClass(MemberClass<XYZ> memb) {
+            mWha = memb;
+        }
+
+        public Class<XYZ> foo() throws NoSuchMethodException {
+            return null;
+        }
+    }
+
+    /* for reflection testing (getClasses vs getDeclaredClasses) */
+    static public class PublicMemberClass {
+        float mBlah;
+    }
+
+    /*
+     * Dump a variety of class attributes.
+     */
+    public static void printClassAttrs(Class clazz) {
+        final boolean WORKING = false;
+        Class clazz2;
+
+        System.out.println("***** " + clazz + ":");
+
+        System.out.println("  name: "
+            + clazz.getName());
+        System.out.println("  canonical: "
+            + clazz.getCanonicalName());
+        System.out.println("  simple: "
+            + clazz.getSimpleName());
+        System.out.println("  genericSignature: "
+            + getSignatureAttribute(clazz));
+
+        System.out.println("  super: "
+            + clazz.getSuperclass());
+        if (WORKING) System.out.println("  genericSuperclass: "
+            + clazz.getGenericSuperclass());
+        System.out.println("  declaring: "
+            + clazz.getDeclaringClass());
+        System.out.println("  enclosing: "
+            + clazz.getEnclosingClass());
+        System.out.println("  enclosingCon: "
+            + clazz.getEnclosingConstructor());
+        System.out.println("  enclosingMeth: "
+            + clazz.getEnclosingMethod());
+        System.out.println("  modifiers: "
+            + clazz.getModifiers());
+        System.out.println("  package: "
+            + clazz.getPackage());
+
+        System.out.println("  declaredClasses: "
+            + stringifyTypeArray(clazz.getDeclaredClasses()));
+        System.out.println("  member classes: "
+            + stringifyTypeArray(clazz.getClasses()));
+
+        System.out.println("  isAnnotation: "
+            + clazz.isAnnotation());
+        System.out.println("  isAnonymous: "
+            + clazz.isAnonymousClass());
+        System.out.println("  isArray: "
+            + clazz.isArray());
+        System.out.println("  isEnum: "
+            + clazz.isEnum());
+        System.out.println("  isInterface: "
+            + clazz.isInterface());
+        System.out.println("  isLocalClass: "
+            + clazz.isLocalClass());
+        System.out.println("  isMemberClass: "
+            + clazz.isMemberClass());
+        System.out.println("  isPrimitive: "
+            + clazz.isPrimitive());
+        System.out.println("  isSynthetic: "
+            + clazz.isSynthetic());
+
+        if (WORKING) System.out.println("  genericInterfaces: "
+            + stringifyTypeArray(clazz.getGenericInterfaces()));
+    }
+
+    /*
+     * Convert an array of Type into a string.  Start with an array count.
+     */
+    private static String stringifyTypeArray(Type[] types) {
+        StringBuilder stb = new StringBuilder();
+        boolean first = true;
+
+        stb.append("[" + types.length + "]");
+
+        for (Type t: types) {
+            if (first) {
+                stb.append(" ");
+                first = false;
+            } else {
+                stb.append(", ");
+            }
+            stb.append(t.toString());
+        }
+
+        return stb.toString();
+    }
+}
diff --git a/tests/031-class-attributes/src/Main.java b/tests/031-class-attributes/src/Main.java
new file mode 100644
index 0000000..bc6b749
--- /dev/null
+++ b/tests/031-class-attributes/src/Main.java
@@ -0,0 +1,5 @@
+public class Main {
+    public static void main(String[] args) {
+        ClassAttrs.main();
+    }
+}
diff --git a/tests/031-class-attributes/src/OtherClass.java b/tests/031-class-attributes/src/OtherClass.java
new file mode 100644
index 0000000..0b4526e
--- /dev/null
+++ b/tests/031-class-attributes/src/OtherClass.java
@@ -0,0 +1,2 @@
+class OtherClass {
+}
diff --git a/tests/031-class-attributes/src/otherpackage/OtherPackageClass.java b/tests/031-class-attributes/src/otherpackage/OtherPackageClass.java
new file mode 100644
index 0000000..9652b77
--- /dev/null
+++ b/tests/031-class-attributes/src/otherpackage/OtherPackageClass.java
@@ -0,0 +1,4 @@
+package otherpackage;
+
+public class OtherPackageClass {
+}
diff --git a/tests/032-concrete-sub/expected.txt b/tests/032-concrete-sub/expected.txt
new file mode 100644
index 0000000..56f25bb
--- /dev/null
+++ b/tests/032-concrete-sub/expected.txt
@@ -0,0 +1,6 @@
+calling abs.doStuff()
+In AbstractBase.doStuff (src2)
+Got expected exception from abs.doStuff().
+class modifiers=1025
+meth modifiers=1025
+Got expected failure
diff --git a/tests/032-concrete-sub/info.txt b/tests/032-concrete-sub/info.txt
new file mode 100644
index 0000000..1956994
--- /dev/null
+++ b/tests/032-concrete-sub/info.txt
@@ -0,0 +1,3 @@
+This tests some facets of abstract method handling, notably situations
+where a concrete class and its abstract superclass were compiled with
+different notions about which methods are abstract.
diff --git a/tests/032-concrete-sub/src/AbstractBase.java b/tests/032-concrete-sub/src/AbstractBase.java
new file mode 100644
index 0000000..6281189
--- /dev/null
+++ b/tests/032-concrete-sub/src/AbstractBase.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Abstract base class.
+ */
+public abstract class AbstractBase {
+    public void doStuff() {
+        System.out.println("In AbstractBase.doStuff");
+    }
+
+    public void abstractOrNot() {}
+}
diff --git a/tests/032-concrete-sub/src/ConcreteSub.java b/tests/032-concrete-sub/src/ConcreteSub.java
new file mode 100644
index 0000000..083f25d
--- /dev/null
+++ b/tests/032-concrete-sub/src/ConcreteSub.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Test insertion of an abstract method in a superclass.
+ */
+public class ConcreteSub extends AbstractBase {
+    private static void callBase(AbstractBase abs) {
+        System.out.println("calling abs.doStuff()");
+        abs.doStuff();
+    }
+
+    public static void main() {
+        ConcreteSub sub = new ConcreteSub();
+
+        try {
+            callBase(sub);
+        } catch (AbstractMethodError ame) {
+            System.out.println("Got expected exception from abs.doStuff().");
+        }
+
+        /*
+         * Check reflection stuff.
+         */
+        Class absClass = AbstractBase.class;
+        Method meth;
+
+        System.out.println("class modifiers=" + absClass.getModifiers());
+
+        try {
+            meth = absClass.getMethod("redefineMe", (Class[]) null);
+        } catch (NoSuchMethodException nsme) {
+            nsme.printStackTrace();
+            return;
+        }
+        System.out.println("meth modifiers=" + meth.getModifiers());
+    }
+}
diff --git a/tests/032-concrete-sub/src/ConcreteSub2.java b/tests/032-concrete-sub/src/ConcreteSub2.java
new file mode 100644
index 0000000..0a9e67e
--- /dev/null
+++ b/tests/032-concrete-sub/src/ConcreteSub2.java
@@ -0,0 +1,26 @@
+/*
+ * 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 conversion of a concrete method to an abstract method.  This class
+ * will fail verification because there is no implementation of the
+ * abstractOrNot() method.
+ */
+public class ConcreteSub2 extends AbstractBase {
+    public void doStuff() {
+        abstractOrNot();
+    }
+}
diff --git a/tests/032-concrete-sub/src/Main.java b/tests/032-concrete-sub/src/Main.java
new file mode 100644
index 0000000..4a5193d
--- /dev/null
+++ b/tests/032-concrete-sub/src/Main.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test insertion of an abstract method in a superclass.
+ */
+public class Main {
+    public static void main(String[] args) {
+        ConcreteSub.main();
+
+        try {
+            // Dalvik verifier stops here (VerifyError)
+            ConcreteSub2 blah = new ConcreteSub2();
+            // other VMs fail here (AbstractMethodError)
+            blah.doStuff();
+            System.err.println("Succeeded unexpectedly");
+        } catch (VerifyError ve) {
+            System.out.println("Got expected failure");
+        } catch (AbstractMethodError ame) {
+            System.out.println("Got expected failure");
+        }
+    }
+}
diff --git a/tests/032-concrete-sub/src2/AbstractBase.java b/tests/032-concrete-sub/src2/AbstractBase.java
new file mode 100644
index 0000000..534f738
--- /dev/null
+++ b/tests/032-concrete-sub/src2/AbstractBase.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/**
+ * Abstract base class.
+ */
+public abstract class AbstractBase {
+    public void doStuff() {
+        System.out.println("In AbstractBase.doStuff (src2)");
+        redefineMe();
+    }
+
+    public abstract void redefineMe();
+
+    public abstract void abstractOrNot();
+}
diff --git a/tests/033-class-init-deadlock/expected.txt b/tests/033-class-init-deadlock/expected.txt
new file mode 100644
index 0000000..387a426
--- /dev/null
+++ b/tests/033-class-init-deadlock/expected.txt
@@ -0,0 +1,7 @@
+Deadlock test starting.
+A initializing...
+B initializing...
+Deadlock test interupting threads.
+Deadlock test main thread bailing.
+A initialized: false
+B initialized: false
diff --git a/tests/033-class-init-deadlock/info.txt b/tests/033-class-init-deadlock/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/033-class-init-deadlock/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/033-class-init-deadlock/src/Main.java b/tests/033-class-init-deadlock/src/Main.java
new file mode 100644
index 0000000..27c4922
--- /dev/null
+++ b/tests/033-class-init-deadlock/src/Main.java
@@ -0,0 +1,51 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * This causes most VMs to lock up.
+ *
+ * Interrupting threads in class initialization should NOT work.
+ */
+public class Main {
+    public static boolean aInitialized = false;
+    public static boolean bInitialized = false;
+
+    static public void main(String[] args) {
+        Thread thread1, thread2;
+
+        System.out.println("Deadlock test starting.");
+        thread1 = new Thread() { public void run() { new A(); } };
+        thread2 = new Thread() { public void run() { new B(); } };
+        thread1.start();
+        thread2.start();
+
+        try { Thread.sleep(6000); } catch (InterruptedException ie) { }
+
+        System.out.println("Deadlock test interupting threads.");
+        thread1.interrupt();
+        thread2.interrupt();
+        System.out.println("Deadlock test main thread bailing.");
+        System.out.println("A initialized: " + aInitialized);
+        System.out.println("B initialized: " + bInitialized);
+        System.exit(0);
+    }
+}
+
+class A {
+    static {
+        System.out.println("A initializing...");
+        try { Thread.sleep(3000); } catch (InterruptedException ie) { }
+        new B();
+        System.out.println("A initialized");
+        Main.aInitialized = true;
+    }
+}
+
+class B {
+    static {
+        System.out.println("B initializing...");
+        try { Thread.sleep(3000); } catch (InterruptedException ie) { }
+        new A();
+        System.out.println("B initialized");
+        Main.bInitialized = true;
+    }
+}
diff --git a/tests/034-call-null/expected.txt b/tests/034-call-null/expected.txt
new file mode 100644
index 0000000..5ffbe05
--- /dev/null
+++ b/tests/034-call-null/expected.txt
@@ -0,0 +1,3 @@
+java.lang.NullPointerException
+	at Main.main(Main.java:12)
+	at dalvik.system.NativeStart.main(Native Method)
diff --git a/tests/034-call-null/info.txt b/tests/034-call-null/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/034-call-null/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/034-call-null/src/Main.java b/tests/034-call-null/src/Main.java
new file mode 100644
index 0000000..a0a129e
--- /dev/null
+++ b/tests/034-call-null/src/Main.java
@@ -0,0 +1,14 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Main {
+    int mFoo = 27;
+
+    private void doStuff() {
+        System.out.println("mFoo is " + mFoo);
+    }
+
+    public static void main(String[] args) {
+        Main instance = null;
+        instance.doStuff();
+    }
+}
diff --git a/tests/035-enum/expected.txt b/tests/035-enum/expected.txt
new file mode 100644
index 0000000..50f2791
--- /dev/null
+++ b/tests/035-enum/expected.txt
@@ -0,0 +1,3 @@
+found field CRAWLING
+  synthetic? false
+  enum? true
diff --git a/tests/035-enum/info.txt b/tests/035-enum/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/035-enum/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/035-enum/src/Main.java b/tests/035-enum/src/Main.java
new file mode 100644
index 0000000..09fcc98
--- /dev/null
+++ b/tests/035-enum/src/Main.java
@@ -0,0 +1,23 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.lang.reflect.Field;
+
+/**
+ * Try some stuff with enumerations.
+ */
+public class Main {
+    public enum Shubbery { GROUND, CRAWLING, HANGING }
+
+    public static void main(String[] args) {
+        Field field;
+        try {
+            field = Shubbery.class.getDeclaredField("CRAWLING");
+        } catch (NoSuchFieldException nsfe) {
+            throw new RuntimeException(nsfe);
+        }
+
+        System.out.println("found field " + field.getName());
+        System.out.println("  synthetic? " + field.isSynthetic());
+        System.out.println("  enum? " + field.isEnumConstant());
+    }
+}
diff --git a/tests/036-finalizer/expected.txt b/tests/036-finalizer/expected.txt
new file mode 100644
index 0000000..f9b29b0
--- /dev/null
+++ b/tests/036-finalizer/expected.txt
@@ -0,0 +1,14 @@
+wimp: wahoo
+gc
+finalizer executed: wahoo
+wimp: null
+finalize
+wimp: null
+sleep
+reborn: wahoo
+wimp: null
+reset reborn
+gc + finalize
+sleep
+reborn: nothing
+wimp: null
diff --git a/tests/036-finalizer/info.txt b/tests/036-finalizer/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/036-finalizer/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/036-finalizer/src/FinalizerTest.java b/tests/036-finalizer/src/FinalizerTest.java
new file mode 100644
index 0000000..420ec34
--- /dev/null
+++ b/tests/036-finalizer/src/FinalizerTest.java
@@ -0,0 +1,23 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.lang.ref.WeakReference;
+
+public class FinalizerTest {
+    public static FinalizerTest mNothing = new FinalizerTest("nothing");
+    public static FinalizerTest mReborn = mNothing;
+
+    public String mMsg = "default";
+
+    public FinalizerTest(String msg) {
+        mMsg = msg;
+    }
+
+    public String toString() {
+        return mMsg;
+    }
+
+    protected void finalize() {
+        System.out.println("finalizer executed: " + mMsg);
+        mReborn = this;
+    }
+}
diff --git a/tests/036-finalizer/src/Main.java b/tests/036-finalizer/src/Main.java
new file mode 100644
index 0000000..c29cc11
--- /dev/null
+++ b/tests/036-finalizer/src/Main.java
@@ -0,0 +1,107 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Some finalizer tests.
+ *
+ * This only works if System.runFinalization() causes finalizers to run
+ * immediately or very soon.
+ */
+public class Main {
+    private static void snooze(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ie) {
+            System.out.println("Snooze: " + ie.getMessage());
+        }
+    }
+
+    public static WeakReference makeRef() {
+        /*
+         * Make ft in another thread, so there is no danger of
+         * a conservative reference leaking onto the main thread's
+         * stack.
+         */
+
+        final WeakReference[] wimp = new WeakReference[1];
+        Thread t = new Thread() {
+                public void run() {
+                    FinalizerTest ft = new FinalizerTest("wahoo");
+                    wimp[0] = new WeakReference(ft);
+                    ft = null;
+                }
+            };
+
+        t.start();
+
+        try {
+            t.join();
+        } catch (InterruptedException ie) {
+            throw new RuntimeException(ie);
+        }
+
+        return wimp[0];
+    }
+
+    public static String wimpString(final WeakReference wimp) {
+        /*
+         * Do the work in another thread, so there is no danger of a
+         * conservative reference to ft leaking onto the main thread's
+         * stack.
+         */
+
+        final String[] s = new String[1];
+        Thread t = new Thread() {
+                public void run() {
+                    Object ref = wimp.get();
+                    if (ref != null) {
+                        s[0] = ref.toString();
+                    }
+                }
+            };
+
+        t.start();
+
+        try {
+            t.join();
+        } catch (InterruptedException ie) {
+            throw new RuntimeException(ie);
+        }
+
+        return s[0];
+    }
+
+    public static void main(String[] args) {
+        WeakReference wimp = makeRef();
+
+        System.out.println("wimp: " + wimpString(wimp));
+
+        /* this will try to collect and finalize ft */
+        System.out.println("gc");
+        System.gc();
+
+        System.out.println("wimp: " + wimpString(wimp));
+        System.out.println("finalize");
+        System.runFinalization();
+        System.out.println("wimp: " + wimpString(wimp));
+
+        System.out.println("sleep");
+        snooze(1000);
+
+        System.out.println("reborn: " + FinalizerTest.mReborn);
+        System.out.println("wimp: " + wimpString(wimp));
+        System.out.println("reset reborn");
+        System.gc();
+        FinalizerTest.mReborn = FinalizerTest.mNothing;
+        System.out.println("gc + finalize");
+        System.gc();
+        System.runFinalization();
+
+        System.out.println("sleep");
+        snooze(1000);
+
+        System.out.println("reborn: " + FinalizerTest.mReborn);
+        System.out.println("wimp: " + wimpString(wimp));
+    }
+}
diff --git a/tests/037-inherit/expected.txt b/tests/037-inherit/expected.txt
new file mode 100644
index 0000000..1fb9912
--- /dev/null
+++ b/tests/037-inherit/expected.txt
@@ -0,0 +1,3 @@
+magic is 64.0
+ 0: 64.0
+ 1: 64.0
diff --git a/tests/037-inherit/info.txt b/tests/037-inherit/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/037-inherit/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/037-inherit/src/Main.java b/tests/037-inherit/src/Main.java
new file mode 100644
index 0000000..55b782e
--- /dev/null
+++ b/tests/037-inherit/src/Main.java
@@ -0,0 +1,37 @@
+public class Main {
+    static void arrayCluster(IMagic[] magicArray) {
+        int i;
+
+        for (i = 0; i < magicArray.length; i++)
+            System.out.println(" " + i + ": " + magicArray[i].getSomeData());
+    }
+
+    public static void main(String args[]) {
+        MagicClass magic = new MagicClass();
+
+        System.out.print("magic is ");
+        System.out.println(magic.getSomeData());
+
+        MagicClass magicArray[] = new MagicClass[2];
+        magicArray[0] = new MagicClass();
+        magicArray[1] = new MagicClass();
+        arrayCluster(magicArray);
+    }
+}
+
+class IntSource {
+    public int getMagicInt() { return 64; }
+}
+
+interface IMagic {
+    public double getSomeData();
+
+    IntSource mIntSource = new IntSource();
+    public int MAGIC_INT = mIntSource.getMagicInt();
+}
+
+class MagicClass implements IMagic {
+    public double getSomeData() {
+        return this.MAGIC_INT;
+    }
+}
diff --git a/tests/038-inner-null/expected.txt b/tests/038-inner-null/expected.txt
new file mode 100644
index 0000000..0be8ffd
--- /dev/null
+++ b/tests/038-inner-null/expected.txt
@@ -0,0 +1,5 @@
+new Special()
+java.lang.NullPointerException
+	at Main$Special.callInner(Main.java:17)
+	at Main.main(Main.java:6)
+	at dalvik.system.NativeStart.main(Native Method)
diff --git a/tests/038-inner-null/info.txt b/tests/038-inner-null/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/038-inner-null/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/038-inner-null/src/Main.java b/tests/038-inner-null/src/Main.java
new file mode 100644
index 0000000..acc8764
--- /dev/null
+++ b/tests/038-inner-null/src/Main.java
@@ -0,0 +1,27 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Main {
+    public static void main(String[] args) {
+        Special special = new Special();
+        special.callInner();
+    }
+
+    public static class Special {
+        Blort mBlort = null;
+
+        Special() {
+            System.out.println("new Special()");
+        }
+
+        public void callInner() {
+            mBlort.repaint();
+        }
+    }
+
+    private class Blort {
+        public void repaint() {
+            System.out.println("shouldn't see this");
+        }
+    }
+
+}
diff --git a/tests/039-join-main/expected.txt b/tests/039-join-main/expected.txt
new file mode 100644
index 0000000..37e6d77
--- /dev/null
+++ b/tests/039-join-main/expected.txt
@@ -0,0 +1,5 @@
+Starting thread 'Joiner'
+@ JoinMainSub running
+JoinMain starter returning
+@ JoinMainSub successfully joined main
+@ JoinMainSub bailing
diff --git a/tests/039-join-main/info.txt b/tests/039-join-main/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/039-join-main/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/039-join-main/src/Main.java b/tests/039-join-main/src/Main.java
new file mode 100644
index 0000000..0644f1c
--- /dev/null
+++ b/tests/039-join-main/src/Main.java
@@ -0,0 +1,41 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Make sure that a sub-thread can join the main thread.
+ */
+public class Main {
+    public static void main(String[] args) {
+        Thread t;
+
+        t = new Thread(new JoinMainSub(Thread.currentThread()), "Joiner");
+        System.out.print("Starting thread '" + t.getName() + "'\n");
+        t.start();
+
+        try { Thread.sleep(1000); }
+        catch (InterruptedException ie) {}
+
+        System.out.print("JoinMain starter returning\n");
+    }
+}
+
+class JoinMainSub implements Runnable {
+    private Thread mJoinMe;
+
+    public JoinMainSub(Thread joinMe) {
+        mJoinMe = joinMe;
+    }
+
+    public void run() {
+        System.out.print("@ JoinMainSub running\n");
+
+        try {
+            mJoinMe.join();
+            System.out.print("@ JoinMainSub successfully joined main\n");
+        } catch (InterruptedException ie) {
+            System.out.print("@ JoinMainSub interrupted!\n");
+        }
+        finally {
+            System.out.print("@ JoinMainSub bailing\n");
+        }
+    }
+}
diff --git a/tests/040-miranda/expected.txt b/tests/040-miranda/expected.txt
new file mode 100644
index 0000000..e22bbd9
--- /dev/null
+++ b/tests/040-miranda/expected.txt
@@ -0,0 +1,12 @@
+MirandaClass:
+  inInterface:  true
+  inInterface2: 27
+  inAbstract:   false
+MirandaAbstract / MirandaClass:
+  inInterface:  true
+  inInterface2: 27
+  inAbstract:   false
+MirandaAbstract / MirandaClass2:
+  inInterface:  true
+  inInterface2: 28
+  inAbstract:   true
diff --git a/tests/040-miranda/info.txt b/tests/040-miranda/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/040-miranda/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/040-miranda/src/Main.java b/tests/040-miranda/src/Main.java
new file mode 100644
index 0000000..558806a
--- /dev/null
+++ b/tests/040-miranda/src/Main.java
@@ -0,0 +1,27 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public class Main {
+    public static void main(String[] args) {
+        MirandaClass mir = new MirandaClass();
+        System.out.println("MirandaClass:");
+        System.out.println("  inInterface:  " + mir.inInterface());
+        System.out.println("  inInterface2: " + mir.inInterface2());
+        System.out.println("  inAbstract:   " + mir.inAbstract());
+
+        /* try again through abstract class; results should be identical */
+        MirandaAbstract mira = mir;
+        System.out.println("MirandaAbstract / MirandaClass:");
+        System.out.println("  inInterface:  " + mira.inInterface());
+        System.out.println("  inInterface2: " + mira.inInterface2());
+        System.out.println("  inAbstract:   " + mira.inAbstract());
+
+        MirandaAbstract mira2 = new MirandaClass2();
+        System.out.println("MirandaAbstract / MirandaClass2:");
+        System.out.println("  inInterface:  " + mira2.inInterface());
+        System.out.println("  inInterface2: " + mira2.inInterface2());
+        System.out.println("  inAbstract:   " + mira2.inAbstract());
+    }
+}
diff --git a/tests/040-miranda/src/MirandaAbstract.java b/tests/040-miranda/src/MirandaAbstract.java
new file mode 100644
index 0000000..b603ce6
--- /dev/null
+++ b/tests/040-miranda/src/MirandaAbstract.java
@@ -0,0 +1,16 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public abstract class MirandaAbstract implements MirandaInterface, MirandaInterface2
+{
+    protected MirandaAbstract() { }
+
+    //public abstract boolean inInterface();
+    //public abstract int inInterface2();
+
+    public boolean inAbstract() {
+        return true;
+    }
+}
diff --git a/tests/040-miranda/src/MirandaClass.java b/tests/040-miranda/src/MirandaClass.java
new file mode 100644
index 0000000..3bf6704
--- /dev/null
+++ b/tests/040-miranda/src/MirandaClass.java
@@ -0,0 +1,24 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public class MirandaClass extends MirandaAbstract {
+
+    public MirandaClass() {}
+
+    public boolean inInterface() {
+        //System.out.println("    MirandaClass inInterface");
+        return true;
+    }
+
+    public int inInterface2() {
+        //System.out.println("    MirandaClass inInterface2");
+        return 27;
+    }
+
+    public boolean inAbstract() {
+        //System.out.println("    MirandaClass inAbstract");
+        return false;
+    }
+}
diff --git a/tests/040-miranda/src/MirandaClass2.java b/tests/040-miranda/src/MirandaClass2.java
new file mode 100644
index 0000000..e9bdf2b
--- /dev/null
+++ b/tests/040-miranda/src/MirandaClass2.java
@@ -0,0 +1,9 @@
+class MirandaClass2 extends MirandaAbstract {
+    public boolean inInterface() {
+        return true;
+    }
+
+    public int inInterface2() {
+        return 28;
+    }
+}
diff --git a/tests/040-miranda/src/MirandaInterface.java b/tests/040-miranda/src/MirandaInterface.java
new file mode 100644
index 0000000..2c0a59a
--- /dev/null
+++ b/tests/040-miranda/src/MirandaInterface.java
@@ -0,0 +1,10 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public interface MirandaInterface {
+
+    public boolean inInterface();
+
+}
diff --git a/tests/040-miranda/src/MirandaInterface2.java b/tests/040-miranda/src/MirandaInterface2.java
new file mode 100644
index 0000000..83b6af8
--- /dev/null
+++ b/tests/040-miranda/src/MirandaInterface2.java
@@ -0,0 +1,12 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public interface MirandaInterface2 {
+
+    public boolean inInterface();
+
+    public int inInterface2();
+
+}
diff --git a/tests/041-narrowing/expected.txt b/tests/041-narrowing/expected.txt
new file mode 100644
index 0000000..93b8590
--- /dev/null
+++ b/tests/041-narrowing/expected.txt
@@ -0,0 +1,38 @@
+
+Double.POSITIVE_INFINITY = 7ff0000000000000
+Double.NEGATIVE_INFINITY = fff0000000000000
+Float.POSITIVE_INFINITY  = 7ff0000000000000
+Float.NEGATIVE_INFINITY  = fff0000000000000
+Double.NaN               = 7ff8000000000000
+Float.NaN                = 7ff8000000000000
+
+(byte) Double.NaN  =               00 expected:               00
+(short) Double.NaN =             0000 expected:             0000
+(int) Double.NaN   =         00000000 expected:         00000000
+(long) Double.NaN  = 0000000000000000 expected: 0000000000000000
+
+(byte) Float.NaN  =               00 expected:               00
+(short) Float.NaN =             0000 expected:             0000
+(int) Float.NaN   =         00000000 expected:         00000000
+(long) Float.NaN  = 0000000000000000 expected: 0000000000000000
+
+(byte) Double.POSITIVE_INFINITY  =               ff expected:               ff
+(short) Double.POSITIVE_INFINITY =             ffff expected:             ffff
+(int) Double.POSITIVE_INFINITY   =         7fffffff expected:         7fffffff
+(long) Double.POSITIVE_INFINITY  = 7fffffffffffffff expected: 7fffffffffffffff
+
+(byte) Double.NEGATIVE_INFINITY  =               00 expected:               00
+(short) Double.NEGATIVE_INFINITY =             0000 expected:             0000
+(int) Double.NEGATIVE_INFINITY   =         80000000 expected:         80000000
+(long) Double.NEGATIVE_INFINITY  = 8000000000000000 expected: 8000000000000000
+
+(byte) Float.POSITIVE_INFINITY   =               ff expected:               ff
+(short) Float.POSITIVE_INFINITY  =             ffff expected:             ffff
+(int) Float.POSITIVE_INFINITY    =         7fffffff expected:         7fffffff
+(long) Float.POSITIVE_INFINITY   = 7fffffffffffffff expected: 7fffffffffffffff
+
+(byte) Float.NEGATIVE_INFINITY   =               00 expected:               00
+(short) Float.NEGATIVE_INFINITY  =             0000 expected:             0000
+(int) Float.NEGATIVE_INFINITY    =         80000000 expected:         80000000
+(long) Float.NEGATIVE_INFINITY   = 8000000000000000 expected: 8000000000000000
+
diff --git a/tests/041-narrowing/info.txt b/tests/041-narrowing/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/041-narrowing/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/041-narrowing/src/Main.java b/tests/041-narrowing/src/Main.java
new file mode 100644
index 0000000..eb9d64a
--- /dev/null
+++ b/tests/041-narrowing/src/Main.java
@@ -0,0 +1,99 @@
+public class Main {
+    public static void main(String[] args) {
+        test_printNarrowing();
+    }
+
+    public static void test_printNarrowing() {
+
+        System.out.println();
+        System.out.println("Double.POSITIVE_INFINITY = "
+                + Long.toHexString(Double.doubleToRawLongBits(Double.POSITIVE_INFINITY)));
+        System.out.println("Double.NEGATIVE_INFINITY = "
+                + Long.toHexString(Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY)));
+        System.out.println("Float.POSITIVE_INFINITY  = "
+                + Long.toHexString(Double.doubleToRawLongBits(Float.POSITIVE_INFINITY)));
+        System.out.println("Float.NEGATIVE_INFINITY  = "
+                + Long.toHexString(Double.doubleToRawLongBits(Float.NEGATIVE_INFINITY)));
+        System.out.println("Double.NaN               = "
+                + Long.toHexString(Double.doubleToRawLongBits(Double.NaN)));
+        System.out.println("Float.NaN                = "
+                + Long.toHexString(Double.doubleToRawLongBits(Float.NaN)));
+        double dbl2 = Double.NaN;
+        System.out.println();
+        System.out.println("(byte) Double.NaN  =               "
+                + (Long.toHexString((byte)dbl2).equals("0") ? "00" : Long.toHexString((byte)dbl2)
+                        .substring(6)) + " expected:               00");
+        System.out.println("(short) Double.NaN =             "
+                + (Integer.toHexString((short)dbl2).equals("0") ? "0000" : Integer.toHexString(
+                        (short)dbl2).substring(4)) + " expected:             0000");
+        System.out.println("(int) Double.NaN   =         "
+                + (Integer.toHexString((int)dbl2).equals("0") ? "00000000" : Integer
+                        .toHexString((int)dbl2)) + " expected:         00000000");
+        System.out.println("(long) Double.NaN  = "
+                + (Long.toHexString((long)dbl2).equals("0") ? "0000000000000000" : Long
+                        .toHexString((long)dbl2)) + " expected: 0000000000000000");
+        float fl2 = Float.NaN;
+        System.out.println();
+        System.out.println("(byte) Float.NaN  =               "
+                + (Long.toHexString((byte)fl2).equals("0") ? "00" : Long.toHexString((byte)fl2)
+                        .substring(6)) + " expected:               00");
+        System.out.println("(short) Float.NaN =             "
+                + (Integer.toHexString((short)fl2).equals("0") ? "0000" : Integer.toHexString(
+                        (short)fl2).substring(4)) + " expected:             0000");
+        System.out.println("(int) Float.NaN   =         "
+                + (Integer.toHexString((int)fl2).equals("0") ? "00000000" : Integer
+                        .toHexString((int)fl2)) + " expected:         00000000");
+        System.out.println("(long) Float.NaN  = "
+                + (Long.toHexString((long)fl2).equals("0") ? "0000000000000000" : Long
+                        .toHexString((long)fl2)) + " expected: 0000000000000000");
+        double dbl3 = Double.POSITIVE_INFINITY;
+        System.out.println();
+        System.out.println("(byte) Double.POSITIVE_INFINITY  =               "
+                + (Integer.toHexString((byte)dbl3).equals("0") ? "00" : Integer.toHexString(
+                        (byte)dbl3).substring(6)) + " expected:               ff");
+        System.out.println("(short) Double.POSITIVE_INFINITY =             "
+                + (Integer.toHexString((short)dbl3).equals("0") ? "0000" : Integer.toHexString(
+                        (short)dbl3).substring(4)) + " expected:             ffff");
+        System.out.println("(int) Double.POSITIVE_INFINITY   =         "
+                + Integer.toHexString((int)dbl3) + " expected:         7fffffff");
+        System.out.println("(long) Double.POSITIVE_INFINITY  = " + Long.toHexString((long)dbl3)
+                + " expected: 7fffffffffffffff");
+        double dbl4 = Double.NEGATIVE_INFINITY;
+        System.out.println();
+        System.out.println("(byte) Double.NEGATIVE_INFINITY  = "
+                + (Long.toHexString((byte)dbl4).equals("0") ? "              00" : Long
+                        .toHexString((byte)dbl4)) + " expected:               00");
+        System.out.println("(short) Double.NEGATIVE_INFINITY = "
+                + (Integer.toHexString((short)dbl4).equals("0") ? "            0000" : Long
+                        .toHexString((short)dbl4)) + " expected:             0000");
+        System.out.println("(int) Double.NEGATIVE_INFINITY   =         "
+                + Integer.toHexString((int)dbl4) + " expected:         80000000");
+        System.out.println("(long) Double.NEGATIVE_INFINITY  = " + Long.toHexString((long)dbl4)
+                + " expected: 8000000000000000");
+        float fl3 = Float.POSITIVE_INFINITY;
+        System.out.println();
+        System.out.println("(byte) Float.POSITIVE_INFINITY   =               "
+                + (Integer.toHexString((byte)fl3).equals("0") ? "00" : Integer.toHexString(
+                        (byte)fl3).substring(6)) + " expected:               ff");
+        System.out.println("(short) Float.POSITIVE_INFINITY  =             "
+                + (Integer.toHexString((short)fl3).equals("0") ? "0000" : Integer.toHexString(
+                        (short)fl3).substring(4)) + " expected:             ffff");
+        System.out.println("(int) Float.POSITIVE_INFINITY    =         "
+                + Integer.toHexString((int)fl3) + " expected:         7fffffff");
+        System.out.println("(long) Float.POSITIVE_INFINITY   = " + Long.toHexString((long)fl3)
+                + " expected: 7fffffffffffffff");
+        float fl4 = Float.NEGATIVE_INFINITY;
+        System.out.println();
+        System.out.println("(byte) Float.NEGATIVE_INFINITY   = "
+                + (Long.toHexString((byte)fl4).equals("0") ? "              00" : Long
+                        .toHexString((byte)fl4)) + " expected:               00");
+        System.out.println("(short) Float.NEGATIVE_INFINITY  = "
+                + (Integer.toHexString((short)fl4).equals("0") ? "            0000" : Long
+                        .toHexString((short)fl4)) + " expected:             0000");
+        System.out.println("(int) Float.NEGATIVE_INFINITY    =         "
+                + Integer.toHexString((int)fl4) + " expected:         80000000");
+        System.out.println("(long) Float.NEGATIVE_INFINITY   = " + Long.toHexString((long)fl4)
+                + " expected: 8000000000000000");
+        System.out.println();
+    }
+}
diff --git a/tests/042-new-instance/expected.txt b/tests/042-new-instance/expected.txt
new file mode 100644
index 0000000..53447db
--- /dev/null
+++ b/tests/042-new-instance/expected.txt
@@ -0,0 +1,8 @@
+LocalClass succeeded
+Got expected PackageAccess complaint
+LocalClass3 succeeded
+Got expected InstantationError
+Cons LocalClass failed as expected
+Cons LocalClass2 succeeded
+Cons got expected PackageAccess complaint
+Cons got expected InstantationException
diff --git a/tests/042-new-instance/info.txt b/tests/042-new-instance/info.txt
new file mode 100644
index 0000000..49c9e02
--- /dev/null
+++ b/tests/042-new-instance/info.txt
@@ -0,0 +1,2 @@
+Test various permutations of Class.newInstance and Constructor.newInstance,
+looking for correct handling of access rights and abstract classes.
diff --git a/tests/042-new-instance/src/Main.java b/tests/042-new-instance/src/Main.java
new file mode 100644
index 0000000..8faef13
--- /dev/null
+++ b/tests/042-new-instance/src/Main.java
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Constructor;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Test instance creation.
+ */
+public class Main {
+    public static void main(String[] args) {
+        testClassNewInstance();
+        testConstructorNewInstance();
+    }
+
+    /**
+     * Tests Class.newInstance().
+     */
+    static void testClassNewInstance() {
+        // should succeed
+        try {
+            Class c = Class.forName("LocalClass");
+            Object obj = c.newInstance();
+            System.out.println("LocalClass succeeded");
+        } catch (Exception ex) {
+            System.err.println("LocalClass failed");
+            ex.printStackTrace();
+        }
+
+        // should fail
+        try {
+            Class c = Class.forName("otherpackage.PackageAccess");
+            Object obj = c.newInstance();
+            System.err.println("ERROR: PackageAccess succeeded unexpectedly");
+        } catch (IllegalAccessException iae) {
+            System.out.println("Got expected PackageAccess complaint");
+        } catch (Exception ex) {
+            System.err.println("Got unexpected PackageAccess failure");
+            ex.printStackTrace();
+        }
+
+        LocalClass3.main();
+
+        try {
+            MaybeAbstract ma = new MaybeAbstract();
+            System.err.println("ERROR: MaybeAbstract succeeded unexpectedly");
+        } catch (InstantiationError ie) {
+            System.out.println("Got expected InstantationError");
+        } catch (Exception ex) {
+            System.err.println("Got unexpected MaybeAbstract failure");
+        }
+    }
+
+    /**
+     * Tests Constructor.newInstance().
+     */
+    static void testConstructorNewInstance() {
+        // should fail -- getConstructor only returns public constructors
+        try {
+            Class c = Class.forName("LocalClass");
+            Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+            System.err.println("Cons LocalClass succeeded unexpectedly");
+        } catch (NoSuchMethodException nsme) {
+            System.out.println("Cons LocalClass failed as expected");
+        } catch (Exception ex) {
+            System.err.println("Cons LocalClass failed strangely");
+            ex.printStackTrace();
+        }
+
+        // should succeed
+        try {
+            Class c = Class.forName("LocalClass2");
+            Constructor cons = c.getConstructor((Class[]) null);
+            Object obj = cons.newInstance();
+            System.out.println("Cons LocalClass2 succeeded");
+        } catch (Exception ex) {
+            System.err.println("Cons LocalClass2 failed");
+            ex.printStackTrace();
+        }
+
+        // should fail
+        try {
+            Class c = Class.forName("otherpackage.PackageAccess");
+            Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+            System.err.println("ERROR: Cons PackageAccess succeeded unexpectedly");
+        } catch (NoSuchMethodException nsme) {
+            System.out.println("Cons got expected PackageAccess complaint");
+        } catch (Exception ex) {
+            System.err.println("Cons got unexpected PackageAccess failure");
+            ex.printStackTrace();
+        }
+
+        // should fail
+        try {
+            Class c = Class.forName("MaybeAbstract");
+            Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+            Object obj = cons.newInstance();
+            System.err.println("ERROR: Cons MaybeAbstract succeeded unexpectedly");
+        } catch (InstantiationException ie) {
+            // note InstantiationException vs. InstantiationError
+            System.out.println("Cons got expected InstantationException");
+        } catch (Exception ex) {
+            System.err.println("Cons got unexpected MaybeAbstract failure");
+            ex.printStackTrace();
+        }
+    }
+}
+
+class LocalClass {
+    // this class has a default constructor with package visibility
+}
+
+class LocalClass2 {
+    public LocalClass2() {}
+}
+
+
+class LocalClass3 {
+    public static void main() {
+        try {
+            CC.newInstance();
+            System.out.println("LocalClass3 succeeded");
+        } catch (Exception ex) {
+            System.err.println("Got unexpected LocalClass3 failure");
+            ex.printStackTrace();
+        }
+    }
+
+    static class CC {
+        private CC() {}
+
+        static Object newInstance() {
+            try {
+                Class c = CC.class;
+                return c.newInstance();
+            } catch (Exception ex) {
+                ex.printStackTrace();
+                return null;
+            }
+        }
+    }
+}
diff --git a/tests/042-new-instance/src/MaybeAbstract.java b/tests/042-new-instance/src/MaybeAbstract.java
new file mode 100644
index 0000000..6d3b05b
--- /dev/null
+++ b/tests/042-new-instance/src/MaybeAbstract.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+public /*abstract*/ class MaybeAbstract {
+    public MaybeAbstract() {}
+    int foo() { return 0; }
+}
diff --git a/tests/042-new-instance/src/otherpackage/PackageAccess.java b/tests/042-new-instance/src/otherpackage/PackageAccess.java
new file mode 100644
index 0000000..0749d67
--- /dev/null
+++ b/tests/042-new-instance/src/otherpackage/PackageAccess.java
@@ -0,0 +1,6 @@
+package otherpackage;
+
+class PackageAccess {
+    /*package*/ PackageAccess() {
+    }
+}
diff --git a/tests/042-new-instance/src2/MaybeAbstract.java b/tests/042-new-instance/src2/MaybeAbstract.java
new file mode 100644
index 0000000..8b70a07
--- /dev/null
+++ b/tests/042-new-instance/src2/MaybeAbstract.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+public abstract class MaybeAbstract {
+    public MaybeAbstract() {}
+    int foo() { return 0; }
+}
diff --git a/tests/043-privates/expected.txt b/tests/043-privates/expected.txt
new file mode 100644
index 0000000..2779ec7
--- /dev/null
+++ b/tests/043-privates/expected.txt
@@ -0,0 +1,6 @@
+PrivatePackage --> PrivatePackage!
+PrivatePackage --> PrivatePackage!
+PrivatePackage --> PrivatePackage!
+PrivatePackageSub --> PrivatePackageSub!
+PrivatePackage --> PrivatePackage!
+PrivatePackage --> PrivatePackage!
diff --git a/tests/043-privates/info.txt b/tests/043-privates/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/043-privates/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/043-privates/src/Main.java b/tests/043-privates/src/Main.java
new file mode 100644
index 0000000..73b4d79
--- /dev/null
+++ b/tests/043-privates/src/Main.java
@@ -0,0 +1,45 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Make sure private methods don't inherit.
+ */
+public class Main {
+    public static void main(String args[]) {
+        PrivatePackage inst1 = new PrivatePackage();
+        PrivatePackage inst2 = new PrivatePackageSub();
+        PrivatePackageSub inst3 = new PrivatePackageSub();
+
+        System.out.println("PrivatePackage --> " + inst1.getStr());
+        System.out.println("PrivatePackage --> " + inst2.getStr());
+        System.out.println("PrivatePackage --> " + inst3.getStr());
+        System.out.println("PrivatePackageSub --> " + inst3.getStrSub());
+
+        inst1.stretchTest();
+    }
+}
+
+class PrivatePackage {
+    public String getStr() {
+        return privGetStr();
+    }
+
+    private String privGetStr() {
+        return "PrivatePackage!";
+    }
+
+    public void stretchTest() {
+        PrivatePackage inst = new PrivatePackageSub();
+        System.out.println("PrivatePackage --> " + inst.getStr());
+        System.out.println("PrivatePackage --> " + inst.privGetStr());
+    }
+}
+
+class PrivatePackageSub extends PrivatePackage {
+    public String getStrSub() {
+        return privGetStr();
+    }
+
+    private String privGetStr() {
+        return "PrivatePackageSub!";
+    }
+}
diff --git a/tests/044-proxy/expected.txt b/tests/044-proxy/expected.txt
new file mode 100644
index 0000000..69a94f2
--- /dev/null
+++ b/tests/044-proxy/expected.txt
@@ -0,0 +1,80 @@
+Invoke public abstract void Shapes.circle(int)
+ 0: 3
+--- circle 3
+Success: method circle res=null
+Invoke public abstract int Quads.rectangle(int,int)
+ 0: 10
+ 1: 20
+--- rectangle 10,20
+Success: method rectangle res=4
+Invoke public abstract java.lang.String Shapes.blob()
+ (no args)
+--- blob
+Success: method blob res=mix
+Invoke public abstract int Quads.rectangle(int,int)
+ 0: 15
+ 1: 25
+--- rectangle 15,25
+Success: method rectangle res=4
+Invoke public abstract int Quads.trapezoid(int,double,int)
+ 0: 6
+ 1: 81.18
+ 2: 4
+--- trap 6,4,81.18
+Success: method trapezoid res=8
+Invoke public abstract int Colors.red(float)
+ 0: 1.0
+--- red 1.0
+Success: method red res=0
+Invoke public abstract double Colors.blue(int)
+ 0: 777
+--- blue 777
+Success: method blue res=2.54
+Invoke public abstract int Colors.mauve(java.lang.String)
+ 0: sorry
+--- mauve sorry
+Success: method mauve res=3
+Invoke public abstract java.lang.String Shapes.blob()
+ (no args)
+--- blob
+Success: method blob res=mix
+Invoke public abstract void Shapes.upChuck()
+ (no args)
+Got expected ioobe
+Invoke public abstract void Shapes.upCheck() throws java.lang.InterruptedException
+ (no args)
+Got expected ie
+
+Proxy methods: [public native boolean $Proxy0.equals(java.lang.Object), public native int $Proxy0.hashCode(), public native java.lang.String $Proxy0.toString(), public native int $Proxy0.rectangle(int,int), public native int $Proxy0.square(int,int), public native int $Proxy0.trapezoid(int,double,int), public native java.lang.String $Proxy0.blob(), public native void $Proxy0.circle(int), public native void $Proxy0.upCheck(), public native void $Proxy0.upChuck(), public native double $Proxy0.blue(int), public native R0aa $Proxy0.checkMe(), public native int $Proxy0.green(double), public native int $Proxy0.mauve(java.lang.String), public native int $Proxy0.red(float)]
+Decl annos: []
+Param annos (1) : [[]]
+Proxy fields: [private static [[Ljava.lang.Throwable; $Proxy0.throws]
+Dupe threw expected exception
+Clash threw expected exception
+Clash2 threw expected exception
+Clash3 threw expected exception
+Clash4 threw expected exception
+Invoke public abstract void InterfaceW1.throwFunky()
+ (no args)
+Got expected UTE
+Invoke public abstract void InterfaceW1.throwFunky2() throws BaseException,java.lang.NoSuchMethodException,java.io.IOException
+ (no args)
+Got expected IOE
+Invoke public abstract void InterfaceW1.throwFunky2() throws BaseException,java.lang.NoSuchMethodException,java.io.IOException
+ (no args)
+Got expected IOE
+Invoke public abstract void InterfaceW1.throwException() throws BaseException
+ (no args)
+Got expected UTE
+Invoke public abstract void InterfaceW1.throwBase() throws BaseException
+ (no args)
+Got expected UTE
+Invoke public abstract void InterfaceW1.throwSub() throws BaseException
+ (no args)
+Got expected exception
+Invoke public abstract void InterfaceW1.throwSubSub() throws BaseException
+ (no args)
+Got expected exception
+Invoke public abstract void InterfaceW1.bothThrowBase() throws BaseException,SubException,SubSubException
+ (no args)
+Got expected exception
diff --git a/tests/044-proxy/info.txt b/tests/044-proxy/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/044-proxy/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/044-proxy/src/BasicTest.java b/tests/044-proxy/src/BasicTest.java
new file mode 100644
index 0000000..2a453c4
--- /dev/null
+++ b/tests/044-proxy/src/BasicTest.java
@@ -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.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+
+/**
+ * Do some basic tests.
+ */
+public class BasicTest {
+
+    public static void main(String[] args) {
+        Mix proxyMe = new Mix();
+        Object proxy = createProxy(proxyMe);
+
+        if (!Proxy.isProxyClass(proxy.getClass()))
+            System.err.println("not a proxy class?");
+        if (Proxy.getInvocationHandler(proxy) == null)
+            System.err.println("ERROR: Proxy.getInvocationHandler is null");
+
+        /* take it for a spin; verifies instanceof constraint */
+        Shapes shapes = (Shapes) proxy;
+        shapes.circle(3);
+        shapes.rectangle(10, 20);
+        shapes.blob();
+        Quads quads = (Quads) proxy;
+        quads.rectangle(15, 25);
+        quads.trapezoid(6, 81.18, 4);
+        Colors colors = (Colors) proxy;
+        colors.red(1.0f);
+        colors.blue(777);
+        colors.mauve("sorry");
+        colors.blob();
+
+        try {
+            shapes.upChuck();
+            System.out.println("Didn't get expected exception");
+        } catch (IndexOutOfBoundsException ioobe) {
+            System.out.println("Got expected ioobe");
+        }
+        try {
+            shapes.upCheck();
+            System.out.println("Didn't get expected exception");
+        } catch (InterruptedException ie) {
+            System.out.println("Got expected ie");
+        }
+
+        /*
+         * Exercise annotations on Proxy classes.  This is mostly to ensure
+         * that annotation calls work correctly on generated classes.
+         */
+        System.out.println("");
+        Method[] methods = proxy.getClass().getDeclaredMethods();
+        System.out.println("Proxy methods: " + Arrays.deepToString(methods));
+        Method meth = methods[methods.length -1];
+        System.out.println("Decl annos: " + Arrays.deepToString(meth.getDeclaredAnnotations()));
+        Annotation[][] paramAnnos = meth.getParameterAnnotations();
+        System.out.println("Param annos (" + paramAnnos.length + ") : "
+            + Arrays.deepToString(paramAnnos));
+        Field[] fields = proxy.getClass().getDeclaredFields();
+        System.out.println("Proxy fields: " + Arrays.deepToString(fields));
+    }
+
+    static Object createProxy(Object proxyMe) {
+        /* declare an object that will handle the method calls */
+        InvocationHandler handler = new MyInvocationHandler(proxyMe);
+
+        /* create the proxy class */
+        Class proxyClass = Proxy.getProxyClass(Shapes.class.getClassLoader(),
+                            new Class[] { Quads.class, Colors.class });
+
+        /* create a proxy object, passing the handler object in */
+        Object proxy = null;
+        try {
+            Constructor<Class> cons;
+            cons = proxyClass.getConstructor(
+                            new Class[] { InvocationHandler.class });
+            //System.out.println("Constructor is " + cons);
+            proxy = cons.newInstance(new Object[] { handler });
+        } catch (NoSuchMethodException nsme) {
+            System.err.println("failed: " + nsme);
+        } catch (InstantiationException ie) {
+            System.err.println("failed: " + ie);
+        } catch (IllegalAccessException ie) {
+            System.err.println("failed: " + ie);
+        } catch (InvocationTargetException ite) {
+            System.err.println("failed: " + ite);
+        }
+
+        return proxy;
+    }
+}
+
+/*
+ * Some interfaces.
+ */
+interface Shapes {
+    public void circle(int r);
+    public int rectangle(int x, int y);
+
+    public String blob();
+
+    public R0base checkMe();
+    public void upChuck();
+    public void upCheck() throws InterruptedException;
+}
+
+interface Quads extends Shapes {
+    public int rectangle(int x, int y);
+    public int square(int x, int y);
+    public int trapezoid(int x, double off, int y);
+
+    public R0a checkMe();
+}
+
+/*
+ * More interfaces.
+ */
+interface Colors {
+    public int red(float howRed);
+    public int green(double howGreen);
+    public double blue(int howBlue);
+    public int mauve(String apology);
+
+    public String blob();
+
+    public R0aa checkMe();
+}
+
+/*
+ * Some return types.
+ */
+class R0base { int mBlah;  }
+class R0a extends R0base { int mBlah_a;  }
+class R0aa extends R0a { int mBlah_aa;  }
+
+
+/*
+ * A class that implements them all.
+ */
+class Mix implements Quads, Colors {
+    public void circle(int r) {
+        System.out.println("--- circle " + r);
+    }
+    public int rectangle(int x, int y) {
+        System.out.println("--- rectangle " + x + "," + y);
+        return 4;
+    }
+    public int square(int x, int y) {
+        System.out.println("--- square " + x + "," + y);
+        return 4;
+    }
+    public int trapezoid(int x, double off, int y) {
+        System.out.println("--- trap " + x + "," + y + "," + off);
+        return 8;
+    }
+    public String blob() {
+        System.out.println("--- blob");
+        return "mix";
+    }
+
+    public int red(float howRed) {
+        System.out.println("--- red " + howRed);
+        return 0;
+    }
+    public int green(double howGreen) {
+        System.out.println("--- green " + howGreen);
+        return 1;
+    }
+    public double blue(int howBlue) {
+        System.out.println("--- blue " + howBlue);
+        return 2.54;
+    }
+    public int mauve(String apology) {
+        System.out.println("--- mauve " + apology);
+        return 3;
+    }
+
+    public R0aa checkMe() {
+        return null;
+    }
+    public void upChuck() {
+        throw new IndexOutOfBoundsException("upchuck");
+    }
+    public void upCheck() throws InterruptedException {
+        throw new InterruptedException("upcheck");
+    }
+}
+
+/*
+ * Invocation handler, defining the implementation of the proxy functions.
+ */
+class MyInvocationHandler implements InvocationHandler {
+    Object mObj;
+
+    public MyInvocationHandler(Object obj) {
+        mObj = obj;
+    }
+
+    /*
+     * This is called when anything gets invoked in the proxy object.
+     */
+    public Object invoke(Object proxy, Method method, Object[] args)
+        throws Throwable {
+
+        Object result = null;
+
+        // Trap Object calls.  This is important here to avoid a recursive
+        // invocation of toString() in the print statements below.
+        if (method.getDeclaringClass() == java.lang.Object.class) {
+            //System.out.println("!!! object " + method.getName());
+            if (method.getName().equals("toString"))
+                return super.toString();
+            else if (method.getName().equals("hashCode"))
+                return Integer.valueOf(super.hashCode());
+            else if (method.getName().equals("equals"))
+                return Boolean.valueOf(super.equals(args[0]));
+            else
+                throw new RuntimeException("huh?");
+        }
+
+        System.out.println("Invoke " + method);
+        if (args == null || args.length == 0) {
+            System.out.println(" (no args)");
+        } else {
+            for (int i = 0; i < args.length; i++)
+                System.out.println(" " + i + ": " + args[i]);
+        }
+
+        try {
+            if (true)
+                result = method.invoke(mObj, args);
+            else
+                result = -1;
+            System.out.println("Success: method " + method.getName()
+                + " res=" + result);
+        } catch (InvocationTargetException ite) {
+            throw ite.getTargetException();
+        } catch (IllegalAccessException iae) {
+            throw new RuntimeException(iae);
+        }
+        return result;
+    }
+}
diff --git a/tests/044-proxy/src/Clash.java b/tests/044-proxy/src/Clash.java
new file mode 100644
index 0000000..adeffdc
--- /dev/null
+++ b/tests/044-proxy/src/Clash.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/*
+ * Try to instantiate a proxy class with interfaces that have conflicting
+ * duplicate methods (primitive vs. object).
+ */
+public class Clash {
+    public static void main(String[] args) {
+        InvocationHandler handler = new ClashInvocationHandler();
+
+        /* try passing in the same interface twice */
+        try {
+            Proxy.newProxyInstance(Clash.class.getClassLoader(),
+                new Class[] { Interface1A.class, Interface1A.class },
+                handler);
+            System.err.println("Dupe did not throw expected exception");
+        } catch (IllegalArgumentException iae) {
+            System.out.println("Dupe threw expected exception");
+        }
+
+        try {
+            Proxy.newProxyInstance(Clash.class.getClassLoader(),
+                new Class[] { Interface1A.class, Interface1B.class },
+                handler);
+            System.err.println("Clash did not throw expected exception");
+        } catch (IllegalArgumentException iae) {
+            System.out.println("Clash threw expected exception");
+        }
+    }
+}
+
+interface Interface1A {
+    public int thisIsOkay();
+
+    public float thisIsTrouble();
+}
+
+interface Interface1B {
+    public int thisIsOkay();
+
+    public Object thisIsTrouble();
+}
+
+class ClashInvocationHandler implements InvocationHandler {
+    /* don't really need to do anything -- should never get this far */
+    public Object invoke(Object proxy, Method method, Object[] args)
+        throws Throwable {
+
+        return null;
+    }
+}
diff --git a/tests/044-proxy/src/Clash2.java b/tests/044-proxy/src/Clash2.java
new file mode 100644
index 0000000..2a384f4
--- /dev/null
+++ b/tests/044-proxy/src/Clash2.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/*
+ * Try to instantiate a proxy class with interfaces that have conflicting
+ * duplicate methods (primitive types).
+ */
+public class Clash2 {
+    public static void main(String[] args) {
+        InvocationHandler handler = new Clash2InvocationHandler();
+
+        try {
+            Proxy.newProxyInstance(Clash.class.getClassLoader(),
+                new Class[] { Interface2A.class, Interface2B.class },
+                handler);
+            System.err.println("Clash2 did not throw expected exception");
+        } catch (IllegalArgumentException iae) {
+            System.out.println("Clash2 threw expected exception");
+        }
+    }
+}
+
+interface Interface2A {
+    public int thisIsOkay();
+
+    public int thisIsTrouble();
+}
+
+interface Interface2B {
+    public int thisIsOkay();
+
+    public short thisIsTrouble();
+}
+
+class Clash2InvocationHandler implements InvocationHandler {
+    /* don't really need to do anything -- should never get this far */
+    public Object invoke(Object proxy, Method method, Object[] args)
+        throws Throwable {
+
+        return null;
+    }
+}
diff --git a/tests/044-proxy/src/Clash3.java b/tests/044-proxy/src/Clash3.java
new file mode 100644
index 0000000..6d6f2f2
--- /dev/null
+++ b/tests/044-proxy/src/Clash3.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/*
+ * Try to instantiate a proxy class with interfaces that have conflicting
+ * duplicate methods (type tree with interface).
+ */
+public class Clash3 {
+    public static void main(String[] args) {
+        InvocationHandler handler = new Clash3InvocationHandler();
+
+        try {
+            Proxy.newProxyInstance(Clash.class.getClassLoader(),
+                new Class[] {
+                    Interface3a.class,
+                    Interface3base.class,
+                    Interface3aa.class,
+                    Interface3b.class },
+                handler);
+            System.err.println("Clash3 did not throw expected exception");
+        } catch (IllegalArgumentException iae) {
+            System.out.println("Clash3 threw expected exception");
+        }
+    }
+}
+
+class R3base implements I3 { int mBlah; public void x() {} }
+class R3a extends R3base { int mBlah_a;  }
+class R3aa extends R3a { int mBlah_aa;  }
+class R3b implements I3 { int mBlah_b; public void x() {} }
+
+interface I3 {
+    void x();
+}
+
+interface Interface3base {
+    public R3base thisIsTrouble();
+}
+
+interface Interface3a {
+    public R3a thisIsTrouble();
+}
+interface Interface3aa {
+    public R3aa thisIsTrouble();
+}
+interface Interface3b {
+    public R3b thisIsTrouble();
+}
+
+class Clash3InvocationHandler implements InvocationHandler {
+    /* don't really need to do anything -- should never get this far */
+    public Object invoke(Object proxy, Method method, Object[] args)
+        throws Throwable {
+
+        return null;
+    }
+}
diff --git a/tests/044-proxy/src/Clash4.java b/tests/044-proxy/src/Clash4.java
new file mode 100644
index 0000000..1bfb37f
--- /dev/null
+++ b/tests/044-proxy/src/Clash4.java
@@ -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.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/*
+ * Try to instantiate a proxy class with interfaces that have conflicting
+ * duplicate methods (tree of types).
+ */
+public class Clash4 {
+    public static void main(String[] args) {
+        InvocationHandler handler = new Clash4InvocationHandler();
+
+        try {
+            Proxy.newProxyInstance(Clash.class.getClassLoader(),
+                new Class[] {
+                    Interface4a.class,
+                    Interface4aa.class,
+                    Interface4base.class,
+                    Interface4b.class,
+                    Interface4bb.class },
+                handler);
+            System.err.println("Clash4 did not throw expected exception");
+        } catch (IllegalArgumentException iae) {
+            System.out.println("Clash4 threw expected exception");
+            //System.out.println(iae);
+        }
+    }
+}
+
+class R4base { int mBlah;  }
+class R4a extends R4base { int mBlah_a;  }
+class R4aa extends R4a { int mBlah_aa;  }
+class R4b extends R4base { int mBlah_b;  }
+class R4bb extends R4b { int mBlah_bb;  }
+
+interface Interface4base {
+    public R4base thisIsTrouble();
+}
+
+interface Interface4a {
+    public R4a thisIsTrouble();
+}
+interface Interface4aa {
+    public R4aa thisIsTrouble();
+}
+interface Interface4b {
+    public R4b thisIsTrouble();
+}
+interface Interface4bb {
+    public R4bb thisIsTrouble();
+}
+
+class Clash4InvocationHandler implements InvocationHandler {
+    /* don't really need to do anything -- should never get this far */
+    public Object invoke(Object proxy, Method method, Object[] args)
+        throws Throwable {
+
+        return null;
+    }
+}
diff --git a/tests/044-proxy/src/Main.java b/tests/044-proxy/src/Main.java
new file mode 100644
index 0000000..01926af
--- /dev/null
+++ b/tests/044-proxy/src/Main.java
@@ -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.
+ */
+
+/**
+ * Test java.lang.reflect.Proxy
+ */
+public class Main {
+    public static void main(String[] args) {
+        BasicTest.main(null);
+        Clash.main(null);
+        Clash2.main(null);
+        Clash3.main(null);
+        Clash4.main(null);
+        WrappedThrow.main(null);
+    }
+}
diff --git a/tests/044-proxy/src/WrappedThrow.java b/tests/044-proxy/src/WrappedThrow.java
new file mode 100644
index 0000000..27ae84e
--- /dev/null
+++ b/tests/044-proxy/src/WrappedThrow.java
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.UndeclaredThrowableException;
+
+/*
+ * Create a Proxy class that blah.
+ */
+public class WrappedThrow {
+    public static void main(String[] args) {
+        WTMix mix = new WTMix();
+        InvocationHandler handler = new WTInvocationHandler(mix);
+        Object proxy;
+
+        try {
+            proxy = Proxy.newProxyInstance(WrappedThrow.class.getClassLoader(),
+                new Class[] { InterfaceW1.class, InterfaceW2.class },
+                handler);
+        } catch (IllegalArgumentException iae) {
+            System.out.println("WT init failed");
+            return;
+        }
+
+        InterfaceW1 if1 = (InterfaceW1) proxy;
+        InterfaceW2 if2 = (InterfaceW2) proxy;
+        try {
+            if1.throwFunky();
+            System.err.println("No exception thrown");
+        } catch (UndeclaredThrowableException ute) {
+            System.out.println("Got expected UTE");
+        } catch (Throwable t) {
+            System.err.println("Got unexpected exception: " + t);
+        }
+
+        try {
+            if1.throwFunky2();
+            System.err.println("No exception thrown");
+        } catch (IOException ioe) {
+            System.out.println("Got expected IOE");
+        } catch (Throwable t) {
+            System.err.println("Got unexpected exception: " + t);
+        }
+
+        try {
+            if2.throwFunky2();
+            System.err.println("No exception thrown");
+        } catch (IOException ioe) {
+            System.out.println("Got expected IOE");
+        } catch (Throwable t) {
+            System.err.println("Got unexpected exception: " + t);
+        }
+
+        /*
+         * Throw exceptions, walking down the hierarchy.
+         */
+        try {
+            if1.throwException();
+            System.err.println("No exception thrown");
+        } catch (UndeclaredThrowableException ute) {
+            System.out.println("Got expected UTE");
+        } catch (Throwable t) {
+            System.err.println("Got unexpected exception: " + t);
+        }
+
+        try {
+            if1.throwBase();
+            System.err.println("No exception thrown");
+        } catch (UndeclaredThrowableException ute) {
+            System.out.println("Got expected UTE");
+        } catch (Throwable t) {
+            System.err.println("Got unexpected exception: " + t);
+        }
+
+        try {
+            if2.throwSub();
+            System.err.println("No exception thrown");
+        } catch (SubException se) {
+            System.out.println("Got expected exception");
+        } catch (Throwable t) {
+            System.err.println("Got unexpected exception: " + t);
+        }
+
+        try {
+            if2.throwSubSub();
+            System.err.println("No exception thrown");
+        } catch (SubException se) {
+            System.out.println("Got expected exception");
+        } catch (Throwable t) {
+            System.err.println("Got unexpected exception: " + t);
+        }
+
+        /*
+         * Make sure that, if the class explicitly allows the base
+         * class of an exception, that we still allow it.
+         */
+        try {
+            if1.bothThrowBase();
+            System.err.println("No exception thrown");
+        } catch (BaseException se) {
+            System.out.println("Got expected exception");
+        } catch (Throwable t) {
+            System.err.println("Got unexpected exception: " + t);
+        }
+    }
+}
+
+class BaseException extends Exception {}
+class SubException extends BaseException {}
+class SubSubException extends SubException {}
+
+interface InterfaceW1 {
+    public void throwFunky();
+
+    public void throwFunky2() throws BaseException,
+           NoSuchMethodException, IOException;
+
+    public void throwException() throws BaseException;
+    public void throwBase() throws BaseException;
+    public void throwSub() throws BaseException;
+    public void throwSubSub() throws BaseException;
+
+    public void bothThrowBase() throws BaseException, SubException, SubSubException;
+}
+
+interface InterfaceW2 {
+    public void throwFunky2() throws InterruptedException,
+           NoSuchMethodException, IOException;
+
+    public void throwException() throws SubException;
+    public void throwBase() throws SubException;
+    public void throwSub() throws SubException;
+    public void throwSubSub() throws SubException;
+
+    public void bothThrowBase() throws SubException, BaseException, SubSubException;
+}
+
+/**
+ * Implement all of the proxied interfaces.
+ */
+class WTMix implements InterfaceW1, InterfaceW2 {
+    public int dastardlyDeed() throws SubException {
+        System.out.println("Throwing SubException");
+        throw new SubException();
+    }
+
+    /* these don't actually get called; they just cause exceptions */
+    public void throwFunky() {}
+    public void throwFunky2() {}
+    public void throwException() throws SubException {}
+    public void throwBase() throws SubException {}
+    public void throwSub() throws SubException {}
+    public void throwSubSub() throws SubException {}
+
+    public void bothThrowBase() throws BaseException, SubException {}
+}
+
+/**
+ * Invocation handler for our proxy class.
+ */
+class WTInvocationHandler implements InvocationHandler {
+    private Object mObj;
+
+    public WTInvocationHandler(Object obj) {
+        mObj = obj;
+    }
+
+    /*
+     * This is called when anything gets invoked in the proxy object.
+     */
+    public Object invoke(Object proxy, Method method, Object[] args)
+        throws Throwable {
+
+        Object result = null;
+
+        // Trap Object calls.  This is important here to avoid a recursive
+        // invocation of toString() in the print statements below.
+        if (method.getDeclaringClass() == java.lang.Object.class) {
+            //System.out.println("!!! object " + method.getName());
+            if (method.getName().equals("toString"))
+                return super.toString();
+            else if (method.getName().equals("hashCode"))
+                return Integer.valueOf(super.hashCode());
+            else if (method.getName().equals("equals"))
+                return Boolean.valueOf(super.equals(args[0]));
+            else
+                throw new RuntimeException("huh?");
+        }
+
+        System.out.println("Invoke " + method);
+        if (args == null || args.length == 0) {
+            System.out.println(" (no args)");
+        } else {
+            for (int i = 0; i < args.length; i++)
+                System.out.println(" " + i + ": " + args[i]);
+        }
+
+        try {
+            if (method.getName().equals("throwFunky"))
+                throw new InterruptedException("fake");
+            if (method.getName().equals("throwFunky2"))
+                throw new IOException("fake2");
+            if (method.getName().equals("throwException"))
+                throw new Exception();
+            if (method.getName().equals("throwBase"))
+                throw new BaseException();
+            if (method.getName().equals("throwSub"))
+                throw new SubException();
+            if (method.getName().equals("throwSubSub"))
+                throw new SubSubException();
+            if (method.getName().equals("bothThrowBase"))
+                throw new BaseException();
+
+            if (true)
+                result = method.invoke(mObj, args);
+            else
+                result = -1;
+            System.out.println("Success: method " + method.getName()
+                + " res=" + result);
+        } catch (InvocationTargetException ite) {
+            throw ite.getTargetException();
+        } catch (IllegalAccessException iae) {
+            throw new RuntimeException(iae);
+        }
+        return result;
+    }
+}
diff --git a/tests/045-reflect-array/expected.txt b/tests/045-reflect-array/expected.txt
new file mode 100644
index 0000000..5c609b5
--- /dev/null
+++ b/tests/045-reflect-array/expected.txt
@@ -0,0 +1,6 @@
+ReflectArrayTest.testSingleInt passed
+ReflectArrayTest.testSingle passed
+ReflectArrayTest.testMultiInt passed
+zero one two ++
+ReflectArrayTest.testMulti passed
+ReflectArrayTest passed
diff --git a/tests/045-reflect-array/info.txt b/tests/045-reflect-array/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/045-reflect-array/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/045-reflect-array/src/Main.java b/tests/045-reflect-array/src/Main.java
new file mode 100644
index 0000000..c70e291
--- /dev/null
+++ b/tests/045-reflect-array/src/Main.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ */
+
+import java.lang.reflect.Array;
+
+/**
+ * Test java.lang.reflect.Array.
+ */
+public class Main {
+    public static void main(String[] args) {
+        testSingleInt();
+        testSingle();
+        testMultiInt();
+        testMulti();
+
+        System.out.println("ReflectArrayTest passed");
+    }
+
+    static void testSingleInt() {
+        Object intArray;
+
+        intArray = Array.newInstance(Integer.TYPE, 2);
+
+        int[] array = (int[]) intArray;
+        array[0] = 5;
+        Array.setInt(intArray, 1, 6);
+
+        if (Array.getInt(intArray, 0) != 5)
+            throw new RuntimeException();
+        if (array[1] != 6)
+            throw new RuntimeException();
+        try {
+            array[2] = 27;
+            throw new RuntimeException("store should have failed");
+        }
+        catch (ArrayIndexOutOfBoundsException abe) {
+        }
+        if (array.length != Array.getLength(intArray) ||
+            array.length != 2)
+        {
+            throw new RuntimeException("bad len");
+        }
+
+        int[][] wrongArray;
+        try {
+            wrongArray = (int[][]) intArray;
+            throw new RuntimeException("cast should have failed");
+        }
+        catch (ClassCastException cce) {
+        }
+
+        intArray = Array.newInstance(Integer.TYPE, 0);
+        if (Array.getLength(intArray) != 0)
+            throw new RuntimeException();
+        System.out.println("ReflectArrayTest.testSingleInt passed");
+    }
+
+    static void testSingle() {
+        Object strArray;
+
+        strArray = Array.newInstance(String.class, 2);
+
+        String[] array = (String[]) strArray;
+        array[0] = "entry zero";
+        Array.set(strArray, 1, "entry one");
+
+        //System.out.println("array: " + array);
+
+        if (!"entry zero".equals(Array.get(strArray, 0)))
+            throw new RuntimeException();
+        if (!"entry one".equals(array[1]))
+            throw new RuntimeException();
+
+        if (array.length != Array.getLength(strArray) ||
+            array.length != 2)
+        {
+            throw new RuntimeException("bad len");
+        }
+        System.out.println("ReflectArrayTest.testSingle passed");
+    }
+
+    static void testMultiInt() {
+        Object intIntIntArray;
+        int[] dimensions = { 3, 2, 1 };
+
+        intIntIntArray = Array.newInstance(Integer.TYPE, dimensions);
+        int[][][] array3 = (int[][][]) intIntIntArray;
+
+        array3[0][0][0] = 123;      // trouble
+        array3[2][1][0] = 456;
+
+        try {
+            array3[2][1][1] = 768;
+            throw new RuntimeException("store should have failed");
+        }
+        catch (ArrayIndexOutOfBoundsException abe) {
+        }
+        System.out.println("ReflectArrayTest.testMultiInt passed");
+    }
+
+    static void testMulti() {
+        Object strStrStrArray;
+        int[] dimensions = { 1, 2, 3 };
+
+        strStrStrArray = Array.newInstance(String.class, dimensions);
+        String[][][] array3 = (String[][][]) strStrStrArray;
+
+        array3[0][0][0] = "zero zero zero";
+        array3[0][1][2] = "zero one two";
+
+        try {
+            array3[1][0][0] = "bad store";
+            throw new RuntimeException("store should have failed");
+        }
+        catch (ArrayIndexOutOfBoundsException abe) {
+        }
+
+        try {
+            String[][] array2 = (String[][]) strStrStrArray;
+            throw new RuntimeException("expecting bad cast");
+        }
+        catch (ClassCastException cce) {
+        }
+
+        String[] strar = new String[4];
+        strar[2] = "zero one two ++";
+        array3[0][1] = strar;
+        System.out.println(array3[0][1][2]);
+        //System.out.println("array3: " + array3);
+
+
+        int[] dimensions2 = { 1, 2 };
+        strStrStrArray = Array.newInstance(String[].class, dimensions2);
+        array3 = (String[][][]) strStrStrArray;
+
+        array3[0][1] = new String[3];
+        array3[0][1][2] = "zero one two";
+        try {
+            array3[1][0][0] = "bad store";
+            throw new RuntimeException("store should have failed");
+        }
+        catch (ArrayIndexOutOfBoundsException abe) {
+        }
+        System.out.println("ReflectArrayTest.testMulti passed");
+    }
+}
diff --git a/tests/046-reflect/expected.txt b/tests/046-reflect/expected.txt
new file mode 100644
index 0000000..3be8d1c
--- /dev/null
+++ b/tests/046-reflect/expected.txt
@@ -0,0 +1,97 @@
+Method name is myMethod
+ Declaring class is Target
+ Arg 0: int
+ Exc 0: java.lang.NullPointerException
+ Exc 1: java.io.IOException
+ Return type is int
+ Access flags are 0x1
+Method name is myMethod
+ Declaring class is SuperTarget
+ Arg 0: float
+ Return type is int
+ Access flags are 0x1
+Method name is myNoargMethod
+ Declaring class is Target
+ Return type is void
+ Access flags are 0x9
+Method name is myMethod
+ Declaring class is Target
+ Arg 0: [Ljava.lang.String;
+ Arg 1: float
+ Arg 2: char
+ Return type is int
+ Access flags are 0x1
+SuperTarget constructor ()V
+Target constructor ()V
+Before, float is 3.1415925
+myMethod: hi there 3.1415925 Q !
+Result of invoke: 7
+Calling no-arg void-return method
+myNoargMethod ()V
+throwingMethod
+Invoke got expected exception:
+java.lang.reflect.InvocationTargetException
+java.lang.NullPointerException: gratuitous throw!
+
+Field name is string1
+ Declaring class is Target
+ Field type is java.lang.String
+ Access flags are 0x1
+  string1 value is 'hey'
+  ::: hey:yo:there
+  string1 value is now 'a new string'
+  ::: a new string:yo:there
+  got expected illegal obj store exc
+  got the other expected access exc
+  got expected arg exc
+pubLong initial value is 1122334455667788
+pubLong new value is 9988776655443322
+Field name is superInt
+ Declaring class is SuperTarget
+ Field type is int
+ Access flags are 0x1
+  superInt value is 1010101
+  superInt boxed is 1010101
+  superInt value is now 20202
+  superInt value (from short) is now 30303
+  superInt value is now 40404
+  got expected long->int failure
+  got expected long->int failure
+  got expected string->int failure
+  got expected int->short failure
+Field name is superClassInt
+ Declaring class is SuperTarget
+ Field type is int
+ Access flags are 0x9
+  superClassInt value is 1010102
+Field name is staticDouble
+ Declaring class is Target
+ Field type is double
+ Access flags are 0x9
+  staticDoubleVal value is 3.3
+  got expected double->long failure
+as expected: aPrivateInt not found
+Field name is constantString
+ Declaring class is Target
+ Field type is java.lang.String
+ Access flags are 0x19
+  Constant test value is a constant string
+Field name is cantTouchThis
+ Declaring class is Target
+ Field type is int
+ Access flags are 0x11
+  cantTouchThis is 77
+  got expected set-final failure
+
+  cantTouchThis is now 77
+cons modifiers=1
+SuperTarget constructor ()V
+Target constructor (IF)V : ii=7 ff=3.3333
+myMethod (I)I
+ arg=17 anInt=7
+ReflectTest done!
+checkType invoking null
+checkType got expected exception
+got methods
+NoisyInitUser is initializing
+NoisyInit is initializing
diff --git a/tests/046-reflect/info.txt b/tests/046-reflect/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/046-reflect/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/046-reflect/src/Main.java b/tests/046-reflect/src/Main.java
new file mode 100644
index 0000000..399a417
--- /dev/null
+++ b/tests/046-reflect/src/Main.java
@@ -0,0 +1,435 @@
+// Copyright 2006 The Android Open Source Project
+
+import java.lang.reflect.*;
+import java.io.IOException;
+import java.util.Collections;
+
+/**
+ * Reflection test.
+ */
+public class Main {
+    void printMethodInfo(Method meth) {
+        Class[] params, exceptions;
+        int i;
+
+        System.out.println("Method name is " + meth.getName());
+        System.out.println(" Declaring class is "
+            + meth.getDeclaringClass().getName());
+        params = meth.getParameterTypes();
+        for (i = 0; i < params.length; i++)
+            System.out.println(" Arg " + i + ": " + params[i].getName());
+        exceptions = meth.getExceptionTypes();
+        for (i = 0; i < exceptions.length; i++)
+            System.out.println(" Exc " + i + ": " + exceptions[i].getName());
+        System.out.println(" Return type is " + meth.getReturnType().getName());
+        System.out.println(" Access flags are 0x"
+            + Integer.toHexString(meth.getModifiers()));
+        //System.out.println(" GenericStr is " + meth.toGenericString());
+    }
+
+    void printFieldInfo(Field field) {
+        System.out.println("Field name is " + field.getName());
+        System.out.println(" Declaring class is "
+            + field.getDeclaringClass().getName());
+        System.out.println(" Field type is " + field.getType().getName());
+        System.out.println(" Access flags are 0x"
+            + Integer.toHexString(field.getModifiers()));
+    }
+
+    private void showStrings(Target instance)
+        throws NoSuchFieldException, IllegalAccessException {
+
+        Class target = Target.class;
+        String one, two, three, four;
+        Field field = null;
+
+        field = target.getField("string1");
+        one = (String) field.get(instance);
+
+        field = target.getField("string2");
+        two = (String) field.get(instance);
+
+        field = target.getField("string3");
+        three = (String) field.get(instance);
+
+        System.out.println("  ::: " + one + ":" + two + ":" + three);
+    }
+
+    public void run() {
+        Class target = Target.class;
+        Method meth = null;
+        Field field = null;
+        boolean excep;
+
+        try {
+            meth = target.getMethod("myMethod", new Class[] { int.class });
+
+            if (meth.getDeclaringClass() != target)
+                throw new RuntimeException();
+            printMethodInfo(meth);
+
+            meth = target.getMethod("myMethod", new Class[] { float.class });
+            printMethodInfo(meth);
+
+            meth = target.getMethod("myNoargMethod", (Class[]) null);
+            printMethodInfo(meth);
+
+            meth = target.getMethod("myMethod",
+                new Class[] { String[].class, float.class, char.class });
+            printMethodInfo(meth);
+
+            Target instance = new Target();
+            Object[] argList = new Object[] {
+                new String[] { "hi there" },
+                new Float(3.1415926f),
+                new Character('Q')
+            };
+            System.out.println("Before, float is "
+                + ((Float)argList[1]).floatValue());
+
+            Integer boxval;
+            boxval = (Integer) meth.invoke(instance, argList);
+            System.out.println("Result of invoke: " + boxval.intValue());
+
+            System.out.println("Calling no-arg void-return method");
+            meth = target.getMethod("myNoargMethod", (Class[]) null);
+            meth.invoke(instance, (Object[]) null);
+
+            /* try invoking a method that throws an exception */
+            meth = target.getMethod("throwingMethod", (Class[]) null);
+            try {
+                meth.invoke(instance, (Object[]) null);
+                System.out.println("GLITCH: didn't throw");
+            } catch (InvocationTargetException ite) {
+                System.out.println("Invoke got expected exception:");
+                System.out.println(ite.getClass().getName());
+                System.out.println(ite.getCause());
+            }
+            catch (Exception ex) {
+                System.out.println("GLITCH: invoke got wrong exception:");
+                ex.printStackTrace();
+            }
+            System.out.println("");
+
+
+            field = target.getField("string1");
+            if (field.getDeclaringClass() != target)
+                throw new RuntimeException();
+            printFieldInfo(field);
+            String strVal = (String) field.get(instance);
+            System.out.println("  string1 value is '" + strVal + "'");
+
+            showStrings(instance);
+
+            field.set(instance, new String("a new string"));
+            strVal = (String) field.get(instance);
+            System.out.println("  string1 value is now '" + strVal + "'");
+
+            showStrings(instance);
+
+            try {
+                field.set(instance, new Object());
+                System.out.println("WARNING: able to store Object into String");
+            }
+            catch (IllegalArgumentException iae) {
+                System.out.println("  got expected illegal obj store exc");
+            }
+
+
+            try {
+                String four;
+                field = target.getField("string4");
+                four = (String) field.get(instance);
+                System.out.println("WARNING: able to access string4: "
+                    + four);
+            }
+            catch (IllegalAccessException iae) {
+                System.out.println("  got expected access exc");
+            }
+            catch (NoSuchFieldException nsfe) {
+                System.out.println("  got the other expected access exc");
+            }
+            try {
+                String three;
+                field = target.getField("string3");
+                three = (String) field.get(this);
+                System.out.println("WARNING: able to get string3 in wrong obj: "
+                    + three);
+            }
+            catch (IllegalArgumentException iae) {
+                System.out.println("  got expected arg exc");
+            }
+
+            /*
+             * Try setting a field to null.
+             */
+            String four;
+            field = target.getDeclaredField("string3");
+            field.set(instance, null);
+
+            /*
+             * Do some stuff with long.
+             */
+            long longVal;
+            field = target.getField("pubLong");
+            longVal = field.getLong(instance);
+            System.out.println("pubLong initial value is " +
+                Long.toHexString(longVal));
+            field.setLong(instance, 0x9988776655443322L);
+            longVal = field.getLong(instance);
+            System.out.println("pubLong new value is " +
+                Long.toHexString(longVal));
+
+
+            field = target.getField("superInt");
+            if (field.getDeclaringClass() == target)
+                throw new RuntimeException();
+            printFieldInfo(field);
+            int intVal = field.getInt(instance);
+            System.out.println("  superInt value is " + intVal);
+            Integer boxedIntVal = (Integer) field.get(instance);
+            System.out.println("  superInt boxed is " + boxedIntVal);
+
+            field.set(instance, new Integer(20202));
+            intVal = field.getInt(instance);
+            System.out.println("  superInt value is now " + intVal);
+            field.setShort(instance, (short)30303);
+            intVal = field.getInt(instance);
+            System.out.println("  superInt value (from short) is now " +intVal);
+            field.setInt(instance, 40404);
+            intVal = field.getInt(instance);
+            System.out.println("  superInt value is now " + intVal);
+            try {
+                field.set(instance, new Long(123));
+                System.out.println("FAIL: expected exception not thrown");
+            }
+            catch (IllegalArgumentException iae) {
+                System.out.println("  got expected long->int failure");
+            }
+            try {
+                field.setLong(instance, 123);
+                System.out.println("FAIL: expected exception not thrown");
+            }
+            catch (IllegalArgumentException iae) {
+                System.out.println("  got expected long->int failure");
+            }
+            try {
+                field.set(instance, new String("abc"));
+                System.out.println("FAIL: expected exception not thrown");
+            }
+            catch (IllegalArgumentException iae) {
+                System.out.println("  got expected string->int failure");
+            }
+
+            try {
+                field.getShort(instance);
+                System.out.println("FAIL: expected exception not thrown");
+            }
+            catch (IllegalArgumentException iae) {
+                System.out.println("  got expected int->short failure");
+            }
+
+            field = target.getField("superClassInt");
+            printFieldInfo(field);
+            int superClassIntVal = field.getInt(instance);
+            System.out.println("  superClassInt value is " + superClassIntVal);
+
+            field = target.getField("staticDouble");
+            printFieldInfo(field);
+            double staticDoubleVal = field.getDouble(null);
+            System.out.println("  staticDoubleVal value is " + staticDoubleVal);
+
+            try {
+                field.getLong(instance);
+                System.out.println("FAIL: expected exception not thrown");
+            }
+            catch (IllegalArgumentException iae) {
+                System.out.println("  got expected double->long failure");
+            }
+
+            excep = false;
+            try {
+                field = target.getField("aPrivateInt");
+                printFieldInfo(field);
+            }
+            catch (NoSuchFieldException nsfe) {
+                System.out.println("as expected: aPrivateInt not found");
+                excep = true;
+            }
+            if (!excep)
+                System.out.println("BUG: got aPrivateInt");
+
+
+            field = target.getField("constantString");
+            printFieldInfo(field);
+            String val = (String) field.get(instance);
+            System.out.println("  Constant test value is " + val);
+
+
+            field = target.getField("cantTouchThis");
+            printFieldInfo(field);
+            intVal = field.getInt(instance);
+            System.out.println("  cantTouchThis is " + intVal);
+            try {
+                field.setInt(instance, 99);
+                System.out.println("ERROR: set-final succeeded\n");
+            } catch (IllegalAccessException iae) {
+                System.out.println("  got expected set-final failure\n");
+            }
+            intVal = field.getInt(instance);
+            System.out.println("  cantTouchThis is now " + intVal);
+
+            Constructor<Target> cons;
+            Target targ;
+            Object[] args;
+
+            cons = target.getConstructor(new Class[] { int.class,float.class });
+            args = new Object[] { new Integer(7), new Float(3.3333) };
+            System.out.println("cons modifiers=" + cons.getModifiers());
+            targ = cons.newInstance(args);
+            targ.myMethod(17);
+
+        }
+        catch (Exception ex) {
+            System.out.println("----- unexpected exception -----");
+            ex.printStackTrace();
+        }
+
+        System.out.println("ReflectTest done!");
+    }
+
+    public static void checkType() {
+        Method m;
+
+        try {
+            m = Collections.class.getDeclaredMethod("checkType",
+                            Object.class, Class.class);
+        } catch (NoSuchMethodException nsme) {
+            nsme.printStackTrace();
+            return;
+        }
+
+        m.setAccessible(true);
+        try {
+            m.invoke(null, new Object(), Object.class);
+        } catch (IllegalAccessException iae) {
+            iae.printStackTrace();
+            return;
+        } catch (InvocationTargetException ite) {
+            ite.printStackTrace();
+            return;
+        }
+
+        try {
+            System.out.println("checkType invoking null");
+            m.invoke(null, new Object(), int.class);
+            System.out.println("ERROR: should throw InvocationTargetException");
+        } catch (InvocationTargetException ite) {
+            System.out.println("checkType got expected exception");
+        } catch (IllegalAccessException iae) {
+            iae.printStackTrace();
+            return;
+        }
+    }
+
+    public static void checkInit() {
+        Class niuClass = NoisyInitUser.class;
+        Method[] methods;
+
+        methods = niuClass.getDeclaredMethods();
+        System.out.println("got methods");
+        /* neither NoisyInit nor NoisyInitUser should be initialized yet */
+        NoisyInitUser niu = new NoisyInitUser();
+        NoisyInit ni = new NoisyInit();
+    }
+
+    public static void main(String[] args) {
+        Main test = new Main();
+        test.run();
+
+        checkType();
+        checkInit();
+    }
+}
+
+
+class SuperTarget {
+    public SuperTarget() {
+        System.out.println("SuperTarget constructor ()V");
+        superInt = 1010101;
+        superClassInt = 1010102;
+    }
+
+    public int myMethod(float floatArg) {
+        System.out.println("myMethod (F)I " + floatArg);
+        return 6;
+    }
+
+    public int superInt;
+    public static int superClassInt;
+}
+
+class Target extends SuperTarget {
+    public Target() {
+        System.out.println("Target constructor ()V");
+    }
+
+    public Target(int ii, float ff) {
+        System.out.println("Target constructor (IF)V : ii="
+            + ii + " ff=" + ff);
+        anInt = ii;
+    }
+
+    public int myMethod(int intarg) throws NullPointerException, IOException {
+        System.out.println("myMethod (I)I");
+        System.out.println(" arg=" + intarg + " anInt=" + anInt);
+        return 5;
+    }
+
+    public int myMethod(String[] strarg, float f, char c) {
+        System.out.println("myMethod: " + strarg[0] + " " + f + " " + c + " !");
+        return 7;
+    }
+
+    public static void myNoargMethod() {
+        System.out.println("myNoargMethod ()V");
+    }
+
+    public void throwingMethod() {
+        System.out.println("throwingMethod");
+        throw new NullPointerException("gratuitous throw!");
+    }
+
+    public void misc() {
+        System.out.println("misc");
+    }
+
+    public int anInt;
+    public String string1 = "hey";
+    public String string2 = "yo";
+    public String string3 = "there";
+    private String string4 = "naughty";
+    public static final String constantString = "a constant string";
+    private int aPrivateInt;
+
+    public final int cantTouchThis = 77;
+
+    public long pubLong = 0x1122334455667788L;
+
+    public static double staticDouble = 3.3;
+}
+
+class NoisyInit {
+    static {
+        System.out.println("NoisyInit is initializing");
+        //Throwable th = new Throwable();
+        //th.printStackTrace();
+    }
+}
+
+class NoisyInitUser {
+    static {
+        System.out.println("NoisyInitUser is initializing");
+    }
+    public void createNoisyInit(NoisyInit ni) {}
+}
diff --git a/tests/047-returns/expected.txt b/tests/047-returns/expected.txt
new file mode 100644
index 0000000..160f69c
--- /dev/null
+++ b/tests/047-returns/expected.txt
@@ -0,0 +1,10 @@
+pick 1
+one running
+one
+1
+pick 2
+two running
+two
+2
+pick 3
+three running
diff --git a/tests/047-returns/info.txt b/tests/047-returns/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/047-returns/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/047-returns/src/Main.java b/tests/047-returns/src/Main.java
new file mode 100644
index 0000000..d53c4a7
--- /dev/null
+++ b/tests/047-returns/src/Main.java
@@ -0,0 +1,65 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Return stuff.
+ */
+public class Main {
+    public static void main(String[] args) {
+
+        System.out.println("pick 1");
+        pickOne(1).run();
+        System.out.println(((CommonInterface)pickOne(1)).doStuff());
+
+        System.out.println("pick 2");
+        pickOne(2).run();
+        System.out.println(((CommonInterface)pickOne(2)).doStuff());
+
+        System.out.println("pick 3");
+        pickOne(3).run();
+    }
+
+    public static Runnable pickOne(int which) {
+        Runnable runme;
+
+        if (which == 1)
+            runme = new ClassOne();
+        else if (which == 2)
+            runme = new ClassTwo();
+        else if (which == 3)
+            runme = new ClassThree();
+        else
+            runme = null;
+
+        return runme;
+    }
+}
+
+class ClassOne implements CommonInterface, Runnable {
+    public void run() {
+        System.out.println("one running");
+    }
+    public int doStuff() {
+        System.out.println("one");
+        return 1;
+    }
+}
+
+class ClassTwo implements CommonInterface, Runnable {
+    public void run() {
+        System.out.println("two running");
+    }
+    public int doStuff() {
+        System.out.println("two");
+        return 2;
+    }
+}
+
+class ClassThree implements Runnable {
+    public void run() {
+        System.out.println("three running");
+    }
+}
+
+interface CommonInterface {
+    int doStuff();
+}
diff --git a/tests/048-server-socket/expected.txt b/tests/048-server-socket/expected.txt
new file mode 100644
index 0000000..23c3e84
--- /dev/null
+++ b/tests/048-server-socket/expected.txt
@@ -0,0 +1,4 @@
+opened!
+closed!
+reopened!
+done
diff --git a/tests/048-server-socket/info.txt b/tests/048-server-socket/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/048-server-socket/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/048-server-socket/src/Main.java b/tests/048-server-socket/src/Main.java
new file mode 100644
index 0000000..55dbf9a
--- /dev/null
+++ b/tests/048-server-socket/src/Main.java
@@ -0,0 +1,52 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.net.ServerSocket;
+import java.io.IOException;
+
+
+/**
+ * Quick server socket test.
+ */
+public class Main {
+    private static void snooze(int sec) {
+        try {
+            Thread.sleep(sec * 1000);
+        } catch (InterruptedException ie) {
+            ie.printStackTrace();
+        }
+    }
+
+    public static void main(String[] args) {
+        ServerSocket socket;
+
+        try {
+            socket = new ServerSocket(7890);
+        } catch (IOException ioe) {
+            System.out.println("couldn't open socket " + ioe.getMessage());
+            return;
+        }
+
+        System.out.println("opened!");
+        snooze(1);
+
+        try {
+            socket.close();
+        } catch (IOException ioe) {
+            System.out.println("couldn't close socket " + ioe.getMessage());
+            return;
+        }
+
+        System.out.println("closed!");
+        snooze(1);
+
+        try {
+            socket = new ServerSocket(7890);
+        } catch (IOException ioe) {
+            System.out.println("couldn't reopen socket " + ioe.getMessage());
+            return;
+        }
+
+        System.out.println("reopened!");
+        System.out.println("done");
+    }
+}
diff --git a/tests/049-show-object/expected.txt b/tests/049-show-object/expected.txt
new file mode 100644
index 0000000..4613c39
--- /dev/null
+++ b/tests/049-show-object/expected.txt
@@ -0,0 +1,11 @@
+d is 3.1415
+class: class [Ljava.lang.Object;
+0: null
+1: null
+2: null
+3: null
+4: null
+class: class [Ljava.lang.String;
+0: hey
+1: you
+2: there
diff --git a/tests/049-show-object/info.txt b/tests/049-show-object/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/049-show-object/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/049-show-object/src/Main.java b/tests/049-show-object/src/Main.java
new file mode 100644
index 0000000..d31eeda
--- /dev/null
+++ b/tests/049-show-object/src/Main.java
@@ -0,0 +1,34 @@
+// Copyright 2008 The Android Open Source Project
+
+/*
+ * Some basic operations for testing the debugger.
+ */
+public class Main {
+    long mLong = 0x1122334455667788L;
+
+    public Main() {
+        double d = 3.1415;
+        System.out.println("d is " + d);
+    }
+
+    public static void showObject(Object[] foo) {
+        int xyz = 27;
+        System.out.println("class: " + foo.getClass());
+
+        for (int i = 0; i < foo.length; i++) {
+            System.out.println(i + ": "  + foo[i]);
+        }
+    }
+
+    public static void main(String[] args) {
+        int x = 5;
+        Main testObj = new Main();
+
+        Object[] array = new Object[5];
+        showObject(array);
+
+        String[] niftyStrings = new String[] { "hey", "you", "there" };
+        array = niftyStrings;
+        showObject(array);
+    }
+}
diff --git a/tests/050-sync-test/expected.txt b/tests/050-sync-test/expected.txt
new file mode 100644
index 0000000..c2a7031
--- /dev/null
+++ b/tests/050-sync-test/expected.txt
@@ -0,0 +1,34 @@
+Sleep Test
+GOING
+GONE
+
+Count Test
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+Final result: 10
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+Final result: 20
+main: all done
+
+Interrupt Test
+SleepyThread.run starting
+SleepyThread.run starting
+interrupting other (isAlive=true)
+thread#0 interrupted, flag=false
diff --git a/tests/050-sync-test/info.txt b/tests/050-sync-test/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/050-sync-test/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/050-sync-test/src/Main.java b/tests/050-sync-test/src/Main.java
new file mode 100644
index 0000000..c2ea192
--- /dev/null
+++ b/tests/050-sync-test/src/Main.java
@@ -0,0 +1,179 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test synchronization primitives.
+ *
+ * TODO: this should be re-written to be a little more rigorous and/or
+ * useful.  Also, the ThreadDeathHandler stuff should be exposed or
+ * split out.
+ */
+public class Main {
+    public static void main(String[] args) {
+        System.out.println("Sleep Test");
+        sleepTest();
+
+        System.out.println("\nCount Test");
+        countTest();
+
+        System.out.println("\nInterrupt Test");
+        interruptTest();
+    }
+
+    static void sleepTest() {
+        System.out.println("GOING");
+        try {
+            Thread.sleep(1000);
+        }
+        catch (InterruptedException ie) {
+            System.out.println("INTERRUPT!");
+            ie.printStackTrace();
+        }
+        System.out.println("GONE");
+    }
+
+    static void countTest() {
+        CpuThread one, two;
+
+        one = new CpuThread(1);
+        two = new CpuThread(2);
+
+        one.start();
+        two.start();
+
+        try {
+            Thread.sleep(100);
+        }
+        catch (InterruptedException ie) {
+            System.out.println("INTERRUPT!");
+            ie.printStackTrace();
+        }
+
+        //System.out.println("main: off and running");
+
+        try {
+            one.join();
+            two.join();
+        }
+        catch (InterruptedException ie) {
+            System.out.println("INTERRUPT!");
+            ie.printStackTrace();
+        }
+        System.out.println("main: all done");
+    }
+
+    static void interruptTest() {
+        SleepyThread sleepy, pesky;
+
+        sleepy = new SleepyThread(null);
+        pesky = new SleepyThread(sleepy);
+
+        sleepy.setPriority(4);
+        sleepy.start();
+        pesky.start();
+        pesky.setPriority(3);
+    }
+}
+
+class CpuThread extends Thread {
+    static Object mSyncable = new Object();
+    static int mCount = 0;
+    int mNumber;
+
+    CpuThread(int num) {
+        super("CpuThread " + num);
+        mNumber = num;
+    }
+
+    public void run() {
+        //System.out.print("thread running -- ");
+        //System.out.println(Thread.currentThread().getName());
+
+        for (int i = 0; i < 10; i++) {
+            output(mNumber);
+        }
+
+        System.out.print("Final result: ");
+        System.out.println(mCount);
+    }
+
+    void output(int num) {
+        /*
+         * Delete the next line; last "final result" should != 20.
+         */
+        synchronized (mSyncable)
+        {
+            int i, count;
+
+            count = mCount;
+
+            System.out.print("going: ");
+            System.out.println(num);
+
+            /* burn CPU; adjust end value so we exceed scheduler quantum */
+            for (int j = 0; j < 5000; j++)
+                ;
+
+            count++;
+            mCount = count;
+        }
+    }
+}
+
+class SleepyThread extends Thread {
+    private SleepyThread mOther;
+    private Integer[] mWaitOnMe;      // any type of object will do
+
+    private static int count = 0;
+
+    SleepyThread(SleepyThread other) {
+        mOther = other;
+        mWaitOnMe = new Integer[] { 1, 2 };
+
+        setName("thread#" + count);
+        count++;
+    }
+
+    public void run() {
+        System.out.println("SleepyThread.run starting");
+
+        if (false) {
+            ThreadDeathHandler threadHandler =
+                new ThreadDeathHandler("SYNC THREAD");
+            Thread.currentThread().setUncaughtExceptionHandler(threadHandler);
+            throw new NullPointerException("die");
+        }
+
+        if (mOther == null) {
+            boolean intr = false;
+
+            try {
+                synchronized (mWaitOnMe) {
+                    mWaitOnMe.wait(9000);
+                }
+            }
+            catch (InterruptedException ie) {
+                // Expecting this; interrupted should be false.
+                System.out.println(Thread.currentThread().getName() +
+                        " interrupted, flag=" + Thread.interrupted());
+                intr = true;
+            }
+            catch (Exception ex) {
+                ex.printStackTrace();
+            }
+
+            if (!intr)
+                System.out.println("NOT INTERRUPTED");
+        } else {
+            try {
+                Thread.sleep(2000);
+            }
+            catch (InterruptedException ie) {
+                System.out.println("PESKY INTERRUPTED?");
+            }
+
+            System.out.println("interrupting other (isAlive="
+                + mOther.isAlive() + ")");
+            mOther.interrupt();
+        }
+    }
+}
diff --git a/tests/050-sync-test/src/ThreadDeathHandler.java b/tests/050-sync-test/src/ThreadDeathHandler.java
new file mode 100644
index 0000000..5ea61a5
--- /dev/null
+++ b/tests/050-sync-test/src/ThreadDeathHandler.java
@@ -0,0 +1,19 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.lang.Thread.UncaughtExceptionHandler;
+
+/**
+ * Report death-by-uncaught-exception.
+ */
+public class ThreadDeathHandler implements Thread.UncaughtExceptionHandler {
+    private String mMyMessage;
+
+    public ThreadDeathHandler(String msg) {
+        mMyMessage = msg;
+    }
+
+    public void uncaughtException(Thread t, Throwable e) {
+        System.err.println("Uncaught exception " + mMyMessage + "!");
+        e.printStackTrace();
+    }
+}
diff --git a/tests/051-thread/expected.txt b/tests/051-thread/expected.txt
new file mode 100644
index 0000000..fbe32f6
--- /dev/null
+++ b/tests/051-thread/expected.txt
@@ -0,0 +1,518 @@
+running 0
+running 1
+running 2
+running 3
+running 4
+running 5
+running 6
+running 7
+running 8
+running 9
+running 10
+running 11
+running 12
+running 13
+running 14
+running 15
+running 16
+running 17
+running 18
+running 19
+running 20
+running 21
+running 22
+running 23
+running 24
+running 25
+running 26
+running 27
+running 28
+running 29
+running 30
+running 31
+running 32
+running 33
+running 34
+running 35
+running 36
+running 37
+running 38
+running 39
+running 40
+running 41
+running 42
+running 43
+running 44
+running 45
+running 46
+running 47
+running 48
+running 49
+running 50
+running 51
+running 52
+running 53
+running 54
+running 55
+running 56
+running 57
+running 58
+running 59
+running 60
+running 61
+running 62
+running 63
+running 64
+running 65
+running 66
+running 67
+running 68
+running 69
+running 70
+running 71
+running 72
+running 73
+running 74
+running 75
+running 76
+running 77
+running 78
+running 79
+running 80
+running 81
+running 82
+running 83
+running 84
+running 85
+running 86
+running 87
+running 88
+running 89
+running 90
+running 91
+running 92
+running 93
+running 94
+running 95
+running 96
+running 97
+running 98
+running 99
+running 100
+running 101
+running 102
+running 103
+running 104
+running 105
+running 106
+running 107
+running 108
+running 109
+running 110
+running 111
+running 112
+running 113
+running 114
+running 115
+running 116
+running 117
+running 118
+running 119
+running 120
+running 121
+running 122
+running 123
+running 124
+running 125
+running 126
+running 127
+running 128
+running 129
+running 130
+running 131
+running 132
+running 133
+running 134
+running 135
+running 136
+running 137
+running 138
+running 139
+running 140
+running 141
+running 142
+running 143
+running 144
+running 145
+running 146
+running 147
+running 148
+running 149
+running 150
+running 151
+running 152
+running 153
+running 154
+running 155
+running 156
+running 157
+running 158
+running 159
+running 160
+running 161
+running 162
+running 163
+running 164
+running 165
+running 166
+running 167
+running 168
+running 169
+running 170
+running 171
+running 172
+running 173
+running 174
+running 175
+running 176
+running 177
+running 178
+running 179
+running 180
+running 181
+running 182
+running 183
+running 184
+running 185
+running 186
+running 187
+running 188
+running 189
+running 190
+running 191
+running 192
+running 193
+running 194
+running 195
+running 196
+running 197
+running 198
+running 199
+running 200
+running 201
+running 202
+running 203
+running 204
+running 205
+running 206
+running 207
+running 208
+running 209
+running 210
+running 211
+running 212
+running 213
+running 214
+running 215
+running 216
+running 217
+running 218
+running 219
+running 220
+running 221
+running 222
+running 223
+running 224
+running 225
+running 226
+running 227
+running 228
+running 229
+running 230
+running 231
+running 232
+running 233
+running 234
+running 235
+running 236
+running 237
+running 238
+running 239
+running 240
+running 241
+running 242
+running 243
+running 244
+running 245
+running 246
+running 247
+running 248
+running 249
+running 250
+running 251
+running 252
+running 253
+running 254
+running 255
+running 256
+running 257
+running 258
+running 259
+running 260
+running 261
+running 262
+running 263
+running 264
+running 265
+running 266
+running 267
+running 268
+running 269
+running 270
+running 271
+running 272
+running 273
+running 274
+running 275
+running 276
+running 277
+running 278
+running 279
+running 280
+running 281
+running 282
+running 283
+running 284
+running 285
+running 286
+running 287
+running 288
+running 289
+running 290
+running 291
+running 292
+running 293
+running 294
+running 295
+running 296
+running 297
+running 298
+running 299
+running 300
+running 301
+running 302
+running 303
+running 304
+running 305
+running 306
+running 307
+running 308
+running 309
+running 310
+running 311
+running 312
+running 313
+running 314
+running 315
+running 316
+running 317
+running 318
+running 319
+running 320
+running 321
+running 322
+running 323
+running 324
+running 325
+running 326
+running 327
+running 328
+running 329
+running 330
+running 331
+running 332
+running 333
+running 334
+running 335
+running 336
+running 337
+running 338
+running 339
+running 340
+running 341
+running 342
+running 343
+running 344
+running 345
+running 346
+running 347
+running 348
+running 349
+running 350
+running 351
+running 352
+running 353
+running 354
+running 355
+running 356
+running 357
+running 358
+running 359
+running 360
+running 361
+running 362
+running 363
+running 364
+running 365
+running 366
+running 367
+running 368
+running 369
+running 370
+running 371
+running 372
+running 373
+running 374
+running 375
+running 376
+running 377
+running 378
+running 379
+running 380
+running 381
+running 382
+running 383
+running 384
+running 385
+running 386
+running 387
+running 388
+running 389
+running 390
+running 391
+running 392
+running 393
+running 394
+running 395
+running 396
+running 397
+running 398
+running 399
+running 400
+running 401
+running 402
+running 403
+running 404
+running 405
+running 406
+running 407
+running 408
+running 409
+running 410
+running 411
+running 412
+running 413
+running 414
+running 415
+running 416
+running 417
+running 418
+running 419
+running 420
+running 421
+running 422
+running 423
+running 424
+running 425
+running 426
+running 427
+running 428
+running 429
+running 430
+running 431
+running 432
+running 433
+running 434
+running 435
+running 436
+running 437
+running 438
+running 439
+running 440
+running 441
+running 442
+running 443
+running 444
+running 445
+running 446
+running 447
+running 448
+running 449
+running 450
+running 451
+running 452
+running 453
+running 454
+running 455
+running 456
+running 457
+running 458
+running 459
+running 460
+running 461
+running 462
+running 463
+running 464
+running 465
+running 466
+running 467
+running 468
+running 469
+running 470
+running 471
+running 472
+running 473
+running 474
+running 475
+running 476
+running 477
+running 478
+running 479
+running 480
+running 481
+running 482
+running 483
+running 484
+running 485
+running 486
+running 487
+running 488
+running 489
+running 490
+running 491
+running 492
+running 493
+running 494
+running 495
+running 496
+running 497
+running 498
+running 499
+running 500
+running 501
+running 502
+running 503
+running 504
+running 505
+running 506
+running 507
+running 508
+running 509
+running 510
+running 511
+Starting thread 'Thready'
+@ Thread running
+@ Got expected setDaemon exception
+@ Thread bailing
+Thread starter returning
+thread test done
diff --git a/tests/051-thread/info.txt b/tests/051-thread/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/051-thread/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/051-thread/src/Main.java b/tests/051-thread/src/Main.java
new file mode 100644
index 0000000..9acc89e
--- /dev/null
+++ b/tests/051-thread/src/Main.java
@@ -0,0 +1,73 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test some basic thread stuff.
+ */
+public class Main {
+    public static void main(String[] args) {
+        for (int i = 0; i < 512; i++) {
+            MyThread myThread = new MyThread();
+            myThread.start();
+            try {
+                Thread.sleep(1);
+            } catch (InterruptedException ie) {
+                ie.printStackTrace();
+            }
+        }
+
+        go();
+        System.out.println("thread test done");
+    }
+
+    public static void go() {
+        Thread t = new Thread(null, new ThreadTestSub(), "Thready", 7168);
+
+        t.setDaemon(false);
+
+        System.out.print("Starting thread '" + t.getName() + "'\n");
+        t.start();
+
+        try {
+            t.join();
+        } catch (InterruptedException ex) {
+            ex.printStackTrace();
+        }
+
+        System.out.print("Thread starter returning\n");
+    }
+
+    /*
+     * Simple thread capacity test.
+     */
+    static class MyThread extends Thread {
+        private static int mCount = 0;
+        public void run() {
+            System.out.println("running " + (mCount++));
+        }
+    }
+}
+
+class ThreadTestSub implements Runnable {
+    public void run() {
+        System.out.print("@ Thread running\n");
+
+        try {
+            Thread.currentThread().setDaemon(true);
+            System.out.print("@ FAILED: setDaemon() succeeded\n");
+        } catch (IllegalThreadStateException itse) {
+            System.out.print("@ Got expected setDaemon exception\n");
+        }
+
+        //if (true)
+        //    throw new NullPointerException();
+        try {
+            Thread.sleep(2000);
+        }
+        catch (InterruptedException ie) {
+            System.out.print("@ Interrupted!\n");
+        }
+        finally {
+            System.out.print("@ Thread bailing\n");
+        }
+    }
+}
diff --git a/tests/052-verifier-fun/expected.txt b/tests/052-verifier-fun/expected.txt
new file mode 100644
index 0000000..5662675
--- /dev/null
+++ b/tests/052-verifier-fun/expected.txt
@@ -0,0 +1,2 @@
+BlahOne
+Zorch.
diff --git a/tests/052-verifier-fun/info.txt b/tests/052-verifier-fun/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/052-verifier-fun/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/052-verifier-fun/src/Blah.java b/tests/052-verifier-fun/src/Blah.java
new file mode 100644
index 0000000..edd6c9d
--- /dev/null
+++ b/tests/052-verifier-fun/src/Blah.java
@@ -0,0 +1,4 @@
+public abstract class Blah {
+    public void unrelatedStuff() {
+    }
+}
diff --git a/tests/052-verifier-fun/src/BlahFeature.java b/tests/052-verifier-fun/src/BlahFeature.java
new file mode 100644
index 0000000..ea0e18a
--- /dev/null
+++ b/tests/052-verifier-fun/src/BlahFeature.java
@@ -0,0 +1,3 @@
+public interface BlahFeature {
+    public void doStuff();
+}
diff --git a/tests/052-verifier-fun/src/BlahOne.java b/tests/052-verifier-fun/src/BlahOne.java
new file mode 100644
index 0000000..ed423cd
--- /dev/null
+++ b/tests/052-verifier-fun/src/BlahOne.java
@@ -0,0 +1,5 @@
+public class BlahOne extends Blah implements BlahFeature {
+    public void doStuff() {
+        System.out.println("BlahOne");
+    }
+}
diff --git a/tests/052-verifier-fun/src/BlahTwo.java b/tests/052-verifier-fun/src/BlahTwo.java
new file mode 100644
index 0000000..cff3670
--- /dev/null
+++ b/tests/052-verifier-fun/src/BlahTwo.java
@@ -0,0 +1,5 @@
+public class BlahTwo extends Blah implements BlahFeature {
+    public void doStuff() {
+        System.out.println("BlahTwo");
+    }
+}
diff --git a/tests/052-verifier-fun/src/Main.java b/tests/052-verifier-fun/src/Main.java
new file mode 100644
index 0000000..ca960cf
--- /dev/null
+++ b/tests/052-verifier-fun/src/Main.java
@@ -0,0 +1,109 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.lang.reflect.Type;
+
+/**
+ * Throw a few things at the verifier, all of which are expected to pass.
+ */
+public class Main {
+    static public void main(String[] args) {
+        tryBlah(1);
+
+        System.out.println("Zorch.");
+    }
+
+    /*
+     * Make sure the verifier is handling type merge of arrays of
+     * references correctly.
+     */
+    static Object[] arrayCheck1(int wanted) {
+        String[] arrayOne;
+        Integer[] arrayTwo;
+
+        arrayOne = new String[1];
+        arrayTwo = new Integer[1];
+
+        switch (wanted) {
+            case 0:     return arrayOne;
+            case 1:     return arrayTwo;
+            default:    return null;
+        }
+    }
+
+    static Object arrayCheck1b(int wanted) {
+        String[] arrayOne;
+        Integer[] arrayTwo;
+        int[] arrayThree;
+
+        arrayOne = new String[1];
+        arrayTwo = new Integer[1];
+        arrayThree = new int[1];
+
+        switch (wanted) {
+            case 0:     return arrayOne;
+            case 1:     return arrayTwo;
+            case 2:     return arrayThree;
+            default:    return null;
+        }
+    }
+
+    static Object[] arrayCheck2(int wanted) {
+        String[][] arrayOne;
+        String[][] arrayTwo;
+        Integer[][] arrayThree;
+
+        arrayOne = new String[1][];
+        arrayTwo = new String[1][];
+        arrayThree = new Integer[1][];
+
+        switch (wanted) {
+            case 0:     return arrayOne;
+            case 1:     return arrayTwo;
+            case 2:     return arrayThree;
+            default:    return null;
+        }
+    }
+
+    static Object[] arrayCheck3(int wanted) {
+        String[][] arrayTwo;
+        String[][][][] arrayFour;
+
+        arrayTwo = new String[1][];
+        arrayFour = new String[1][][][];
+
+        switch (wanted) {
+            case 0:     return arrayTwo;
+            case 1:     return arrayFour;
+            default:    return null;
+        }
+    }
+
+    /*
+     * Check return type merge.
+     */
+    private Type[] typeTest() {
+        if(this == null) {
+            return (Class<?>[])null;
+        }
+        return (Type[])null;
+    }
+
+
+    /*
+     * Exercise the blahs.
+     */
+    static void tryBlah(int num) {
+        BlahFeature feature = null;     // interface ref
+
+        switch (num) {
+            case 1:
+                feature = new BlahOne();
+                break;
+            default:
+                feature = new BlahTwo();
+                break;
+        }
+
+        feature.doStuff();
+    }
+}
diff --git a/tests/053-wait-some/expected.txt b/tests/053-wait-some/expected.txt
new file mode 100644
index 0000000..182892c
--- /dev/null
+++ b/tests/053-wait-some/expected.txt
@@ -0,0 +1,7 @@
+Caught expected exception on neg arg
+Waiting for 200ms...
+Waiting for 500ms...
+Waiting for 1000ms...
+Waiting for 2000ms...
+Waiting for 3500ms...
+Waiting for 8000ms...
diff --git a/tests/053-wait-some/info.txt b/tests/053-wait-some/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/053-wait-some/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/053-wait-some/src/Main.java b/tests/053-wait-some/src/Main.java
new file mode 100644
index 0000000..51e6c52
--- /dev/null
+++ b/tests/053-wait-some/src/Main.java
@@ -0,0 +1,71 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Exercise Object.wait(), comparing results against wall clock time.
+ */
+public class Main {
+    /* delays, in milliseconds */
+    private final static long[] DELAYS = {
+        200, 500, 1000, 2000, 3500, 8000
+    };
+
+    public static void main(String[] args) {
+        boolean timing = (args.length >= 1) && args[0].equals("--timing");
+        doit(timing);
+    }
+
+    public static void doit(boolean timing) {
+        Object sleepy = new Object();
+        long start, end;
+
+        synchronized (sleepy) {
+            try {
+                sleepy.wait(-500);
+                System.out.println("HEY: didn't throw on negative arg");
+            } catch (IllegalArgumentException iae) {
+                System.out.println("Caught expected exception on neg arg");
+            } catch (InterruptedException ie) {
+                ie.printStackTrace();
+            }
+
+            for(long delay : DELAYS) {
+                System.out.println("Waiting for " + delay + "ms...");
+
+                start = System.currentTimeMillis();
+                try {
+                    sleepy.wait(delay);
+                } catch (InterruptedException ie) {
+                    ie.printStackTrace();
+                }
+                end = System.currentTimeMillis();
+
+                long elapsed = end - start;
+                boolean showTime = timing;
+
+                if (! timing) {
+                    long epsilon = delay / 10;
+                    if (epsilon > 50) {
+                        epsilon = 50;
+                    }
+
+                    long min = delay - epsilon;
+                    long max = delay + epsilon;
+
+                    if (elapsed < min) {
+                        System.out.println("  Elapsed time was too short");
+                        showTime = true;
+                    } else if (elapsed > max) {
+                        System.out.println("  Elapsed time was too long: "
+                            + "elapsed=" + elapsed + " max=" + max);
+                        showTime = true;
+                    }
+                }
+
+                if (showTime) {
+                    System.out.println("  Wall clock elapsed "
+                            + elapsed + "ms");
+                }
+            }
+        }
+    }
+}
diff --git a/tests/054-uncaught/expected.txt b/tests/054-uncaught/expected.txt
new file mode 100644
index 0000000..e7473be
--- /dev/null
+++ b/tests/054-uncaught/expected.txt
@@ -0,0 +1,21 @@
+Test 1
+Uncaught exception DEFAULT!
+java.lang.NullPointerException: Hi diddly-ho, neighborino.
+	at Main.catchTheUncaught(Main.java:49)
+	at Main$Helper.run(Main.java:60)
+Test 2
+Uncaught exception THREAD!
+java.lang.NullPointerException: Hi diddly-ho, neighborino.
+	at Main.catchTheUncaught(Main.java:49)
+	at Main$Helper.run(Main.java:60)
+Test 3
+Uncaught exception THREAD!
+java.lang.NullPointerException: Hi diddly-ho, neighborino.
+	at Main.catchTheUncaught(Main.java:49)
+	at Main$Helper.run(Main.java:60)
+Test 1
+Uncaught exception DEFAULT!
+java.lang.NullPointerException: Hi diddly-ho, neighborino.
+	at Main.catchTheUncaught(Main.java:49)
+	at Main.main(Main.java:12)
+	at dalvik.system.NativeStart.main(Native Method)
diff --git a/tests/054-uncaught/info.txt b/tests/054-uncaught/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/054-uncaught/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/054-uncaught/src/Main.java b/tests/054-uncaught/src/Main.java
new file mode 100644
index 0000000..4ee6b05
--- /dev/null
+++ b/tests/054-uncaught/src/Main.java
@@ -0,0 +1,63 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test the uncaught exception handler.
+ */
+public class Main {
+    public static void main(String[] args) {
+        testThread(1);
+        testThread(2);
+        testThread(3);
+
+        catchTheUncaught(1);
+    }
+
+    private static void testThread(int which) {
+        Thread t = new Helper(which);
+        t.start();
+
+        try {
+            t.join();
+        } catch (InterruptedException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    static void catchTheUncaught(int which) {
+        ThreadDeathHandler defHandler = new ThreadDeathHandler("DEFAULT");
+        ThreadDeathHandler threadHandler = new ThreadDeathHandler("THREAD");
+
+        System.out.println("Test " + which);
+        switch (which) {
+            case 1: {
+                Thread.setDefaultUncaughtExceptionHandler(defHandler);
+                break;
+            }
+            case 2: {
+                Thread.currentThread().setUncaughtExceptionHandler(
+                        threadHandler);
+                break;
+            }
+            case 3: {
+                Thread.setDefaultUncaughtExceptionHandler(defHandler);
+                Thread.currentThread().setUncaughtExceptionHandler(
+                        threadHandler);
+                break;
+            }
+        }
+
+        throw new NullPointerException("Hi diddly-ho, neighborino.");
+    }
+
+    private static class Helper extends Thread {
+        private int which;
+
+        public Helper(int which) {
+            this.which = which;
+        }
+
+        public void run() {
+            catchTheUncaught(which);
+        }
+    }
+}
diff --git a/tests/054-uncaught/src/ThreadDeathHandler.java b/tests/054-uncaught/src/ThreadDeathHandler.java
new file mode 100644
index 0000000..5ea61a5
--- /dev/null
+++ b/tests/054-uncaught/src/ThreadDeathHandler.java
@@ -0,0 +1,19 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.lang.Thread.UncaughtExceptionHandler;
+
+/**
+ * Report death-by-uncaught-exception.
+ */
+public class ThreadDeathHandler implements Thread.UncaughtExceptionHandler {
+    private String mMyMessage;
+
+    public ThreadDeathHandler(String msg) {
+        mMyMessage = msg;
+    }
+
+    public void uncaughtException(Thread t, Throwable e) {
+        System.err.println("Uncaught exception " + mMyMessage + "!");
+        e.printStackTrace();
+    }
+}
diff --git a/tests/055-enum-performance/expected.txt b/tests/055-enum-performance/expected.txt
new file mode 100644
index 0000000..d81c194
--- /dev/null
+++ b/tests/055-enum-performance/expected.txt
@@ -0,0 +1,12 @@
+FOUR
+ONE
+FOURTEEN
+NINE
+FIVE
+TWELVE
+SamePackagePublicEnum
+basis: performed 40000 iterations
+test1: performed 10000 iterations
+test2: performed 10000 iterations
+test3: performed 10000 iterations
+Timing is acceptable.
diff --git a/tests/055-enum-performance/info.txt b/tests/055-enum-performance/info.txt
new file mode 100644
index 0000000..2ea1b9d
--- /dev/null
+++ b/tests/055-enum-performance/info.txt
@@ -0,0 +1,2 @@
+This is a performance test of Enum.valueOf(). To see the numbers, invoke
+this test with the "--timing" option.
diff --git a/tests/055-enum-performance/src/Main.java b/tests/055-enum-performance/src/Main.java
new file mode 100644
index 0000000..64d03eb
--- /dev/null
+++ b/tests/055-enum-performance/src/Main.java
@@ -0,0 +1,199 @@
+import otherpackage.OtherPackagePublicEnum;
+
+public class Main {
+    /** used by {@link #basisCall} */
+    static private int basisTestValue = 12;
+
+    static public void main(String[] args) throws Exception {
+        boolean timing = (args.length >= 1) && args[0].equals("--timing");
+        run(timing);
+    }
+
+    static public void run(boolean timing) {
+        preTest();
+
+        long time0 = System.nanoTime();
+        int count1 = test1(500);
+        long time1 = System.nanoTime();
+        int count2 = test2(500);
+        long time2 = System.nanoTime();
+        int count3 = test3(500);
+        long time3 = System.nanoTime();
+        int count4 = basis(2000);
+        long time4 = System.nanoTime();
+
+        System.out.println("basis: performed " + count4 + " iterations");
+        System.out.println("test1: performed " + count1 + " iterations");
+        System.out.println("test2: performed " + count2 + " iterations");
+        System.out.println("test3: performed " + count3 + " iterations");
+
+        double msec1 = (time1 - time0) / (double) count1 / 1000000;
+        double msec2 = (time2 - time1) / (double) count2 / 1000000;
+        double msec3 = (time3 - time2) / (double) count3 / 1000000;
+        double basisMsec = (time4 - time3) / (double) count4 / 1000000;
+
+        double avg = (msec1 + msec2 + msec3) / 3;
+        if (avg < (basisMsec * 25)) {
+            System.out.println("Timing is acceptable.");
+        } else {
+            System.out.println("Iterations are taking too long!");
+            timing = true;
+        }
+
+        if (timing) {
+            System.out.printf("basis time: %.3g msec\n", basisMsec);
+            System.out.printf("test1: %.3g msec per iteration\n", msec1);
+            System.out.printf("test2: %.3g msec per iteration\n", msec2);
+            System.out.printf("test3: %.3g msec per iteration\n", msec3);
+        }
+
+    }
+
+    static public void preTest() {
+        /*
+         * This is meant to ensure that the basic enum functionality
+         * really is working.
+         */
+
+        Class<SamePackagePublicEnum> c = SamePackagePublicEnum.class;
+
+        System.out.println(Enum.valueOf(c, "FOUR"));
+        System.out.println(Enum.valueOf(c, "ONE"));
+        System.out.println(Enum.valueOf(c, "FOURTEEN"));
+        System.out.println(Enum.valueOf(c, "NINE"));
+        System.out.println(Enum.valueOf(c, "FIVE"));
+        System.out.println(Enum.valueOf(c, "TWELVE"));
+
+        System.out.println(Enum.valueOf(c, "ZERO").getClass().getName());
+    }
+
+    static public int basis(int iters) {
+        /*
+         * The basis time is the time taken to call a static method
+         * passing two arguments, which in turn accesses a static
+         * variable, compares a string, and does a little trivial math
+         * and a trivial comparison. (That is, this is a mini
+         * "omnibus" performance metric.) This is clearly going to be
+         * much faster than Enum.valueOf(), which is why we multiply
+         * the time before testing.
+         */
+        for (int i = iters; i > 0; i--) {
+            basisCall(i, "aname");
+            basisCall(i, "bname");
+            basisCall(i, "cname");
+            basisCall(i, "dname");
+            basisCall(i, "ename");
+            basisCall(i, "fname");
+            basisCall(i, "gname");
+            basisCall(i, "hname");
+            basisCall(i, "iname");
+            basisCall(i, "jname");
+            basisCall(i, "kname");
+            basisCall(i, "lname");
+            basisCall(i, "mname");
+            basisCall(i, "nname");
+            basisCall(i, "oname");
+            basisCall(i, "pname");
+            basisCall(i, "qname");
+            basisCall(i, "rname");
+            basisCall(i, "sname");
+            basisCall(i, "tname");
+        }
+
+        return iters * 20;
+    }
+
+    static public int basisCall(int i, String name) {
+        int compare = name.compareTo("fuzzbot");
+
+        if (i < (basisTestValue * compare)) {
+            return basisTestValue;
+        } else {
+            return i;
+        }
+    }
+
+    static public int test1(int iters) {
+        Class<SamePackagePublicEnum> c = SamePackagePublicEnum.class;
+        for (int i = iters; i > 0; i--) {
+            Enum.valueOf(c, "ZERO");
+            Enum.valueOf(c, "ONE");
+            Enum.valueOf(c, "TWO");
+            Enum.valueOf(c, "THREE");
+            Enum.valueOf(c, "FOUR");
+            Enum.valueOf(c, "FIVE");
+            Enum.valueOf(c, "SIX");
+            Enum.valueOf(c, "SEVEN");
+            Enum.valueOf(c, "EIGHT");
+            Enum.valueOf(c, "NINE");
+            Enum.valueOf(c, "TEN");
+            Enum.valueOf(c, "ELEVEN");
+            Enum.valueOf(c, "TWELVE");
+            Enum.valueOf(c, "THIRTEEN");
+            Enum.valueOf(c, "FOURTEEN");
+            Enum.valueOf(c, "FIFTEEN");
+            Enum.valueOf(c, "SIXTEEN");
+            Enum.valueOf(c, "SEVENTEEN");
+            Enum.valueOf(c, "EIGHTEEN");
+            Enum.valueOf(c, "NINETEEN");
+        }
+
+        return iters * 20;
+    }
+
+    static public int test2(int iters) {
+        Class<SamePackagePrivateEnum> c = SamePackagePrivateEnum.class;
+        for (int i = iters; i > 0; i--) {
+            Enum.valueOf(c, "ZERO");
+            Enum.valueOf(c, "ONE");
+            Enum.valueOf(c, "TWO");
+            Enum.valueOf(c, "THREE");
+            Enum.valueOf(c, "FOUR");
+            Enum.valueOf(c, "FIVE");
+            Enum.valueOf(c, "SIX");
+            Enum.valueOf(c, "SEVEN");
+            Enum.valueOf(c, "EIGHT");
+            Enum.valueOf(c, "NINE");
+            Enum.valueOf(c, "TEN");
+            Enum.valueOf(c, "ELEVEN");
+            Enum.valueOf(c, "TWELVE");
+            Enum.valueOf(c, "THIRTEEN");
+            Enum.valueOf(c, "FOURTEEN");
+            Enum.valueOf(c, "FIFTEEN");
+            Enum.valueOf(c, "SIXTEEN");
+            Enum.valueOf(c, "SEVENTEEN");
+            Enum.valueOf(c, "EIGHTEEN");
+            Enum.valueOf(c, "NINETEEN");
+        }
+
+        return iters * 20;
+    }
+
+    static public int test3(int iters) {
+        Class<OtherPackagePublicEnum> c = OtherPackagePublicEnum.class;
+        for (int i = iters; i > 0; i--) {
+            Enum.valueOf(c, "ZERO");
+            Enum.valueOf(c, "ONE");
+            Enum.valueOf(c, "TWO");
+            Enum.valueOf(c, "THREE");
+            Enum.valueOf(c, "FOUR");
+            Enum.valueOf(c, "FIVE");
+            Enum.valueOf(c, "SIX");
+            Enum.valueOf(c, "SEVEN");
+            Enum.valueOf(c, "EIGHT");
+            Enum.valueOf(c, "NINE");
+            Enum.valueOf(c, "TEN");
+            Enum.valueOf(c, "ELEVEN");
+            Enum.valueOf(c, "TWELVE");
+            Enum.valueOf(c, "THIRTEEN");
+            Enum.valueOf(c, "FOURTEEN");
+            Enum.valueOf(c, "FIFTEEN");
+            Enum.valueOf(c, "SIXTEEN");
+            Enum.valueOf(c, "SEVENTEEN");
+            Enum.valueOf(c, "EIGHTEEN");
+            Enum.valueOf(c, "NINETEEN");
+        }
+
+        return iters * 20;
+    }
+}
diff --git a/tests/055-enum-performance/src/SamePackagePrivateEnum.java b/tests/055-enum-performance/src/SamePackagePrivateEnum.java
new file mode 100644
index 0000000..b6759f6
--- /dev/null
+++ b/tests/055-enum-performance/src/SamePackagePrivateEnum.java
@@ -0,0 +1,5 @@
+/*package*/ enum SamePackagePrivateEnum {
+    ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE,
+        TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN, SIXTEEN,
+        SEVENTEEN, EIGHTEEN, NINETEEN;
+}
diff --git a/tests/055-enum-performance/src/SamePackagePublicEnum.java b/tests/055-enum-performance/src/SamePackagePublicEnum.java
new file mode 100644
index 0000000..3a1c230
--- /dev/null
+++ b/tests/055-enum-performance/src/SamePackagePublicEnum.java
@@ -0,0 +1,5 @@
+public enum SamePackagePublicEnum {
+    ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE,
+        TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN, SIXTEEN,
+        SEVENTEEN, EIGHTEEN, NINETEEN;
+}
diff --git a/tests/055-enum-performance/src/otherpackage/OtherPackagePublicEnum.java b/tests/055-enum-performance/src/otherpackage/OtherPackagePublicEnum.java
new file mode 100644
index 0000000..4ef4d78
--- /dev/null
+++ b/tests/055-enum-performance/src/otherpackage/OtherPackagePublicEnum.java
@@ -0,0 +1,7 @@
+package otherpackage;
+
+public enum OtherPackagePublicEnum {
+    ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE,
+        TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN, SIXTEEN,
+        SEVENTEEN, EIGHTEEN, NINETEEN;
+}
diff --git a/tests/056-const-string-jumbo/build b/tests/056-const-string-jumbo/build
new file mode 100644
index 0000000..c5e35db
--- /dev/null
+++ b/tests/056-const-string-jumbo/build
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# Write out files with 32768 total static string declarations, so that
+# the reference to "zorch" in the real test file will be guaranteed to
+# need a jumbo string reference (it sorts last after all the others).
+# Note: Each string reference is stored in a separate static variable,
+# and that variable's name is also represented in the strings, which
+# is why we can just have 32768 and not 65536 declarations.
+
+awk '
+BEGIN {
+    writeFile("Zorch1", 0, 16383);
+    writeFile("Zorch2", 16384, 32767);
+}
+function writeFile(name, start, end) {
+    fileName = "src/" name ".java";
+    printf("public class %s {\n", name) > fileName;
+    for (i = start; i <= end; i++) {
+        printf("    static public final String s%d = \"%d\";\n",
+            i, i) > fileName;
+    }
+    printf("}\n") > fileName;
+}'
+
+mkdir classes
+${JAVAC} -d classes src/*.java
+
+dx -JXmx500m --debug --dex --no-optimize --positions=none --no-locals \
+    --dump-to=classes.lst --output=classes.dex classes
+zip test.jar classes.dex
diff --git a/tests/056-const-string-jumbo/expected.txt b/tests/056-const-string-jumbo/expected.txt
new file mode 100644
index 0000000..bebbf9e
--- /dev/null
+++ b/tests/056-const-string-jumbo/expected.txt
@@ -0,0 +1 @@
+zorch
diff --git a/tests/056-const-string-jumbo/info.txt b/tests/056-const-string-jumbo/info.txt
new file mode 100644
index 0000000..c4ba856
--- /dev/null
+++ b/tests/056-const-string-jumbo/info.txt
@@ -0,0 +1 @@
+Test that the opcode const-string/jumbo works.
diff --git a/tests/056-const-string-jumbo/src/Main.java b/tests/056-const-string-jumbo/src/Main.java
new file mode 100644
index 0000000..68d6e53
--- /dev/null
+++ b/tests/056-const-string-jumbo/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+    static public void main(String[] args) {
+        System.out.println("zorch");
+    }
+}
diff --git a/tests/057-iteration-performance/expected.txt b/tests/057-iteration-performance/expected.txt
new file mode 100644
index 0000000..9d59a5e
--- /dev/null
+++ b/tests/057-iteration-performance/expected.txt
@@ -0,0 +1,11 @@
+Running A...
+Running B...
+Running C...
+Running D...
+Running E...
+Running F...
+Running G...
+Running H...
+Done with runs.
+
+All times are within the expected ranges.
diff --git a/tests/057-iteration-performance/info.txt b/tests/057-iteration-performance/info.txt
new file mode 100644
index 0000000..36b5adc
--- /dev/null
+++ b/tests/057-iteration-performance/info.txt
@@ -0,0 +1,2 @@
+This is a performance test of various iterator uses. To see the numbers,
+invoke this test with the "--timing" option.
diff --git a/tests/057-iteration-performance/src/Main.java b/tests/057-iteration-performance/src/Main.java
new file mode 100644
index 0000000..d562802
--- /dev/null
+++ b/tests/057-iteration-performance/src/Main.java
@@ -0,0 +1,1108 @@
+/*
+ * 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.
+ */
+
+import java.util.ArrayList;
+
+/**
+ * The matrix of tests includes the A-E axis for loop body contents and
+ * the 0-5 axis for iterator style.
+ *
+ * <ul>
+ * <li>A: empty body</li>
+ * <li>B: array element access and update</li>
+ * <li>C: instance field access and update</li>
+ * <li>D: method call to empty method</li>
+ * <li>E: synch and then method call to empty method</li>
+ * <li>F: 5 method calls to empty method</li>
+ * <li>G: one small object allocation (empty constructor)</li>
+ * <li>H: copy 8k of bytes from one array to another</li>
+ * </ul>
+ *
+ * <ul>
+ * <li>0: for() loop backward to 0</li>
+ * <li>1: for() loop forward to local variable</li>
+ * <li>2: for() loop forward to array length</li>
+ * <li>3: for(:) loop over array</li>
+ * <li>4: for() loop forward to instance variable</li>
+ * <li>5: for() loop forward to trivial method call</li>
+ * <li>6: for(:) loop over ArrayList</li>
+ * </ul>
+ */
+public class Main {
+    static public final int BODIES = 8;
+    static public final int LOOPS = 7;
+
+    static public void main(String[] args) throws Exception {
+        boolean timing = (args.length >= 1) && args[0].equals("--timing");
+
+        int iters = 100;
+        double probeSec;
+
+        for (;;) {
+            long t0 = System.nanoTime();
+            runAllTests(iters, false);
+            long t1 = System.nanoTime();
+
+            probeSec = (t1 - t0) / 1000000000.0;
+            if (probeSec > 0.25) {
+                break;
+            }
+
+            iters *= 2;
+        }
+
+        // Attempt to arrange for the real test to take 20 seconds.
+        iters = (int) ((iters / probeSec) * 20);
+
+        if (timing) {
+            System.out.println("iters = " + iters);
+        }
+
+        run(timing, iters);
+    }
+
+    static private enum Normalization {
+        NONE, PER_COLUMN, TOP_LEFT;
+    }
+
+    static public void printTimings(double[][] timings, Normalization norm) {
+        System.out.println();
+        System.out.printf("%-7s   A        B        C        D        E" +
+                "        F        G        H\n",
+                (norm == Normalization.NONE) ? "(usec)" : "(ratio)");
+        System.out.println("      -------- -------- -------- -------- " +
+                "-------- -------- -------- --------");
+
+        double bases[] = new double[BODIES];
+        for (int i = 0; i < BODIES; i++) {
+            double n;
+            switch (norm) {
+                case PER_COLUMN:  n = timings[i][0]; break;
+                case TOP_LEFT:    n = timings[0][0]; break;
+                default /*NONE*/: n = 1.0;           break;
+            }
+            bases[i] = n;
+        }
+
+        for (int i = 0; i < LOOPS; i++) {
+            System.out.printf("%4d: %8.3g %8.3g %8.3g %8.3g %8.3g %8.3g " +
+                    "%8.3g %8.3g\n",
+                    i,
+                    timings[0][i] / bases[0],
+                    timings[1][i] / bases[1],
+                    timings[2][i] / bases[2],
+                    timings[3][i] / bases[3],
+                    timings[4][i] / bases[4],
+                    timings[5][i] / bases[5],
+                    timings[6][i] / bases[6],
+                    timings[7][i] / bases[7]);
+        }
+    }
+
+    static public void run(boolean timing, int iters) {
+        double[][] timings = null; // assign to avoid apparent javac bug
+
+        // Try up to 5 times to get good times.
+        for (int i = 0; i < 5; i++) {
+            double[][] newTimings = runAllTests(iters, timing || (i == 0));
+
+            if (timings == null) {
+                timings = newTimings;
+            } else {
+                combineTimings(timings, newTimings, i);
+            }
+
+            if (checkTimes(timings, timing)) {
+                break;
+            }
+        }
+
+        System.out.println("Done with runs.");
+
+        boolean goodTimes = checkTimes(timings, true);
+
+        if (! goodTimes) {
+            timing = true;
+        }
+
+        if (timing) {
+            printTimings(timings, Normalization.NONE);
+            printTimings(timings, Normalization.TOP_LEFT);
+            printTimings(timings, Normalization.PER_COLUMN);
+        } else {
+            System.out.println("\nAll times are within the expected ranges.");
+        }
+    }
+
+    static public void combineTimings(double[][] target, double[][] newTimes,
+            int oldWeight) {
+        for (int i = 0; i < target.length; i++) {
+            for (int j = 0; j < target[i].length; j++) {
+                target[i][j] =
+                    ((target[i][j] * oldWeight) + newTimes[i][j])
+                    / (oldWeight + 1);
+            }
+        }
+    }
+
+    static public boolean checkTimes(double[][] timings, boolean print) {
+        // expected increase over A1
+        double[][] expected = {
+            {  1.0,  2.3,  2.4,  3.3,  6.5, 12.0, 57.0,  94.0 },
+            {  1.2,  2.4,  2.5,  3.4,  6.6, 12.2, 60.0,  95.0 },
+            {  1.5,  2.6,  2.9,  3.5,  6.7, 12.4, 63.0,  96.0 },
+            {  1.6,  2.8,  2.9,  3.6,  6.8, 12.6, 63.5,  97.0 },
+            {  1.7,  3.0,  2.9,  3.7,  6.9, 12.8, 64.0,  98.0 },
+            {  6.0,  6.0,  6.0,  7.0, 10.0, 15.0, 64.5, 105.0 },
+            { 31.0, 31.2, 31.5, 34.0, 41.0, 43.0, 91.0, 135.0 },
+        };
+
+        boolean good = true;
+
+        for (int x = 0; x < BODIES; x++) {
+            for (int y = 0; y < LOOPS; y++) {
+                double ratio = timings[x][y] / timings[0][0];
+                if (ratio > expected[y][x]) {
+                    if (print) {
+                        System.out.printf("%c%d is too slow: %.3g vs. %.3g\n",
+                                (char) (x + 'A'), y, ratio, expected[y][x]);
+                    }
+                    good = false;
+                }
+            }
+        }
+
+        return good;
+    }
+
+    static public double[][] runAllTests(int iters, boolean print) {
+        // diters is used to get usec, not nanosec; hence the extra 1000.
+        double diters = (double) iters * INNER_COUNT * 1000;
+
+        double[][] timings = new double[BODIES][LOOPS];
+        long t0, t1, t2, t3, t4, t5, t6, t7;
+
+        // Column A
+
+        if (print) {
+            System.out.println("Running A...");
+        }
+
+        t0 = System.nanoTime();
+        testA0(iters);
+        t1 = System.nanoTime();
+        testA1(iters);
+        t2 = System.nanoTime();
+        testA2(iters);
+        t3 = System.nanoTime();
+        testA3(iters);
+        t4 = System.nanoTime();
+        testA4(iters);
+        t5 = System.nanoTime();
+        testA5(iters);
+        t6 = System.nanoTime();
+        testA6(iters);
+        t7 = System.nanoTime();
+
+        timings[0][0] = (t1 - t0) / diters;
+        timings[0][1] = (t2 - t1) / diters;
+        timings[0][2] = (t3 - t2) / diters;
+        timings[0][3] = (t4 - t3) / diters;
+        timings[0][4] = (t5 - t4) / diters;
+        timings[0][5] = (t6 - t5) / diters;
+        timings[0][6] = (t7 - t6) / diters;
+
+        // Column B
+
+        if (print) {
+            System.out.println("Running B...");
+        }
+
+        t0 = System.nanoTime();
+        testB0(iters);
+        t1 = System.nanoTime();
+        testB1(iters);
+        t2 = System.nanoTime();
+        testB2(iters);
+        t3 = System.nanoTime();
+        testB3(iters);
+        t4 = System.nanoTime();
+        testB4(iters);
+        t5 = System.nanoTime();
+        testB5(iters);
+        t6 = System.nanoTime();
+        testB6(iters);
+        t7 = System.nanoTime();
+
+        timings[1][0] = (t1 - t0) / diters;
+        timings[1][1] = (t2 - t1) / diters;
+        timings[1][2] = (t3 - t2) / diters;
+        timings[1][3] = (t4 - t3) / diters;
+        timings[1][4] = (t5 - t4) / diters;
+        timings[1][5] = (t6 - t5) / diters;
+        timings[1][6] = (t7 - t6) / diters;
+
+        // Column C
+
+        if (print) {
+            System.out.println("Running C...");
+        }
+
+        t0 = System.nanoTime();
+        testC0(iters);
+        t1 = System.nanoTime();
+        testC1(iters);
+        t2 = System.nanoTime();
+        testC2(iters);
+        t3 = System.nanoTime();
+        testC3(iters);
+        t4 = System.nanoTime();
+        testC4(iters);
+        t5 = System.nanoTime();
+        testC5(iters);
+        t6 = System.nanoTime();
+        testC6(iters);
+        t7 = System.nanoTime();
+
+        timings[2][0] = (t1 - t0) / diters;
+        timings[2][1] = (t2 - t1) / diters;
+        timings[2][2] = (t3 - t2) / diters;
+        timings[2][3] = (t4 - t3) / diters;
+        timings[2][4] = (t5 - t4) / diters;
+        timings[2][5] = (t6 - t5) / diters;
+        timings[2][6] = (t7 - t6) / diters;
+
+        // Column D
+
+        if (print) {
+            System.out.println("Running D...");
+        }
+
+        t0 = System.nanoTime();
+        testD0(iters);
+        t1 = System.nanoTime();
+        testD1(iters);
+        t2 = System.nanoTime();
+        testD2(iters);
+        t3 = System.nanoTime();
+        testD3(iters);
+        t4 = System.nanoTime();
+        testD4(iters);
+        t5 = System.nanoTime();
+        testD5(iters);
+        t6 = System.nanoTime();
+        testD6(iters);
+        t7 = System.nanoTime();
+
+        timings[3][0] = (t1 - t0) / diters;
+        timings[3][1] = (t2 - t1) / diters;
+        timings[3][2] = (t3 - t2) / diters;
+        timings[3][3] = (t4 - t3) / diters;
+        timings[3][4] = (t5 - t4) / diters;
+        timings[3][5] = (t6 - t5) / diters;
+        timings[3][6] = (t7 - t6) / diters;
+
+        // Column E
+
+        if (print) {
+            System.out.println("Running E...");
+        }
+
+        t0 = System.nanoTime();
+        testE0(iters);
+        t1 = System.nanoTime();
+        testE1(iters);
+        t2 = System.nanoTime();
+        testE2(iters);
+        t3 = System.nanoTime();
+        testE3(iters);
+        t4 = System.nanoTime();
+        testE4(iters);
+        t5 = System.nanoTime();
+        testE5(iters);
+        t6 = System.nanoTime();
+        testE6(iters);
+        t7 = System.nanoTime();
+
+        timings[4][0] = (t1 - t0) / diters;
+        timings[4][1] = (t2 - t1) / diters;
+        timings[4][2] = (t3 - t2) / diters;
+        timings[4][3] = (t4 - t3) / diters;
+        timings[4][4] = (t5 - t4) / diters;
+        timings[4][5] = (t6 - t5) / diters;
+        timings[4][6] = (t7 - t6) / diters;
+
+        // Column F
+
+        if (print) {
+            System.out.println("Running F...");
+        }
+
+        t0 = System.nanoTime();
+        testF0(iters);
+        t1 = System.nanoTime();
+        testF1(iters);
+        t2 = System.nanoTime();
+        testF2(iters);
+        t3 = System.nanoTime();
+        testF3(iters);
+        t4 = System.nanoTime();
+        testF4(iters);
+        t5 = System.nanoTime();
+        testF5(iters);
+        t6 = System.nanoTime();
+        testF6(iters);
+        t7 = System.nanoTime();
+
+        timings[5][0] = (t1 - t0) / diters;
+        timings[5][1] = (t2 - t1) / diters;
+        timings[5][2] = (t3 - t2) / diters;
+        timings[5][3] = (t4 - t3) / diters;
+        timings[5][4] = (t5 - t4) / diters;
+        timings[5][5] = (t6 - t5) / diters;
+        timings[5][6] = (t7 - t6) / diters;
+
+        // Reduce the iters for the last two, since they're much slower.
+
+        iters /= 5;
+        diters /= 5;
+
+        // Column G
+
+        if (print) {
+            System.out.println("Running G...");
+        }
+
+        t0 = System.nanoTime();
+        testG0(iters);
+        t1 = System.nanoTime();
+        testG1(iters);
+        t2 = System.nanoTime();
+        testG2(iters);
+        t3 = System.nanoTime();
+        testG3(iters);
+        t4 = System.nanoTime();
+        testG4(iters);
+        t5 = System.nanoTime();
+        testG5(iters);
+        t6 = System.nanoTime();
+        testG6(iters);
+        t7 = System.nanoTime();
+
+        timings[6][0] = (t1 - t0) / diters;
+        timings[6][1] = (t2 - t1) / diters;
+        timings[6][2] = (t3 - t2) / diters;
+        timings[6][3] = (t4 - t3) / diters;
+        timings[6][4] = (t5 - t4) / diters;
+        timings[6][5] = (t6 - t5) / diters;
+        timings[6][6] = (t7 - t6) / diters;
+
+        // Column H
+
+        if (print) {
+            System.out.println("Running H...");
+        }
+
+        t0 = System.nanoTime();
+        testH0(iters);
+        t1 = System.nanoTime();
+        testH1(iters);
+        t2 = System.nanoTime();
+        testH2(iters);
+        t3 = System.nanoTime();
+        testH3(iters);
+        t4 = System.nanoTime();
+        testH4(iters);
+        t5 = System.nanoTime();
+        testH5(iters);
+        t6 = System.nanoTime();
+        testH6(iters);
+        t7 = System.nanoTime();
+
+        timings[7][0] = (t1 - t0) / diters;
+        timings[7][1] = (t2 - t1) / diters;
+        timings[7][2] = (t3 - t2) / diters;
+        timings[7][3] = (t4 - t3) / diters;
+        timings[7][4] = (t5 - t4) / diters;
+        timings[7][5] = (t6 - t5) / diters;
+        timings[7][6] = (t7 - t6) / diters;
+
+        return timings;
+    }
+
+    // Helper bits and pieces
+
+    static private final int INNER_COUNT = 100;
+    static private final int[] INNER_ARRAY = new int[INNER_COUNT];
+    static private final ArrayList<Object> INNER_LIST =
+        new ArrayList<Object>(INNER_COUNT);
+    static private final Target TARGET = new Target();
+    static private final int ARRAY_BYTES = 8192;
+    static private final byte[] BYTES_1 = new byte[ARRAY_BYTES];
+    static private final byte[] BYTES_2 = new byte[ARRAY_BYTES];
+
+    static {
+        for (int i = 0; i < INNER_COUNT; i++) {
+            INNER_LIST.add(null);
+        }
+    }
+
+    public static class Target {
+        public int value;
+        public int size = INNER_COUNT;
+
+        public void simple() {
+            // empty
+        }
+
+        public int size() {
+            return size;
+        }
+    }
+
+    // The tests themselves
+
+    static public void testA0(int iters) {
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = INNER_COUNT; i > 0; i--) {
+                // empty
+            }
+        }
+    }
+
+    static public void testA1(int iters) {
+        int count = INNER_COUNT;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < count; i++) {
+                // empty
+            }
+        }
+    }
+
+    static public void testA2(int iters) {
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < array.length; i++) {
+                // empty
+            }
+        }
+    }
+
+    static public void testA3(int iters) {
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i : array) {
+                // empty
+            }
+        }
+    }
+
+    static public void testA4(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size; i++) {
+                // empty
+            }
+        }
+    }
+
+    static public void testA5(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size(); i++) {
+                // empty
+            }
+        }
+    }
+
+    static public void testA6(int iters) {
+        ArrayList<Object> list = INNER_LIST;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (Object o : list) {
+                // empty
+            }
+        }
+    }
+
+    static public void testB0(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = INNER_COUNT; i > 0; i--) {
+                target.value++;
+            }
+        }
+    }
+
+    static public void testB1(int iters) {
+        Target target = TARGET;
+        int count = INNER_COUNT;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < count; i++) {
+                target.value++;
+            }
+        }
+    }
+
+    static public void testB2(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < array.length; i++) {
+                target.value++;
+            }
+        }
+    }
+
+    static public void testB3(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i : array) {
+                target.value++;
+            }
+        }
+    }
+
+    static public void testB4(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size; i++) {
+                target.value++;
+            }
+        }
+    }
+
+    static public void testB5(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size(); i++) {
+                target.value++;
+            }
+        }
+    }
+
+    static public void testB6(int iters) {
+        Target target = TARGET;
+        ArrayList<Object> list = INNER_LIST;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (Object o : list) {
+                target.value++;
+            }
+        }
+    }
+
+    static public void testC0(int iters) {
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = INNER_COUNT - 1; i >= 0; i--) {
+                array[i]++;
+            }
+        }
+    }
+
+    static public void testC1(int iters) {
+        int[] array = INNER_ARRAY;
+        int count = INNER_COUNT;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < count; i++) {
+                array[i]++;
+            }
+        }
+    }
+
+    static public void testC2(int iters) {
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < array.length; i++) {
+                array[i]++;
+            }
+        }
+    }
+
+    static public void testC3(int iters) {
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i : array) {
+                array[0] = i + 1;
+            }
+        }
+    }
+
+    static public void testC4(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size; i++) {
+                array[i]++;
+            }
+        }
+    }
+
+    static public void testC5(int iters) {
+        int[] array = INNER_ARRAY;
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size(); i++) {
+                array[i]++;
+            }
+        }
+    }
+
+    static public void testC6(int iters) {
+        int[] array = INNER_ARRAY;
+        ArrayList<Object> list = INNER_LIST;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (Object o : list) {
+                array[0]++;
+            }
+        }
+    }
+
+    static public void testD0(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = INNER_COUNT; i > 0; i--) {
+                target.simple();
+            }
+        }
+    }
+
+    static public void testD1(int iters) {
+        Target target = TARGET;
+        int count = INNER_COUNT;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < count; i++) {
+                target.simple();
+            }
+        }
+    }
+
+    static public void testD2(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < array.length; i++) {
+                target.simple();
+            }
+        }
+    }
+
+    static public void testD3(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i : array) {
+                target.simple();
+            }
+        }
+    }
+
+    static public void testD4(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size; i++) {
+                target.simple();
+            }
+        }
+    }
+
+    static public void testD5(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size(); i++) {
+                target.simple();
+            }
+        }
+    }
+
+    static public void testD6(int iters) {
+        Target target = TARGET;
+        ArrayList<Object> list = INNER_LIST;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (Object o : list) {
+                target.simple();
+            }
+        }
+    }
+
+    static public void testE0(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = INNER_COUNT; i > 0; i--) {
+                synchronized (target) {
+                    target.simple();
+                }
+            }
+        }
+    }
+
+    static public void testE1(int iters) {
+        Target target = TARGET;
+        int count = INNER_COUNT;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < count; i++) {
+                synchronized (target) {
+                    target.simple();
+                }
+            }
+        }
+    }
+
+    static public void testE2(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < array.length; i++) {
+                synchronized (target) {
+                    target.simple();
+                }
+            }
+        }
+    }
+
+    static public void testE3(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i : array) {
+                synchronized (target) {
+                    target.simple();
+                }
+            }
+        }
+    }
+
+    static public void testE4(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size; i++) {
+                synchronized (target) {
+                    target.simple();
+                }
+            }
+        }
+    }
+
+    static public void testE5(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size(); i++) {
+                synchronized (target) {
+                    target.simple();
+                }
+            }
+        }
+    }
+
+    static public void testE6(int iters) {
+        Target target = TARGET;
+        ArrayList<Object> list = INNER_LIST;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (Object o : list) {
+                synchronized (target) {
+                    target.simple();
+                }
+            }
+        }
+    }
+
+    static public void testF0(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = INNER_COUNT; i > 0; i--) {
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+            }
+        }
+    }
+
+    static public void testF1(int iters) {
+        Target target = TARGET;
+        int count = INNER_COUNT;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < count; i++) {
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+            }
+        }
+    }
+
+    static public void testF2(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < array.length; i++) {
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+            }
+        }
+    }
+
+    static public void testF3(int iters) {
+        Target target = TARGET;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i : array) {
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+            }
+        }
+    }
+
+    static public void testF4(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size; i++) {
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+            }
+        }
+    }
+
+    static public void testF5(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size(); i++) {
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+            }
+        }
+    }
+
+    static public void testF6(int iters) {
+        Target target = TARGET;
+        ArrayList<Object> list = INNER_LIST;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (Object o : list) {
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+                target.simple();
+            }
+        }
+    }
+
+    static public void testG0(int iters) {
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = INNER_COUNT; i > 0; i--) {
+                new Target();
+            }
+        }
+    }
+
+    static public void testG1(int iters) {
+        int count = INNER_COUNT;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < count; i++) {
+                new Target();
+            }
+        }
+    }
+
+    static public void testG2(int iters) {
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < array.length; i++) {
+                new Target();
+            }
+        }
+    }
+
+    static public void testG3(int iters) {
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i : array) {
+                new Target();
+            }
+        }
+    }
+
+    static public void testG4(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size; i++) {
+                new Target();
+            }
+        }
+    }
+
+    static public void testG5(int iters) {
+        Target target = TARGET;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size(); i++) {
+                new Target();
+            }
+        }
+    }
+
+    static public void testG6(int iters) {
+        ArrayList<Object> list = INNER_LIST;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (Object o : list) {
+                new Target();
+            }
+        }
+    }
+
+    static public void testH0(int iters) {
+        byte[] b1 = BYTES_1;
+        byte[] b2 = BYTES_2;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = INNER_COUNT; i > 0; i--) {
+                System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+            }
+        }
+    }
+
+    static public void testH1(int iters) {
+        byte[] b1 = BYTES_1;
+        byte[] b2 = BYTES_2;
+        int count = INNER_COUNT;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < count; i++) {
+                System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+            }
+        }
+    }
+
+    static public void testH2(int iters) {
+        byte[] b1 = BYTES_1;
+        byte[] b2 = BYTES_2;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < array.length; i++) {
+                System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+            }
+        }
+    }
+
+    static public void testH3(int iters) {
+        byte[] b1 = BYTES_1;
+        byte[] b2 = BYTES_2;
+        int[] array = INNER_ARRAY;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i : array) {
+                System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+            }
+        }
+    }
+
+    static public void testH4(int iters) {
+        Target target = TARGET;
+        byte[] b1 = BYTES_1;
+        byte[] b2 = BYTES_2;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size; i++) {
+                System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+            }
+        }
+    }
+
+    static public void testH5(int iters) {
+        Target target = TARGET;
+        byte[] b1 = BYTES_1;
+        byte[] b2 = BYTES_2;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (int i = 0; i < target.size(); i++) {
+                System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+            }
+        }
+    }
+
+    static public void testH6(int iters) {
+        byte[] b1 = BYTES_1;
+        byte[] b2 = BYTES_2;
+        ArrayList<Object> list = INNER_LIST;
+
+        for (int outer = iters; outer > 0; outer--) {
+            for (Object o : list) {
+                System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+            }
+        }
+    }
+}
diff --git a/tests/058-enum-order/expected.txt b/tests/058-enum-order/expected.txt
new file mode 100644
index 0000000..b812404
--- /dev/null
+++ b/tests/058-enum-order/expected.txt
@@ -0,0 +1,5 @@
+0: CORN
+1: BLUEBERRY
+2: CRANBERRY
+3: BRAN
+4: BLACKBERRY
diff --git a/tests/058-enum-order/info.txt b/tests/058-enum-order/info.txt
new file mode 100644
index 0000000..b9809fd
--- /dev/null
+++ b/tests/058-enum-order/info.txt
@@ -0,0 +1 @@
+Test that the ordering of enums is as expected.
diff --git a/tests/058-enum-order/src/Main.java b/tests/058-enum-order/src/Main.java
new file mode 100644
index 0000000..2cd6052
--- /dev/null
+++ b/tests/058-enum-order/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * 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 enum ordering.
+ */
+public class Main {
+    public static enum Muffin {
+        CORN, BLUEBERRY, CRANBERRY, BRAN, BLACKBERRY;
+    }
+
+    public static void main(String args[]) {
+        Muffin[] array = Muffin.class.getEnumConstants();
+        for (Muffin m : array) {
+            System.out.println(m.ordinal() + ": " + m);
+        }
+    }
+}
diff --git a/tests/059-finalizer-throw/expected.txt b/tests/059-finalizer-throw/expected.txt
new file mode 100644
index 0000000..cbc9ece
--- /dev/null
+++ b/tests/059-finalizer-throw/expected.txt
@@ -0,0 +1,2 @@
+In finalizer
+done
diff --git a/tests/059-finalizer-throw/info.txt b/tests/059-finalizer-throw/info.txt
new file mode 100644
index 0000000..6261372
--- /dev/null
+++ b/tests/059-finalizer-throw/info.txt
@@ -0,0 +1 @@
+Verify that exceptions thrown from finalizers are ignored.
diff --git a/tests/059-finalizer-throw/src/Main.java b/tests/059-finalizer-throw/src/Main.java
new file mode 100644
index 0000000..42260e4
--- /dev/null
+++ b/tests/059-finalizer-throw/src/Main.java
@@ -0,0 +1,56 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/*
+ * Throw an exception from a finalizer and make sure it's harmless.  Under
+ * Dalvik this may also generate a warning in the log file.
+ */
+public class Main {
+    static Object waiter = new Object();
+    static volatile boolean didFinal = false;
+
+    static void createAndForget() {
+        Main main = new Main();
+    }
+
+    public static void main(String[] args) {
+        createAndForget();
+
+        System.gc();
+        System.runFinalization();
+
+        new Timer(true).schedule(new TimerTask() {
+                public void run() {
+                    System.out.println("Timed out, exiting");
+                    System.exit(1);
+                }
+            }, 30000);
+
+        while (!didFinal) {
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException ie) {
+                System.err.println(ie);
+            }
+        }
+
+        /* give it a chance to cause mayhem */
+        try {
+            Thread.sleep(750);
+        } catch (InterruptedException ie) {
+            System.err.println(ie);
+        }
+
+        System.out.println("done");
+    }
+
+    protected void finalize() throws Throwable {
+        System.out.println("In finalizer");
+
+        didFinal = true;
+
+        throw new InterruptedException("whee");
+    }
+}
diff --git a/tests/061-out-of-memory/expected.txt b/tests/061-out-of-memory/expected.txt
new file mode 100644
index 0000000..e1ed5da
--- /dev/null
+++ b/tests/061-out-of-memory/expected.txt
@@ -0,0 +1,6 @@
+tests beginning
+testOomeLarge beginning
+testOomeLarge succeeded
+testOomeSmall beginning
+testOomeSmall succeeded
+tests succeeded
diff --git a/tests/061-out-of-memory/info.txt b/tests/061-out-of-memory/info.txt
new file mode 100644
index 0000000..523f3a2
--- /dev/null
+++ b/tests/061-out-of-memory/info.txt
@@ -0,0 +1 @@
+Tests the various ways that an OutOfMemoryError can be constructed and thrown.
diff --git a/tests/061-out-of-memory/src/Main.java b/tests/061-out-of-memory/src/Main.java
new file mode 100644
index 0000000..fcf7136
--- /dev/null
+++ b/tests/061-out-of-memory/src/Main.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+import java.util.LinkedList;
+
+/**
+ * Exercise the construction and throwing of OutOfMemoryError.
+ */
+public class Main {
+    public static void main(String args[]) {
+        System.out.println("tests beginning");
+        testOomeLarge();
+        testOomeSmall();
+        System.out.println("tests succeeded");
+    }
+
+    private static void testOomeLarge() {
+        System.out.println("testOomeLarge beginning");
+
+        /* Just shy of the typical max heap size so that it will actually
+         * try to allocate it instead of short-circuiting.
+         */
+        final int SIXTEEN_MB = (16 * 1024 * 1024 - 32);
+
+        Boolean sawEx = false;
+        byte a[];
+
+        try {
+            a = new byte[SIXTEEN_MB];
+        } catch (OutOfMemoryError oom) {
+            //Log.i(TAG, "HeapTest/OomeLarge caught " + oom);
+            sawEx = true;
+        }
+
+        if (!sawEx) {
+            throw new RuntimeException("Test failed: " +
+                    "OutOfMemoryError not thrown");
+        }
+
+        System.out.println("testOomeLarge succeeded");
+    }
+
+    /* Do this in another method so that the GC has a chance of freeing the
+     * list afterwards.  Even if we null out list when we're done, the conservative
+     * GC may see a stale pointer to it in a register.
+     */
+    private static boolean testOomeSmallInternal() {
+        final int SIXTEEN_MB = (16 * 1024 * 1024);
+        final int LINK_SIZE = 6 * 4; // estimated size of a LinkedList's node
+
+        LinkedList<Object> list = new LinkedList<Object>();
+
+        /* Allocate progressively smaller objects to fill up the entire heap.
+         */
+        int objSize = 1 * 1024 * 1024;
+        while (objSize >= LINK_SIZE) {
+            boolean sawEx = false;
+            try {
+                for (int i = 0; i < SIXTEEN_MB / objSize; i++) {
+                    list.add((Object)new byte[objSize]);
+                }
+            } catch (OutOfMemoryError oom) {
+                sawEx = true;
+            }
+
+            if (!sawEx) {
+                return false;
+            }
+
+            objSize = (objSize * 4) / 5;
+        }
+
+        return true;
+    }
+
+    private static void testOomeSmall() {
+        System.out.println("testOomeSmall beginning");
+        if (!testOomeSmallInternal()) {
+            /* Can't reliably throw this from inside the internal function, because
+             * we may not be able to allocate the RuntimeException.
+             */
+            throw new RuntimeException("Test failed: " +
+                    "OutOfMemoryError not thrown while filling heap");
+        }
+        System.out.println("testOomeSmall succeeded");
+    }
+}
diff --git a/tests/062-character-encodings/expected.txt b/tests/062-character-encodings/expected.txt
new file mode 100644
index 0000000..b395a2a
--- /dev/null
+++ b/tests/062-character-encodings/expected.txt
@@ -0,0 +1 @@
+Missing: []
diff --git a/tests/062-character-encodings/info.txt b/tests/062-character-encodings/info.txt
new file mode 100644
index 0000000..bdf60bf
--- /dev/null
+++ b/tests/062-character-encodings/info.txt
@@ -0,0 +1 @@
+Test that the list of character encodings is what we expect.
diff --git a/tests/062-character-encodings/src/Main.java b/tests/062-character-encodings/src/Main.java
new file mode 100644
index 0000000..6e9f0cd
--- /dev/null
+++ b/tests/062-character-encodings/src/Main.java
@@ -0,0 +1,25 @@
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.Set;
+
+public class Main {
+    static public void main(String[] args) throws Exception {
+        // These charsets must be provided; anything else is optional.
+        List<String> standardCharsets = Arrays.asList("US-ASCII", "ISO-8859-1",
+                "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16");
+
+        SortedMap<String, Charset> all = Charset.availableCharsets();
+        Set<String> needed = new HashSet<String>(standardCharsets);
+        for (Map.Entry<String, Charset> e : all.entrySet()) {
+            String canonicalName = e.getKey();
+            needed.remove(canonicalName);
+        }
+        System.out.println("Missing: " + needed);
+    }
+}
diff --git a/tests/063-process-manager/expected.txt b/tests/063-process-manager/expected.txt
new file mode 100644
index 0000000..8360239
--- /dev/null
+++ b/tests/063-process-manager/expected.txt
@@ -0,0 +1,15 @@
+process manager: nonexistent
+
+spawning child #1
+spawning child
+process manager: RUNNABLE
+child died
+process manager: WAITING
+
+spawning child #2
+spawning child
+process manager: RUNNABLE
+child died
+process manager: WAITING
+
+done!
diff --git a/tests/063-process-manager/info.txt b/tests/063-process-manager/info.txt
new file mode 100644
index 0000000..e5907c4
--- /dev/null
+++ b/tests/063-process-manager/info.txt
@@ -0,0 +1,2 @@
+Test that spawning a child process and then reaping it (a) works and (b)
+doesn't cause the system to busy-wait.
diff --git a/tests/063-process-manager/src/Main.java b/tests/063-process-manager/src/Main.java
new file mode 100644
index 0000000..c94b8ad
--- /dev/null
+++ b/tests/063-process-manager/src/Main.java
@@ -0,0 +1,43 @@
+import java.util.Map;
+
+public class Main {
+    static public void main(String[] args) throws Exception {
+        checkManager();
+        for (int i = 1; i <= 2; i++) {
+            System.out.println("\nspawning child #" + i);
+            child();
+            Thread.sleep(2000);
+            checkManager();
+        }
+        System.out.println("\ndone!");
+    }
+
+    static private void child() throws Exception {
+        System.out.println("spawning child");
+        ProcessBuilder pb = new ProcessBuilder("/system/bin/sleep", "5");
+        Process proc = pb.start();
+        Thread.sleep(1000);
+        checkManager();
+        proc.waitFor();
+        System.out.println("child died");
+    }
+
+    static private void checkManager() {
+        Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
+        boolean found = false;
+
+        for (Map.Entry<Thread, StackTraceElement[]> entry :
+                 traces.entrySet()) {
+            Thread t = entry.getKey();
+            String name = t.getName();
+            if (name.equals("java.lang.ProcessManager")) {
+                System.out.println("process manager: " + t.getState());
+                found = true;
+            }
+        }
+
+        if (! found) {
+            System.out.println("process manager: nonexistent");
+        }
+    }
+}
diff --git a/tests/064-field-access/expected.txt b/tests/064-field-access/expected.txt
new file mode 100644
index 0000000..0af56ba
--- /dev/null
+++ b/tests/064-field-access/expected.txt
@@ -0,0 +1,2 @@
+good
+Got expected failure
diff --git a/tests/064-field-access/info.txt b/tests/064-field-access/info.txt
new file mode 100644
index 0000000..442baf2
--- /dev/null
+++ b/tests/064-field-access/info.txt
@@ -0,0 +1,10 @@
+The documentation lists exceptional conditions and the exceptions that
+should be thrown, but doesn't say which exception previals 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 can only throw one or the other.
+
+This exercises the various failure modes to ensure that behavior is
+equivalent, and not merely spec-compliant.
diff --git a/tests/064-field-access/src/GetNonexistent.java b/tests/064-field-access/src/GetNonexistent.java
new file mode 100644
index 0000000..faad686
--- /dev/null
+++ b/tests/064-field-access/src/GetNonexistent.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class GetNonexistent {
+    public static void main(String[] args) {
+        Object obj = Holder.mObject;
+    }
+}
diff --git a/tests/064-field-access/src/Holder.java b/tests/064-field-access/src/Holder.java
new file mode 100644
index 0000000..5e34024
--- /dev/null
+++ b/tests/064-field-access/src/Holder.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public class Holder {
+    public static Object mObject = new Object();
+}
diff --git a/tests/064-field-access/src/Main.java b/tests/064-field-access/src/Main.java
new file mode 100644
index 0000000..c068d23
--- /dev/null
+++ b/tests/064-field-access/src/Main.java
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+
+import other.OtherPackage;
+
+import java.lang.reflect.Field;
+
+/*
+ * Test field access through reflection.
+ */
+public class Main {
+    public static void main(String[] args) {
+        SubOther.main(null);
+
+        try {
+            GetNonexistent.main(null);
+            System.err.println("Not expected to succeed");
+        } catch (VerifyError fe) {
+            // dalvik
+            System.out.println("Got expected failure");
+        } catch (NoSuchFieldError nsfe) {
+            // reference
+            System.out.println("Got expected failure");
+        }
+    }
+
+    /*
+     * Get the field specified by "field" from "obj".
+     *
+     * "type" determines which "get" call is made, e.g. 'B' turns into
+     * field.getByte().
+     *
+     * The "expectedException" must match the class of the exception thrown,
+     * or be null if no exception was expected.
+     *
+     * On success, the boxed value retrieved is returned.
+     */
+    public Object getValue(Field field, Object obj, char type,
+            Class expectedException) {
+
+        Object result = null;
+        try {
+            switch (type) {
+            case 'Z':
+                result = new Boolean(field.getBoolean(obj));
+                break;
+            case 'B':
+                result = new Byte(field.getByte(obj));
+                break;
+            case 'S':
+                result = new Short(field.getShort(obj));
+                break;
+            case 'C':
+                result = new Character(field.getChar(obj));
+                break;
+            case 'I':
+                result = new Integer(field.getInt(obj));
+                break;
+            case 'J':
+                result = new Long(field.getLong(obj));
+                break;
+            case 'F':
+                result = new Float(field.getFloat(obj));
+                break;
+            case 'D':
+                result = new Double(field.getDouble(obj));
+                break;
+            case 'L':
+                result = field.get(obj);
+                break;
+            default:
+                throw new RuntimeException("bad type '" + type + "'");
+            }
+
+            /* success; expected? */
+            if (expectedException != null) {
+                Throwable th = new Throwable();
+                System.err.println("ERROR: call succeeded, was expecting "
+                    + expectedException);
+                th.printStackTrace();
+            }
+        } catch (Exception ex) {
+            if (expectedException == null) {
+                System.err.println("ERROR: call failed unexpectedly: "
+                    + ex.getClass());
+                ex.printStackTrace();
+            } else {
+                if (!expectedException.equals(ex.getClass())) {
+                    System.err.println("ERROR: incorrect exception: wanted "
+                        + expectedException.getName() + ", got "
+                        + ex.getClass());
+                    ex.printStackTrace();
+                }
+            }
+        }
+
+        return result;
+    }
+}
+
+/*
+ * Local class with some fields.
+ */
+class SamePackage {
+    public byte pubByteField;
+
+    protected byte protByteField;
+    protected Object protObjectField;
+
+    private float privFloatField;
+}
+
+/*
+ * This is a sub-class of OtherPackage, which should be allowed to access
+ * the various protected fields.
+ */
+class SubOther extends OtherPackage {
+
+    protected long protLongField = 0x1122334455667788L;
+
+    /*
+     * Perform the various tests.
+     *
+     * localInst.getValue() is performed using an instance of Main as the
+     * source of the reflection call.  otherInst.getValue() uses a subclass
+     * of OtherPackage as the source.
+     */
+    public static void main(String[] args) {
+        SubOther subOther = new SubOther();
+        subOther.doTests();
+    }
+
+    public void doTests() {
+        Class localClass = SamePackage.class;
+        Class otherClass = OtherPackage.class;
+        Field localPubByteField, localProtByteField, localProtObjectField,
+              localPrivFloatField;
+        Field otherPubCharField, otherProtShortField, otherProtObjectField,
+              otherPkgDoubleField;
+        Field subProtLongField;
+        Main localInst = new Main();
+        SamePackage samePkgInst = new SamePackage();
+        OtherPackage otherPkgInst = new OtherPackage();
+        Object plainObj = new Object();
+
+        /*
+         * Locate the various fields.
+         */
+        try {
+            localPubByteField = localClass.getDeclaredField("pubByteField");
+            localProtByteField = localClass.getDeclaredField("protByteField");
+            localProtObjectField = localClass.getDeclaredField("protObjectField");
+            localPrivFloatField = localClass.getDeclaredField("privFloatField");
+
+            otherPubCharField = otherClass.getDeclaredField("pubCharField");
+            otherProtShortField = otherClass.getDeclaredField("protShortField");
+            otherProtObjectField = otherClass.getDeclaredField("protObjectField");
+            otherPkgDoubleField = otherClass.getDeclaredField("pkgDoubleField");
+
+            subProtLongField = getClass().getDeclaredField("protLongField");
+        } catch (NoSuchFieldException nsfe) {
+            throw new RuntimeException(nsfe);
+        }
+
+        /*
+         * Get a public field from a class in the same package.
+         */
+        localInst.getValue(localPubByteField, samePkgInst, 'B', null);
+
+        /*
+         * Get a protected field from a class in the same package.
+         */
+        this.getValue(localProtByteField, samePkgInst, 'B', null);
+
+        /*
+         * Get a private field from a class in the same package.
+         */
+        this.getValue(localPrivFloatField, samePkgInst, 'F',
+            IllegalAccessException.class);
+
+        /*
+         * Get a protected field from otherInst's superclass.
+         *
+         * We can get at "this.protShortField" but not
+         * "otherPkgInst.protShortField" because we can only access
+         * protected fields in instances of our class -- being a subclass
+         * of OtherPackage does not allow us to modify protected fields in
+         * all other subclasses of OtherPackage.
+         */
+        this.getValue(otherProtShortField, this, 'S',
+            null);
+        this.getValue(otherProtShortField, otherPkgInst, 'S',
+            IllegalAccessException.class);
+        this.getValue(otherPkgDoubleField, otherPkgInst, 'D',
+            IllegalAccessException.class);
+
+        /*
+         * Null object.  Different exceptions based on which package
+         * we would be trying to access and whether or not our object
+         * has the correct type.
+         */
+        localInst.getValue(localPubByteField, null, 'B',
+            NullPointerException.class);
+
+        this.getValue(subProtLongField, null, 'J',
+            NullPointerException.class);
+
+        this.getValue(localPrivFloatField, null, 'F',
+            IllegalAccessException.class);
+
+        localInst.getValue(otherProtShortField, null, 'S',
+            IllegalAccessException.class);
+        this.getValue(otherProtShortField, null, 'S',
+            IllegalAccessException.class);
+        this.getValue(otherPkgDoubleField, null, 'D',
+            IllegalAccessException.class);
+
+        localInst.getValue(otherProtShortField, null, 'Z',
+            IllegalAccessException.class);
+        /* -- Dalvik VM currently throws NPE
+        this.getValue(subProtLongField, null, 'Z',
+            IllegalArgumentException.class);
+        */
+
+        /*
+         * Valid object, wrong field type.
+         */
+        this.getValue(subProtLongField, this, 'J',
+            null);
+        this.getValue(localProtByteField, samePkgInst, 'Z',
+            IllegalArgumentException.class);
+        this.getValue(subProtLongField, this, 'Z',
+            IllegalArgumentException.class);
+        this.getValue(localPrivFloatField, this, 'Z',
+            IllegalAccessException.class);
+        this.getValue(localPrivFloatField, this, 'Z',
+            IllegalAccessException.class);
+        localInst.getValue(otherProtShortField, otherPkgInst, 'Z',
+            IllegalAccessException.class);
+        this.getValue(otherProtShortField, otherPkgInst, 'Z',
+            IllegalAccessException.class);
+
+        /*
+         * Wrong object.
+         */
+        this.getValue(subProtLongField, plainObj, 'J',
+            IllegalArgumentException.class);
+
+        /* wrong object + private field */
+        this.getValue(localPrivFloatField, plainObj, 'F',
+            IllegalAccessException.class);
+
+        /* wrong object + wrong field type */
+        this.getValue(subProtLongField, plainObj, 'Z',
+            IllegalArgumentException.class);
+
+        /* wrong object + invalid access */
+        localInst.getValue(otherProtShortField, plainObj, 'S',
+            IllegalAccessException.class);
+        this.getValue(otherProtShortField, plainObj, 'S',
+            IllegalAccessException.class);
+
+        System.out.println("good");
+    }
+
+    /*
+     * [this is a clone of Main.getValue() -- the class issuing the
+     * reflection call is significant]
+     */
+    public Object getValue(Field field, Object obj, char type,
+            Class expectedException) {
+
+        Object result = null;
+        try {
+            switch (type) {
+            case 'Z':
+                result = new Boolean(field.getBoolean(obj));
+                break;
+            case 'B':
+                result = new Byte(field.getByte(obj));
+                break;
+            case 'S':
+                result = new Short(field.getShort(obj));
+                break;
+            case 'C':
+                result = new Character(field.getChar(obj));
+                break;
+            case 'I':
+                result = new Integer(field.getInt(obj));
+                break;
+            case 'J':
+                result = new Long(field.getLong(obj));
+                break;
+            case 'F':
+                result = new Float(field.getFloat(obj));
+                break;
+            case 'D':
+                result = new Double(field.getDouble(obj));
+                break;
+            case 'L':
+                result = field.get(obj);
+                break;
+            default:
+                throw new RuntimeException("bad type '" + type + "'");
+            }
+
+            /* success; expected? */
+            if (expectedException != null) {
+                Throwable th = new Throwable();
+                System.err.println("ERROR: call succeeded, was expecting "
+                    + expectedException);
+                th.printStackTrace();
+            }
+        } catch (Exception ex) {
+            if (expectedException == null) {
+                System.err.println("ERROR: call failed unexpectedly: "
+                    + ex.getClass());
+                ex.printStackTrace();
+            } else {
+                if (!expectedException.equals(ex.getClass())) {
+                    System.err.println("ERROR: incorrect exception: wanted "
+                        + expectedException.getName() + ", got "
+                        + ex.getClass());
+                    ex.printStackTrace();
+                }
+            }
+        }
+
+        return result;
+    }
+
+}
diff --git a/tests/064-field-access/src/other/OtherPackage.java b/tests/064-field-access/src/other/OtherPackage.java
new file mode 100644
index 0000000..a595db5
--- /dev/null
+++ b/tests/064-field-access/src/other/OtherPackage.java
@@ -0,0 +1,15 @@
+// Copyright 2008 The Android Open Source Project
+
+package other;
+
+/*
+ * Declare a few fields to reflect upon.
+ */
+public class OtherPackage {
+    public char pubCharField = 0x8765;
+
+    protected short protShortField = 0x1234;
+    protected Object protObjectField = "blah";
+
+    double pkgDoubleField = 3.141592654;
+}
diff --git a/tests/064-field-access/src2/Holder.java b/tests/064-field-access/src2/Holder.java
new file mode 100644
index 0000000..28224d7
--- /dev/null
+++ b/tests/064-field-access/src2/Holder.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public class Holder {
+    //public static Object mObject = new Object();
+}
diff --git a/tests/065-mismatched-implements/expected.txt b/tests/065-mismatched-implements/expected.txt
new file mode 100644
index 0000000..09c0596
--- /dev/null
+++ b/tests/065-mismatched-implements/expected.txt
@@ -0,0 +1 @@
+Got expected ICCE
diff --git a/tests/065-mismatched-implements/info.txt b/tests/065-mismatched-implements/info.txt
new file mode 100644
index 0000000..74c3ff3
--- /dev/null
+++ b/tests/065-mismatched-implements/info.txt
@@ -0,0 +1,2 @@
+This tests what happens when class A implements interface B, but somebody
+turns B into an abstract class without rebuilding A.
diff --git a/tests/065-mismatched-implements/src/Base.java b/tests/065-mismatched-implements/src/Base.java
new file mode 100644
index 0000000..8623ad7
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Base.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Base implements Defs {
+    public void func() {
+        System.out.println("whee");
+    }
+};
diff --git a/tests/065-mismatched-implements/src/Defs.java b/tests/065-mismatched-implements/src/Defs.java
new file mode 100644
index 0000000..bab92d8
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Defs.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+public interface Defs {
+    public void func();
+
+    // func2 not defined
+}
diff --git a/tests/065-mismatched-implements/src/Indirect.java b/tests/065-mismatched-implements/src/Indirect.java
new file mode 100644
index 0000000..023e409
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Indirect.java
@@ -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.
+ */
+
+/**
+ * Error indirection class.
+ *
+ * Some VMs will load this class and fail on the "new" call, others will
+ * refuse to load this class at all.
+ */
+public class Indirect {
+    public static void main() {
+        Base base = new Base();
+    }
+}
diff --git a/tests/065-mismatched-implements/src/Main.java b/tests/065-mismatched-implements/src/Main.java
new file mode 100644
index 0000000..5975b99
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Main.java
@@ -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.
+ */
+
+/*
+ * Test field access through reflection.
+ */
+public class Main {
+    public static void main(String[] args) {
+        try {
+            Indirect.main();
+            System.err.println("Succeeded unexpectedly");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected ICCE");
+        }
+    }
+}
diff --git a/tests/065-mismatched-implements/src2/Defs.java b/tests/065-mismatched-implements/src2/Defs.java
new file mode 100644
index 0000000..e7eb8a1
--- /dev/null
+++ b/tests/065-mismatched-implements/src2/Defs.java
@@ -0,0 +1,11 @@
+// Copyright 2008 The Android Open Source Project
+
+public abstract class Defs {
+    public void func() {
+        System.out.println("yo");
+    }
+
+    public void func2() {
+        System.out.println("yo yo");
+    }
+}
diff --git a/tests/066-mismatched-super/expected.txt b/tests/066-mismatched-super/expected.txt
new file mode 100644
index 0000000..09c0596
--- /dev/null
+++ b/tests/066-mismatched-super/expected.txt
@@ -0,0 +1 @@
+Got expected ICCE
diff --git a/tests/066-mismatched-super/info.txt b/tests/066-mismatched-super/info.txt
new file mode 100644
index 0000000..7865ffc
--- /dev/null
+++ b/tests/066-mismatched-super/info.txt
@@ -0,0 +1,2 @@
+This tests what happens when class A extends abstract class B, but somebody
+turns B into an interface without rebuilding A.
diff --git a/tests/066-mismatched-super/src/Base.java b/tests/066-mismatched-super/src/Base.java
new file mode 100644
index 0000000..6180c8b
--- /dev/null
+++ b/tests/066-mismatched-super/src/Base.java
@@ -0,0 +1,5 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Base extends Defs {
+    // no need to implement func(), provided by abstract class
+};
diff --git a/tests/066-mismatched-super/src/Defs.java b/tests/066-mismatched-super/src/Defs.java
new file mode 100644
index 0000000..e7eb8a1
--- /dev/null
+++ b/tests/066-mismatched-super/src/Defs.java
@@ -0,0 +1,11 @@
+// Copyright 2008 The Android Open Source Project
+
+public abstract class Defs {
+    public void func() {
+        System.out.println("yo");
+    }
+
+    public void func2() {
+        System.out.println("yo yo");
+    }
+}
diff --git a/tests/066-mismatched-super/src/Indirect.java b/tests/066-mismatched-super/src/Indirect.java
new file mode 100644
index 0000000..023e409
--- /dev/null
+++ b/tests/066-mismatched-super/src/Indirect.java
@@ -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.
+ */
+
+/**
+ * Error indirection class.
+ *
+ * Some VMs will load this class and fail on the "new" call, others will
+ * refuse to load this class at all.
+ */
+public class Indirect {
+    public static void main() {
+        Base base = new Base();
+    }
+}
diff --git a/tests/066-mismatched-super/src/Main.java b/tests/066-mismatched-super/src/Main.java
new file mode 100644
index 0000000..5975b99
--- /dev/null
+++ b/tests/066-mismatched-super/src/Main.java
@@ -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.
+ */
+
+/*
+ * Test field access through reflection.
+ */
+public class Main {
+    public static void main(String[] args) {
+        try {
+            Indirect.main();
+            System.err.println("Succeeded unexpectedly");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected ICCE");
+        }
+    }
+}
diff --git a/tests/066-mismatched-super/src2/Defs.java b/tests/066-mismatched-super/src2/Defs.java
new file mode 100644
index 0000000..bab92d8
--- /dev/null
+++ b/tests/066-mismatched-super/src2/Defs.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+public interface Defs {
+    public void func();
+
+    // func2 not defined
+}
diff --git a/tests/067-preemptive-unpark/expected.txt b/tests/067-preemptive-unpark/expected.txt
new file mode 100644
index 0000000..12bfee0
--- /dev/null
+++ b/tests/067-preemptive-unpark/expected.txt
@@ -0,0 +1,5 @@
+Test starting
+GC'ing
+Asking thread to park
+park() returned quickly
+Test succeeded!
diff --git a/tests/067-preemptive-unpark/info.txt b/tests/067-preemptive-unpark/info.txt
new file mode 100644
index 0000000..0bc0c61
--- /dev/null
+++ b/tests/067-preemptive-unpark/info.txt
@@ -0,0 +1 @@
+Test that Unsafe.unpark() operates as expected, in particular across a gc.
diff --git a/tests/067-preemptive-unpark/src/Main.java b/tests/067-preemptive-unpark/src/Main.java
new file mode 100644
index 0000000..a16219e
--- /dev/null
+++ b/tests/067-preemptive-unpark/src/Main.java
@@ -0,0 +1,107 @@
+import sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+
+public class Main {
+    private static Unsafe UNSAFE;
+
+    public static void main(String[] args) throws Exception {
+        setUp();
+
+        ParkTester test = new ParkTester();
+
+        System.out.println("Test starting");
+
+        test.start();
+        UNSAFE.unpark(test);
+        clearStack(10);
+
+        System.out.println("GC'ing");
+        System.gc();
+        System.gc();
+
+        System.out.println("Asking thread to park");
+        test.parkNow = true;
+
+        try {
+            Thread.sleep(1500);
+        } catch (InterruptedException ex) {
+            // Ignore it.
+        }
+
+        if (test.success) {
+            System.out.println("Test succeeded!");
+        } else {
+            System.out.println("Test failed.");
+        }
+    }
+
+    /**
+     * Set up {@link #UNSAFE}.
+     */
+    public static void setUp() {
+        /*
+         * Subvert the access check to get the unique Unsafe instance.
+         * We can do this because there's no security manager
+         * installed when running the test.
+         */
+        try {
+            Field field = Unsafe.class.getDeclaredField("THE_ONE");
+            field.setAccessible(true);
+
+            UNSAFE = (Unsafe) field.get(null);
+        } catch (NoSuchFieldException ex) {
+            throw new RuntimeException(ex);
+        } catch (IllegalAccessException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    /**
+     * Scribbles on the stack to help ensure we don't have a fake
+     * pointer that would keep would-be garbage alive.
+     */
+    private static void clearStack(int depth) {
+        int a = 0;
+        int b = 0;
+        int c = 0;
+        int d = 0;
+        int e = 0;
+        int f = 0;
+        int g = 0;
+        int h = 0;
+        int i = 0;
+        int j = 0;
+
+        if (depth > 0) {
+            clearStack(depth - 1);
+        }
+    }
+
+    private static class ParkTester extends Thread {
+        public volatile boolean parkNow = false;
+        public volatile boolean success = false;
+
+        public void run() {
+            while (!parkNow) {
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException ex) {
+                    // Ignore it.
+                }
+            }
+
+            long start = System.currentTimeMillis();
+            UNSAFE.park(false, 500 * 1000000); // 500 msec
+            long elapsed = System.currentTimeMillis() - start;
+
+            if (elapsed > 200) {
+                System.out.println("park()ed for " + elapsed + " msec");
+                success = false;
+            } else {
+                System.out.println("park() returned quickly");
+                success = true;
+            }
+        }
+    }
+}
diff --git a/tests/068-classloader/expected.txt b/tests/068-classloader/expected.txt
new file mode 100644
index 0000000..bf131ee
--- /dev/null
+++ b/tests/068-classloader/expected.txt
@@ -0,0 +1,13 @@
+base: class DoubledImplement
+base2: class DoubledImplement2
+Got expected access exception #1
+Got expected CNFE/IAE #2
+Got expected CNFE/IAE #3
+Got expected LinkageError on DE
+Got DEO result DoubledExtendOkay 1
+Got LinkageError on GD
+Got LinkageError on TA
+Ctor: doubled implement, type 1
+DoubledImplement one
+Got LinkageError on DI (early)
+Got LinkageError on IDI (early)
diff --git a/tests/068-classloader/info.txt b/tests/068-classloader/info.txt
new file mode 100644
index 0000000..421e52a
--- /dev/null
+++ b/tests/068-classloader/info.txt
@@ -0,0 +1,8 @@
+Class loaders allow code to "redefine" a given class, e.g. it's possible to
+have multiple classes called "com.android.Blah" loaded simultaneously.  The
+classes are distinct and must be treated as such.  This test exercises
+some situations in which a VM that only checks the UTF-8 signatures could
+mix things up.
+
+This also tests a couple of situations in which an IllegalAccessException
+is expected.
diff --git a/tests/068-classloader/src-ex/AbstractGet.java b/tests/068-classloader/src-ex/AbstractGet.java
new file mode 100644
index 0000000..db13b32
--- /dev/null
+++ b/tests/068-classloader/src-ex/AbstractGet.java
@@ -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.
+ */
+
+/**
+ * Verify that we don't reject this with a LinkageError.
+ */
+public class AbstractGet extends AbstractBase {
+    public DoubledExtendOkay getExtended() {
+        return new DoubledExtendOkay();
+    }
+}
+
+/**
+ * Abstract class, does not declare getAbstract.  This cause the VM to
+ * generate a "miranda" method.
+ */
+abstract class AbstractBase extends BaseOkay {
+    public abstract DoubledExtendOkay getExtended();
+}
diff --git a/tests/068-classloader/src-ex/DoubledExtend.java b/tests/068-classloader/src-ex/DoubledExtend.java
new file mode 100644
index 0000000..6ad2708
--- /dev/null
+++ b/tests/068-classloader/src-ex/DoubledExtend.java
@@ -0,0 +1,20 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Doubled sub-class, form #2.
+ */
+public class DoubledExtend extends Base {
+    public DoubledExtend() {
+        //System.out.println("Ctor: doubled extend, type 2");
+    }
+
+    @Override
+    public DoubledExtend getExtended() {
+        //System.out.println("getExtended 2");
+        return new DoubledExtend();
+    }
+
+    public String getStr() {
+        return "DoubledExtend 2";
+    }
+}
diff --git a/tests/068-classloader/src-ex/DoubledExtendOkay.java b/tests/068-classloader/src-ex/DoubledExtendOkay.java
new file mode 100644
index 0000000..9674875
--- /dev/null
+++ b/tests/068-classloader/src-ex/DoubledExtendOkay.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+/**
+ * "Okay" doubled sub-class, form #2.
+ */
+public class DoubledExtendOkay extends BaseOkay {
+    public DoubledExtendOkay() {
+        //System.out.println("Ctor: doubled extend okay, type 2");
+    }
+
+    /*
+    @Override
+    public DoubledExtendOkay getExtended() {
+        //System.out.println("getExtended 2");
+        return new DoubledExtendOkay();
+    }
+    */
+
+    public String getStr() {
+        return "DoubledExtendOkay 2";
+    }
+}
diff --git a/tests/068-classloader/src-ex/DoubledImplement.java b/tests/068-classloader/src-ex/DoubledImplement.java
new file mode 100644
index 0000000..5c44fc3
--- /dev/null
+++ b/tests/068-classloader/src-ex/DoubledImplement.java
@@ -0,0 +1,18 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Doubled sub-class, form #2.
+ */
+public class DoubledImplement implements ICommon {
+    public DoubledImplement() {
+        System.out.println("Ctor: doubled implement, type 2");
+    }
+
+    public DoubledImplement getDoubledInstance() {
+        return new DoubledImplement();
+    }
+
+    public void two() {
+        System.out.println("DoubledImplement two");
+    }
+}
diff --git a/tests/068-classloader/src-ex/DoubledImplement2.java b/tests/068-classloader/src-ex/DoubledImplement2.java
new file mode 100644
index 0000000..24ecb65
--- /dev/null
+++ b/tests/068-classloader/src-ex/DoubledImplement2.java
@@ -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.
+ */
+
+/**
+ * Another doubled sub-class, form #2.
+ */
+public class DoubledImplement2 implements ICommon2 {
+    public DoubledImplement2() {
+        System.out.println("Ctor: doubled implement, type 2");
+    }
+
+    public DoubledImplement2 getDoubledInstance2() {
+        return new DoubledImplement2();
+    }
+
+    public void two() {
+        System.out.println("DoubledImplement2 two");
+    }
+}
diff --git a/tests/068-classloader/src-ex/GetDoubled.java b/tests/068-classloader/src-ex/GetDoubled.java
new file mode 100644
index 0000000..28ada1e
--- /dev/null
+++ b/tests/068-classloader/src-ex/GetDoubled.java
@@ -0,0 +1,26 @@
+/*
+ * 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 interface we implement was declared in a different class loader,
+ * which means the DoubledExtend we return is not the one it was declared
+ * to return.
+ */
+public class GetDoubled implements IGetDoubled {
+    public DoubledExtendOkay getDoubled() {
+        return new DoubledExtendOkay();
+    }
+}
diff --git a/tests/068-classloader/src-ex/IfaceImpl.java b/tests/068-classloader/src-ex/IfaceImpl.java
new file mode 100644
index 0000000..7e9c27d
--- /dev/null
+++ b/tests/068-classloader/src-ex/IfaceImpl.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class IfaceImpl implements IfaceSub {
+    public DoubledImplement2 getDoubledInstance2() {
+        return new DoubledImplement2();
+    }
+}
diff --git a/tests/068-classloader/src-ex/IfaceSub.java b/tests/068-classloader/src-ex/IfaceSub.java
new file mode 100644
index 0000000..7e512e7
--- /dev/null
+++ b/tests/068-classloader/src-ex/IfaceSub.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public interface IfaceSub extends IfaceSuper {
+    public DoubledImplement2 getDoubledInstance2();
+}
diff --git a/tests/068-classloader/src-ex/Inaccessible1.java b/tests/068-classloader/src-ex/Inaccessible1.java
new file mode 100644
index 0000000..415a8a1
--- /dev/null
+++ b/tests/068-classloader/src-ex/Inaccessible1.java
@@ -0,0 +1,11 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Non-public class, inaccessible from Main.  Note the constructor is
+ * public.
+ */
+class Inaccessible1 extends SimpleBase {
+    public Inaccessible1() {
+        System.out.println("--- inaccessible1");
+    }
+}
diff --git a/tests/068-classloader/src-ex/Inaccessible2.java b/tests/068-classloader/src-ex/Inaccessible2.java
new file mode 100644
index 0000000..dc20c21
--- /dev/null
+++ b/tests/068-classloader/src-ex/Inaccessible2.java
@@ -0,0 +1,10 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Public class that can't access its base.
+ */
+public class Inaccessible2 extends InaccessibleBase {
+    public Inaccessible2() {
+        System.out.println("--- inaccessible2");
+    }
+}
diff --git a/tests/068-classloader/src-ex/Inaccessible3.java b/tests/068-classloader/src-ex/Inaccessible3.java
new file mode 100644
index 0000000..771d0f7
--- /dev/null
+++ b/tests/068-classloader/src-ex/Inaccessible3.java
@@ -0,0 +1,10 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Public class that can't access its interface.
+ */
+public class Inaccessible3 implements InaccessibleInterface {
+    public Inaccessible3() {
+        System.out.println("--- inaccessible3");
+    }
+}
diff --git a/tests/068-classloader/src/Base.java b/tests/068-classloader/src/Base.java
new file mode 100644
index 0000000..b297a8a
--- /dev/null
+++ b/tests/068-classloader/src/Base.java
@@ -0,0 +1,16 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Common base class.
+ */
+public class Base {
+    public Base() {}
+
+    public DoubledExtend getExtended() {
+        return new DoubledExtend();
+    }
+
+    public static String doStuff(DoubledExtend dt) {
+        return dt.getStr();
+    }
+}
diff --git a/tests/068-classloader/src/BaseOkay.java b/tests/068-classloader/src/BaseOkay.java
new file mode 100644
index 0000000..48b7796
--- /dev/null
+++ b/tests/068-classloader/src/BaseOkay.java
@@ -0,0 +1,38 @@
+/*
+ * 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 base class.
+ */
+public class BaseOkay implements IDoubledExtendOkay {
+    public BaseOkay() {}
+
+    public DoubledExtendOkay getExtended() {
+        return new DoubledExtendOkay();
+    }
+
+    public static String doStuff(DoubledExtendOkay dt) {
+        return dt.getStr();
+    }
+}
+
+/**
+ * Interface that declares the not-overridden method.  This exists to ensure
+ * that the existence of an interface doesn't trip the check.
+ */
+interface IDoubledExtendOkay {
+    public DoubledExtendOkay getExtended();
+}
diff --git a/tests/068-classloader/src/DoubledExtend.java b/tests/068-classloader/src/DoubledExtend.java
new file mode 100644
index 0000000..5f8ebc2
--- /dev/null
+++ b/tests/068-classloader/src/DoubledExtend.java
@@ -0,0 +1,20 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Doubled sub-class, form #1.
+ */
+public class DoubledExtend extends Base {
+    public DoubledExtend() {
+        //System.out.println("Ctor: doubled extend, type 1");
+    }
+
+    @Override
+    public DoubledExtend getExtended() {
+        System.out.println("getExtended 1");
+        return new DoubledExtend();
+    }
+
+    public String getStr() {
+        return "DoubledExtend 1";
+    }
+}
diff --git a/tests/068-classloader/src/DoubledExtendOkay.java b/tests/068-classloader/src/DoubledExtendOkay.java
new file mode 100644
index 0000000..e226e5f
--- /dev/null
+++ b/tests/068-classloader/src/DoubledExtendOkay.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+/**
+ * "Okay" doubled sub-class, form #1.
+ */
+public class DoubledExtendOkay extends BaseOkay {
+    public DoubledExtendOkay() {
+        //System.out.println("Ctor: doubled extend okay, type 1");
+    }
+
+    /*
+    @Override
+    public DoubledExtendOkay getExtended() {
+        System.out.println("getExtended 1");
+        return new DoubledExtendOkay();
+    }
+    */
+
+    public String getStr() {
+        return "DoubledExtendOkay 1";
+    }
+}
diff --git a/tests/068-classloader/src/DoubledImplement.java b/tests/068-classloader/src/DoubledImplement.java
new file mode 100644
index 0000000..64ec5e2
--- /dev/null
+++ b/tests/068-classloader/src/DoubledImplement.java
@@ -0,0 +1,18 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Doubled sub-class, form #1.
+ */
+class DoubledImplement implements ICommon {
+    public DoubledImplement() {
+        System.out.println("Ctor: doubled implement, type 1");
+    }
+
+    public DoubledImplement getDoubledInstance() {
+        return new DoubledImplement();
+    }
+
+    public void one() {
+        System.out.println("DoubledImplement one");
+    }
+}
diff --git a/tests/068-classloader/src/DoubledImplement2.java b/tests/068-classloader/src/DoubledImplement2.java
new file mode 100644
index 0000000..12c036c
--- /dev/null
+++ b/tests/068-classloader/src/DoubledImplement2.java
@@ -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.
+ */
+
+/**
+ * Another doubled sub-class, form #1.
+ */
+public class DoubledImplement2 implements ICommon2 {
+    public DoubledImplement2() {
+        System.out.println("Ctor: doubled implement, type 1");
+    }
+
+    public DoubledImplement2 getDoubledInstance2() {
+        return new DoubledImplement2();
+    }
+
+    public void one() {
+        System.out.println("DoubledImplement2 one");
+    }
+}
diff --git a/tests/068-classloader/src/FancyLoader.java b/tests/068-classloader/src/FancyLoader.java
new file mode 100644
index 0000000..173b08f
--- /dev/null
+++ b/tests/068-classloader/src/FancyLoader.java
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A class loader with atypical behavior: we try to load a private
+ * class implementation before asking the system or boot loader.  This
+ * is used to create multiple classes with identical names in a single VM.
+ *
+ * If DexFile is available, we use that; if not, we assume we're not in
+ * Dalvik and instantiate the class with defineClass().
+ *
+ * The location of the DEX files and class data is dependent upon the
+ * test framework.
+ */
+public class FancyLoader extends ClassLoader {
+    /* this is where the "alternate" .class files live */
+    static final String CLASS_PATH = "classes-ex/";
+
+    /* this is the "alternate" DEX/Jar file */
+    static final String DEX_FILE = "test-ex.jar";
+
+    /* on Dalvik, this is a DexFile; otherwise, it's null */
+    private Class mDexClass;
+
+    private Object mDexFile;
+
+    /**
+     * Construct FancyLoader, grabbing a reference to the DexFile class
+     * if we're running under Dalvik.
+     */
+    public FancyLoader(ClassLoader parent) {
+        super(parent);
+
+        try {
+            mDexClass = parent.loadClass("dalvik/system/DexFile");
+        } catch (ClassNotFoundException cnfe) {
+            // ignore -- not running Dalvik
+        }
+    }
+
+    /**
+     * Finds the class with the specified binary name.
+     *
+     * We search for a file in CLASS_PATH or pull an entry from DEX_FILE.
+     * If we don't find a match, we throw an exception.
+     */
+    protected Class<?> findClass(String name) throws ClassNotFoundException
+    {
+        if (mDexClass != null) {
+            return findClassDalvik(name);
+        } else {
+            return findClassNonDalvik(name);
+        }
+    }
+
+    /**
+     * Finds the class with the specified binary name, from a DEX file.
+     */
+    private Class<?> findClassDalvik(String name)
+        throws ClassNotFoundException {
+
+        if (mDexFile == null) {
+            synchronized (FancyLoader.class) {
+                Constructor ctor;
+                /*
+                 * Construct a DexFile object through reflection.
+                 */
+                try {
+                    ctor = mDexClass.getConstructor(new Class[] {String.class});
+                } catch (NoSuchMethodException nsme) {
+                    throw new ClassNotFoundException("getConstructor failed",
+                        nsme);
+                }
+
+                try {
+                    mDexFile = ctor.newInstance(DEX_FILE);
+                } catch (InstantiationException ie) {
+                    throw new ClassNotFoundException("newInstance failed", ie);
+                } catch (IllegalAccessException iae) {
+                    throw new ClassNotFoundException("newInstance failed", iae);
+                } catch (InvocationTargetException ite) {
+                    throw new ClassNotFoundException("newInstance failed", ite);
+                }
+            }
+        }
+
+        /*
+         * Call DexFile.loadClass(String, ClassLoader).
+         */
+        Method meth;
+
+        try {
+            meth = mDexClass.getMethod("loadClass",
+                    new Class[] { String.class, ClassLoader.class });
+        } catch (NoSuchMethodException nsme) {
+            throw new ClassNotFoundException("getMethod failed", nsme);
+        }
+
+        try {
+            meth.invoke(mDexFile, name, this);
+        } catch (IllegalAccessException iae) {
+            throw new ClassNotFoundException("loadClass failed", iae);
+        } catch (InvocationTargetException ite) {
+            throw new ClassNotFoundException("loadClass failed",
+                ite.getCause());
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds the class with the specified binary name, from .class files.
+     */
+    private Class<?> findClassNonDalvik(String name)
+        throws ClassNotFoundException {
+
+        String pathName = CLASS_PATH + name + ".class";
+        //System.out.println("--- Fancy: looking for " + pathName);
+
+        File path = new File(pathName);
+        RandomAccessFile raf;
+
+        try {
+            raf = new RandomAccessFile(path, "r");
+        } catch (FileNotFoundException fnfe) {
+            throw new ClassNotFoundException("Not found: " + pathName);
+        }
+
+        /* read the entire file in */
+        byte[] fileData;
+        try {
+            fileData = new byte[(int) raf.length()];
+            raf.readFully(fileData);
+        } catch (IOException ioe) {
+            throw new ClassNotFoundException("Read error: " + pathName);
+        } finally {
+            try {
+                raf.close();
+            } catch (IOException ioe) {
+                // drop
+            }
+        }
+
+        /* create the class */
+        //System.out.println("--- Fancy: defining " + name);
+        try {
+            return defineClass(name, fileData, 0, fileData.length);
+        } catch (Throwable th) {
+            throw new ClassNotFoundException("defineClass failed", th);
+        }
+    }
+
+    /**
+     * Load a class.
+     *
+     * Normally a class loader wouldn't override this, but we want our
+     * version of the class to take precedence over an already-loaded
+     * version.
+     *
+     * We still want the system classes (e.g. java.lang.Object) from the
+     * bootstrap class loader.
+     */
+    protected Class<?> loadClass(String name, boolean resolve)
+        throws ClassNotFoundException
+    {
+        Class res;
+
+        /*
+         * 1. Invoke findLoadedClass(String) to check if the class has
+         * already been loaded.
+         *
+         * This doesn't change.
+         */
+        res = findLoadedClass(name);
+        if (res != null) {
+            System.out.println("FancyLoader.loadClass: "
+                + name + " already loaded");
+            if (resolve)
+                resolveClass(res);
+            return res;
+        }
+
+        /*
+         * 3. Invoke the findClass(String) method to find the class.
+         */
+        try {
+            res = findClass(name);
+            if (resolve)
+                resolveClass(res);
+        }
+        catch (ClassNotFoundException e) {
+            // we couldn't find it, so eat the exception and keep going
+        }
+
+        /*
+         * 2. Invoke the loadClass method on the parent class loader.  If
+         * the parent loader is null the class loader built-in to the
+         * virtual machine is used, instead.
+         *
+         * (Since we're not in java.lang, we can't actually invoke the
+         * parent's loadClass() method, but we passed our parent to the
+         * super-class which can take care of it for us.)
+         */
+        res = super.loadClass(name, resolve);   // returns class or throws
+        return res;
+    }
+}
diff --git a/tests/068-classloader/src/ICommon.java b/tests/068-classloader/src/ICommon.java
new file mode 100644
index 0000000..35a98cc
--- /dev/null
+++ b/tests/068-classloader/src/ICommon.java
@@ -0,0 +1,8 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Common interface.
+ */
+public interface ICommon {
+    public DoubledImplement getDoubledInstance();
+}
diff --git a/tests/068-classloader/src/ICommon2.java b/tests/068-classloader/src/ICommon2.java
new file mode 100644
index 0000000..6d81afc
--- /dev/null
+++ b/tests/068-classloader/src/ICommon2.java
@@ -0,0 +1,22 @@
+/*
+ * 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 interface.
+ */
+public interface ICommon2 {
+    public DoubledImplement2 getDoubledInstance2();
+}
diff --git a/tests/068-classloader/src/IGetDoubled.java b/tests/068-classloader/src/IGetDoubled.java
new file mode 100644
index 0000000..08cd1ce
--- /dev/null
+++ b/tests/068-classloader/src/IGetDoubled.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * Interface, loaded from one loader, used from another.
+ */
+public interface IGetDoubled {
+    public DoubledExtendOkay getDoubled();
+}
diff --git a/tests/068-classloader/src/IfaceSuper.java b/tests/068-classloader/src/IfaceSuper.java
new file mode 100644
index 0000000..36d278c
--- /dev/null
+++ b/tests/068-classloader/src/IfaceSuper.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public interface IfaceSuper {
+    public DoubledImplement2 getDoubledInstance2();
+}
diff --git a/tests/068-classloader/src/InaccessibleBase.java b/tests/068-classloader/src/InaccessibleBase.java
new file mode 100644
index 0000000..83af665
--- /dev/null
+++ b/tests/068-classloader/src/InaccessibleBase.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Non-public base class, inaccessible from alternate class loader.
+ */
+class InaccessibleBase {
+}
diff --git a/tests/068-classloader/src/InaccessibleInterface.java b/tests/068-classloader/src/InaccessibleInterface.java
new file mode 100644
index 0000000..7f52b80
--- /dev/null
+++ b/tests/068-classloader/src/InaccessibleInterface.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Non-public interface class, inaccessible from alternate class loader.
+ */
+interface InaccessibleInterface {
+}
diff --git a/tests/068-classloader/src/Main.java b/tests/068-classloader/src/Main.java
new file mode 100644
index 0000000..1bc7b04
--- /dev/null
+++ b/tests/068-classloader/src/Main.java
@@ -0,0 +1,425 @@
+/*
+ * 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 test.
+ */
+public class Main {
+    /**
+     * Main entry point.
+     */
+    public static void main(String[] args) {
+        FancyLoader loader;
+
+        loader = new FancyLoader(ClassLoader.getSystemClassLoader());
+        //System.out.println("SYSTEM: " + ClassLoader.getSystemClassLoader());
+        //System.out.println("ALTERN: " + loader);
+
+        /*
+         * This statement has no effect on this program, but it can
+         * change the point where a LinkageException is thrown in
+         * testImplement().  When this is present the "reference
+         * implementation" throws an exception from Class.newInstance(),
+         * when it's absent the exception is deferred until the first time
+         * we call a method that isn't actually implemented.
+         *
+         * This isn't the class that fails -- it's a class with the same
+         * name in the "fancy" class loader --  but the VM thinks it has a
+         * reference to one of these; presumably the difference is that
+         * without this the VM finds itself holding a reference to an
+         * instance of an uninitialized class.
+         */
+        System.out.println("base: " + DoubledImplement.class);
+        System.out.println("base2: " + DoubledImplement2.class);
+
+        /*
+         * Run tests.
+         */
+        testAccess1(loader);
+        testAccess2(loader);
+        testAccess3(loader);
+
+        testExtend(loader);
+        testExtendOkay(loader);
+        testInterface(loader);
+        testAbstract(loader);
+        testImplement(loader);
+        testIfaceImplement(loader);
+    }
+
+    /**
+     * See if we can load a class that isn't public to us.  We should be
+     * able to load it but not instantiate it.
+     */
+    static void testAccess1(ClassLoader loader) {
+        Class altClass;
+
+        try {
+            altClass = loader.loadClass("Inaccessible1");
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("loadClass failed");
+            cnfe.printStackTrace();
+            return;
+        }
+
+        /* instantiate */
+        Object obj;
+        try {
+            obj = altClass.newInstance();
+            System.err.println("ERROR: Inaccessible1 was accessible");
+        } catch (InstantiationException ie) {
+            System.err.println("newInstance failed: " + ie);
+            return;
+        } catch (IllegalAccessException iae) {
+            System.out.println("Got expected access exception #1");
+            //System.out.println("+++ " + iae);
+            return;
+        }
+    }
+
+    /**
+     * See if we can load a class whose base class is not accessible to it
+     * (though the base *is* accessible to us).
+     */
+    static void testAccess2(ClassLoader loader) {
+        Class altClass;
+
+        try {
+            altClass = loader.loadClass("Inaccessible2");
+            System.err.println("ERROR: Inaccessible2 was accessible");
+        } catch (ClassNotFoundException cnfe) {
+            Throwable cause = cnfe.getCause();
+            if (cause instanceof IllegalAccessError) {
+                System.out.println("Got expected CNFE/IAE #2");
+            } else {
+                System.err.println("Got unexpected CNFE/IAE #2");
+                cnfe.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * See if we can load a class with an inaccessible interface.
+     */
+    static void testAccess3(ClassLoader loader) {
+        Class altClass;
+
+        try {
+            altClass = loader.loadClass("Inaccessible3");
+            System.err.println("ERROR: Inaccessible3 was accessible");
+        } catch (ClassNotFoundException cnfe) {
+            Throwable cause = cnfe.getCause();
+            if (cause instanceof IllegalAccessError) {
+                System.out.println("Got expected CNFE/IAE #3");
+            } else {
+                System.err.println("Got unexpected CNFE/IAE #3");
+                cnfe.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * Test a doubled class that extends the base class.
+     */
+    static void testExtend(ClassLoader loader) {
+        Class doubledExtendClass;
+        Object obj;
+
+        /* get the "alternate" version of DoubledExtend */
+        try {
+            doubledExtendClass = loader.loadClass("DoubledExtend");
+            //System.out.println("+++ DoubledExtend is " + doubledExtendClass
+            //    + " in " + doubledExtendClass.getClassLoader());
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("loadClass failed: " + cnfe);
+            return;
+        }
+
+        /* instantiate */
+        try {
+            obj = doubledExtendClass.newInstance();
+        } catch (InstantiationException ie) {
+            System.err.println("newInstance failed: " + ie);
+            return;
+        } catch (IllegalAccessException iae) {
+            System.err.println("newInstance failed: " + iae);
+            return;
+        } catch (LinkageError le) {
+            System.out.println("Got expected LinkageError on DE");
+            return;
+        }
+
+        /* use the base class reference to get a CL-specific instance */
+        Base baseRef = (Base) obj;
+        DoubledExtend de = baseRef.getExtended();
+
+        /* try to call through it */
+        try {
+            String result;
+
+            result = Base.doStuff(de);
+            System.err.println("ERROR: did not get LinkageError on DE");
+            System.err.println("(result=" + result + ")");
+        } catch (LinkageError le) {
+            System.out.println("Got expected LinkageError on DE");
+            return;
+        }
+    }
+
+    /**
+     * Test a doubled class that extends the base class, but is okay since
+     * it doesn't override the base class method.
+     */
+    static void testExtendOkay(ClassLoader loader) {
+        Class doubledExtendOkayClass;
+        Object obj;
+
+        /* get the "alternate" version of DoubledExtendOkay */
+        try {
+            doubledExtendOkayClass = loader.loadClass("DoubledExtendOkay");
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("loadClass failed: " + cnfe);
+            return;
+        }
+
+        /* instantiate */
+        try {
+            obj = doubledExtendOkayClass.newInstance();
+        } catch (InstantiationException ie) {
+            System.err.println("newInstance failed: " + ie);
+            return;
+        } catch (IllegalAccessException iae) {
+            System.err.println("newInstance failed: " + iae);
+            return;
+        } catch (LinkageError le) {
+            System.err.println("Got unexpected LinkageError on DEO");
+            le.printStackTrace();
+            return;
+        }
+
+        /* use the base class reference to get a CL-specific instance */
+        BaseOkay baseRef = (BaseOkay) obj;
+        DoubledExtendOkay de = baseRef.getExtended();
+
+        /* try to call through it */
+        try {
+            String result;
+
+            result = BaseOkay.doStuff(de);
+            System.out.println("Got DEO result " + result);
+        } catch (LinkageError le) {
+            System.err.println("Got unexpected LinkageError on DEO");
+            le.printStackTrace();
+            return;
+        }
+    }
+
+    /**
+     * Try to access a doubled class through a class that implements
+     * an interface declared in a different class.
+     */
+    static void testInterface(ClassLoader loader) {
+        Class getDoubledClass;
+        Object obj;
+
+        /* get GetDoubled from the "alternate" class loader */
+        try {
+            getDoubledClass = loader.loadClass("GetDoubled");
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("loadClass failed: " + cnfe);
+            return;
+        }
+
+        /* instantiate */
+        try {
+            obj = getDoubledClass.newInstance();
+        } catch (InstantiationException ie) {
+            System.err.println("newInstance failed: " + ie);
+            return;
+        } catch (IllegalAccessException iae) {
+            System.err.println("newInstance failed: " + iae);
+            return;
+        } catch (LinkageError le) {
+            // Dalvik bails here
+            System.out.println("Got LinkageError on GD");
+            return;
+        }
+
+        /*
+         * Cast the object to the interface, and try to use it.
+         */
+        IGetDoubled iface = (IGetDoubled) obj;
+        try {
+            /* "de" will be the wrong variety of DoubledExtendOkay */
+            DoubledExtendOkay de = iface.getDoubled();
+            // reference impl bails here
+            String str = de.getStr();
+        } catch (LinkageError le) {
+            System.out.println("Got LinkageError on GD");
+            return;
+        }
+        System.err.println("Should have failed by now on GetDoubled");
+    }
+
+    /**
+     * Throw an abstract class into the middle and see what happens.
+     */
+    static void testAbstract(ClassLoader loader) {
+        Class abstractGetClass;
+        Object obj;
+
+        /* get AbstractGet from the "alternate" loader */
+        try {
+            abstractGetClass = loader.loadClass("AbstractGet");
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("loadClass ta failed: " + cnfe);
+            return;
+        }
+
+        /* instantiate */
+        try {
+            obj = abstractGetClass.newInstance();
+        } catch (InstantiationException ie) {
+            System.err.println("newInstance failed: " + ie);
+            return;
+        } catch (IllegalAccessException iae) {
+            System.err.println("newInstance failed: " + iae);
+            return;
+        } catch (LinkageError le) {
+            System.out.println("Got LinkageError on TA");
+            return;
+        }
+
+        /* use the base class reference to get a CL-specific instance */
+        BaseOkay baseRef = (BaseOkay) obj;
+        DoubledExtendOkay de = baseRef.getExtended();
+
+        /* try to call through it */
+        try {
+            String result;
+
+            result = BaseOkay.doStuff(de);
+        } catch (LinkageError le) {
+            System.out.println("Got LinkageError on TA");
+            return;
+        }
+        System.err.println("Should have failed by now in testAbstract");
+    }
+
+    /**
+     * Test a doubled class that implements a common interface.
+     */
+    static void testImplement(ClassLoader loader) {
+        Class doubledImplementClass;
+        Object obj;
+
+        useImplement(new DoubledImplement(), true);
+
+        /* get the "alternate" version of DoubledImplement */
+        try {
+            doubledImplementClass = loader.loadClass("DoubledImplement");
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("loadClass failed: " + cnfe);
+            return;
+        }
+
+        /* instantiate */
+        try {
+            obj = doubledImplementClass.newInstance();
+        } catch (InstantiationException ie) {
+            System.err.println("newInstance failed: " + ie);
+            return;
+        } catch (IllegalAccessException iae) {
+            System.err.println("newInstance failed: " + iae);
+            return;
+        } catch (LinkageError le) {
+            System.out.println("Got LinkageError on DI (early)");
+            return;
+        }
+
+        /* if we lived this long, try to do something with it */
+        ICommon icommon = (ICommon) obj;
+        useImplement(icommon.getDoubledInstance(), false);
+    }
+
+    /**
+     * Do something with a DoubledImplement instance.
+     */
+    static void useImplement(DoubledImplement di, boolean isOne) {
+        //System.out.println("useObject: " + di.toString() + " -- "
+        //    + di.getClass().getClassLoader());
+        try {
+            di.one();
+            if (!isOne) {
+                System.err.println("ERROR: did not get LinkageError on DI");
+            }
+        } catch (LinkageError le) {
+            if (!isOne) {
+                System.out.println("Got LinkageError on DI (late)");
+            } else {
+                throw le;
+            }
+        }
+    }
+
+
+    /**
+     * Test a class that implements an interface with a super-interface
+     * that refers to a doubled class.
+     */
+    static void testIfaceImplement(ClassLoader loader) {
+        Class ifaceImplClass;
+        Object obj;
+
+        /*
+         * Create an instance of IfaceImpl.  We also pull in
+         * DoubledImplement2 from the other class loader; without this
+         * we don't fail in some implementations.
+         */
+        try {
+            ifaceImplClass = loader.loadClass("IfaceImpl");
+            ifaceImplClass = loader.loadClass("DoubledImplement2");
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("loadClass failed: " + cnfe);
+            return;
+        }
+
+        /* instantiate */
+        try {
+            obj = ifaceImplClass.newInstance();
+        } catch (InstantiationException ie) {
+            System.err.println("newInstance failed: " + ie);
+            return;
+        } catch (IllegalAccessException iae) {
+            System.err.println("newInstance failed: " + iae);
+            return;
+        } catch (LinkageError le) {
+            System.out.println("Got LinkageError on IDI (early)");
+            //System.out.println(le);
+            return;
+        }
+
+        /*
+         * Without the pre-load of FancyLoader->DoubledImplement2, some
+         * implementations will happily execute through this part.  "obj"
+         * comes from FancyLoader, but the di2 returned from ifaceSuper
+         * comes from the application class loader.
+         */
+        IfaceSuper ifaceSuper = (IfaceSuper) obj;
+        DoubledImplement2 di2 = ifaceSuper.getDoubledInstance2();
+        di2.one();
+    }
+}
diff --git a/tests/068-classloader/src/SimpleBase.java b/tests/068-classloader/src/SimpleBase.java
new file mode 100644
index 0000000..fd56db9
--- /dev/null
+++ b/tests/068-classloader/src/SimpleBase.java
@@ -0,0 +1,8 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Simple, public base class.
+ */
+public class SimpleBase {
+    public SimpleBase() {}
+}
diff --git a/tests/068-classloader/src/Useless.java b/tests/068-classloader/src/Useless.java
new file mode 100644
index 0000000..f51d9a8
--- /dev/null
+++ b/tests/068-classloader/src/Useless.java
@@ -0,0 +1,4 @@
+
+public class Useless implements ICommon {
+    public DoubledImplement getDoubledInstance() { return null; }
+}
diff --git a/tests/069-field-type/expected.txt b/tests/069-field-type/expected.txt
new file mode 100644
index 0000000..8828178
--- /dev/null
+++ b/tests/069-field-type/expected.txt
@@ -0,0 +1,4 @@
+Assignment was allowed
+Got expected IncompatibleClassChangeError
+In compareTo
+Done
diff --git a/tests/069-field-type/info.txt b/tests/069-field-type/info.txt
new file mode 100644
index 0000000..6e3a22f
--- /dev/null
+++ b/tests/069-field-type/info.txt
@@ -0,0 +1,4 @@
+This tests to see if the VM allows you to store a reference to an
+inappropriate object type in an instance field.  By compiling two
+versions of the field-holder class we can bypass the compiler's type
+safety.
diff --git a/tests/069-field-type/src/Blah.java b/tests/069-field-type/src/Blah.java
new file mode 100644
index 0000000..fd98336
--- /dev/null
+++ b/tests/069-field-type/src/Blah.java
@@ -0,0 +1,9 @@
+
+/**
+ * Trivial class; must implement an interesting interface.
+ */
+public class Blah implements Runnable {
+    public void run() {
+        System.out.println("run");
+    }
+}
diff --git a/tests/069-field-type/src/Holder.java b/tests/069-field-type/src/Holder.java
new file mode 100644
index 0000000..e3c9f89
--- /dev/null
+++ b/tests/069-field-type/src/Holder.java
@@ -0,0 +1,7 @@
+
+/**
+ * Simple class with one field.
+ */
+public class Holder {
+    public Runnable mValue;
+}
diff --git a/tests/069-field-type/src/Main.java b/tests/069-field-type/src/Main.java
new file mode 100644
index 0000000..f9885e6
--- /dev/null
+++ b/tests/069-field-type/src/Main.java
@@ -0,0 +1,34 @@
+
+/**
+ * Create some objects and store them into an instance field.
+ */
+public class Main {
+    /**
+     * Entry point.
+     */
+    public static void main(String[] args) {
+        Holder holder = new Holder();
+
+        Blah blah = new Blah();
+
+        /* strictly speaking, this should fail */
+        holder.mValue = blah;
+
+        System.out.println("Assignment was allowed");
+
+        /* try to use the reference; should fail */
+        try {
+            holder.mValue.run();
+            System.err.println("ERROR: did not get expected ICCE");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected IncompatibleClassChangeError");
+        }
+
+        /* for fun, verify that it's the "alternate" type */
+        //Comparable cmpx = holder.mValue;      /* compiler rejects */
+        Comparable cmp = (Comparable) holder.mValue;
+        cmp.compareTo(cmp);
+
+        System.out.println("Done");
+    }
+}
diff --git a/tests/069-field-type/src2/Blah.java b/tests/069-field-type/src2/Blah.java
new file mode 100644
index 0000000..1bffff6
--- /dev/null
+++ b/tests/069-field-type/src2/Blah.java
@@ -0,0 +1,10 @@
+
+/**
+ * Trivial class; must implement an interesting interface.
+ */
+public class Blah implements Comparable {
+    public int compareTo(Object another) {
+        System.out.println("In compareTo");
+        return 0;
+    }
+}
diff --git a/tests/070-nio-buffer/expected.txt b/tests/070-nio-buffer/expected.txt
new file mode 100644
index 0000000..e271001
--- /dev/null
+++ b/tests/070-nio-buffer/expected.txt
@@ -0,0 +1,3 @@
+Got expected buffer overflow exception
+Got expected out-of-bounds exception
+Got expected buffer overflow exception
diff --git a/tests/070-nio-buffer/info.txt b/tests/070-nio-buffer/info.txt
new file mode 100644
index 0000000..761714e
--- /dev/null
+++ b/tests/070-nio-buffer/info.txt
@@ -0,0 +1 @@
+Exercise NIO buffers (e.g. java.nio.ByteBuffer).
diff --git a/tests/070-nio-buffer/src/Main.java b/tests/070-nio-buffer/src/Main.java
new file mode 100644
index 0000000..bfcab3a
--- /dev/null
+++ b/tests/070-nio-buffer/src/Main.java
@@ -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.
+ */
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+public class Main {
+    public static void main(String[] args) {
+         intFloatTest();
+         basicShortTest();
+    }
+
+    /*
+     * Create a buffer and fiddle with it.
+     */
+    public static void basicShortTest() {
+        ByteBuffer directBuf = ByteBuffer.allocateDirect(64);
+        //ByteBuffer directBuf = ByteBuffer.allocateDirect(65);
+
+        ShortBuffer shortBuf = directBuf.asShortBuffer();
+
+        short[] myShorts = {
+            1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007,
+            1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015,
+            1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023,
+            1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031
+        };
+
+        shortBuf.position(0);
+        shortBuf.put(myShorts, 0, 32);      // should work
+        shortBuf.position(0);
+        shortBuf.put(myShorts, 16, 16);     // should work
+        shortBuf.put(myShorts, 16, 16);     // advance to end
+
+        try {
+            shortBuf.put(myShorts, 0, 1);     // should fail
+            System.err.println("ERROR: out-of-bounds put succeeded\n");
+        } catch (BufferOverflowException boe) {
+            System.out.println("Got expected buffer overflow exception");
+        }
+
+        try {
+            shortBuf.position(0);
+            shortBuf.put(myShorts, 0, 33);     // should fail
+            System.err.println("ERROR: out-of-bounds put succeeded\n");
+        } catch (IndexOutOfBoundsException ioobe) {
+            System.out.println("Got expected out-of-bounds exception");
+        }
+
+        try {
+            shortBuf.position(16);
+            shortBuf.put(myShorts, 0, 17);     // should fail
+            System.err.println("ERROR: out-of-bounds put succeeded\n");
+        } catch (BufferOverflowException boe) {
+            System.out.println("Got expected buffer overflow exception");
+        }
+    }
+
+    /*
+     * Try this with either floats or ints; ints fail with
+     * BufferOverflowException, floats work.
+     *
+     * From http://code.google.com/p/android/issues/detail?id=1585 .
+     */
+    public static void intFloatTest() {
+        ByteBuffer direct = ByteBuffer.allocateDirect(100);
+        direct.order(ByteOrder.nativeOrder());
+        IntBuffer int1 = direct.asIntBuffer();
+        int data[] = new int[25];
+        //FloatBuffer int1 = direct.asFloatBuffer();
+        //float data[] = new float[25];
+        int1.clear ();
+        int1.put (data);
+        int1.position (0);
+
+        int1.clear ();
+        int1.put (data);
+        int1.position (0);
+    }
+}
diff --git a/tests/071-dexfile/expected.txt b/tests/071-dexfile/expected.txt
new file mode 100644
index 0000000..b7af75e
--- /dev/null
+++ b/tests/071-dexfile/expected.txt
@@ -0,0 +1,3 @@
+Constructing another
+Got expected ULE
+done
diff --git a/tests/071-dexfile/info.txt b/tests/071-dexfile/info.txt
new file mode 100644
index 0000000..54d9ed0
--- /dev/null
+++ b/tests/071-dexfile/info.txt
@@ -0,0 +1,4 @@
+Exercise some Dalvik-specific DEX file features.  This is not expected to
+work on other VMs.
+
+NOTE: the test requires that /sdcard exists and is writable.
diff --git a/tests/071-dexfile/src-ex/Another.java b/tests/071-dexfile/src-ex/Another.java
new file mode 100644
index 0000000..c978c59
--- /dev/null
+++ b/tests/071-dexfile/src-ex/Another.java
@@ -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.
+ */
+
+public class Another {
+    public Another() {
+        System.out.println("Constructing another");
+
+        /* not expected to work; just exercises the call */
+        try {
+            System.loadLibrary("nonexistent");
+        } catch (UnsatisfiedLinkError ule) {
+            System.out.println("Got expected ULE");
+        }
+    }
+}
diff --git a/tests/071-dexfile/src/Main.java b/tests/071-dexfile/src/Main.java
new file mode 100644
index 0000000..d71aec0
--- /dev/null
+++ b/tests/071-dexfile/src/Main.java
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+
+/**
+ * DexFile tests (Dalvik-specific).
+ */
+public class Main {
+    private static final String CLASS_PATH = "test-ex.jar";
+    private static final String ODEX_DIR = "/sdcard";
+    //private static final String ODEX_DIR = ".";
+    private static final String ODEX_ALT = "/tmp";
+    private static final String LIB_DIR = "/nowhere/nothing/";
+
+    /**
+     * Prep the environment then run the test.
+     */
+    public static void main(String[] args) {
+        Process p;
+        try {
+            /*
+             * Create a sub-process to see if the ProcessManager wait
+             * interferes with the dexopt invocation wait.
+             *
+             * /dev/random never hits EOF, so we're sure that we'll still
+             * be waiting for the process to complete.  On the device it
+             * stops pretty quickly (which means the child won't be
+             * spinning).
+             */
+            ProcessBuilder pb = new ProcessBuilder("cat", "/dev/random");
+            p = pb.start();
+        } catch (IOException ioe) {
+            System.err.println("cmd failed: " + ioe.getMessage());
+            p = null;
+        }
+
+        try {
+            testDexClassLoader();
+        } finally {
+            // shouldn't be necessary, but it's good to be tidy
+            if (p != null)
+                p.destroy();
+
+            // let the ProcessManager's daemon thread finish before we shut down
+            // (avoids the occasional segmentation fault)
+            try {
+                Thread.sleep(500);
+            } catch (Exception ex) {}
+        }
+
+        System.out.println("done");
+    }
+
+    /**
+     * Create a class loader, explicitly specifying the source DEX and
+     * the location for the optimized DEX.
+     */
+    private static void testDexClassLoader() {
+        ClassLoader dexClassLoader = getDexClassLoader();
+
+        Class anotherClass;
+        try {
+            anotherClass = dexClassLoader.loadClass("Another");
+        } catch (ClassNotFoundException cnfe) {
+            throw new RuntimeException("Another?");
+        }
+
+        Object another;
+        try {
+            another = anotherClass.newInstance();
+        } catch (IllegalAccessException ie) {
+            throw new RuntimeException("new another", ie);
+        } catch (InstantiationException ie) {
+            throw new RuntimeException("new another", ie);
+        }
+
+        // not expected to work; just exercises the call
+        dexClassLoader.getResource("nonexistent");
+    }
+
+    /*
+     * Create an instance of DexClassLoader.  The test harness doesn't
+     * have visibility into dalvik.system.*, so we do this through
+     * reflection.
+     */
+    private static ClassLoader getDexClassLoader() {
+        String odexDir;
+
+        /*
+        String androidData = System.getenv("ANDROID_DATA");
+        if (androidData == null)
+            androidData = "";
+        odexDir = androidData + "/" + ODEX_DIR;
+        */
+
+        File test = new File(ODEX_DIR);
+        if (test.isDirectory())
+            odexDir = ODEX_DIR;
+        else
+            odexDir = ODEX_ALT;
+        //System.out.println("Output dir is " + odexDir);
+
+        ClassLoader myLoader = Main.class.getClassLoader();
+        Class dclClass;
+        try {
+            dclClass = myLoader.loadClass("dalvik.system.DexClassLoader");
+        } catch (ClassNotFoundException cnfe) {
+            throw new RuntimeException("dalvik.system.DexClassLoader not found");
+        }
+
+        Constructor ctor;
+        try {
+            ctor = dclClass.getConstructor(String.class, String.class,
+                String.class, ClassLoader.class);
+        } catch (NoSuchMethodException nsme) {
+            throw new RuntimeException("DCL ctor", nsme);
+        }
+
+        // create an instance, using the path we found
+        Object dclObj;
+        try {
+            dclObj = ctor.newInstance(CLASS_PATH, odexDir, LIB_DIR, myLoader);
+        } catch (Exception ex) {
+            throw new RuntimeException("DCL newInstance", ex);
+        }
+
+        return (ClassLoader) dclObj;
+    }
+}
diff --git a/tests/072-precise-gc/expected.txt b/tests/072-precise-gc/expected.txt
new file mode 100644
index 0000000..18ec087
--- /dev/null
+++ b/tests/072-precise-gc/expected.txt
@@ -0,0 +1,2 @@
+Valid refs: 0
+String0String1String2String3String4String5String6String7String8String9
diff --git a/tests/072-precise-gc/info.txt b/tests/072-precise-gc/info.txt
new file mode 100644
index 0000000..b0b2cea
--- /dev/null
+++ b/tests/072-precise-gc/info.txt
@@ -0,0 +1 @@
+Try to detect whether precise GC is working.
diff --git a/tests/072-precise-gc/src/Main.java b/tests/072-precise-gc/src/Main.java
new file mode 100644
index 0000000..e049221
--- /dev/null
+++ b/tests/072-precise-gc/src/Main.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+import java.lang.ref.WeakReference;
+
+public class Main {
+    public static void main(String[] args) {
+        staleStackTest();
+    }
+
+    public static void staleStackTest() {
+        WeakReference wrefs[] = new WeakReference[10];
+
+        populate(wrefs);
+
+        check(wrefs);
+    }
+
+    static void populate(WeakReference[] wrefs) {
+        /*
+         * Get a bunch of non-constant String objects into registers.  These
+         * should be the first locals declared.
+         */
+        String str0 = generateString("String", 0);
+        String str1 = generateString("String", 1);
+        String str2 = generateString("String", 2);
+        String str3 = generateString("String", 3);
+        String str4 = generateString("String", 4);
+        String str5 = generateString("String", 5);
+        String str6 = generateString("String", 6);
+        String str7 = generateString("String", 7);
+        String str8 = generateString("String", 8);
+        String str9 = generateString("String", 9);
+
+        /* stuff them into the weak references array */
+        wrefs[0] = new WeakReference(str0);
+        wrefs[1] = new WeakReference(str1);
+        wrefs[2] = new WeakReference(str2);
+        wrefs[3] = new WeakReference(str3);
+        wrefs[4] = new WeakReference(str4);
+        wrefs[5] = new WeakReference(str5);
+        wrefs[6] = new WeakReference(str6);
+        wrefs[7] = new WeakReference(str7);
+        wrefs[8] = new WeakReference(str8);
+        wrefs[9] = new WeakReference(str9);
+    }
+
+    static String generateString(String base, int num) {
+        return base + num;
+    }
+
+    static void check(WeakReference[] wrefs) {
+        /*
+         * Declare locals so that our stack overlaps the same region
+         * that populate() did.
+         */
+        String str0;
+        String str1;
+        String str2;
+        String str3;
+        String str4;
+        String str5;
+        String str6;
+        String str7;
+        String str8;
+        String str9;
+        int numValid = 0;
+
+        /*
+         * This *should* blow out all the weakly-reference objects.  If
+         * we still have stale copies of references on the stack, a
+         * conservative GC will try to hold on to those objects and the
+         * count will be nonzero.
+         *
+         * Getting a zero result here isn't conclusive, but it's a strong
+         * indicator that precise GC is having an impact.
+         */
+        System.gc();
+
+        for (int i = 0; i < wrefs.length; i++) {
+            if (wrefs[i].get() != null)
+                numValid++;
+        }
+
+        System.out.println("Valid refs: " + numValid);
+
+        /* use the locals in case the compiler gets smart */
+        str0 = generateString("String", 0);
+        str1 = generateString("String", 1);
+        str2 = generateString("String", 2);
+        str3 = generateString("String", 3);
+        str4 = generateString("String", 4);
+        str5 = generateString("String", 5);
+        str6 = generateString("String", 6);
+        str7 = generateString("String", 7);
+        str8 = generateString("String", 8);
+        str9 = generateString("String", 9);
+        System.out.println(str0+str1+str2+str3+str4+str5+str6+str7+str8+str9);
+    }
+}
diff --git a/tests/073-mismatched-field/expected.txt b/tests/073-mismatched-field/expected.txt
new file mode 100644
index 0000000..90fbab8
--- /dev/null
+++ b/tests/073-mismatched-field/expected.txt
@@ -0,0 +1 @@
+Got expected failure
diff --git a/tests/073-mismatched-field/info.txt b/tests/073-mismatched-field/info.txt
new file mode 100644
index 0000000..4a15263
--- /dev/null
+++ b/tests/073-mismatched-field/info.txt
@@ -0,0 +1,3 @@
+Test behavior when an instance field is overlapped (through separate
+compilation) by a static field.  The VM is expected to detect the conflict
+and throw an IncompatibleClassChangeError when the field is accessed.
diff --git a/tests/073-mismatched-field/src/IMain.java b/tests/073-mismatched-field/src/IMain.java
new file mode 100644
index 0000000..3ad5ecb
--- /dev/null
+++ b/tests/073-mismatched-field/src/IMain.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public interface IMain {
+    //static int f = 123;
+}
diff --git a/tests/073-mismatched-field/src/Main.java b/tests/073-mismatched-field/src/Main.java
new file mode 100644
index 0000000..70709c0
--- /dev/null
+++ b/tests/073-mismatched-field/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+public class Main extends SuperMain implements IMain {
+    public static void main(String[] args) {
+        Main main = new Main();
+        main.doit();
+    }
+
+    void doit() {
+        try {
+            System.out.println("value=" + this.f);
+            System.err.println("Succeeded unexpectedly");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected failure");
+        }
+    }
+}
diff --git a/tests/073-mismatched-field/src/SuperMain.java b/tests/073-mismatched-field/src/SuperMain.java
new file mode 100644
index 0000000..48a9bab
--- /dev/null
+++ b/tests/073-mismatched-field/src/SuperMain.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public class SuperMain {
+    public int f = 456;
+}
diff --git a/tests/073-mismatched-field/src2/IMain.java b/tests/073-mismatched-field/src2/IMain.java
new file mode 100644
index 0000000..136f2a1
--- /dev/null
+++ b/tests/073-mismatched-field/src2/IMain.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+public interface IMain {
+    static int f = 123;
+}
diff --git a/tests/074-gc-thrash/expected.txt b/tests/074-gc-thrash/expected.txt
new file mode 100644
index 0000000..2669165
--- /dev/null
+++ b/tests/074-gc-thrash/expected.txt
@@ -0,0 +1,2 @@
+Running (10 seconds) ...
+Done.
diff --git a/tests/074-gc-thrash/info.txt b/tests/074-gc-thrash/info.txt
new file mode 100644
index 0000000..ded1582
--- /dev/null
+++ b/tests/074-gc-thrash/info.txt
@@ -0,0 +1 @@
+This thrashes the memory allocator and garbage collector for a brief period.
diff --git a/tests/074-gc-thrash/src/Main.java b/tests/074-gc-thrash/src/Main.java
new file mode 100644
index 0000000..f85aa4b
--- /dev/null
+++ b/tests/074-gc-thrash/src/Main.java
@@ -0,0 +1,337 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+public class Main {
+    public static volatile boolean quit = false;
+    public static final boolean DEBUG = false;
+
+    private static final boolean WRITE_HPROF_DATA = false;
+    private static final int TEST_TIME = 10;
+    private static final String OUTPUT_FILE = "gc-thrash.hprof";
+
+    public static void main(String[] args) {
+        // dump heap before
+
+        System.out.println("Running (" + TEST_TIME + " seconds) ...");
+        runTests();
+
+        Method dumpHprofDataMethod = null;
+        String dumpFile = null;
+
+        if (WRITE_HPROF_DATA) {
+            dumpHprofDataMethod = getDumpHprofDataMethod();
+            if (dumpHprofDataMethod != null) {
+                dumpFile = getDumpFileName();
+                System.out.println("Sending output to " + dumpFile);
+            }
+        }
+
+        System.gc();
+        System.runFinalization();
+        System.gc();
+
+        if (WRITE_HPROF_DATA && dumpHprofDataMethod != null) {
+            try {
+                dumpHprofDataMethod.invoke(null, dumpFile);
+            } catch (IllegalAccessException iae) {
+                System.err.println(iae);
+            } catch (InvocationTargetException ite) {
+                System.err.println(ite);
+            }
+        }
+
+        System.out.println("Done.");
+    }
+
+    /**
+     * Finds VMDebug.dumpHprofData() through reflection.  In the reference
+     * implementation this will not be available.
+     *
+     * @return the reflection object, or null if the method can't be found
+     */
+    private static Method getDumpHprofDataMethod() {
+        ClassLoader myLoader = Main.class.getClassLoader();
+        Class vmdClass;
+        try {
+            vmdClass = myLoader.loadClass("dalvik.system.VMDebug");
+        } catch (ClassNotFoundException cnfe) {
+            return null;
+        }
+
+        Method meth;
+        try {
+            meth = vmdClass.getMethod("dumpHprofData",
+                    new Class[] { String.class });
+        } catch (NoSuchMethodException nsme) {
+            System.err.println("Found VMDebug but not dumpHprofData method");
+            return null;
+        }
+
+        return meth;
+    }
+
+    private static String getDumpFileName() {
+        File tmpDir = new File("/tmp");
+        if (tmpDir.exists() && tmpDir.isDirectory()) {
+            return "/tmp/" + OUTPUT_FILE;
+        }
+
+        File sdcard = new File("/sdcard");
+        if (sdcard.exists() && sdcard.isDirectory()) {
+            return "/sdcard/" + OUTPUT_FILE;
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Run the various tests for a set period.
+     */
+    public static void runTests() {
+        Robin robin = new Robin();
+        Deep deep = new Deep();
+        Large large = new Large();
+
+        /* start all threads */
+        robin.start();
+        deep.start();
+        large.start();
+
+        /* let everybody run for 10 seconds */
+        sleep(TEST_TIME * 1000);
+
+        quit = true;
+
+        try {
+            /* wait for all threads to stop */
+            robin.join();
+            deep.join();
+            large.join();
+        } catch (InterruptedException ie) {
+            System.err.println("join was interrupted");
+        }
+    }
+
+    /**
+     * Sleeps for the "ms" milliseconds.
+     */
+    public static void sleep(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ie) {
+            System.err.println("sleep was interrupted");
+        }
+    }
+
+    /**
+     * Sleeps briefly, allowing other threads some CPU time to get started.
+     */
+    public static void startupDelay() {
+        sleep(500);
+    }
+}
+
+
+/**
+ * Allocates useless objects and holds on to several of them.
+ *
+ * Uses a single large array of references, replaced repeatedly in round-robin
+ * order.
+ */
+class Robin extends Thread {
+    private static final int ARRAY_SIZE = 40960;
+    int sleepCount = 0;
+
+    public void run() {
+        Main.startupDelay();
+
+        String strings[] = new String[ARRAY_SIZE];
+        int idx = 0;
+
+        while (!Main.quit) {
+            strings[idx] = makeString(idx);
+
+            if (idx % (ARRAY_SIZE / 4) == 0) {
+                Main.sleep(400);
+                sleepCount++;
+            }
+
+            idx = (idx + 1) % ARRAY_SIZE;
+        }
+
+        if (Main.DEBUG)
+            System.out.println("Robin: sleepCount=" + sleepCount);
+    }
+
+    private String makeString(int val) {
+        return new String("Robin" + val);
+    }
+}
+
+
+/**
+ * Allocates useless objects in recursive calls.
+ */
+class Deep extends Thread {
+    private static final int MAX_DEPTH = 61;
+
+    private static String strong[] = new String[MAX_DEPTH];
+    private static WeakReference weak[] = new WeakReference[MAX_DEPTH];
+
+    public void run() {
+        int iter = 0;
+        boolean once = false;
+
+        Main.startupDelay();
+
+        while (!Main.quit) {
+            dive(0, iter);
+            once = true;
+            iter += MAX_DEPTH;
+        }
+
+        if (!once) {
+            System.err.println("not even once?");
+            return;
+        }
+
+        /*
+         * Check the results of the last trip through.  Everything in
+         * "weak" should be matched in "strong", and the two should be
+         * equivalent (object-wise, not just string-equality-wise).
+         */
+        for (int i = 0; i < MAX_DEPTH; i++) {
+            if (strong[i] != weak[i].get()) {
+                System.err.println("Deep: " + i + " strong=" + strong[i] +
+                    ", weak=" + weak[i].get());
+            }
+        }
+
+        /*
+         * Wipe "strong", do a GC, see if "weak" got collected.
+         */
+        for (int i = 0; i < MAX_DEPTH; i++)
+            strong[i] = null;
+
+        System.gc();
+
+        for (int i = 0; i < MAX_DEPTH; i++) {
+            if (weak[i].get() != null) {
+                System.err.println("Deep: weak still has " + i);
+            }
+        }
+
+        if (Main.DEBUG)
+            System.out.println("Deep: iters=" + iter / MAX_DEPTH);
+    }
+
+    /**
+     * Recursively dive down, setting one or more local variables.
+     *
+     * We pad the stack out with locals, attempting to create a mix of
+     * valid and invalid references on the stack.
+     */
+    private String dive(int depth, int iteration) {
+        String str0;
+        String str1;
+        String str2;
+        String str3;
+        String str4;
+        String str5;
+        String str6;
+        String str7;
+        String funStr;
+
+        funStr = "";
+
+        switch (iteration % 8) {
+            case 0:
+                funStr = str0 = makeString(iteration);
+                break;
+            case 1:
+                funStr = str1 = makeString(iteration);
+                break;
+            case 2:
+                funStr = str2 = makeString(iteration);
+                break;
+            case 3:
+                funStr = str3 = makeString(iteration);
+                break;
+            case 4:
+                funStr = str4 = makeString(iteration);
+                break;
+            case 5:
+                funStr = str5 = makeString(iteration);
+                break;
+            case 6:
+                funStr = str6 = makeString(iteration);
+                break;
+            case 7:
+                funStr = str7 = makeString(iteration);
+                break;
+        }
+
+        strong[depth] = funStr;
+        weak[depth] = new WeakReference(funStr);
+
+        if (depth+1 < MAX_DEPTH)
+            dive(depth+1, iteration+1);
+        else
+            Main.sleep(100);
+
+        return funStr;
+    }
+
+    private String makeString(int val) {
+        return new String("Deep" + val);
+    }
+}
+
+
+/**
+ * Allocates large useless objects.
+ */
+class Large extends Thread {
+    public void run() {
+        byte[] chunk;
+        int count = 0;
+        int sleepCount = 0;
+
+        Main.startupDelay();
+
+        while (!Main.quit) {
+            chunk = new byte[100000];
+            pretendToUse(chunk);
+
+            count++;
+            if ((count % 500) == 0) {
+                Main.sleep(400);
+                sleepCount++;
+            }
+        }
+
+        if (Main.DEBUG)
+            System.out.println("Large: sleepCount=" + sleepCount);
+    }
+
+    public void pretendToUse(byte[] chunk) {}
+}
diff --git a/tests/075-verification-error/expected.txt b/tests/075-verification-error/expected.txt
new file mode 100644
index 0000000..6e4f584
--- /dev/null
+++ b/tests/075-verification-error/expected.txt
@@ -0,0 +1,12 @@
+Got expected InstantationError
+Got expected NoSuchFieldError
+Got expected NoSuchFieldError
+Got expected NoSuchMethodError
+Got expected NoSuchMethodError
+Got expected IllegalAccessError (ifield)
+Got expected IllegalAccessError (sfield)
+Got expected IllegalAccessError (method)
+Got expected IllegalAccessError (smethod)
+Got expected IllegalAccessError (meth-class)
+Got expected IllegalAccessError (field-class)
+Got expected IllegalAccessError (meth-meth)
diff --git a/tests/075-verification-error/info.txt b/tests/075-verification-error/info.txt
new file mode 100644
index 0000000..be688ff
--- /dev/null
+++ b/tests/075-verification-error/info.txt
@@ -0,0 +1 @@
+Exercise deferred verification error reporting.
diff --git a/tests/075-verification-error/src/Main.java b/tests/075-verification-error/src/Main.java
new file mode 100644
index 0000000..51d648c
--- /dev/null
+++ b/tests/075-verification-error/src/Main.java
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+import other.Mutant;
+import other.InaccessibleClass;
+import other.InaccessibleMethod;
+
+/**
+ * Test some problematic situations that the verifier detects.
+ */
+public class Main {
+    public static final boolean VERBOSE = false;
+
+    public static void main(String[] args) {
+        testClassNewInstance();
+        testMissingStuff();
+        testBadAccess();
+    }
+
+    /**
+     * Try to create a new instance of an abstract class.
+     */
+    static void testClassNewInstance() {
+        try {
+            MaybeAbstract ma = new MaybeAbstract();
+            System.err.println("ERROR: MaybeAbstract succeeded unexpectedly");
+        } catch (InstantiationError ie) {
+            System.out.println("Got expected InstantationError");
+            if (VERBOSE) System.out.println("--- " + ie);
+        } catch (Exception ex) {
+            System.err.println("Got unexpected MaybeAbstract failure");
+        }
+    }
+
+    /**
+     * Test stuff that disappears.
+     */
+    static void testMissingStuff() {
+        Mutant mutant = new Mutant();
+
+        try {
+            int x = mutant.disappearingField;
+        } catch (NoSuchFieldError nsfe) {
+            System.out.println("Got expected NoSuchFieldError");
+            if (VERBOSE) System.out.println("--- " + nsfe);
+        }
+
+        try {
+            int y = Mutant.disappearingStaticField;
+        } catch (NoSuchFieldError nsfe) {
+            System.out.println("Got expected NoSuchFieldError");
+            if (VERBOSE) System.out.println("--- " + nsfe);
+        }
+
+        try {
+            mutant.disappearingMethod();
+        } catch (NoSuchMethodError nsme) {
+            System.out.println("Got expected NoSuchMethodError");
+            if (VERBOSE) System.out.println("--- " + nsme);
+        }
+
+        try {
+            Mutant.disappearingStaticMethod();
+        } catch (NoSuchMethodError nsme) {
+            System.out.println("Got expected NoSuchMethodError");
+            if (VERBOSE) System.out.println("--- " + nsme);
+        }
+    }
+
+    /**
+     * Test stuff that becomes inaccessible.
+     */
+    static void testBadAccess() {
+        Mutant mutant = new Mutant();
+
+        try {
+            int x = mutant.inaccessibleField;
+            System.err.println("ERROR: bad access succeeded\n");
+        } catch (IllegalAccessError iae) {
+            System.out.println("Got expected IllegalAccessError (ifield)");
+            if (VERBOSE) System.out.println("--- " + iae);
+        }
+
+        try {
+            int y = Mutant.inaccessibleStaticField;
+            System.err.println("ERROR: bad access succeeded\n");
+        } catch (IllegalAccessError iae) {
+            System.out.println("Got expected IllegalAccessError (sfield)");
+            if (VERBOSE) System.out.println("--- " + iae);
+        }
+
+        try {
+            mutant.inaccessibleMethod();
+            System.err.println("ERROR: bad access succeeded\n");
+        } catch (IllegalAccessError iae) {
+            System.out.println("Got expected IllegalAccessError (method)");
+            if (VERBOSE) System.out.println("--- " + iae);
+        }
+
+        try {
+            Mutant.inaccessibleStaticMethod();
+            System.err.println("ERROR: bad access succeeded\n");
+        } catch (IllegalAccessError iae) {
+            System.out.println("Got expected IllegalAccessError (smethod)");
+            if (VERBOSE) System.out.println("--- " + iae);
+        }
+
+        try {
+            /* accessible static method in an inaccessible class */
+            InaccessibleClass.test();
+            System.err.println("ERROR: bad meth-class access succeeded\n");
+        } catch (IllegalAccessError iae) {
+            System.out.println("Got expected IllegalAccessError (meth-class)");
+            if (VERBOSE) System.out.println("--- " + iae);
+        }
+
+        try {
+            /* accessible static field in an inaccessible class */
+            int blah = InaccessibleClass.blah;
+            System.err.println("ERROR: bad field-class access succeeded\n");
+        } catch (IllegalAccessError iae) {
+            System.out.println("Got expected IllegalAccessError (field-class)");
+            if (VERBOSE) System.out.println("--- " + iae);
+        }
+
+        try {
+            /* inaccessible static method in an accessible class */
+            InaccessibleMethod.test();
+            System.err.println("ERROR: bad access succeeded\n");
+        } catch (IllegalAccessError iae) {
+            System.out.println("Got expected IllegalAccessError (meth-meth)");
+            if (VERBOSE) System.out.println("--- " + iae);
+        }
+    }
+}
diff --git a/tests/075-verification-error/src/MaybeAbstract.java b/tests/075-verification-error/src/MaybeAbstract.java
new file mode 100644
index 0000000..6d3b05b
--- /dev/null
+++ b/tests/075-verification-error/src/MaybeAbstract.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+public /*abstract*/ class MaybeAbstract {
+    public MaybeAbstract() {}
+    int foo() { return 0; }
+}
diff --git a/tests/075-verification-error/src/other/InaccessibleClass.java b/tests/075-verification-error/src/other/InaccessibleClass.java
new file mode 100644
index 0000000..b9bdfc4
--- /dev/null
+++ b/tests/075-verification-error/src/other/InaccessibleClass.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package other;
+
+public class InaccessibleClass {
+    public static void test() {}
+
+    public static int blah = 5;
+}
diff --git a/tests/075-verification-error/src/other/InaccessibleMethod.java b/tests/075-verification-error/src/other/InaccessibleMethod.java
new file mode 100644
index 0000000..0460373
--- /dev/null
+++ b/tests/075-verification-error/src/other/InaccessibleMethod.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package other;
+
+public class InaccessibleMethod {
+    public static void test() {}
+}
diff --git a/tests/075-verification-error/src/other/Mutant.java b/tests/075-verification-error/src/other/Mutant.java
new file mode 100644
index 0000000..ec4754b
--- /dev/null
+++ b/tests/075-verification-error/src/other/Mutant.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package other;
+
+/**
+ * Parts of this class will disappear or change form.
+ */
+public class Mutant {
+    public int disappearingField = 3;
+    public static int disappearingStaticField = 4;
+
+    public void disappearingMethod() {
+        System.out.println("bye");
+    }
+    public static void disappearingStaticMethod() {
+        System.out.println("kthxbai");
+    }
+
+    public int inaccessibleField = 5;
+    public static int inaccessibleStaticField = 6;
+
+    public void inaccessibleMethod() {
+        System.out.println("no");
+    }
+
+    public static void inaccessibleStaticMethod() {
+        System.out.println("nay");
+    }
+}
diff --git a/tests/075-verification-error/src2/MaybeAbstract.java b/tests/075-verification-error/src2/MaybeAbstract.java
new file mode 100644
index 0000000..8b70a07
--- /dev/null
+++ b/tests/075-verification-error/src2/MaybeAbstract.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+public abstract class MaybeAbstract {
+    public MaybeAbstract() {}
+    int foo() { return 0; }
+}
diff --git a/tests/075-verification-error/src2/other/InaccessibleClass.java b/tests/075-verification-error/src2/other/InaccessibleClass.java
new file mode 100644
index 0000000..812fac9
--- /dev/null
+++ b/tests/075-verification-error/src2/other/InaccessibleClass.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package other;
+
+/*package*/ class InaccessibleClass {
+    public static void test() {}
+
+    public static int blah = 5;
+}
diff --git a/tests/075-verification-error/src2/other/InaccessibleMethod.java b/tests/075-verification-error/src2/other/InaccessibleMethod.java
new file mode 100644
index 0000000..9fb844e
--- /dev/null
+++ b/tests/075-verification-error/src2/other/InaccessibleMethod.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package other;
+
+public class InaccessibleMethod {
+    /*package*/ static void test() {}
+}
diff --git a/tests/075-verification-error/src2/other/Mutant.java b/tests/075-verification-error/src2/other/Mutant.java
new file mode 100644
index 0000000..67cd36d
--- /dev/null
+++ b/tests/075-verification-error/src2/other/Mutant.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package other;
+
+/**
+ * Parts of this class will disappear or change form.
+ */
+public class Mutant {
+    //public int disappearingField = 3;
+    //public static int disappearingStaticField = 4;
+
+    //public static void disappearingMethod() {
+    //    System.out.println("bye");
+    //}
+    //public static void disappearingStaticMethod() {
+    //    System.out.println("kthxbai");
+    //}
+
+    protected int inaccessibleField = 5;
+    protected static int inaccessibleStaticField = 6;
+
+    protected void inaccessibleMethod() {
+        System.out.println("no");
+    }
+
+    protected static void inaccessibleStaticMethod() {
+        System.out.println("nay");
+    }
+}
diff --git a/tests/076-boolean-put/expected.txt b/tests/076-boolean-put/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/tests/076-boolean-put/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/tests/076-boolean-put/info.txt b/tests/076-boolean-put/info.txt
new file mode 100644
index 0000000..5b3ef4d
--- /dev/null
+++ b/tests/076-boolean-put/info.txt
@@ -0,0 +1,3 @@
+This checks a case where javac generates code that stores a byte into a
+boolean field.  The code as generated should not pass the verifier, so the
+verifier had to be "loosened" to allow this case.
diff --git a/tests/076-boolean-put/src/Main.java b/tests/076-boolean-put/src/Main.java
new file mode 100644
index 0000000..b325422
--- /dev/null
+++ b/tests/076-boolean-put/src/Main.java
@@ -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.
+ */
+
+/**
+ * Test access to private boolean fields.
+ *
+ * Accessing private boolean fields from an inner class causes the compiler
+ * to generate an accessor method that performs the boolean operation.
+ * Unfortunately the generated method takes an integer as an argument,
+ * not a boolean, which makes the verifier upset when the result of the
+ * operation is written back to a boolean field.
+ */
+public class Main {
+    private boolean mInstance;
+    private static boolean mStatic;
+
+    public static void main(String[] args) {
+        Main foo = new Main();
+        foo.test();
+
+        System.out.println("Done");
+    }
+
+    void test() {
+        Innard innard = new Innard();
+        innard.doStuff();
+    }
+
+    class Innard {
+        void doStuff() {
+            mInstance |= true;
+            mStatic |= true;
+        }
+    }
+}
diff --git a/tests/077-method-override/expected.txt b/tests/077-method-override/expected.txt
new file mode 100644
index 0000000..2e9bda3
--- /dev/null
+++ b/tests/077-method-override/expected.txt
@@ -0,0 +1,15 @@
+declaredInBase: Base
+notDeclaredInBase: Derived
+wasOverridden: Derived
+overrideWithPublic: Derived
+overrideProtectedWithPublic: Derived
+overridePublicWithProtected: Derived
+overridePublicWithPrivate: Base
+overridePrivateWithPublic: Base
+overridePrivateWithPublic: Derived
+overrideVirtualWithStatic: Base
+overrideVirtualWithStatic: Derived
+overrideStaticWithVirtual: Base
+overrideStaticWithVirtual: Derived
+Got expected exception - ovws
+Got expected exception - oswv
diff --git a/tests/077-method-override/info.txt b/tests/077-method-override/info.txt
new file mode 100644
index 0000000..914b4f2
--- /dev/null
+++ b/tests/077-method-override/info.txt
@@ -0,0 +1,2 @@
+Test various forms of method overrides, including some not allowed by the
+compiler but possible with separate compilation.
diff --git a/tests/077-method-override/src/Base.java b/tests/077-method-override/src/Base.java
new file mode 100644
index 0000000..befe2e2
--- /dev/null
+++ b/tests/077-method-override/src/Base.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+public class Base {
+    public void declaredInBase() {
+        System.out.println("declaredInBase: Base");
+    }
+
+    public void overridden() {
+        System.out.println("overridden: Base");
+    }
+
+    /* src2: removed */
+    public void wasOverridden() {
+        System.out.println("wasOverridden: Base");
+    }
+
+    public void callOverrideWithPublic() {
+        overrideWithPublic();
+    }
+    public void overrideWithPublic() {
+        System.out.println("overrideWithPublic: Base");
+    }
+
+    public void callOverridePublicWithProtected() {
+        overridePublicWithProtected();
+    }
+    /* src2: public */
+    protected void overridePublicWithProtected() {
+        System.out.println("overridePublicWithProtected: Base");
+    }
+
+    public void callOverrideProtectedWithPublic() {
+        overrideProtectedWithPublic();
+    }
+    protected void overrideProtectedWithPublic() {
+        System.out.println("overrideProtectedWithPublic: Base");
+    }
+
+    public void callOverridePublicWithPrivate() {
+        overridePublicWithPrivate();
+    }
+    /* src2: public */
+    private void overridePublicWithPrivate() {
+        System.out.println("overridePublicWithPrivate: Base");
+    }
+
+    public void callOverridePrivateWithPublic() {
+        overridePrivateWithPublic();
+    }
+    private void overridePrivateWithPublic() {
+        System.out.println("overridePrivateWithPublic: Base");
+    }
+
+    public void callOverrideVirtualWithStatic() {
+        overrideVirtualWithStatic();
+    }
+    /* src2: non-static */
+    public static void overrideVirtualWithStatic() {
+        System.out.println("overrideVirtualWithStatic: Base");
+    }
+
+    public void callOverrideStaticWithVirtual() {
+        overrideStaticWithVirtual();
+    }
+    /* src2: static */
+    public void overrideStaticWithVirtual() {
+        System.out.println("overrideStaticWithVirtual: Base");
+    }
+}
diff --git a/tests/077-method-override/src/Derived.java b/tests/077-method-override/src/Derived.java
new file mode 100644
index 0000000..7dc43d0
--- /dev/null
+++ b/tests/077-method-override/src/Derived.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+public class Derived extends Base {
+    public static void notDeclaredInBase() {
+        System.out.println("notDeclaredInBase: Derived");
+    }
+
+    public void overridden() {
+        System.out.println("overridden: Derived");
+    }
+
+    public void wasOverridden() {
+        System.out.println("wasOverridden: Derived");
+    }
+
+    public void overrideWithPublic() {
+        System.out.println("overrideWithPublic: Derived");
+    }
+
+    protected void overridePublicWithProtected() {
+        System.out.println("overridePublicWithProtected: Derived");
+    }
+
+    public void overrideProtectedWithPublic() {
+        System.out.println("overrideProtectedWithPublic: Derived");
+    }
+
+    private void overridePublicWithPrivate() {
+        System.out.println("overridePublicWithPrivate: Derived");
+    }
+
+    public void overridePrivateWithPublic() {
+        System.out.println("overridePrivateWithPublic: Derived");
+    }
+
+    /* not really an "override"; just has same method signature */
+    public static void overrideVirtualWithStatic() {
+        System.out.println("overrideVirtualWithStatic: Derived");
+    }
+
+    /* not really an "override"; just has same method signature */
+    public void overrideStaticWithVirtual() {
+        System.out.println("overrideStaticWithVirtual: Derived");
+    }
+}
diff --git a/tests/077-method-override/src/Main.java b/tests/077-method-override/src/Main.java
new file mode 100644
index 0000000..2d10ee0
--- /dev/null
+++ b/tests/077-method-override/src/Main.java
@@ -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.
+ */
+
+public class Main {
+    public static void main(String args[]) {
+        Derived derived = new Derived();
+
+        derived.declaredInBase();
+        derived.notDeclaredInBase();
+        derived.wasOverridden();
+
+        derived.callOverrideWithPublic();
+        derived.callOverrideProtectedWithPublic();
+        derived.callOverridePublicWithProtected();
+        derived.callOverridePublicWithPrivate();
+        derived.callOverridePrivateWithPublic();
+        derived.overridePrivateWithPublic();
+        derived.callOverrideVirtualWithStatic();
+        derived.overrideVirtualWithStatic();
+        derived.callOverrideStaticWithVirtual();
+        derived.overrideStaticWithVirtual();
+
+        try {
+            ((Base)derived).overrideVirtualWithStatic();
+        } catch (NoSuchMethodError nsme) {
+            /* NSME is subclass of ICCE, so check it explicitly */
+            System.err.println("Got NSME - ovws");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected exception - ovws");
+        }
+
+        try {
+            ((Base)derived).overrideStaticWithVirtual();
+        } catch (NoSuchMethodError nsme) {
+            System.err.println("Got NSME - ovws");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected exception - oswv");
+        }
+    }
+}
diff --git a/tests/077-method-override/src2/Base.java b/tests/077-method-override/src2/Base.java
new file mode 100644
index 0000000..ab2a28b
--- /dev/null
+++ b/tests/077-method-override/src2/Base.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+public class Base {
+    public void declaredInBase() {
+        System.out.println("declaredInBase: Base");
+    }
+
+    public void overridden() {
+        System.out.println("overridden: Base");
+    }
+
+    /* src2: removed */
+    //public void wasOverridden() {
+    //    System.out.println("wasOverridden: Base");
+    //}
+
+    public void callOverrideWithPublic() {
+        overrideWithPublic();
+    }
+    public void overrideWithPublic() {
+        System.out.println("overrideWithPublic: Base");
+    }
+
+    public void callOverridePublicWithProtected() {
+        overridePublicWithProtected();
+    }
+    /* src2: public */
+    public void overridePublicWithProtected() {
+        System.out.println("overridePublicWithProtected: Base");
+    }
+
+    public void callOverrideProtectedWithPublic() {
+        overrideProtectedWithPublic();
+    }
+    protected void overrideProtectedWithPublic() {
+        System.out.println("overrideProtectedWithPublic: Base");
+    }
+
+    public void callOverridePublicWithPrivate() {
+        overridePublicWithPrivate();
+    }
+    /* src2: public */
+    public void overridePublicWithPrivate() {
+        System.out.println("overridePublicWithPrivate: Base");
+    }
+
+    public void callOverridePrivateWithPublic() {
+        overridePrivateWithPublic();
+    }
+    private void overridePrivateWithPublic() {
+        System.out.println("overridePrivateWithPublic: Base");
+    }
+
+    public void callOverrideVirtualWithStatic() {
+        overrideVirtualWithStatic();
+    }
+    /* src2: non-static */
+    public void overrideVirtualWithStatic() {
+        System.out.println("overrideVirtualWithStatic: Base");
+    }
+
+    public void callOverrideStaticWithVirtual() {
+        overrideStaticWithVirtual();
+    }
+    public static void overrideStaticWithVirtual() {
+        System.out.println("overrideStaticWithVirtual: Base");
+    }
+}
diff --git a/tests/078-polymorphic-virtual/expected.txt b/tests/078-polymorphic-virtual/expected.txt
new file mode 100644
index 0000000..0d29728
--- /dev/null
+++ b/tests/078-polymorphic-virtual/expected.txt
@@ -0,0 +1,3 @@
+10000000
+20000000
+30000000
diff --git a/tests/078-polymorphic-virtual/info.txt b/tests/078-polymorphic-virtual/info.txt
new file mode 100644
index 0000000..7c8a561
--- /dev/null
+++ b/tests/078-polymorphic-virtual/info.txt
@@ -0,0 +1,2 @@
+Stress test predicted chaining for overloaded virtual callsite with 3 resolved
+calless invoked 10,000,000 times each in three threads.
diff --git a/tests/078-polymorphic-virtual/src/Base.java b/tests/078-polymorphic-virtual/src/Base.java
new file mode 100644
index 0000000..ec3aadd
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Base.java
@@ -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.
+ */
+
+public class Base extends Thread {
+    int value;
+
+    public void run() {
+        for (int i = 0; i < 10000000; i++) {
+            incrimentValue();
+        }
+    }
+
+    public void incrimentValue() {
+    }
+
+    public int getValue() {
+        return value;
+    }
+}
diff --git a/tests/078-polymorphic-virtual/src/Derived1.java b/tests/078-polymorphic-virtual/src/Derived1.java
new file mode 100644
index 0000000..57bd3b0
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Derived1.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Derived1 extends Base {
+    public void incrimentValue() {
+        value += 1;
+    }
+}
diff --git a/tests/078-polymorphic-virtual/src/Derived2.java b/tests/078-polymorphic-virtual/src/Derived2.java
new file mode 100644
index 0000000..1d7de57
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Derived2.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Derived2 extends Base {
+    public void incrimentValue() {
+        value += 2;
+    }
+}
diff --git a/tests/078-polymorphic-virtual/src/Derived3.java b/tests/078-polymorphic-virtual/src/Derived3.java
new file mode 100644
index 0000000..c2594d2
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Derived3.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Derived3 extends Base {
+    public void incrimentValue() {
+        value += 3;
+    }
+}
diff --git a/tests/078-polymorphic-virtual/src/Main.java b/tests/078-polymorphic-virtual/src/Main.java
new file mode 100644
index 0000000..0514e53
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Main.java
@@ -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.
+ */
+
+public class Main {
+    public static void main(String args[]) {
+        Derived1 derived1 = new Derived1();
+        Derived2 derived2 = new Derived2();
+        Derived3 derived3 = new Derived3();
+
+        derived1.start();
+        derived2.start();
+        derived3.start();
+
+        try {
+            derived1.join();
+            derived2.join();
+            derived3.join();
+        } catch (Exception e) {
+            System.out.println(e);
+            return;
+        }
+
+        System.out.println(derived1.getValue());
+        System.out.println(derived2.getValue());
+        System.out.println(derived3.getValue());
+    }
+}
diff --git a/tests/079-phantom/expected.txt b/tests/079-phantom/expected.txt
new file mode 100644
index 0000000..a932b77
--- /dev/null
+++ b/tests/079-phantom/expected.txt
@@ -0,0 +1,14 @@
+start
+Created Bitmap one: 10x10 (100)
+Created Bitmap two: 20x20 (101)
+Created Bitmap three/four: 20x20 (101)
+Drawing Bitmap two: 20x20 (101)
+nulling 1
+freeNativeStorage: 100
+nulling 2
+nulling 3
+nulling 4
+freeNativeStorage: 101
+intr
+Bitmap has shut down
+done
diff --git a/tests/079-phantom/info.txt b/tests/079-phantom/info.txt
new file mode 100644
index 0000000..d974e2c
--- /dev/null
+++ b/tests/079-phantom/info.txt
@@ -0,0 +1 @@
+Exercise phantom references.
diff --git a/tests/079-phantom/src/Bitmap.java b/tests/079-phantom/src/Bitmap.java
new file mode 100644
index 0000000..9d03cbd
--- /dev/null
+++ b/tests/079-phantom/src/Bitmap.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.PhantomReference;
+import java.util.ArrayList;
+
+public class Bitmap {
+    String mName;           /* for debugging */
+    int mWidth, mHeight;
+    Bitmap.NativeWrapper mNativeWrapper;
+
+    private static int sSerial = 100;
+    private static ArrayList sPhantomList = new ArrayList<PhantomWrapper>();
+    private static ReferenceQueue<PhantomWrapper> sPhantomQueue =
+            new ReferenceQueue<PhantomWrapper>();
+    private static BitmapWatcher sWatcher = new BitmapWatcher(sPhantomQueue);
+    static {
+        sWatcher.start();
+    };
+
+    Bitmap(String name, int width, int height, Bitmap.NativeWrapper nativeData) {
+        mName = name;
+        mWidth = width;
+        mHeight = height;
+        mNativeWrapper = nativeData;
+
+        System.out.println("Created " + this);
+    }
+
+    public String toString() {
+        return "Bitmap " + mName + ": " + mWidth + "x" + mHeight + " (" +
+                mNativeWrapper.mNativeData + ")";
+    }
+
+    public void drawAt(int x, int y) {
+        System.out.println("Drawing " + this);
+    }
+
+    public static void shutDown() {
+        sWatcher.shutDown();
+        try {
+            sWatcher.join();
+        } catch (InterruptedException ie) {
+            System.out.println("join intr");
+        }
+        System.out.println("Bitmap has shut down");
+    }
+
+    /*
+     * Pretend we're allocating native storage.  Just returns a unique
+     * serial number.
+     */
+    static Bitmap.NativeWrapper allocNativeStorage(int width, int height) {
+        int nativeData;
+
+        synchronized (Bitmap.class) {
+            nativeData = sSerial++;
+        }
+
+        Bitmap.NativeWrapper wrapper = new Bitmap.NativeWrapper(nativeData);
+        PhantomWrapper phan = new PhantomWrapper(wrapper, sPhantomQueue,
+                nativeData);
+        sPhantomList.add(phan);
+        return wrapper;
+    }
+
+    static void freeNativeStorage(int nativeDataPtr) {
+        System.out.println("freeNativeStorage: " + nativeDataPtr);
+    }
+
+    /*
+     * Wraps a native data pointer in an object.  When this object is no
+     * longer referenced, we free the native data.
+     */
+    static class NativeWrapper {
+        public NativeWrapper(int nativeDataPtr) {
+            mNativeData = nativeDataPtr;
+        }
+        public int mNativeData;
+
+        /*
+        @Override
+        protected void finalize() throws Throwable {
+            System.out.println("finalized " + mNativeData);
+        }
+        */
+    }
+}
+
+/*
+ * Keep an eye on the native data.
+ *
+ * We keep a copy of the native data pointer value, and set the wrapper
+ * as our referent.  We need the copy because you can't get the referred-to
+ * object back out of a PhantomReference.
+ */
+class PhantomWrapper extends PhantomReference {
+    PhantomWrapper(Bitmap.NativeWrapper wrapper,
+        ReferenceQueue<PhantomWrapper> queue, int nativeDataPtr)
+    {
+        super(wrapper, queue);
+        mNativeData = nativeDataPtr;
+    }
+
+    public int mNativeData;
+}
+
+/*
+ * Thread that watches for un-referenced bitmap data.
+ */
+class BitmapWatcher extends Thread {
+    ReferenceQueue<PhantomWrapper> mQueue;
+    volatile boolean mQuit = false;
+
+    BitmapWatcher(ReferenceQueue<PhantomWrapper> queue) {
+        mQueue = queue;
+        setName("Bitmap Watcher");
+    }
+
+    public void run() {
+        while (!mQuit) {
+            try {
+                PhantomWrapper ref = (PhantomWrapper) mQueue.remove();
+                //System.out.println("dequeued ref " + ref.mNativeData +
+                //    " - " + ref);
+                Bitmap.freeNativeStorage(ref.mNativeData);
+                //ref.clear();
+            } catch (InterruptedException ie) {
+                System.out.println("intr");
+            }
+        }
+    }
+
+    public void shutDown() {
+        mQuit = true;
+        interrupt();
+    }
+}
diff --git a/tests/079-phantom/src/Main.java b/tests/079-phantom/src/Main.java
new file mode 100644
index 0000000..9c459c9
--- /dev/null
+++ b/tests/079-phantom/src/Main.java
@@ -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.
+ */
+
+public class Main {
+    Bitmap mBitmap1, mBitmap2, mBitmap3, mBitmap4;
+
+    public static void sleep(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException ie) {
+            System.err.println("sleep interrupted");
+        }
+    }
+
+    public static void main(String args[]) {
+        System.out.println("start");
+
+        Main main = new Main();
+        main.run();
+
+        sleep(1000);
+        System.out.println("done");
+    }
+
+    public void run() {
+        createBitmaps();
+
+        System.gc();
+        sleep(250);
+
+        mBitmap2.drawAt(0, 0);
+
+        System.out.println("nulling 1");
+        mBitmap1 = null;
+        System.gc();
+        sleep(500);
+
+        System.out.println("nulling 2");
+        mBitmap2 = null;
+        System.gc();
+        sleep(500);
+
+        System.out.println("nulling 3");
+        mBitmap3 = null;
+        System.gc();
+        sleep(500);
+
+        System.out.println("nulling 4");
+        mBitmap4 = null;
+        System.gc();
+        sleep(500);
+
+        Bitmap.shutDown();
+    }
+
+    /*
+     * Create bitmaps.
+     *
+     * bitmap1 is 10x10 and unique
+     * bitmap2 and bitmap3 are 20x20 and share the same storage.
+     * bitmap4 is just another reference to bitmap3
+     *
+     * When we return there should be no local refs lurking on the stack.
+     */
+    public void createBitmaps() {
+        Bitmap.NativeWrapper dataA = Bitmap.allocNativeStorage(10, 10);
+        Bitmap.NativeWrapper dataB = Bitmap.allocNativeStorage(20, 20);
+        mBitmap1 = new Bitmap("one", 10, 10, dataA);
+        mBitmap2 = new Bitmap("two", 20, 20, dataB);
+        mBitmap3 = mBitmap4 = new Bitmap("three/four", 20, 20, dataB);
+    }
+}
diff --git a/tests/080-oom-throw/expected.txt b/tests/080-oom-throw/expected.txt
new file mode 100644
index 0000000..811f68c
--- /dev/null
+++ b/tests/080-oom-throw/expected.txt
@@ -0,0 +1,2 @@
+Array allocation failed
+Instance allocation failed
diff --git a/tests/080-oom-throw/info.txt b/tests/080-oom-throw/info.txt
new file mode 100644
index 0000000..e8ae6f6
--- /dev/null
+++ b/tests/080-oom-throw/info.txt
@@ -0,0 +1,3 @@
+Inject memory allocation failures for NEW_ARRAY and NEW_INSTANCE and make sure
+the JIT'ed code handles OOM exception correctly since it cannot fall back to
+the interpreter and re-execute the bytecode.
diff --git a/tests/080-oom-throw/src/Main.java b/tests/080-oom-throw/src/Main.java
new file mode 100644
index 0000000..3d75f3d
--- /dev/null
+++ b/tests/080-oom-throw/src/Main.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+public class Main {
+    static class ArrayMemEater {
+        static int blowup(char[][] holder, int size) {
+            int i = 0;
+            try {
+                for ( ; i < size; i++)
+                    holder[i] = new char[128];
+            } catch (OutOfMemoryError oome) {
+                return i;
+            }
+
+            return size;
+        }
+
+        static void confuseCompilerOptimization(char[][] holder) {
+        }
+    }
+
+    static class InstanceMemEater {
+        InstanceMemEater next;
+        double d1, d2, d3, d4, d5, d6, d7, d8;
+
+        static InstanceMemEater blowup() {
+            InstanceMemEater memEater;
+            try {
+                memEater = new InstanceMemEater();
+            } catch (OutOfMemoryError e) {
+                memEater = null;
+            }
+            return memEater;
+        }
+
+        static void confuseCompilerOptimization(InstanceMemEater memEater) {
+        }
+    }
+
+    static void triggerArrayOOM() {
+        int size = 1 * 1024 * 1024;
+        char[][] holder = new char[size][];
+
+        int count = ArrayMemEater.blowup(holder, size);
+        ArrayMemEater.confuseCompilerOptimization(holder);
+        if (count < size) {
+            System.out.println("Array allocation failed");
+        }
+    }
+
+    static void triggerInstanceOOM() {
+        InstanceMemEater memEater = InstanceMemEater.blowup();
+        InstanceMemEater lastMemEater = memEater;
+        do {
+            lastMemEater.next = InstanceMemEater.blowup();
+            lastMemEater = lastMemEater.next;
+        } while (lastMemEater != null);
+        memEater.confuseCompilerOptimization(memEater);
+        System.out.println("Instance allocation failed");
+    }
+
+    public static void main(String[] args) {
+        triggerArrayOOM();
+        triggerInstanceOOM();
+    }
+}
diff --git a/tests/081-hot-exceptions/expected.txt b/tests/081-hot-exceptions/expected.txt
new file mode 100644
index 0000000..2432ff4
--- /dev/null
+++ b/tests/081-hot-exceptions/expected.txt
@@ -0,0 +1,2 @@
+sum = 0
+exception = 1024
diff --git a/tests/081-hot-exceptions/info.txt b/tests/081-hot-exceptions/info.txt
new file mode 100644
index 0000000..cc514f3
--- /dev/null
+++ b/tests/081-hot-exceptions/info.txt
@@ -0,0 +1,3 @@
+Make a hot exception-throwing path to stress test how the trace builder handles
+exceptions encountered during trace selection. The existence of exceptions will
+cause a control flow change to deviate from the current method.
diff --git a/tests/081-hot-exceptions/src/Main.java b/tests/081-hot-exceptions/src/Main.java
new file mode 100644
index 0000000..90e7af2
--- /dev/null
+++ b/tests/081-hot-exceptions/src/Main.java
@@ -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.
+ */
+
+public class Main {
+    static class ArrayObj {
+        int[] array;
+
+        int getArrayElement(int i) throws NullPointerException {
+            return array[i];
+        }
+    }
+
+    public static void main(String[] args) {
+        ArrayObj arrayObj2 = new ArrayObj();
+        int sum = 0;
+        int exception = 0;
+
+        for (int i = 0; i < 1024; i++) {
+            try {
+                // A hot method invocation that always encounters exceptions
+                sum += arrayObj2.getArrayElement(i);
+            } catch (NullPointerException npe) {
+                exception++;
+            }
+        }
+        System.out.println("sum = " + sum);
+        System.out.println("exception = " + exception);
+    }
+}
diff --git a/tests/082-inline-execute/expected.txt b/tests/082-inline-execute/expected.txt
new file mode 100644
index 0000000..5059fe8
--- /dev/null
+++ b/tests/082-inline-execute/expected.txt
@@ -0,0 +1,8 @@
+Length of  : 0
+Length of x : 1
+Length of 01234567890123456789012345678901234567890123456789012345678901234567890123456789 : 80
+Now is the time[0] = "N"
+Now is the time[1] = "o"
+Now is the time[10] = " "
+Now is the time[last] = "e"
+Num throws 2000
diff --git a/tests/082-inline-execute/info.txt b/tests/082-inline-execute/info.txt
new file mode 100644
index 0000000..ddc31fe
--- /dev/null
+++ b/tests/082-inline-execute/info.txt
@@ -0,0 +1,8 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+This test covers the string inline-execute tests, and it done in a
+looping manner to ensure that the tests are translated when a Jit is
+active.
diff --git a/tests/082-inline-execute/src/Main.java b/tests/082-inline-execute/src/Main.java
new file mode 100644
index 0000000..b512091
--- /dev/null
+++ b/tests/082-inline-execute/src/Main.java
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test for Jit's handling of string inline-execute.  Should be tested
+ * twice - once using self-cosimulation (if available) and once without.
+ * The non-self-cosimulation test ensures that the answer computed the first
+ * time through (via the interpreter) is the same after looping enough
+ * to trigger translation.
+ */
+
+import junit.framework.Assert;
+
+public class Main {
+    public static void main(String args[]) {
+        int i;
+        stringLengthTest();
+        stringCharAtTest();
+        stringIndexOfTest();
+        for (i = 0; i < 1000; i++)
+            stringCompareToTest();
+    }
+
+    public static void stringLengthTest() {
+        String str0 = "";
+        String str1 = "x";
+        String str80 = "01234567890123456789012345678901234567890123456789012345678901234567890123456789";
+        int len0 = str0.length();
+        int len1 = str1.length();
+        int len80 = str80.length();
+        int i;
+
+        System.out.println("Length of " + str0 + " : " + len0);
+        System.out.println("Length of " + str1 + " : " + len1);
+        System.out.println("Length of " + str80 + " : " + len80);
+
+        for (i = 0; i < 1000; i++) {
+            assert(str0.length() == len0);
+            assert(str1.length() == len1);
+            assert(str80.length() == len80);
+        }
+    }
+
+    public static void stringCharAtTest() {
+        String testStr = "Now is the time";
+        int under = -1;
+        int over = testStr.length();
+        int numThrown = 0;
+        int numNotThrown = 0;
+        int at0 = testStr.charAt(0);
+        int at1 = testStr.charAt(1);
+        int at10 = testStr.charAt(10);
+        int atLast = testStr.charAt(testStr.length()-1);
+        int i;
+
+        System.out.println(testStr + "[0] = \"" + (char)at0 + "\"");
+        System.out.println(testStr + "[1] = \"" + (char)at1 + "\"");
+        System.out.println(testStr + "[10] = \"" + (char)at10 + "\"");
+        System.out.println(testStr + "[last] = \"" + (char)atLast + "\"");
+
+        for (i = 0; i < 1000; i++) {
+            assert(at0 == testStr.charAt(0));
+            assert(at1 == testStr.charAt(1));
+            assert(at10 == testStr.charAt(10));
+            assert(atLast == testStr.charAt(testStr.length()-1));
+        }
+
+        for (i = 0; i < 1000; i++) {
+            try {
+                testStr.charAt(under);
+                numNotThrown++;
+            } catch (StringIndexOutOfBoundsException sioobe) {
+                numThrown++;
+            }
+            try {
+                testStr.charAt(over);
+                numNotThrown++;
+            } catch (StringIndexOutOfBoundsException sioobe) {
+                numThrown++;
+            }
+        }
+        assert(numNotThrown == 0);
+        System.out.println("Num throws " + numThrown);
+    }
+
+
+    public static void stringIndexOfTest() {
+        String str0 = "";
+        String str3 = "abc";
+        String str10 = "abcdefghij";
+        String str40 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc";
+        int i;
+
+        for (i = 0; i < 1000; i++) {
+            assert(str0.indexOf('a') == -1);
+            assert(str3.indexOf('a') == 0);
+            assert(str3.indexOf('b') == 1);
+            assert(str3.indexOf('c') == 2);
+            assert(str10.indexOf('j') == 9);
+            assert(str40.indexOf('a') == 0);
+            assert(str40.indexOf('b') == 38);
+            assert(str40.indexOf('c') == 39);
+            assert(str0.indexOf('a',20) == -1);
+            assert(str0.indexOf('a',0) == -1);
+            assert(str0.indexOf('a',-1) == -1);
+            assert(str3.indexOf('a',0) == 0);
+            assert(str3.indexOf('a',1) == -1);
+            assert(str3.indexOf('a',1234) == -1);
+            assert(str3.indexOf('b',0) == 1);
+            assert(str3.indexOf('b',1) == 1);
+            assert(str3.indexOf('c',2) == 2);
+            assert(str10.indexOf('j',5) == 9);
+            assert(str10.indexOf('j',9) == 9);
+            assert(str40.indexOf('a',10) == 10);
+            assert(str40.indexOf('b',40) == -1);
+        }
+
+    }
+
+    public static void stringCompareToTest() {
+        String test = "0123456789";
+        String test1 = new String("0123456789");    // different object
+        String test2 = new String("0123456780");    // different value
+        String offset = new String("xxx0123456789yyy");
+        String sub = offset.substring(3, 13);
+        String str32 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+        String str33 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy";
+        String lc = "abcdefg";
+        String uc = "ABCDEFG";
+        Object blah = new Object();
+
+        for (int i = 0; i < 100; i++) {
+            String y = lc.toUpperCase();
+            Assert.assertTrue(y.equals(uc));
+        }
+
+        Assert.assertEquals(str32.compareTo(str33), -1);
+        Assert.assertEquals(str33.compareTo(str32), 1);
+
+        Assert.assertTrue(test.equals(test));
+        Assert.assertTrue(test.equals(test1));
+        Assert.assertFalse(test.equals(test2));
+
+        Assert.assertEquals(test.compareTo(test1), 0);
+        Assert.assertTrue(test1.compareTo(test2) > 0);
+        Assert.assertTrue(test2.compareTo(test1) < 0);
+
+        /* compare string with a nonzero offset, in left/right side */
+        Assert.assertEquals(test.compareTo(sub), 0);
+        Assert.assertEquals(sub.compareTo(test), 0);
+        Assert.assertTrue(test.equals(sub));
+        Assert.assertTrue(sub.equals(test));
+        /* same base, one is a substring */
+        Assert.assertFalse(offset.equals(sub));
+        Assert.assertFalse(sub.equals(offset));
+        /* wrong class */
+        Assert.assertFalse(test.equals(blah));
+
+        /* null ptr - throw */
+        try {
+            test.compareTo(null);
+            Assert.fail("didn't get expected npe");
+        } catch (NullPointerException npe) {
+        }
+        /* null ptr - ok */
+        Assert.assertFalse(test.equals(null));
+
+        test = test.substring(1);
+        Assert.assertTrue(test.equals("123456789"));
+        Assert.assertFalse(test.equals(test1));
+
+        test = test.substring(1);
+        Assert.assertTrue(test.equals("23456789"));
+
+        test = test.substring(1);
+        Assert.assertTrue(test.equals("3456789"));
+
+        test = test.substring(1);
+        Assert.assertTrue(test.equals("456789"));
+
+        test = test.substring(3,5);
+        Assert.assertTrue(test.equals("78"));
+
+        test = "this/is/a/path";
+        String[] strings = test.split("/");
+        Assert.assertEquals(4, strings.length);
+
+        Assert.assertEquals("this is a path", test.replaceAll("/", " "));
+        Assert.assertEquals("this is a path", test.replace("/", " "));
+    }
+
+}
diff --git a/tests/082-inline-execute/src/junit/framework/Assert.java b/tests/082-inline-execute/src/junit/framework/Assert.java
new file mode 100644
index 0000000..364e646
--- /dev/null
+++ b/tests/082-inline-execute/src/junit/framework/Assert.java
@@ -0,0 +1,291 @@
+package junit.framework;
+
+/**
+ * A set of assert methods.  Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+    /**
+     * Protect constructor since it is a static only class
+     */
+    protected Assert() {
+    }
+
+    /**
+     * Asserts that a condition is true. If it isn't it throws
+     * an AssertionFailedError with the given message.
+     */
+    static public void assertTrue(String message, boolean condition) {
+        if (!condition)
+            fail(message);
+    }
+    /**
+     * Asserts that a condition is true. If it isn't it throws
+     * an AssertionFailedError.
+     */
+    static public void assertTrue(boolean condition) {
+        assertTrue(null, condition);
+    }
+    /**
+     * Asserts that a condition is false. If it isn't it throws
+     * an AssertionFailedError with the given message.
+     */
+    static public void assertFalse(String message, boolean condition) {
+        assertTrue(message, !condition);
+    }
+    /**
+     * Asserts that a condition is false. If it isn't it throws
+     * an AssertionFailedError.
+     */
+    static public void assertFalse(boolean condition) {
+        assertFalse(null, condition);
+    }
+    /**
+     * Fails a test with the given message.
+     */
+    static public void fail(String message) {
+        throw new AssertionFailedError(message);
+    }
+    /**
+     * Fails a test with no message.
+     */
+    static public void fail() {
+        fail(null);
+    }
+    /**
+     * Asserts that two objects are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, Object expected, Object actual) {
+        if (expected == null && actual == null)
+            return;
+        if (expected != null && expected.equals(actual))
+            return;
+        failNotEquals(message, expected, actual);
+    }
+    /**
+     * Asserts that two objects are equal. If they are not
+     * an AssertionFailedError is thrown.
+     */
+    static public void assertEquals(Object expected, Object actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two Strings are equal.
+     */
+    static public void assertEquals(String message, String expected, String actual) {
+        if (expected == null && actual == null)
+            return;
+        if (expected != null && expected.equals(actual))
+            return;
+        throw new ComparisonFailure(message, expected, actual);
+    }
+    /**
+     * Asserts that two Strings are equal.
+     */
+    static public void assertEquals(String expected, String actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two doubles are equal concerning a delta.  If they are not
+     * an AssertionFailedError is thrown with the given message.  If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(String message, double expected, double actual, double delta) {
+        // handle infinity specially since subtracting to infinite values gives NaN and the
+        // the following test fails
+        if (Double.isInfinite(expected)) {
+            if (!(expected == actual))
+                failNotEquals(message, new Double(expected), new Double(actual));
+        } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
+            failNotEquals(message, new Double(expected), new Double(actual));
+    }
+    /**
+     * Asserts that two doubles are equal concerning a delta. If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(double expected, double actual, double delta) {
+        assertEquals(null, expected, actual, delta);
+    }
+    /**
+     * Asserts that two floats are equal concerning a delta. If they are not
+     * an AssertionFailedError is thrown with the given message.  If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(String message, float expected, float actual, float delta) {
+         // handle infinity specially since subtracting to infinite values gives NaN and the
+        // the following test fails
+        if (Float.isInfinite(expected)) {
+            if (!(expected == actual))
+                failNotEquals(message, new Float(expected), new Float(actual));
+        } else if (!(Math.abs(expected-actual) <= delta))
+              failNotEquals(message, new Float(expected), new Float(actual));
+    }
+    /**
+     * Asserts that two floats are equal concerning a delta. If the expected
+     * value is infinity then the delta value is ignored.
+     */
+    static public void assertEquals(float expected, float actual, float delta) {
+        assertEquals(null, expected, actual, delta);
+    }
+    /**
+     * Asserts that two longs are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, long expected, long actual) {
+        assertEquals(message, new Long(expected), new Long(actual));
+    }
+    /**
+     * Asserts that two longs are equal.
+     */
+    static public void assertEquals(long expected, long actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two booleans are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, boolean expected, boolean actual) {
+            assertEquals(message, new Boolean(expected), new Boolean(actual));
+      }
+    /**
+     * Asserts that two booleans are equal.
+      */
+    static public void assertEquals(boolean expected, boolean actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two bytes are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+      static public void assertEquals(String message, byte expected, byte actual) {
+        assertEquals(message, new Byte(expected), new Byte(actual));
+    }
+    /**
+        * Asserts that two bytes are equal.
+     */
+    static public void assertEquals(byte expected, byte actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two chars are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+      static public void assertEquals(String message, char expected, char actual) {
+            assertEquals(message, new Character(expected), new Character(actual));
+      }
+    /**
+     * Asserts that two chars are equal.
+     */
+      static public void assertEquals(char expected, char actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two shorts are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertEquals(String message, short expected, short actual) {
+            assertEquals(message, new Short(expected), new Short(actual));
+    }
+      /**
+     * Asserts that two shorts are equal.
+     */
+    static public void assertEquals(short expected, short actual) {
+        assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that two ints are equal. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+      static public void assertEquals(String message, int expected, int actual) {
+        assertEquals(message, new Integer(expected), new Integer(actual));
+      }
+      /**
+        * Asserts that two ints are equal.
+     */
+      static public void assertEquals(int expected, int actual) {
+          assertEquals(null, expected, actual);
+    }
+    /**
+     * Asserts that an object isn't null.
+     */
+    static public void assertNotNull(Object object) {
+        assertNotNull(null, object);
+    }
+    /**
+     * Asserts that an object isn't null. If it is
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertNotNull(String message, Object object) {
+        assertTrue(message, object != null);
+    }
+    /**
+     * Asserts that an object is null.
+     */
+    static public void assertNull(Object object) {
+        assertNull(null, object);
+    }
+    /**
+     * Asserts that an object is null.  If it is not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertNull(String message, Object object) {
+        assertTrue(message, object == null);
+    }
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * an AssertionFailedError is thrown with the given message.
+     */
+    static public void assertSame(String message, Object expected, Object actual) {
+        if (expected == actual)
+            return;
+        failNotSame(message, expected, actual);
+    }
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * the same an AssertionFailedError is thrown.
+     */
+    static public void assertSame(Object expected, Object actual) {
+        assertSame(null, expected, actual);
+    }
+     /**
+      * Asserts that two objects refer to the same object. If they are not
+      * an AssertionFailedError is thrown with the given message.
+      */
+    static public void assertNotSame(String message, Object expected, Object actual) {
+        if (expected == actual)
+            failSame(message);
+    }
+    /**
+     * Asserts that two objects refer to the same object. If they are not
+     * the same an AssertionFailedError is thrown.
+     */
+    static public void assertNotSame(Object expected, Object actual) {
+        assertNotSame(null, expected, actual);
+    }
+
+    static private void failSame(String message) {
+        String formatted= "";
+         if (message != null)
+             formatted= message+" ";
+         fail(formatted+"expected not same");
+    }
+
+    static private void failNotSame(String message, Object expected, Object actual) {
+        String formatted= "";
+        if (message != null)
+            formatted= message+" ";
+        fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+    }
+
+    static private void failNotEquals(String message, Object expected, Object actual) {
+        fail(format(message, expected, actual));
+    }
+
+    static String format(String message, Object expected, Object actual) {
+        String formatted= "";
+        if (message != null)
+            formatted= message+" ";
+        return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+    }
+}
diff --git a/tests/082-inline-execute/src/junit/framework/AssertionFailedError.java b/tests/082-inline-execute/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..e9cb3a3
--- /dev/null
+++ b/tests/082-inline-execute/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,13 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends Error {
+
+    public AssertionFailedError () {
+    }
+    public AssertionFailedError (String message) {
+        super (message);
+    }
+}
diff --git a/tests/082-inline-execute/src/junit/framework/ComparisonFailure.java b/tests/082-inline-execute/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..0cb2cee
--- /dev/null
+++ b/tests/082-inline-execute/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,68 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ *
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+    private String fExpected;
+    private String fActual;
+
+    /**
+     * Constructs a comparison failure.
+     * @param message the identifying message or null
+     * @param expected the expected string value
+     * @param actual the actual string value
+     */
+    public ComparisonFailure (String message, String expected, String actual) {
+        super (message);
+        fExpected= expected;
+        fActual= actual;
+    }
+
+    /**
+     * Returns "..." in place of common prefix and "..." in
+     * place of common suffix between expected and actual.
+     *
+     * @see java.lang.Throwable#getMessage()
+     */
+    public String getMessage() {
+        if (fExpected == null || fActual == null)
+            return Assert.format(super.getMessage(), fExpected, fActual);
+
+        int end= Math.min(fExpected.length(), fActual.length());
+
+        int i= 0;
+        for(; i < end; i++) {
+            if (fExpected.charAt(i) != fActual.charAt(i))
+                break;
+        }
+        int j= fExpected.length()-1;
+        int k= fActual.length()-1;
+        for (; k >= i && j >= i; k--,j--) {
+            if (fExpected.charAt(j) != fActual.charAt(k))
+                break;
+        }
+        String actual, expected;
+
+        // equal strings
+        if (j < i && k < i) {
+            expected= fExpected;
+            actual= fActual;
+        } else {
+            expected= fExpected.substring(i, j+1);
+            actual= fActual.substring(i, k+1);
+            if (i <= end && i > 0) {
+                expected= "..."+expected;
+                actual= "..."+actual;
+            }
+
+            if (j < fExpected.length()-1)
+                expected= expected+"...";
+            if (k < fActual.length()-1)
+                actual= actual+"...";
+        }
+        return Assert.format(super.getMessage(), expected, actual);
+    }
+}
diff --git a/tests/083-jit-regressions/expected.txt b/tests/083-jit-regressions/expected.txt
new file mode 100644
index 0000000..1f30d21
--- /dev/null
+++ b/tests/083-jit-regressions/expected.txt
@@ -0,0 +1,3 @@
+b2296099 passes
+b2302318 passes
+b2487514 passes
diff --git a/tests/083-jit-regressions/info.txt b/tests/083-jit-regressions/info.txt
new file mode 100644
index 0000000..b791aba
--- /dev/null
+++ b/tests/083-jit-regressions/info.txt
@@ -0,0 +1,10 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+This test covers JIT regressions
+
+2296099 JIT shift bug
+2302318 Crash during spin-on-suspend testing
+2487514 Missed exception in PriorityBlockingQueueTest.testToArray1_BadArg
diff --git a/tests/083-jit-regressions/src/Main.java b/tests/083-jit-regressions/src/Main.java
new file mode 100644
index 0000000..1f1dee3
--- /dev/null
+++ b/tests/083-jit-regressions/src/Main.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+import java.util.concurrent.*;
+
+/**
+ * Test for Jit regressions.
+ */
+public class Main {
+    public static void main(String args[]) throws Exception {
+        b2296099Test();
+        b2302318Test();
+        b2487514Test();
+    }
+
+    static void b2296099Test() throws Exception {
+       int x = -1190771042;
+       int dist = 360530809;
+       int xl = -1190771042;
+       int distl = 360530809;
+
+       for (int i = 0; i < 100000; i++) {
+           int b = rotateLeft(x, dist);
+           if (b != 1030884493)
+               throw new RuntimeException("Unexpected value: " + b
+                       + " after " + i + " iterations");
+       }
+       for (int i = 0; i < 100000; i++) {
+           long bl = rotateLeft(xl, distl);
+           if (bl != 1030884493)
+               throw new RuntimeException("Unexpected value: " + bl
+                       + " after " + i + " iterations");
+       }
+       System.out.println("b2296099 passes");
+   }
+
+    static int rotateLeft(int i, int distance) {
+        return ((i << distance) | (i >>> (-distance)));
+    }
+
+    static void b2302318Test() {
+        System.gc();
+
+        SpinThread slow = new SpinThread(Thread.MIN_PRIORITY);
+        SpinThread fast1 = new SpinThread(Thread.NORM_PRIORITY);
+        SpinThread fast2 = new SpinThread(Thread.MAX_PRIORITY);
+
+        slow.setDaemon(true);
+        fast1.setDaemon(true);
+        fast2.setDaemon(true);
+
+        fast2.start();
+        slow.start();
+        fast1.start();
+        try {
+            Thread.sleep(3000);
+        } catch (InterruptedException ie) {/*ignore */}
+        System.gc();
+
+        System.out.println("b2302318 passes");
+    }
+
+    static void b2487514Test() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(10);
+        int catchCount = 0;
+
+        q.offer(new Integer(0));
+        /*
+         * Warm up the code cache to have toArray() compiled. The key here is
+         * to pass a compatible type so that there are no exceptions when
+         * executing the method body (ie the APUT_OBJECT bytecode).
+         */
+        for (int i = 0; i < 1000; i++) {
+            Integer[] ints = (Integer[]) q.toArray(new Integer[5]);
+        }
+
+        /* Now pass an incompatible type which is guaranteed to throw */
+        for (int i = 0; i < 1000; i++) {
+            try {
+                Object[] obj = q.toArray(new String[5]);
+            }
+            catch (ArrayStoreException  success) {
+                catchCount++;
+            }
+        }
+
+        if (catchCount == 1000) {
+            System.out.println("b2487514 passes");
+        }
+        else {
+            System.out.println("b2487514 fails: catchCount is " + catchCount +
+                               " (expecting 1000)");
+        }
+    }
+}
+
+class SpinThread extends Thread {
+    int mPriority;
+
+    SpinThread(int prio) {
+        super("Spin prio=" + prio);
+        mPriority = prio;
+    }
+
+    public void run() {
+        setPriority(mPriority);
+        while (true) {}
+    }
+}
diff --git a/tests/084-class-init/expected.txt b/tests/084-class-init/expected.txt
new file mode 100644
index 0000000..9865edb
--- /dev/null
+++ b/tests/084-class-init/expected.txt
@@ -0,0 +1,8 @@
+Got expected EIIE for FIELD0
+Got expected NCDFE for FIELD0
+Got expected NCDFE for FIELD1
+SlowInit static block pre-sleep
+SlowInit static block post-sleep
+Fields (child thread): 111222333444
+MethodThread message
+Fields (main thread): 111222333444
diff --git a/tests/084-class-init/info.txt b/tests/084-class-init/info.txt
new file mode 100644
index 0000000..00fa31b
--- /dev/null
+++ b/tests/084-class-init/info.txt
@@ -0,0 +1 @@
+Test class initialization edge cases and race conditions.
diff --git a/tests/084-class-init/src/IntHolder.java b/tests/084-class-init/src/IntHolder.java
new file mode 100644
index 0000000..4012d6e
--- /dev/null
+++ b/tests/084-class-init/src/IntHolder.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * Holds an int.
+ */
+public class IntHolder {
+    private int mValue = 0;
+
+    /**
+     * Constructs an IntHolder with the specified value.  Throws an
+     * exception if the initial value is less than zero.
+     */
+    public IntHolder(int initialVal) {
+        if (initialVal < 0)
+            throw new RuntimeException("negative number");
+
+        mValue = initialVal;
+    }
+
+    public int getValue() {
+        return mValue;
+    }
+    public void setValue(int val) {
+        mValue = val;
+    }
+}
diff --git a/tests/084-class-init/src/Main.java b/tests/084-class-init/src/Main.java
new file mode 100644
index 0000000..89b76f5
--- /dev/null
+++ b/tests/084-class-init/src/Main.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+public class Main {
+    public static void main(String[] args) {
+        checkExceptions();
+        checkTiming();
+    }
+
+    public static void sleep(int msec) {
+        try {
+            Thread.sleep(msec);
+        } catch (InterruptedException ie) {
+            System.err.println("sleep interrupted");
+        }
+    }
+
+    static void checkExceptions() {
+        try {
+            System.out.println(PartialInit.FIELD0);
+            System.err.println("Construction of PartialInit succeeded unexpectedly");
+        } catch (ExceptionInInitializerError eiie) {
+            System.out.println("Got expected EIIE for FIELD0");
+        }
+
+        try {
+            System.out.println(PartialInit.FIELD0);
+            System.err.println("Load of FIELD0 succeeded unexpectedly");
+        } catch (NoClassDefFoundError ncdfe) {
+            System.out.println("Got expected NCDFE for FIELD0");
+        }
+        try {
+            System.out.println(PartialInit.FIELD1);
+            System.err.println("Load of FIELD1 succeeded unexpectedly");
+        } catch (NoClassDefFoundError ncdfe) {
+            System.out.println("Got expected NCDFE for FIELD1");
+        }
+    }
+
+    static void checkTiming() {
+        FieldThread fieldThread = new FieldThread();
+        MethodThread methodThread = new MethodThread();
+
+        fieldThread.start();
+        methodThread.start();
+
+        /* start class init */
+        IntHolder zero = SlowInit.FIELD0;
+
+        /* init complete; allow other threads time to finish printing */
+        Main.sleep(500);
+
+        /* print all values */
+        System.out.println("Fields (main thread): " +
+            SlowInit.FIELD0.getValue() + SlowInit.FIELD1.getValue() +
+            SlowInit.FIELD2.getValue() + SlowInit.FIELD3.getValue());
+    }
+
+    static class FieldThread extends Thread {
+        public void run() {
+            /* allow class init to start */
+            Main.sleep(200);
+
+            /* print fields; should delay until class init completes */
+            System.out.println("Fields (child thread): " +
+                SlowInit.FIELD0.getValue() + SlowInit.FIELD1.getValue() +
+                SlowInit.FIELD2.getValue() + SlowInit.FIELD3.getValue());
+        }
+    }
+
+    static class MethodThread extends Thread {
+        public void run() {
+            /* allow class init to start */
+            Main.sleep(400);
+
+            /* use a method that shouldn't be accessible yet */
+            SlowInit.printMsg("MethodThread message");
+        }
+    }
+}
diff --git a/tests/084-class-init/src/PartialInit.java b/tests/084-class-init/src/PartialInit.java
new file mode 100644
index 0000000..d4c71ff
--- /dev/null
+++ b/tests/084-class-init/src/PartialInit.java
@@ -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.
+ */
+
+
+/**
+ * Partially-initialized class.
+ */
+public class PartialInit {
+    public static final IntHolder FIELD0 = new IntHolder(1);    // succeeds
+    public static final IntHolder FIELD1 = new IntHolder(-2);   // throws
+}
diff --git a/tests/084-class-init/src/SlowInit.java b/tests/084-class-init/src/SlowInit.java
new file mode 100644
index 0000000..8ac72be
--- /dev/null
+++ b/tests/084-class-init/src/SlowInit.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/**
+ * Class that initializes with a pause.
+ */
+public class SlowInit {
+
+    public static final IntHolder FIELD0 = new IntHolder(0);
+    public static final IntHolder FIELD1 = new IntHolder(0);
+    public static final IntHolder FIELD2 = new IntHolder(0);
+    public static final IntHolder FIELD3 = new IntHolder(0);
+
+    public static void printMsg(String msg) {
+        System.out.println(msg);
+    }
+
+    static {
+        FIELD0.setValue(111);
+        FIELD1.setValue(222);
+        printMsg("SlowInit static block pre-sleep");
+        Main.sleep(600);
+        printMsg("SlowInit static block post-sleep");
+        FIELD2.setValue(333);
+        FIELD3.setValue(444);
+    };
+}
diff --git a/tests/084-old-style-inner-class/build b/tests/084-old-style-inner-class/build
new file mode 100644
index 0000000..dc6f3bb
--- /dev/null
+++ b/tests/084-old-style-inner-class/build
@@ -0,0 +1,29 @@
+#!/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.
+
+# Stop if something fails.
+set -e
+
+# We compile for a 1.4 target to suppress the use of EnclosingMethod
+# attributes.
+mkdir classes
+${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'`
+
+# Suppress stderr to keep the inner class warnings out of the expected output.
+dx --debug --dex --dump-to=classes.lst --output=classes.dex \
+    --dump-width=1000 classes 2>/dev/null
+
+zip test.jar classes.dex
diff --git a/tests/084-old-style-inner-class/expected.txt b/tests/084-old-style-inner-class/expected.txt
new file mode 100644
index 0000000..63a0076
--- /dev/null
+++ b/tests/084-old-style-inner-class/expected.txt
@@ -0,0 +1,8 @@
+Class: Main$1
+  getDeclaringClass(): (null)
+  getEnclosingClass(): (null)
+  getEnclosingMethod(): (null)
+Class: Main$2
+  getDeclaringClass(): (null)
+  getEnclosingClass(): (null)
+  getEnclosingMethod(): (null)
diff --git a/tests/084-old-style-inner-class/info.txt b/tests/084-old-style-inner-class/info.txt
new file mode 100644
index 0000000..9e5c4f9
--- /dev/null
+++ b/tests/084-old-style-inner-class/info.txt
@@ -0,0 +1,2 @@
+Test that the conversion of an old-style (pre-1.5) inner class results
+in a loss of inner class reflection information.
diff --git a/tests/084-old-style-inner-class/src/Main.java b/tests/084-old-style-inner-class/src/Main.java
new file mode 100644
index 0000000..c9a5b72
--- /dev/null
+++ b/tests/084-old-style-inner-class/src/Main.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Test reflection on old-style inner classes.
+ */
+public class Main {
+    private static Runnable theRunnable = new Runnable() {
+            public void run() { }
+        };
+
+    private static Runnable create() {
+        return new Runnable() {
+                public void run() { }
+            };
+    }
+
+    private static String nameOf(Class clazz) {
+        return (clazz == null) ? "(null)" : clazz.getName();
+    }
+
+    private static String nameOf(Method meth) {
+        return (meth == null) ? "(null)" : meth.toString();
+    }
+
+    private static void infoFor(Class clazz) {
+        System.out.println("Class: " + nameOf(clazz) + "\n" +
+                "  getDeclaringClass(): " +
+                nameOf(clazz.getDeclaringClass()) + "\n" +
+                "  getEnclosingClass(): " +
+                nameOf(clazz.getEnclosingClass()) + "\n" +
+                "  getEnclosingMethod(): " +
+                nameOf(clazz.getEnclosingMethod()));
+    }
+
+    public static void main(String args[]) {
+        infoFor(theRunnable.getClass());
+        infoFor(create().getClass());
+    }
+}
diff --git a/tests/086-null-super/expected.txt b/tests/086-null-super/expected.txt
new file mode 100644
index 0000000..20c6796
--- /dev/null
+++ b/tests/086-null-super/expected.txt
@@ -0,0 +1 @@
+Got expected ITE/NPE
diff --git a/tests/086-null-super/info.txt b/tests/086-null-super/info.txt
new file mode 100644
index 0000000..f983bd0
--- /dev/null
+++ b/tests/086-null-super/info.txt
@@ -0,0 +1,7 @@
+ClassLoader.loadClass() is expected to throw an exception, usually
+ClassNotFound, if it can't find the given Class, and not return null.
+
+This is a regression test for a defect in Dalvik, which was assuming
+that if there was no exception, the value returned would be non-null.
+
+This test is not expected to work for the reference implementation.
diff --git a/tests/086-null-super/src/Main.java b/tests/086-null-super/src/Main.java
new file mode 100644
index 0000000..6decb20
--- /dev/null
+++ b/tests/086-null-super/src/Main.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Class loader test.
+ */
+public class Main {
+    /**
+     * Thrown when an unexpected Exception is caught internally.
+     */
+    static class TestFailed extends Exception {
+        public TestFailed(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    /**
+     * A class loader which loads classes from the dex file
+     * "test.jar". However, it will return null when asked to load the
+     * class InaccessibleSuper.
+     *
+     * When testing code calls BrokenDexLoader's findBrokenClass(),
+     * a BrokenDexLoader will be the defining loader for the class
+     * Inaccessible.  The VM will call the defining loader for
+     * "InaccessibleSuper", which will return null, which the VM
+     * should be able to deal with gracefully.
+     *
+     * Note that this depends heavily on the Dalvik test harness.
+     */
+    static class BrokenDexLoader extends ClassLoader {
+
+        /** We return null when asked to load InaccessibleSuper. */
+        private static class InaccessibleSuper {}
+        private static class Inaccessible extends InaccessibleSuper {}
+
+        private static final String SUPERCLASS_NAME =
+                "Main$BrokenDexLoader$InaccessibleSuper";
+        private static final String CLASS_NAME =
+                "Main$BrokenDexLoader$Inaccessible";
+
+        private static final String DEX_FILE = "test.jar";
+
+        public BrokenDexLoader(ClassLoader parent) {
+            super(parent);
+        }
+
+        /**
+         * Finds the class with the specified binary name, from DEX_FILE.
+         *
+         * If we don't find a match, we throw an exception.
+         */
+        private Class<?> findDexClass(String name)
+                throws TestFailed, InvocationTargetException
+        {
+
+            try {
+                /*
+                 * Find the DexFile class, and construct a DexFile object
+                 * through reflection, then call loadCLass on it.
+                 */
+                Class mDexClass = ClassLoader.getSystemClassLoader().
+                        loadClass("dalvik/system/DexFile");
+                Constructor ctor = mDexClass.
+                        getConstructor(new Class[] {String.class});
+                Object mDexFile = ctor.newInstance(DEX_FILE);
+                Method meth = mDexClass.
+                        getMethod("loadClass",
+                            new Class[] { String.class, ClassLoader.class });
+                /*
+                 * Invoking loadClass on CLASS_NAME is expected to
+                 * throw an InvocationTargetException. Anything else
+                 * is an error we can't recover from.
+                 */
+                meth.invoke(mDexFile, name, this);
+            } catch (NoSuchMethodException nsme) {
+                throw new TestFailed(nsme);
+            } catch (InstantiationException ie) {
+                throw new TestFailed(ie);
+            } catch (IllegalAccessException iae) {
+                throw new TestFailed(iae);
+            } catch (ClassNotFoundException cnfe) {
+                throw new TestFailed(cnfe);
+            }
+
+            return null;
+        }
+
+        /**
+         * Load a class.
+         *
+         * Return null if the class's name is SUPERCLASS_NAME;
+         * otherwise invoke the super's loadClass method.
+         */
+        public Class<?> loadClass(String name, boolean resolve)
+                throws ClassNotFoundException
+        {
+            if (SUPERCLASS_NAME.equals(name)) {
+                return null;
+            }
+
+            return super.loadClass(name, resolve);
+        }
+
+        /**
+         * Attempt to find the class with the superclass we refuse to
+         * load.  This is expected to throw an
+         * InvocationTargetException, with a NullPointerException as
+         * its cause.
+         */
+        public void findBrokenClass()
+                throws TestFailed, InvocationTargetException
+        {
+            findDexClass(CLASS_NAME);
+        }
+    }
+
+    /**
+     * Main entry point.
+     */
+    public static void main(String[] args)
+            throws TestFailed, ClassNotFoundException {
+        /*
+         * Run test.
+         */
+        testFailLoadAndGc();
+    }
+
+    /**
+     * See if we can GC after a failed load.
+     */
+    static void testFailLoadAndGc() throws TestFailed {
+        try {
+            BrokenDexLoader loader;
+
+            loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader());
+            loader.findBrokenClass();
+            System.err.println("ERROR: Inaccessible was accessible");
+        } catch (InvocationTargetException ite) {
+            Throwable cause = ite.getCause();
+            if (cause instanceof NullPointerException) {
+                System.err.println("Got expected ITE/NPE");
+            } else {
+                System.err.println("Got unexpected ITE");
+                ite.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/tests/087-gc-after-link/expected.txt b/tests/087-gc-after-link/expected.txt
new file mode 100644
index 0000000..3b2d33a
--- /dev/null
+++ b/tests/087-gc-after-link/expected.txt
@@ -0,0 +1,2 @@
+Got expected ITE/NPE
+GC complete.
diff --git a/tests/087-gc-after-link/info.txt b/tests/087-gc-after-link/info.txt
new file mode 100644
index 0000000..9483838
--- /dev/null
+++ b/tests/087-gc-after-link/info.txt
@@ -0,0 +1,8 @@
+This test causes a linkage error, which calls dvmFreeClassInnards on
+the unlinked Class.
+
+This is a regression test for a defect in Dalvik, which was assuming
+that dvmFreeClassInnards could be called twice on the same class.
+
+This test is a modified version of test 086.
+This test is not expected to work for the reference implementation.
diff --git a/tests/087-gc-after-link/src/Main.java b/tests/087-gc-after-link/src/Main.java
new file mode 100644
index 0000000..24adaf7
--- /dev/null
+++ b/tests/087-gc-after-link/src/Main.java
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Class loader test.
+ */
+public class Main {
+    /**
+     * Thrown when an unexpected Exception is caught internally.
+     */
+    static class TestFailed extends Exception {
+        public TestFailed(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    /**
+     * A class loader which loads classes from the dex file
+     * "test.jar". However, it will return null when asked to load the
+     * class InaccessibleSuper.
+     *
+     * When testing code calls BrokenDexLoader's findBrokenClass(),
+     * a BrokenDexLoader will be the defining loader for the class
+     * Inaccessible.  The VM will call the defining loader for
+     * "InaccessibleSuper", which will return null, which the VM
+     * should be able to deal with gracefully.
+     *
+     * Note that this depends heavily on the Dalvik test harness.
+     */
+    static class BrokenDexLoader extends ClassLoader {
+
+        /** We return null when asked to load InaccessibleSuper. */
+        private static class InaccessibleSuper {}
+        private static class Inaccessible extends InaccessibleSuper {}
+
+        private static final String SUPERCLASS_NAME =
+                "Main$BrokenDexLoader$InaccessibleSuper";
+        private static final String CLASS_NAME =
+                "Main$BrokenDexLoader$Inaccessible";
+
+        private static final String DEX_FILE = "test.jar";
+
+        public BrokenDexLoader(ClassLoader parent) {
+            super(parent);
+        }
+
+        /**
+         * Finds the class with the specified binary name, from DEX_FILE.
+         *
+         * If we don't find a match, we throw an exception.
+         */
+        private Class<?> findDexClass(String name)
+                throws TestFailed, InvocationTargetException
+        {
+
+            try {
+                /*
+                 * Find the DexFile class, and construct a DexFile object
+                 * through reflection, then call loadCLass on it.
+                 */
+                Class mDexClass = ClassLoader.getSystemClassLoader().
+                        loadClass("dalvik/system/DexFile");
+                Constructor ctor = mDexClass.
+                        getConstructor(new Class[] {String.class});
+                Object mDexFile = ctor.newInstance(DEX_FILE);
+                Method meth = mDexClass.
+                        getMethod("loadClass",
+                            new Class[] { String.class, ClassLoader.class });
+                /*
+                 * Invoking loadClass on CLASS_NAME is expected to
+                 * throw an InvocationTargetException. Anything else
+                 * is an error we can't recover from.
+                 */
+                meth.invoke(mDexFile, name, this);
+            } catch (NoSuchMethodException nsme) {
+                throw new TestFailed(nsme);
+            } catch (InstantiationException ie) {
+                throw new TestFailed(ie);
+            } catch (IllegalAccessException iae) {
+                throw new TestFailed(iae);
+            } catch (ClassNotFoundException cnfe) {
+                throw new TestFailed(cnfe);
+            }
+
+            return null;
+        }
+
+        /**
+         * Load a class.
+         *
+         * Return null if the class's name is SUPERCLASS_NAME;
+         * otherwise invoke the super's loadClass method.
+         */
+        public Class<?> loadClass(String name, boolean resolve)
+                throws ClassNotFoundException
+        {
+            if (SUPERCLASS_NAME.equals(name)) {
+                return null;
+            }
+
+            return super.loadClass(name, resolve);
+        }
+
+        /**
+         * Attempt to find the class with the superclass we refuse to
+         * load.  This is expected to throw an
+         * InvocationTargetException, with a NullPointerException as
+         * its cause.
+         */
+        public void findBrokenClass()
+                throws TestFailed, InvocationTargetException
+        {
+            findDexClass(CLASS_NAME);
+        }
+    }
+
+    /**
+     * Main entry point.
+     */
+    public static void main(String[] args)
+            throws TestFailed, ClassNotFoundException {
+        /*
+         * Run test.
+         */
+        testFailLoadAndGc();
+    }
+
+    /**
+     * See if we can GC after a failed load.
+     */
+    static void testFailLoadAndGc() throws TestFailed {
+        try {
+            BrokenDexLoader loader;
+
+            loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader());
+            loader.findBrokenClass();
+            System.err.println("ERROR: Inaccessible was accessible");
+        } catch (InvocationTargetException ite) {
+            Throwable cause = ite.getCause();
+            if (cause instanceof NullPointerException) {
+                System.err.println("Got expected ITE/NPE");
+            } else {
+                System.err.println("Got unexpected ITE");
+                ite.printStackTrace();
+            }
+        }
+        System.gc();
+        System.out.println("GC complete.");
+    }
+}
diff --git a/tests/README.txt b/tests/README.txt
new file mode 100644
index 0000000..eb1ce36
--- /dev/null
+++ b/tests/README.txt
@@ -0,0 +1,13 @@
+VM test harness.
+
+Use "./run-all-tests" to run all tests, or "./run-test <number>" to run a
+single test.  Run "./run-test" with no arguments to see command flags;
+in particular, the tests can be run on the desktop, on a USB-attached
+device, or using the desktop "reference implementation".
+
+
+For most tests, the sources are in the "src" subdirectory.  Sources found
+in the "src2" directory are compiled separately but to the same output
+directory; this can be used to exercise "API mismatch" situations by
+replacing class files created in the first pass.  The "src-ex" directory
+is built separately, and is intended for exercising class loaders.
diff --git a/tests/etc/default-build b/tests/etc/default-build
new file mode 100755
index 0000000..b8df442
--- /dev/null
+++ b/tests/etc/default-build
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+if [ -r src2 ]; then
+    ${JAVAC} -d classes `find src2 -name '*.java'`
+fi
+
+dx -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+    --dump-width=1000 classes
+zip test.jar classes.dex
+
+if [ -r src-ex ]; then
+    mkdir classes-ex
+    ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
+    dx -JXmx256m --debug --dex --dump-to=classes-ex.lst \
+        --output=classes-ex.dex --dump-width=1000 classes-ex
+
+    # quick shuffle so that the stored name is "classes.dex"
+    mv classes.dex classes-1.dex
+    mv classes-ex.dex classes.dex
+    zip test-ex.jar classes.dex
+    mv classes.dex classes-ex.dex
+    mv classes-1.dex classes.dex
+fi
diff --git a/tests/etc/default-run b/tests/etc/default-run
new file mode 100755
index 0000000..ecbbbc7
--- /dev/null
+++ b/tests/etc/default-run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+exec ${RUN} "$@"
diff --git a/tests/etc/local-run-test-jar b/tests/etc/local-run-test-jar
new file mode 100755
index 0000000..6155e3f
--- /dev/null
+++ b/tests/etc/local-run-test-jar
@@ -0,0 +1,153 @@
+#!/bin/sh
+#
+# Run the code in test.jar on a host-local virtual machine. The jar should
+# contain a top-level class named Main to run.
+#
+# Options:
+#   --quiet       -- don't chatter
+#   --fast        -- use the fast interpreter (the default)
+#   --jit         -- use the jit
+#   --portable    -- use the portable interpreter
+#   --debug       -- wait for debugger to attach
+#   --valgrind    -- use valgrind
+#   --no-verify   -- turn off verification (on by default)
+#   --no-optimize -- turn off optimization (on by default)
+
+msg() {
+    if [ "$QUIET" = "n" ]; then
+        echo "$@"
+    fi
+}
+
+INTERP=""
+DEBUG="n"
+GDB="n"
+VERIFY="y"
+OPTIMIZE="y"
+VALGRIND="n"
+DEV_MODE="n"
+QUIET="n"
+PRECISE="y"
+
+while true; do
+    if [ "x$1" = "x--quiet" ]; then
+        QUIET="y"
+        shift
+    elif [ "x$1" = "x--jit" ]; then
+        INTERP="jit"
+        msg "Using jit"
+        shift
+    elif [ "x$1" = "x--fast" ]; then
+        INTERP="fast"
+        msg "Using fast interpreter"
+        shift
+    elif [ "x$1" = "x--portable" ]; then
+        INTERP="portable"
+        msg "Using portable interpreter"
+        shift
+    elif [ "x$1" = "x--debug" ]; then
+        DEBUG="y"
+        shift
+    elif [ "x$1" = "x--gdb" ]; then
+        GDB="y"
+        shift
+    elif [ "x$1" = "x--valgrind" ]; then
+        VALGRIND="y"
+        shift
+    elif [ "x$1" = "x--dev" ]; then
+        DEV_MODE="y"
+        shift
+    elif [ "x$1" = "x--no-verify" ]; then
+        VERIFY="n"
+        shift
+    elif [ "x$1" = "x--no-optimize" ]; then
+        OPTIMIZE="n"
+        shift
+    elif [ "x$1" = "x--no-precise" ]; then
+        PRECISE="n"
+        shift
+    elif [ "x$1" = "x--" ]; then
+        shift
+        break
+    elif expr "x$1" : "x--" >/dev/null 2>&1; then
+        echo "unknown option: $1" 1>&2
+        exit 1
+    else
+        break
+    fi
+done
+
+if [ "x$INTERP" = "x" ]; then
+    INTERP="fast"
+    msg "Using fast interpreter by default"
+fi
+
+if [ "$OPTIMIZE" = "y" ]; then
+    if [ "$VERIFY" = "y" ]; then
+        DEX_OPTIMIZE="-Xdexopt:verified"
+    else
+        DEX_OPTIMIZE="-Xdexopt:all"
+    fi
+    msg "Performing optimizations"
+else
+    DEX_OPTIMIZE="-Xdexopt:none"
+    msg "Skipping optimizations"
+fi
+
+if [ "$VERIFY" = "y" ]; then
+    DEX_VERIFY=""
+    msg "Performing verification"
+else
+    DEX_VERIFY="-Xverify:none"
+    msg "Skipping verification"
+fi
+
+if [ "$VALGRIND" = "y" ]; then
+    msg "Running with valgrind"
+    valgrind_cmd="valgrind"
+    #valgrind_cmd="valgrind --leak-check=full"
+else
+    valgrind_cmd=""
+fi
+
+if [ "$PRECISE" = "y" ]; then
+    GC_OPTS="-Xgc:precise -Xgenregmap"
+else
+    GC_OPTS="-Xgc:noprecise"
+fi
+
+msg "------------------------------"
+
+BASE="$OUT" # from build environment
+DATA_DIR=/tmp
+DEBUG_OPTS="-Xcheck:jni -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"
+
+export ANDROID_PRINTF_LOG=brief
+if [ "$DEV_MODE" = "y" ]; then
+    export ANDROID_LOG_TAGS='*:d'
+else
+    export ANDROID_LOG_TAGS='*:s'
+fi
+export ANDROID_DATA="$DATA_DIR"
+export ANDROID_ROOT="${BASE}/system"
+export LD_LIBRARY_PATH="${BASE}/system/lib"
+export DYLD_LIBRARY_PATH="${BASE}/system/lib"
+
+exe="${BASE}/system/bin/dalvikvm"
+framework="${BASE}/system/framework"
+bpath="${framework}/core.jar:${framework}/ext.jar:${framework}/framework.jar"
+
+if [ "$DEBUG" = "y" ]; then
+    PORT=8000
+    msg "Waiting for debugger to connect on localhost:$PORT"
+    DEX_DEBUG="-agentlib:jdwp=transport=dt_socket,addres=$PORT,server=y,suspend=y"
+fi
+
+if [ "$GDB" = "y" ]; then
+    gdb=gdb
+    gdbargs="--args $exe"
+fi
+
+$valgrind_cmd $gdb $exe $gdbargs "-Xbootclasspath:${bpath}" \
+    $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG $GC_OPTS "-Xint:${INTERP}" -ea \
+    -cp test.jar Main "$@"
diff --git a/tests/etc/push-and-run-test-jar b/tests/etc/push-and-run-test-jar
new file mode 100755
index 0000000..df66a8e
--- /dev/null
+++ b/tests/etc/push-and-run-test-jar
@@ -0,0 +1,130 @@
+#!/bin/sh
+#
+# Run the code in test.jar on the device. The jar should contain a top-level
+# class named Main to run.
+#
+# Options:
+#   --quiet       -- don't chatter
+#   --fast        -- use the fast interpreter (the default)
+#   --jit         -- use the jit
+#   --portable    -- use the portable interpreter
+#   --debug       -- wait for debugger to attach
+#   --zygote      -- use the zygote (if so, all other options are ignored)
+#   --dev         -- development mode
+#   --no-verify   -- turn off verification (on by default)
+#   --no-optimize -- turn off optimization (on by default)
+#   --no-precise  -- turn off precise GC (on by default)
+
+msg() {
+    if [ "$QUIET" = "n" ]; then
+        echo "$@"
+    fi
+}
+
+INTERP=""
+DEBUG="n"
+VERIFY="y"
+OPTIMIZE="y"
+ZYGOTE="n"
+QUIET="n"
+PRECISE="y"
+
+while true; do
+    if [ "x$1" = "x--quiet" ]; then
+        QUIET="y"
+        shift
+    elif [ "x$1" = "x--fast" ]; then
+        INTERP="fast"
+        msg "Using fast interpreter"
+        shift
+    elif [ "x$1" = "x--jit" ]; then
+        INTERP="jit"
+        msg "Using jit"
+        shift
+    elif [ "x$1" = "x--portable" ]; then
+        INTERP="portable"
+        msg "Using portable interpreter"
+        shift
+    elif [ "x$1" = "x--debug" ]; then
+        DEBUG="y"
+        shift
+    elif [ "x$1" = "x--zygote" ]; then
+        ZYGOTE="y"
+        msg "Spawning from zygote"
+        shift
+    elif [ "x$1" = "x--dev" ]; then
+        # not used; ignore
+        shift
+    elif [ "x$1" = "x--no-verify" ]; then
+        VERIFY="n"
+        shift
+    elif [ "x$1" = "x--no-optimize" ]; then
+        OPTIMIZE="n"
+        shift
+    elif [ "x$1" = "x--no-precise" ]; then
+        PRECISE="n"
+        shift
+    elif [ "x$1" = "x--" ]; then
+        shift
+        break
+    elif expr "x$1" : "x--" >/dev/null 2>&1; then
+        echo "unknown option: $1" 1>&2
+        exit 1
+    else
+        break
+    fi
+done
+
+if [ "$ZYGOTE" = "n" ]; then
+    if [ "x$INTERP" = "x" ]; then
+        INTERP="fast"
+        msg "Using fast interpreter by default"
+    fi
+
+    if [ "$OPTIMIZE" = "y" ]; then
+        if [ "$VERIFY" = "y" ]; then
+            DEX_OPTIMIZE="-Xdexopt:verified"
+        else
+            DEX_OPTIMIZE="-Xdexopt:all"
+        fi
+        msg "Performing optimizations"
+    else
+        DEX_OPTIMIZE="-Xdexopt:none"
+        msg "Skipping optimizations"
+    fi
+
+    if [ "$VERIFY" = "y" ]; then
+        DEX_VERIFY=""
+        msg "Performing verification"
+    else
+        DEX_VERIFY="-Xverify:none"
+        msg "Skipping verification"
+    fi
+fi
+
+msg "------------------------------"
+
+if [ "$QUIET" = "n" ]; then
+    adb push test.jar /data
+    adb push test-ex.jar /data
+else
+    adb push test.jar /data >/dev/null 2>&1
+    adb push test-ex.jar /data >/dev/null 2>&1
+fi
+
+if [ "$DEBUG" = "y" ]; then
+    DEX_DEBUG="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
+fi
+
+if [ "$PRECISE" = "y" ]; then
+    GC_OPTS="-Xgc:precise -Xgenregmap"
+else
+    GC_OPTS="-Xgc:noprecise"
+fi
+
+if [ "$ZYGOTE" = "y" ]; then
+    adb shell cd /data \; dvz -classpath test.jar Main "$@"
+else
+    adb shell cd /data \; dalvikvm $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG \
+        $GC_OPTS -cp test.jar "-Xint:${INTERP}" -ea Main "$@"
+fi
diff --git a/tests/etc/reference-run-test-classes b/tests/etc/reference-run-test-classes
new file mode 100755
index 0000000..94c8050
--- /dev/null
+++ b/tests/etc/reference-run-test-classes
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Run the code in a classes directory on a host-local reference virtual
+# machine. The jar should contain a top-level class named Main to run.
+#
+# Options:
+#   --quiet       -- don't chatter
+#   --debug       -- wait for debugger to attach
+#   --no-verify   -- turn off verification (on by default)
+#   --dev         -- development mode
+
+msg() {
+    if [ "$QUIET" = "n" ]; then
+        echo "$@"
+    fi
+}
+
+DEBUG="n"
+QUIET="n"
+VERIFY="y"
+
+while true; do
+    if [ "x$1" = "x--quiet" ]; then
+        QUIET="y"
+        shift
+    elif [ "x$1" = "x--debug" ]; then
+        DEBUG="y"
+        shift
+    elif [ "x$1" = "x--no-verify" ]; then
+        VERIFY="n"
+        shift
+    elif [ "x$1" = "x--dev" ]; then
+        # not used; ignore
+        shift
+    elif [ "x$1" = "x--" ]; then
+        shift
+        break
+    elif expr "x$1" : "x--" >/dev/null 2>&1; then
+        echo "unknown option: $1" 1>&2
+        exit 1
+    else
+        break
+    fi
+done
+
+if [ "$VERIFY" = "y" ]; then
+    VERIFY_ARG="-Xverify:all"
+    msg "Performing verification"
+else
+    VERIFY_ARG="-Xverify:none"
+    msg "Skipping verification"
+fi
+
+if [ "$DEBUG" = "y" ]; then
+    PORT=8000
+    msg "Waiting for debugger to connect on localhost:$PORT"
+    DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+fi
+
+${JAVA} ${DEBUG_OPTS} -ea ${VERIFY_ARG} -classpath classes Main "$@"
diff --git a/tests/run-all-tests b/tests/run-all-tests
new file mode 100755
index 0000000..ac01803
--- /dev/null
+++ b/tests/run-all-tests
@@ -0,0 +1,124 @@
+#!/bin/bash
+#
+# 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+run_args=""
+usage="no"
+
+while true; do
+    if [ "x$1" = "x--local" ]; then
+        run_args="${run_args} --local"
+        shift
+    elif [ "x$1" = "x--reference" ]; then
+        run_args="${run_args} --reference"
+        shift
+    elif [ "x$1" = "x--jit" ]; then
+        run_args="${run_args} --jit"
+        shift
+    elif [ "x$1" = "x--fast" ]; then
+        run_args="${run_args} --fast"
+        shift
+    elif [ "x$1" = "x--portable" ]; then
+        run_args="${run_args} --portable"
+        shift
+    elif [ "x$1" = "x--debug" ]; then
+        run_args="${run_args} --debug"
+        shift
+    elif [ "x$1" = "x--zygote" ]; then
+        run_args="${run_args} --zygote"
+        shift
+    elif [ "x$1" = "x--no-verify" ]; then
+        run_args="${run_args} --no-verify"
+        shift
+    elif [ "x$1" = "x--no-optimize" ]; then
+        run_args="${run_args} --no-optimize"
+        shift
+    elif [ "x$1" = "x--valgrind" ]; then
+        run_args="${run_args} --valgrind"
+        shift
+    elif [ "x$1" = "x--dev" ]; then
+        run_args="${run_args} --dev"
+        shift
+    elif [ "x$1" = "x--update" ]; then
+        run_args="${run_args} --update"
+        shift
+    elif [ "x$1" = "x--help" ]; then
+        usage="yes"
+        shift
+    elif expr "x$1" : "x--" >/dev/null 2>&1; then
+        echo "unknown option: $1" 1>&2
+        usage="yes"
+        break
+    else
+        break
+    fi
+done
+
+if [ "$usage" = "yes" ]; then
+    prog=`basename $prog`
+    (
+        echo "usage:"
+        echo "  $prog --help     Print this message."
+        echo "  $prog [options]  Run all tests with the given options."
+        echo "  Options are all passed to run-test; refer to that for " \
+	    "further documentation:"
+        echo "    --debug --dev --fast --local --no-optimize --no-verify" \
+	    "--portable"
+	echo "    --reference --update --valgrind --zygote"
+    ) 1>&2
+    exit 1
+fi
+
+passed=0
+failed=0
+failNames=""
+
+for i in *; do
+    if [ -d "$i" -a -r "$i" -a -r "${i}/info.txt" ]; then
+        ./run-test ${run_args} "$i"
+        if [ "$?" = "0" ]; then
+            ((passed += 1))
+        else
+            ((failed += 1))
+            failNames="$failNames $i"
+        fi
+    fi
+done
+
+echo "passed: $passed test(s)"
+echo "failed: $failed test(s)"
+
+for i in $failNames; do
+    echo "failed: $i"
+done
diff --git a/tests/run-test b/tests/run-test
new file mode 100755
index 0000000..c5e2090
--- /dev/null
+++ b/tests/run-test
@@ -0,0 +1,254 @@
+#!/bin/bash
+#
+# 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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+export JAVA="java"
+export JAVAC="javac -target 1.5"
+export RUN="${progdir}/etc/push-and-run-test-jar"
+
+info="info.txt"
+build="build"
+run="run"
+expected="expected.txt"
+output="output.txt"
+build_output="build-output.txt"
+run_args="--quiet"
+
+dev_mode="no"
+update_mode="no"
+debug_mode="no"
+usage="no"
+
+while true; do
+    if [ "x$1" = "x--local" ]; then
+        RUN="${progdir}/etc/local-run-test-jar"
+        shift
+    elif [ "x$1" = "x--reference" ]; then
+        RUN="${progdir}/etc/reference-run-test-classes"
+        shift
+    elif [ "x$1" = "x--jit" ]; then
+        run_args="${run_args} --jit"
+        shift
+    elif [ "x$1" = "x--fast" ]; then
+        run_args="${run_args} --fast"
+        shift
+    elif [ "x$1" = "x--portable" ]; then
+        run_args="${run_args} --portable"
+        shift
+    elif [ "x$1" = "x--debug" ]; then
+        run_args="${run_args} --debug"
+        shift
+    elif [ "x$1" = "x--gdb" ]; then
+        run_args="${run_args} --gdb"
+        dev_mode="yes"
+        shift
+    elif [ "x$1" = "x--zygote" ]; then
+        run_args="${run_args} --zygote"
+        shift
+    elif [ "x$1" = "x--no-verify" ]; then
+        run_args="${run_args} --no-verify"
+        shift
+    elif [ "x$1" = "x--no-optimize" ]; then
+        run_args="${run_args} --no-optimize"
+        shift
+    elif [ "x$1" = "x--no-precise" ]; then
+        run_args="${run_args} --no-precise"
+        shift
+    elif [ "x$1" = "x--valgrind" ]; then
+        run_args="${run_args} --valgrind"
+        shift
+    elif [ "x$1" = "x--dev" ]; then
+        run_args="${run_args} --dev"
+        dev_mode="yes"
+        shift
+    elif [ "x$1" = "x--update" ]; then
+        update_mode="yes"
+        shift
+    elif [ "x$1" = "x--help" ]; then
+        usage="yes"
+        shift
+    elif expr "x$1" : "x--" >/dev/null 2>&1; then
+        echo "unknown option: $1" 1>&2
+        usage="yes"
+        break
+    else
+        break
+    fi
+done
+
+if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
+    echo "--dev and --update are mutually exclusive" 1>&2
+    usage="yes"
+fi
+
+if [ "$usage" = "no" ]; then
+    if [ "x$1" = "x" -o "x$1" = "x-" ]; then
+        test_dir=`basename "$oldwd"`
+    else
+        test_dir="$1"
+    fi
+
+    if [ '!' -d "$test_dir" ]; then
+        td2=`echo ${test_dir}-*`
+        if [ '!' -d "$td2" ]; then
+            echo "${test_dir}: no such test directory" 1>&2
+            usage="yes"
+        fi
+        test_dir="$td2"
+    fi
+
+    # Shift to get rid of the test name argument. The rest of the arguments
+    # will get passed to the test run.
+    shift
+fi
+
+if [ "$usage" = "yes" ]; then
+    prog=`basename $prog`
+    (
+        echo "usage:"
+        echo "  $prog --help                          Print this message."
+        echo "  $prog [options] [test-name]           Run test normally."
+        echo "  $prog --dev [options] [test-name]     Development mode" \
+	    "(dumps to stdout)."
+        echo "  $prog --update [options] [test-name]  Update mode" \
+	    "(replaces expected.txt)."
+        echo '  Omitting the test name or specifying "-" will use the' \
+	    "current directory."
+        echo "  Runtime Options:"
+        echo "    --fast         Use the fast interpreter (the default)."
+        echo "    --jit          Use the jit."
+        echo "    --portable     Use the portable interpreter."
+        echo "    --debug        Wait for a debugger to attach."
+        #echo "    --gdb          Run under gdb; incompatible with some tests."
+        echo "    --no-verify    Turn off verification (on by default)."
+        echo "    --no-optimize  Turn off optimization (on by default)."
+        echo "    --no-precise   Turn off precise GC (on by default)."
+        echo "    --zygote       Spawn the process from the Zygote." \
+	    "If used, then the"
+	echo "                   other runtime options are ignored."
+        echo "    --local        Use a host-local virtual machine."
+        echo "    --valgrind     Use valgrind when running locally."
+        echo "    --reference    Use a host-local reference virtual machine."
+    ) 1>&2
+    exit 1
+fi
+
+cd "$test_dir"
+test_dir=`pwd`
+
+td_info="${test_dir}/${info}"
+td_expected="${test_dir}/${expected}"
+
+tmp_dir="/tmp/test-$$"
+
+if [ '!' '(' -r "$td_info" -a -r "$td_expected" ')' ]; then
+    echo "${test_dir}: missing files" 1>&2
+    exit 1
+fi
+
+# copy the test to a temp dir and run it
+
+echo "${test_dir}: running..." 1>&2
+
+rm -rf "$tmp_dir"
+cp -Rp "$test_dir" "$tmp_dir"
+cd "$tmp_dir"
+
+if [ '!' -r "$build" ]; then
+    cp "${progdir}/etc/default-build" build
+fi
+
+if [ '!' -r "$run" ]; then
+    cp "${progdir}/etc/default-run" run
+fi
+
+chmod 755 "$build"
+chmod 755 "$run"
+
+good="no"
+if [ "$dev_mode" = "yes" ]; then
+    "./${build}" 2>&1
+    echo "build exit status: $?" 1>&2
+    "./${run}" $run_args "$@" 2>&1
+    echo "run exit status: $?" 1>&2
+    good="yes"
+elif [ "$update_mode" = "yes" ]; then
+    "./${build}" >"$build_output" 2>&1
+    build_exit="$?"
+    if [ "$build_exit" = '0' ]; then
+        "./${run}" $run_args "$@" >"$output" 2>&1
+	sed -e 's/[[:cntrl:]]$//g' < "$output" >"${td_expected}"
+        good="yes"
+    else
+        cat "$build_output" 1>&2
+        echo "build exit status: $build_exit" 1>&2
+    fi
+else
+    "./${build}" >"$build_output" 2>&1
+    build_exit="$?"
+    if [ "$build_exit" = '0' ]; then
+        "./${run}" $run_args "$@" >"$output" 2>&1
+    else
+        cp "$build_output" "$output"
+        echo "build exit status: $build_exit" >>"$output"
+    fi
+    diff --strip-trailing-cr -q "$expected" "$output" >/dev/null
+    if [ "$?" = "0" ]; then
+        # output == expected
+        good="yes"
+        echo "${test_dir}: succeeded!" 1>&2
+    fi
+fi
+
+if [ "$good" = "yes" ]; then
+    cd "$oldwd"
+    rm -rf "$tmp_dir"
+    exit 0
+fi
+
+(
+    if [ "$update_mode" '!=' "yes" ]; then
+        echo "${test_dir}: FAILED!"
+        echo ' '
+        echo '#################### info'
+        cat "${td_info}" | sed 's/^/# /g'
+        echo '#################### diffs'
+        diff --strip-trailing-cr -u "$expected" "$output"
+        echo '####################'
+        echo ' '
+    fi
+    echo "files left in ${tmp_dir}"
+) 1>&2
+
+exit 1
diff --git a/tools/Android.mk b/tools/Android.mk
new file mode 100644
index 0000000..6571161
--- /dev/null
+++ b/tools/Android.mk
@@ -0,0 +1 @@
+include $(all-subdir-makefiles)
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/dexdeps/Android.mk b/tools/dexdeps/Android.mk
new file mode 100644
index 0000000..e1b7e73
--- /dev/null
+++ b/tools/dexdeps/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script files' timestamps are at least as new as the
+# .jar files they wrap.
+
+# the dexdeps script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := dexdeps
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/dexdeps$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/dexdeps | $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+		src \
+	))
+
+include $(subdirs)
diff --git a/tools/dexdeps/README.txt b/tools/dexdeps/README.txt
new file mode 100644
index 0000000..060aecb
--- /dev/null
+++ b/tools/dexdeps/README.txt
@@ -0,0 +1,27 @@
+dexdeps -- DEX external dependency dump
+
+
+This tool dumps a list of fields and methods that a DEX file uses but does
+not define.  When combined with a list of public APIs, it can be used to
+determine whether an APK is accessing fields and calling methods that it
+shouldn't be.  It may also be useful in determining whether an application
+requires a certain minimum API level to execute.
+
+Basic usage:
+
+  dexdeps [options] <file.{dex,apk,jar}>
+
+For zip archives (including .jar and .apk), dexdeps will look for a
+"classes.dex" entry.
+
+Supported options are:
+
+  --format={brief,xml}
+
+    Specifies the output format.
+
+    "brief" produces one line of output for each field and method.  Field
+    and argument types are shown as descriptor strings.
+
+    "xml" produces a larger output file, readable with an XML browser.  Types
+    are shown in a more human-readable form (e.g. "[I" becomes "int[]").
diff --git a/tools/dexdeps/etc/dexdeps b/tools/dexdeps/etc/dexdeps
new file mode 100644
index 0000000..dc628bd
--- /dev/null
+++ b/tools/dexdeps/etc/dexdeps
@@ -0,0 +1,69 @@
+#!/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.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=dexdeps.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+    libdir=`dirname "$progdir"`/tools/lib
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+    libdir=`dirname "$progdir"`/framework
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+    echo `basename "$prog"`": can't find $jarfile"
+    exit 1
+fi
+
+javaOpts=""
+
+# Alternatively, this will extract any parameter "-Jxxx" from the command line
+# and pass them to Java (instead of to dexdeps).
+while expr "x$1" : 'x-J' >/dev/null; do
+    opt=`expr "$1" : '-J\(.*\)'`
+    javaOpts="${javaOpts} -${opt}"
+    shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+    jarpath=`cygpath -w  "$libdir/$jarfile"`
+else
+    jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/tools/dexdeps/etc/manifest.txt b/tools/dexdeps/etc/manifest.txt
new file mode 100644
index 0000000..7606744
--- /dev/null
+++ b/tools/dexdeps/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.dexdeps.Main
diff --git a/tools/dexdeps/src/Android.mk b/tools/dexdeps/src/Android.mk
new file mode 100644
index 0000000..8e4abaf
--- /dev/null
+++ b/tools/dexdeps/src/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+
+# dexdeps java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= dexdeps
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+include $(BUILD_DROIDDOC)
diff --git a/tools/dexdeps/src/com/android/dexdeps/ClassRef.java b/tools/dexdeps/src/com/android/dexdeps/ClassRef.java
new file mode 100644
index 0000000..7f6edc9
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/ClassRef.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package com.android.dexdeps;
+
+import java.util.ArrayList;
+
+public class ClassRef {
+    private String mClassName;
+    private ArrayList<FieldRef> mFieldRefs;
+    private ArrayList<MethodRef> mMethodRefs;
+
+    /**
+     * Initializes a new class reference.
+     */
+    public ClassRef(String className) {
+        mClassName = className;
+        mFieldRefs = new ArrayList<FieldRef>();
+        mMethodRefs = new ArrayList<MethodRef>();
+    }
+
+    /**
+     * Adds the field to the field list.
+     */
+    public void addField(FieldRef fref) {
+        mFieldRefs.add(fref);
+    }
+
+    /**
+     * Returns the field list as an array.
+     */
+    public FieldRef[] getFieldArray() {
+        return mFieldRefs.toArray(new FieldRef[mFieldRefs.size()]);
+    }
+
+    /**
+     * Adds the method to the method list.
+     */
+    public void addMethod(MethodRef mref) {
+        mMethodRefs.add(mref);
+    }
+
+    /**
+     * Returns the method list as an array.
+     */
+    public MethodRef[] getMethodArray() {
+        return mMethodRefs.toArray(new MethodRef[mMethodRefs.size()]);
+    }
+
+    /**
+     * Gets the class name.
+     */
+    public String getName() {
+        return mClassName;
+    }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/DexData.java b/tools/dexdeps/src/com/android/dexdeps/DexData.java
new file mode 100644
index 0000000..7ce5d04
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java
@@ -0,0 +1,593 @@
+/*
+ * 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.
+ */
+
+package com.android.dexdeps;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+
+/**
+ * Data extracted from a DEX file.
+ */
+public class DexData {
+    private RandomAccessFile mDexFile;
+    private HeaderItem mHeaderItem;
+    private String[] mStrings;              // strings from string_data_*
+    private TypeIdItem[] mTypeIds;
+    private ProtoIdItem[] mProtoIds;
+    private FieldIdItem[] mFieldIds;
+    private MethodIdItem[] mMethodIds;
+    private ClassDefItem[] mClassDefs;
+
+    private byte tmpBuf[] = new byte[4];
+    private boolean isBigEndian = false;
+
+    /**
+     * Constructs a new DexData for this file.
+     */
+    public DexData(RandomAccessFile raf) {
+        mDexFile = raf;
+    }
+
+    /**
+     * Loads the contents of the DEX file into our data structures.
+     *
+     * @throws IOException if we encounter a problem while reading
+     * @throws DexDataException if the DEX contents look bad
+     */
+    public void load() throws IOException {
+        parseHeaderItem();
+
+        loadStrings();
+        loadTypeIds();
+        loadProtoIds();
+        loadFieldIds();
+        loadMethodIds();
+        loadClassDefs();
+
+        markInternalClasses();
+    }
+
+
+    /**
+     * Parses the interesting bits out of the header.
+     */
+    void parseHeaderItem() throws IOException {
+        mHeaderItem = new HeaderItem();
+
+        seek(0);
+
+        byte[] magic = new byte[8];
+        readBytes(magic);
+        if (!Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC)) {
+            System.err.println("Magic number is wrong -- are you sure " +
+                "this is a DEX file?");
+            throw new DexDataException();
+        }
+
+        /*
+         * Read the endian tag, so we properly swap things as we read
+         * them from here on.
+         */
+        seek(8+4+20+4+4);
+        mHeaderItem.endianTag = readInt();
+        if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) {
+            /* do nothing */
+        } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){
+            /* file is big-endian (!), reverse future reads */
+            isBigEndian = true;
+        } else {
+            System.err.println("Endian constant has unexpected value " +
+                Integer.toHexString(mHeaderItem.endianTag));
+            throw new DexDataException();
+        }
+
+        seek(8+4+20);  // magic, checksum, signature
+        mHeaderItem.fileSize = readInt();
+        mHeaderItem.headerSize = readInt();
+        /*mHeaderItem.endianTag =*/ readInt();
+        /*mHeaderItem.linkSize =*/ readInt();
+        /*mHeaderItem.linkOff =*/ readInt();
+        /*mHeaderItem.mapOff =*/ readInt();
+        mHeaderItem.stringIdsSize = readInt();
+        mHeaderItem.stringIdsOff = readInt();
+        mHeaderItem.typeIdsSize = readInt();
+        mHeaderItem.typeIdsOff = readInt();
+        mHeaderItem.protoIdsSize = readInt();
+        mHeaderItem.protoIdsOff = readInt();
+        mHeaderItem.fieldIdsSize = readInt();
+        mHeaderItem.fieldIdsOff = readInt();
+        mHeaderItem.methodIdsSize = readInt();
+        mHeaderItem.methodIdsOff = readInt();
+        mHeaderItem.classDefsSize = readInt();
+        mHeaderItem.classDefsOff = readInt();
+        /*mHeaderItem.dataSize =*/ readInt();
+        /*mHeaderItem.dataOff =*/ readInt();
+    }
+
+    /**
+     * Loads the string table out of the DEX.
+     *
+     * First we read all of the string_id_items, then we read all of the
+     * string_data_item.  Doing it this way should allow us to avoid
+     * seeking around in the file.
+     */
+    void loadStrings() throws IOException {
+        int count = mHeaderItem.stringIdsSize;
+        int stringOffsets[] = new int[count];
+
+        //System.out.println("reading " + count + " strings");
+
+        seek(mHeaderItem.stringIdsOff);
+        for (int i = 0; i < count; i++) {
+            stringOffsets[i] = readInt();
+        }
+
+        mStrings = new String[count];
+
+        seek(stringOffsets[0]);
+        for (int i = 0; i < count; i++) {
+            seek(stringOffsets[i]);         // should be a no-op
+            mStrings[i] = readString();
+            //System.out.println("STR: " + i + ": " + mStrings[i]);
+        }
+    }
+
+    /**
+     * Loads the type ID list.
+     */
+    void loadTypeIds() throws IOException {
+        int count = mHeaderItem.typeIdsSize;
+        mTypeIds = new TypeIdItem[count];
+
+        //System.out.println("reading " + count + " typeIds");
+        seek(mHeaderItem.typeIdsOff);
+        for (int i = 0; i < count; i++) {
+            mTypeIds[i] = new TypeIdItem();
+            mTypeIds[i].descriptorIdx = readInt();
+
+            //System.out.println(i + ": " + mTypeIds[i].descriptorIdx +
+            //    " " + mStrings[mTypeIds[i].descriptorIdx]);
+        }
+    }
+
+    /**
+     * Loads the proto ID list.
+     */
+    void loadProtoIds() throws IOException {
+        int count = mHeaderItem.protoIdsSize;
+        mProtoIds = new ProtoIdItem[count];
+
+        //System.out.println("reading " + count + " protoIds");
+        seek(mHeaderItem.protoIdsOff);
+
+        /*
+         * Read the proto ID items.
+         */
+        for (int i = 0; i < count; i++) {
+            mProtoIds[i] = new ProtoIdItem();
+            mProtoIds[i].shortyIdx = readInt();
+            mProtoIds[i].returnTypeIdx = readInt();
+            mProtoIds[i].parametersOff = readInt();
+
+            //System.out.println(i + ": " + mProtoIds[i].shortyIdx +
+            //    " " + mStrings[mProtoIds[i].shortyIdx]);
+        }
+
+        /*
+         * Go back through and read the type lists.
+         */
+        for (int i = 0; i < count; i++) {
+            ProtoIdItem protoId = mProtoIds[i];
+
+            int offset = protoId.parametersOff;
+
+            if (offset == 0) {
+                protoId.types = new int[0];
+                continue;
+            } else {
+                seek(offset);
+                int size = readInt();       // #of entries in list
+                protoId.types = new int[size];
+
+                for (int j = 0; j < size; j++) {
+                    protoId.types[j] = readShort() & 0xffff;
+                }
+            }
+        }
+    }
+
+    /**
+     * Loads the field ID list.
+     */
+    void loadFieldIds() throws IOException {
+        int count = mHeaderItem.fieldIdsSize;
+        mFieldIds = new FieldIdItem[count];
+
+        //System.out.println("reading " + count + " fieldIds");
+        seek(mHeaderItem.fieldIdsOff);
+        for (int i = 0; i < count; i++) {
+            mFieldIds[i] = new FieldIdItem();
+            mFieldIds[i].classIdx = readShort() & 0xffff;
+            mFieldIds[i].typeIdx = readShort() & 0xffff;
+            mFieldIds[i].nameIdx = readInt();
+
+            //System.out.println(i + ": " + mFieldIds[i].nameIdx +
+            //    " " + mStrings[mFieldIds[i].nameIdx]);
+        }
+    }
+
+    /**
+     * Loads the method ID list.
+     */
+    void loadMethodIds() throws IOException {
+        int count = mHeaderItem.methodIdsSize;
+        mMethodIds = new MethodIdItem[count];
+
+        //System.out.println("reading " + count + " methodIds");
+        seek(mHeaderItem.methodIdsOff);
+        for (int i = 0; i < count; i++) {
+            mMethodIds[i] = new MethodIdItem();
+            mMethodIds[i].classIdx = readShort() & 0xffff;
+            mMethodIds[i].protoIdx = readShort() & 0xffff;
+            mMethodIds[i].nameIdx = readInt();
+
+            //System.out.println(i + ": " + mMethodIds[i].nameIdx +
+            //    " " + mStrings[mMethodIds[i].nameIdx]);
+        }
+    }
+
+    /**
+     * Loads the class defs list.
+     */
+    void loadClassDefs() throws IOException {
+        int count = mHeaderItem.classDefsSize;
+        mClassDefs = new ClassDefItem[count];
+
+        //System.out.println("reading " + count + " classDefs");
+        seek(mHeaderItem.classDefsOff);
+        for (int i = 0; i < count; i++) {
+            mClassDefs[i] = new ClassDefItem();
+            mClassDefs[i].classIdx = readInt();
+
+            /* access_flags = */ readInt();
+            /* superclass_idx = */ readInt();
+            /* interfaces_off = */ readInt();
+            /* source_file_idx = */ readInt();
+            /* annotations_off = */ readInt();
+            /* class_data_off = */ readInt();
+            /* static_values_off = */ readInt();
+
+            //System.out.println(i + ": " + mClassDefs[i].classIdx + " " +
+            //    mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]);
+        }
+    }
+
+    /**
+     * Sets the "internal" flag on type IDs which are defined in the
+     * DEX file or within the VM (e.g. primitive classes and arrays).
+     */
+    void markInternalClasses() {
+        for (int i = mClassDefs.length -1; i >= 0; i--) {
+            mTypeIds[mClassDefs[i].classIdx].internal = true;
+        }
+
+        for (int i = 0; i < mTypeIds.length; i++) {
+            String className = mStrings[mTypeIds[i].descriptorIdx];
+
+            if (className.length() == 1) {
+                // primitive class
+                mTypeIds[i].internal = true;
+            } else if (className.charAt(0) == '[') {
+                mTypeIds[i].internal = true;
+            }
+
+            //System.out.println(i + " " +
+            //    (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " +
+            //    mStrings[mTypeIds[i].descriptorIdx]);
+        }
+    }
+
+
+    /*
+     * =======================================================================
+     *      Queries
+     * =======================================================================
+     */
+
+    /**
+     * Returns the class name, given an index into the type_ids table.
+     */
+    private String classNameFromTypeIndex(int idx) {
+        return mStrings[mTypeIds[idx].descriptorIdx];
+    }
+
+    /**
+     * Returns an array of method argument type strings, given an index
+     * into the proto_ids table.
+     */
+    private String[] argArrayFromProtoIndex(int idx) {
+        ProtoIdItem protoId = mProtoIds[idx];
+        String[] result = new String[protoId.types.length];
+
+        for (int i = 0; i < protoId.types.length; i++) {
+            result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx];
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a string representing the method's return type, given an
+     * index into the proto_ids table.
+     */
+    private String returnTypeFromProtoIndex(int idx) {
+        ProtoIdItem protoId = mProtoIds[idx];
+        return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx];
+    }
+
+    /**
+     * Returns an array with all of the class references that don't
+     * correspond to classes in the DEX file.  Each class reference has
+     * a list of the referenced fields and methods associated with
+     * that class.
+     */
+    public ClassRef[] getExternalReferences() {
+        // create a sparse array of ClassRef that parallels mTypeIds
+        ClassRef[] sparseRefs = new ClassRef[mTypeIds.length];
+
+        // create entries for all externally-referenced classes
+        int count = 0;
+        for (int i = 0; i < mTypeIds.length; i++) {
+            if (!mTypeIds[i].internal) {
+                sparseRefs[i] =
+                    new ClassRef(mStrings[mTypeIds[i].descriptorIdx]);
+                count++;
+            }
+        }
+
+        // add fields and methods to the appropriate class entry
+        addExternalFieldReferences(sparseRefs);
+        addExternalMethodReferences(sparseRefs);
+
+        // crunch out the sparseness
+        ClassRef[] classRefs = new ClassRef[count];
+        int idx = 0;
+        for (int i = 0; i < mTypeIds.length; i++) {
+            if (sparseRefs[i] != null)
+                classRefs[idx++] = sparseRefs[i];
+        }
+
+        assert idx == count;
+
+        return classRefs;
+    }
+
+    /**
+     * Runs through the list of field references, inserting external
+     * references into the appropriate ClassRef.
+     */
+    private void addExternalFieldReferences(ClassRef[] sparseRefs) {
+        for (int i = 0; i < mFieldIds.length; i++) {
+            if (!mTypeIds[mFieldIds[i].classIdx].internal) {
+                FieldIdItem fieldId = mFieldIds[i];
+                FieldRef newFieldRef = new FieldRef(
+                        classNameFromTypeIndex(fieldId.classIdx),
+                        classNameFromTypeIndex(fieldId.typeIdx),
+                        mStrings[fieldId.nameIdx]);
+                sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef);
+            }
+        }
+    }
+
+    /**
+     * Runs through the list of method references, inserting external
+     * references into the appropriate ClassRef.
+     */
+    private void addExternalMethodReferences(ClassRef[] sparseRefs) {
+        for (int i = 0; i < mMethodIds.length; i++) {
+            if (!mTypeIds[mMethodIds[i].classIdx].internal) {
+                MethodIdItem methodId = mMethodIds[i];
+                MethodRef newMethodRef = new MethodRef(
+                        classNameFromTypeIndex(methodId.classIdx),
+                        argArrayFromProtoIndex(methodId.protoIdx),
+                        returnTypeFromProtoIndex(methodId.protoIdx),
+                        mStrings[methodId.nameIdx]);
+                sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef);
+            }
+        }
+    }
+
+
+    /*
+     * =======================================================================
+     *      Basic I/O functions
+     * =======================================================================
+     */
+
+    /**
+     * Seeks the DEX file to the specified absolute position.
+     */
+    void seek(int position) throws IOException {
+        mDexFile.seek(position);
+    }
+
+    /**
+     * Fills the buffer by reading bytes from the DEX file.
+     */
+    void readBytes(byte[] buffer) throws IOException {
+        mDexFile.readFully(buffer);
+    }
+
+    /**
+     * Reads a single signed byte value.
+     */
+    byte readByte() throws IOException {
+        mDexFile.readFully(tmpBuf, 0, 1);
+        return tmpBuf[0];
+    }
+
+    /**
+     * Reads a signed 16-bit integer, byte-swapping if necessary.
+     */
+    short readShort() throws IOException {
+        mDexFile.readFully(tmpBuf, 0, 2);
+        if (isBigEndian) {
+            return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8));
+        } else {
+            return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8));
+        }
+    }
+
+    /**
+     * Reads a signed 32-bit integer, byte-swapping if necessary.
+     */
+    int readInt() throws IOException {
+        mDexFile.readFully(tmpBuf, 0, 4);
+
+        if (isBigEndian) {
+            return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) |
+                   ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24);
+        } else {
+            return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) |
+                   ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24);
+        }
+    }
+
+    /**
+     * Reads a variable-length unsigned LEB128 value.  Does not attempt to
+     * verify that the value is valid.
+     *
+     * @throws EOFException if we run off the end of the file
+     */
+    int readUnsignedLeb128() throws IOException {
+        int result = 0;
+        byte val;
+
+        do {
+            val = readByte();
+            result = (result << 7) | (val & 0x7f);
+        } while (val < 0);
+
+        return result;
+    }
+
+    /**
+     * Reads a UTF-8 string.
+     *
+     * We don't know how long the UTF-8 string is, so we have to read one
+     * byte at a time.  We could make an educated guess based on the
+     * utf16_size and seek back if we get it wrong, but seeking backward
+     * may cause the underlying implementation to reload I/O buffers.
+     */
+    String readString() throws IOException {
+        int utf16len = readUnsignedLeb128();
+        byte inBuf[] = new byte[utf16len * 3];      // worst case
+        int idx;
+
+        for (idx = 0; idx < inBuf.length; idx++) {
+            byte val = readByte();
+            if (val == 0)
+                break;
+            inBuf[idx] = val;
+        }
+
+        return new String(inBuf, 0, idx, "UTF-8");
+    }
+
+
+    /*
+     * =======================================================================
+     *      Internal "structure" declarations
+     * =======================================================================
+     */
+
+    /**
+     * Holds the contents of a header_item.
+     */
+    static class HeaderItem {
+        public int fileSize;
+        public int headerSize;
+        public int endianTag;
+        public int stringIdsSize, stringIdsOff;
+        public int typeIdsSize, typeIdsOff;
+        public int protoIdsSize, protoIdsOff;
+        public int fieldIdsSize, fieldIdsOff;
+        public int methodIdsSize, methodIdsOff;
+        public int classDefsSize, classDefsOff;
+
+        /* expected magic values */
+        public static final byte[] DEX_FILE_MAGIC = {
+            0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
+        public static final int ENDIAN_CONSTANT = 0x12345678;
+        public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
+    }
+
+    /**
+     * Holds the contents of a type_id_item.
+     *
+     * This is chiefly a list of indices into the string table.  We need
+     * some additional bits of data, such as whether or not the type ID
+     * represents a class defined in this DEX, so we use an object for
+     * each instead of a simple integer.  (Could use a parallel array, but
+     * since this is a desktop app it's not essential.)
+     */
+    static class TypeIdItem {
+        public int descriptorIdx;       // index into string_ids
+
+        public boolean internal;        // defined within this DEX file?
+    }
+
+    /**
+     * Holds the contents of a proto_id_item.
+     */
+    static class ProtoIdItem {
+        public int shortyIdx;           // index into string_ids
+        public int returnTypeIdx;       // index into type_ids
+        public int parametersOff;       // file offset to a type_list
+
+        public int types[];             // contents of type list
+    }
+
+    /**
+     * Holds the contents of a field_id_item.
+     */
+    static class FieldIdItem {
+        public int classIdx;            // index into type_ids (defining class)
+        public int typeIdx;             // index into type_ids (field type)
+        public int nameIdx;             // index into string_ids
+    }
+
+    /**
+     * Holds the contents of a method_id_item.
+     */
+    static class MethodIdItem {
+        public int classIdx;            // index into type_ids
+        public int protoIdx;            // index into proto_ids
+        public int nameIdx;             // index into string_ids
+    }
+
+    /**
+     * Holds the contents of a class_def_item.
+     *
+     * We don't really need a class for this, but there's some stuff in
+     * the class_def_item that we might want later.
+     */
+    static class ClassDefItem {
+        public int classIdx;            // index into type_ids
+    }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/DexDataException.java b/tools/dexdeps/src/com/android/dexdeps/DexDataException.java
new file mode 100644
index 0000000..873db94
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/DexDataException.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package com.android.dexdeps;
+
+/**
+ * Bad data found inside a DEX file.
+ */
+public class DexDataException extends RuntimeException {
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/FieldRef.java b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java
new file mode 100644
index 0000000..93e87cb
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package com.android.dexdeps;
+
+public class FieldRef {
+    private String mDeclClass, mFieldType, mFieldName;
+
+    /**
+     * Initializes a new field reference.
+     */
+    public FieldRef(String declClass, String fieldType, String fieldName) {
+        mDeclClass = declClass;
+        mFieldType = fieldType;
+        mFieldName = fieldName;
+    }
+
+    /**
+     * Gets the name of the field's declaring class.
+     */
+    public String getDeclClassName() {
+        return mDeclClass;
+    }
+
+    /**
+     * Gets the type name.  Examples: "Ljava/lang/String;", "[I".
+     */
+    public String getTypeName() {
+        return mFieldType;
+    }
+
+    /**
+     * Gets the field name.
+     */
+    public String getName() {
+        return mFieldName;
+    }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/Main.java b/tools/dexdeps/src/com/android/dexdeps/Main.java
new file mode 100644
index 0000000..d48889d
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/Main.java
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+package com.android.dexdeps;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+public class Main {
+    private static final String CLASSES_DEX = "classes.dex";
+
+    private String mInputFileName;
+    private String mOutputFormat = "xml";
+
+    /**
+     * Entry point.
+     */
+    public static void main(String[] args) {
+        Main main = new Main();
+        main.run(args);
+    }
+
+    /**
+     * Start things up.
+     */
+    void run(String[] args) {
+        try {
+            parseArgs(args);
+            RandomAccessFile raf = openInputFile();
+            DexData dexData = new DexData(raf);
+            dexData.load();
+
+            Output.generate(dexData, mOutputFormat);
+        } catch (UsageException ue) {
+            usage();
+            System.exit(2);
+        } catch (IOException ioe) {
+            if (ioe.getMessage() != null)
+                System.err.println("Failed: " + ioe);
+            System.exit(1);
+        } catch (DexDataException dde) {
+            /* a message was already reported, just bail quietly */
+            System.exit(1);
+        }
+    }
+
+    /**
+     * Opens the input file, which could be a .dex or a .jar/.apk with a
+     * classes.dex inside.  If the latter, we extract the contents to a
+     * temporary file.
+     */
+    RandomAccessFile openInputFile() throws IOException {
+        RandomAccessFile raf;
+
+        raf = openInputFileAsZip();
+        if (raf == null) {
+            File inputFile = new File(mInputFileName);
+            raf = new RandomAccessFile(inputFile, "r");
+        }
+
+        return raf;
+    }
+
+    /**
+     * Tries to open the input file as a Zip archive (jar/apk) with a
+     * "classes.dex" inside.
+     *
+     * @return a RandomAccessFile for classes.dex, or null if the input file
+     *         is not a zip archive
+     * @throws IOException if the file isn't found, or it's a zip and
+     *         classes.dex isn't found inside
+     */
+    RandomAccessFile openInputFileAsZip() throws IOException {
+        ZipFile zipFile;
+
+        /*
+         * Try it as a zip file.
+         */
+        try {
+            zipFile = new ZipFile(mInputFileName);
+        } catch (FileNotFoundException fnfe) {
+            /* not found, no point in retrying as non-zip */
+            System.err.println("Unable to open '" + mInputFileName + "': " +
+                fnfe.getMessage());
+            throw fnfe;
+        } catch (ZipException ze) {
+            /* not a zip */
+            return null;
+        }
+
+        /*
+         * We know it's a zip; see if there's anything useful inside.  A
+         * failure here results in some type of IOException (of which
+         * ZipException is a subclass).
+         */
+        ZipEntry entry = zipFile.getEntry(CLASSES_DEX);
+        if (entry == null) {
+            System.err.println("Unable to find '" + CLASSES_DEX +
+                "' in '" + mInputFileName + "'");
+            zipFile.close();
+            throw new ZipException();
+        }
+
+        InputStream zis = zipFile.getInputStream(entry);
+
+        /*
+         * Create a temp file to hold the DEX data, open it, and delete it
+         * to ensure it doesn't hang around if we fail.
+         */
+        File tempFile = File.createTempFile("dexdeps", ".dex");
+        //System.out.println("+++ using temp " + tempFile);
+        RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
+        tempFile.delete();
+
+        /*
+         * Copy all data from input stream to output file.
+         */
+        byte copyBuf[] = new byte[32768];
+        int actual;
+
+        while (true) {
+            actual = zis.read(copyBuf);
+            if (actual == -1)
+                break;
+
+            raf.write(copyBuf, 0, actual);
+        }
+
+        zis.close();
+        raf.seek(0);
+
+        return raf;
+    }
+
+
+    /**
+     * Parses command-line arguments.
+     *
+     * @throws UsageException if arguments are missing or poorly formed
+     */
+    void parseArgs(String[] args) {
+        int idx;
+
+        for (idx = 0; idx < args.length; idx++) {
+            String arg = args[idx];
+
+            if (arg.equals("--") || !arg.startsWith("--")) {
+                break;
+            } else if (arg.startsWith("--format=")) {
+                mOutputFormat = arg.substring(arg.indexOf('=') + 1);
+                if (!mOutputFormat.equals("brief") &&
+                    !mOutputFormat.equals("xml"))
+                {
+                    System.err.println("Unknown format '" + mOutputFormat +"'");
+                    throw new UsageException();
+                }
+                //System.out.println("+++ using format " + mOutputFormat);
+            } else {
+                System.err.println("Unknown option '" + arg + "'");
+                throw new UsageException();
+            }
+        }
+
+        // expecting one argument left
+        if (idx != args.length - 1) {
+            throw new UsageException();
+        }
+
+        mInputFileName = args[idx];
+    }
+
+    /**
+     * Prints command-line usage info.
+     */
+    void usage() {
+        System.err.println("DEX dependency scanner v1.1");
+        System.err.println("Copyright (C) 2009 The Android Open Source Project\n");
+        System.err.println("Usage: dexdeps [options] <file.{dex,apk,jar}>");
+        System.err.println("Options:");
+        System.err.println("  --format={xml,brief}");
+    }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/MethodRef.java b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java
new file mode 100644
index 0000000..67dce62
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package com.android.dexdeps;
+
+public class MethodRef {
+    private String mDeclClass, mReturnType, mMethodName;
+    private String[] mArgTypes;
+
+    /**
+     * Initializes a new field reference.
+     */
+    public MethodRef(String declClass, String[] argTypes, String returnType,
+            String methodName) {
+        mDeclClass = declClass;
+        mArgTypes = argTypes;
+        mReturnType = returnType;
+        mMethodName = methodName;
+    }
+
+    /**
+     * Gets the name of the method's declaring class.
+     */
+    public String getDeclClassName() {
+        return mDeclClass;
+    }
+
+    /**
+     * Gets the method's descriptor.
+     */
+    public String getDescriptor() {
+        return descriptorFromProtoArray(mArgTypes, mReturnType);
+    }
+
+    /**
+     * Gets the method's name.
+     */
+    public String getName() {
+        return mMethodName;
+    }
+
+    /**
+     * Gets an array of method argument types.
+     */
+    public String[] getArgumentTypeNames() {
+        return mArgTypes;
+    }
+
+    /**
+     * Gets the method's return type.  Examples: "Ljava/lang/String;", "[I".
+     */
+    public String getReturnTypeName() {
+        return mReturnType;
+    }
+
+    /**
+     * Returns the method descriptor, given the argument and return type
+     * prototype strings.
+     */
+    private static String descriptorFromProtoArray(String[] protos,
+            String returnType) {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append("(");
+        for (int i = 0; i < protos.length; i++) {
+            builder.append(protos[i]);
+        }
+
+        builder.append(")");
+        builder.append(returnType);
+
+        return builder.toString();
+    }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/Output.java b/tools/dexdeps/src/com/android/dexdeps/Output.java
new file mode 100644
index 0000000..b786825
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/Output.java
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+package com.android.dexdeps;
+
+/**
+ * Generate fancy output.
+ */
+public class Output {
+    private static final String IN0 = "";
+    private static final String IN1 = "  ";
+    private static final String IN2 = "    ";
+    private static final String IN3 = "      ";
+    private static final String IN4 = "        ";
+
+    public static void generate(DexData dexData, String format) {
+        if (format.equals("brief")) {
+            printBrief(dexData);
+        } else if (format.equals("xml")) {
+            printXml(dexData);
+        } else {
+            /* should've been trapped in arg handler */
+            throw new RuntimeException("unknown output format");
+        }
+    }
+
+    /**
+     * Prints the data in a simple human-readable format.
+     */
+    static void printBrief(DexData dexData) {
+        ClassRef[] externClassRefs = dexData.getExternalReferences();
+
+        printClassRefs(externClassRefs);
+        printFieldRefs(externClassRefs);
+        printMethodRefs(externClassRefs);
+    }
+
+    /**
+     * Prints the list of classes in a simple human-readable format.
+     */
+    static void printClassRefs(ClassRef[] classes) {
+        System.out.println("Classes:");
+        for (int i = 0; i < classes.length; i++) {
+            ClassRef ref = classes[i];
+
+            System.out.println(descriptorToDot(ref.getName()));
+        }
+    }
+
+    /**
+     * Prints the list of fields in a simple human-readable format.
+     */
+    static void printFieldRefs(ClassRef[] classes) {
+        System.out.println("\nFields:");
+        for (int i = 0; i < classes.length; i++) {
+            FieldRef[] fields = classes[i].getFieldArray();
+
+            for (int j = 0; j < fields.length; j++) {
+                FieldRef ref = fields[j];
+
+                System.out.println(descriptorToDot(ref.getDeclClassName()) +
+                    "." + ref.getName() + " : " + ref.getTypeName());
+            }
+        }
+    }
+
+    /**
+     * Prints the list of methods in a simple human-readable format.
+     */
+    static void printMethodRefs(ClassRef[] classes) {
+        System.out.println("\nMethods:");
+        for (int i = 0; i < classes.length; i++) {
+            MethodRef[] methods = classes[i].getMethodArray();
+
+            for (int j = 0; j < methods.length; j++) {
+                MethodRef ref = methods[j];
+
+                System.out.println(descriptorToDot(ref.getDeclClassName()) +
+                    "." + ref.getName() + " : " + ref.getDescriptor());
+            }
+        }
+    }
+
+
+    /**
+     * Prints the output in XML format.
+     *
+     * We shouldn't need to XML-escape the field/method info.
+     */
+    static void printXml(DexData dexData) {
+        ClassRef[] externClassRefs = dexData.getExternalReferences();
+
+        System.out.println(IN0 + "<external>");
+
+        /*
+         * Iterate through externClassRefs.  For each class, dump all of
+         * the matching fields and methods.
+         */
+        String prevPackage = null;
+        for (int i = 0; i < externClassRefs.length; i++) {
+            ClassRef cref = externClassRefs[i];
+            String declClassName = cref.getName();
+            String className = classNameOnly(declClassName);
+            String packageName = packageNameOnly(declClassName);
+
+            /*
+             * If we're in a different package, emit the appropriate tags.
+             */
+            if (!packageName.equals(prevPackage)) {
+                if (prevPackage != null) {
+                    System.out.println(IN1 + "</package>");
+                }
+
+                System.out.println(IN1 +
+                    "<package name=\"" + packageName + "\">");
+
+                prevPackage = packageName;
+            }
+
+            System.out.println(IN2 + "<class name=\"" + className + "\">");
+            printXmlFields(cref);
+            printXmlMethods(cref);
+            System.out.println(IN2 + "</class>");
+        }
+
+        if (prevPackage != null)
+            System.out.println(IN1 + "</package>");
+        System.out.println(IN0 + "</external>");
+    }
+
+    /**
+     * Prints the externally-visible fields in XML format.
+     */
+    private static void printXmlFields(ClassRef cref) {
+        FieldRef[] fields = cref.getFieldArray();
+        for (int i = 0; i < fields.length; i++) {
+            FieldRef fref = fields[i];
+
+            System.out.println(IN3 + "<field name=\"" + fref.getName() +
+                "\" type=\"" + descriptorToDot(fref.getTypeName()) + "\"/>");
+        }
+    }
+
+    /**
+     * Prints the externally-visible methods in XML format.
+     */
+    private static void printXmlMethods(ClassRef cref) {
+        MethodRef[] methods = cref.getMethodArray();
+        for (int i = 0; i < methods.length; i++) {
+            MethodRef mref = methods[i];
+            String declClassName = mref.getDeclClassName();
+            boolean constructor;
+
+            constructor = mref.getName().equals("<init>");
+            if (constructor) {
+                // use class name instead of method name
+                System.out.println(IN3 + "<constructor name=\"" +
+                    classNameOnly(declClassName) + "\">");
+            } else {
+                System.out.println(IN3 + "<method name=\"" + mref.getName() +
+                    "\" return=\"" + descriptorToDot(mref.getReturnTypeName()) +
+                    "\">");
+            }
+            String[] args = mref.getArgumentTypeNames();
+            for (int j = 0; j < args.length; j++) {
+                System.out.println(IN4 + "<parameter type=\"" +
+                    descriptorToDot(args[j]) + "\"/>");
+            }
+            if (constructor) {
+                System.out.println(IN3 + "</constructor>");
+            } else {
+                System.out.println(IN3 + "</method>");
+            }
+        }
+    }
+
+
+    /*
+     * =======================================================================
+     *      Utility functions
+     * =======================================================================
+     */
+
+    /**
+     * Converts a single-character primitive type into its human-readable
+     * equivalent.
+     */
+    static String primitiveTypeLabel(char typeChar) {
+        /* primitive type; substitute human-readable name in */
+        switch (typeChar) {
+            case 'B':   return "byte";
+            case 'C':   return "char";
+            case 'D':   return "double";
+            case 'F':   return "float";
+            case 'I':   return "int";
+            case 'J':   return "long";
+            case 'S':   return "short";
+            case 'V':   return "void";
+            case 'Z':   return "boolean";
+            default:
+                /* huh? */
+                System.err.println("Unexpected class char " + typeChar);
+                assert false;
+                return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Converts a type descriptor to human-readable "dotted" form.  For
+     * example, "Ljava/lang/String;" becomes "java.lang.String", and
+     * "[I" becomes "int[].
+     */
+    static String descriptorToDot(String descr) {
+        int targetLen = descr.length();
+        int offset = 0;
+        int arrayDepth = 0;
+
+        /* strip leading [s; will be added to end */
+        while (targetLen > 1 && descr.charAt(offset) == '[') {
+            offset++;
+            targetLen--;
+        }
+        arrayDepth = offset;
+
+        if (targetLen == 1) {
+            descr = primitiveTypeLabel(descr.charAt(offset));
+            offset = 0;
+            targetLen = descr.length();
+        } else {
+            /* account for leading 'L' and trailing ';' */
+            if (targetLen >= 2 && descr.charAt(offset) == 'L' &&
+                descr.charAt(offset+targetLen-1) == ';')
+            {
+                targetLen -= 2;     /* two fewer chars to copy */
+                offset++;           /* skip the 'L' */
+            }
+        }
+
+        char[] buf = new char[targetLen + arrayDepth * 2];
+
+        /* copy class name over */
+        int i;
+        for (i = 0; i < targetLen; i++) {
+            char ch = descr.charAt(offset + i);
+            buf[i] = (ch == '/') ? '.' : ch;
+        }
+
+        /* add the appopriate number of brackets for arrays */
+        while (arrayDepth-- > 0) {
+            buf[i++] = '[';
+            buf[i++] = ']';
+        }
+        assert i == buf.length;
+
+        return new String(buf);
+    }
+
+    /**
+     * Extracts the class name from a type descriptor.
+     */
+    static String classNameOnly(String typeName) {
+        String dotted = descriptorToDot(typeName);
+
+        int start = dotted.lastIndexOf(".");
+        if (start < 0) {
+            return dotted;
+        } else {
+            return dotted.substring(start+1);
+        }
+    }
+
+    /**
+     * Extracts the package name from a type descriptor, and returns it in
+     * dotted form.
+     */
+    static String packageNameOnly(String typeName) {
+        String dotted = descriptorToDot(typeName);
+
+        int end = dotted.lastIndexOf(".");
+        if (end < 0) {
+            /* lives in default package */
+            return "";
+        } else {
+            return dotted.substring(0, end);
+        }
+    }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/UsageException.java b/tools/dexdeps/src/com/android/dexdeps/UsageException.java
new file mode 100644
index 0000000..e695fc7
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/UsageException.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package com.android.dexdeps;
+
+/**
+ * Tells the main entry point to show the usage information and bail.
+ */
+public class UsageException extends RuntimeException {
+}
diff --git a/tools/dmtracedump/Android.mk b/tools/dmtracedump/Android.mk
new file mode 100644
index 0000000..5d3146e
--- /dev/null
+++ b/tools/dmtracedump/Android.mk
@@ -0,0 +1,24 @@
+#
+# Copyright 2006 The Android Open Source Project
+#
+# Java method trace dump tool
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := TraceDump.c
+LOCAL_CFLAGS += -O0 -g
+LOCAL_C_INCLUDES += dalvik/vm
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dmtracedump
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := CreateTestTrace.c
+LOCAL_CFLAGS += -O0 -g
+LOCAL_C_INCLUDES += dalvik/vm
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := create_test_dmtrace
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/dmtracedump/CreateTestTrace.c b/tools/dmtracedump/CreateTestTrace.c
new file mode 100644
index 0000000..0b85d51
--- /dev/null
+++ b/tools/dmtracedump/CreateTestTrace.c
@@ -0,0 +1,493 @@
+/* //device/tools/dmtracedump/CreateTrace.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/*
+ * Create a test file in the format required by dmtrace.
+ */
+#define NOT_VM
+#include "Profile.h"        // from VM header
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <ctype.h>
+
+/*
+ * Values from the header of the data file.
+ */
+typedef struct DataHeader {
+    unsigned int magic;
+    short version;
+    short offsetToData;
+    long long startWhen;
+} DataHeader;
+
+#define VERSION 2
+int versionNumber = VERSION;
+int verbose = 0;
+
+DataHeader header = { 0x574f4c53, VERSION, sizeof(DataHeader), 0LL };
+
+char *versionHeader = "*version\n";
+char *clockDef = "clock=thread-cpu\n";
+
+char *keyThreads =
+"*threads\n"
+"1      main\n"
+"2      foo\n"
+"3      bar\n"
+"4      blah\n";
+
+char *keyEnd = "*end\n";
+
+typedef struct dataRecord {
+    unsigned int        time;
+    int                 threadId;
+    unsigned int        action;         /* 0=entry, 1=exit, 2=exception exit */
+    char                *fullName;
+    char                *className;
+    char                *methodName;
+    char                *signature;
+    unsigned int        methodId;
+} dataRecord;
+
+dataRecord *records;
+
+#define BUF_SIZE 1024
+char buf[BUF_SIZE];
+
+typedef struct stack {
+    dataRecord  **frames;
+    int         indentLevel;
+} stack;
+
+/* Mac OS doesn't have strndup(), so implement it here.
+ */
+char *strndup(const char *src, size_t len)
+{
+    char *dest = (char *) malloc(len + 1);
+    strncpy(dest, src, len);
+    dest[len] = 0;
+    return dest;
+}
+
+/*
+ * Parse the input file.  It looks something like this:
+ * # This is a comment line
+ * 4  1 A
+ * 6  1  B
+ * 8  1  B
+ * 10 1 A
+ *
+ * where the first column is the time, the second column is the thread id,
+ * and the third column is the method (actually just the class name).  The
+ * number of spaces between the 2nd and 3rd columns is the indentation and
+ * determines the call stack.  Each called method must be indented by one
+ * more space.  In the example above, A is called at time 4, A calls B at
+ * time 6, B returns at time 8, and A returns at time 10.  Thread 1 is the
+ * only thread that is running.
+ *
+ * An alternative file format leaves out the first two columns:
+ * A
+ *  B
+ *  B
+ * A
+ *
+ * In this file format, the thread id is always 1, and the time starts at
+ * 2 and increments by 2 for each line.
+ */
+void parseInputFile(const char *inputFileName)
+{
+    unsigned int time = 0, threadId;
+    int len;
+    int linenum = 0;
+    int nextRecord = 0;
+    int indentLevel = 0;
+    stack *callStack;
+
+    FILE *inputFp = fopen(inputFileName, "r");
+    if (inputFp == NULL) {
+        perror(inputFileName);
+        exit(1);
+    }
+
+    /* Count the number of lines in the buffer */
+    int numRecords = 0;
+    int maxThreadId = 1;
+    int maxFrames = 0;
+    char *indentEnd;
+    while (fgets(buf, BUF_SIZE, inputFp)) {
+        char *cp = buf;
+        if (*cp == '#')
+            continue;
+        numRecords += 1;
+        if (isdigit(*cp)) {
+            int time = strtoul(cp, &cp, 0);
+            while (isspace(*cp))
+                cp += 1;
+            int threadId = strtoul(cp, &cp, 0);
+            if (maxThreadId < threadId)
+                maxThreadId = threadId;
+        }
+        indentEnd = cp;
+        while (isspace(*indentEnd))
+            indentEnd += 1;
+        if (indentEnd - cp + 1 > maxFrames)
+            maxFrames = indentEnd - cp + 1;
+    }
+    int numThreads = maxThreadId + 1;
+
+    /* Add space for a sentinel record at the end */
+    numRecords += 1;
+    records = (dataRecord *) malloc(sizeof(dataRecord) * numRecords);
+    callStack = (stack *) malloc(sizeof(stack) * numThreads);
+    int ii;
+    for (ii = 0; ii < numThreads; ++ii) {
+        callStack[ii].frames = NULL;
+        callStack[ii].indentLevel = 0;
+    }
+
+    rewind(inputFp);
+    while (fgets(buf, BUF_SIZE, inputFp)) {
+        int indent;
+        int action;
+        char *save_cp;
+
+        linenum += 1;
+        char *cp = buf;
+
+        /* Skip lines that start with '#' */
+        if (*cp == '#')
+            continue;
+
+        /* Get time and thread id */
+        if (!isdigit(*cp)) {
+            /* If the line does not begin with a digit, then fill in
+             * default values for the time and threadId.
+             */
+            time += 2;
+            threadId = 1;
+        } else {
+            time = strtoul(cp, &cp, 0);
+            while (isspace(*cp))
+                cp += 1;
+            threadId = strtoul(cp, &cp, 0);
+            cp += 1;
+        }
+
+        // Allocate space for the thread stack, if necessary
+        if (callStack[threadId].frames == NULL) {
+            dataRecord **stk;
+            stk = (dataRecord **) malloc(sizeof(dataRecord *) * maxFrames);
+            callStack[threadId].frames = stk;
+        }
+        indentLevel = callStack[threadId].indentLevel;
+
+        save_cp = cp;
+        while (isspace(*cp)) {
+            cp += 1;
+        }
+        indent = cp - save_cp + 1;
+        records[nextRecord].time = time;
+        records[nextRecord].threadId = threadId;
+
+        save_cp = cp;
+        while (*cp != '\n')
+            cp += 1;
+
+        /* Remove trailing spaces */
+        cp -= 1;
+        while (isspace(*cp))
+            cp -= 1;
+        cp += 1;
+        len = cp - save_cp;
+        records[nextRecord].fullName = strndup(save_cp, len);
+
+        /* Parse the name to support "class.method signature" */
+        records[nextRecord].className = NULL;
+        records[nextRecord].methodName = NULL;
+        records[nextRecord].signature = NULL;
+        cp = strchr(save_cp, '.');
+        if (cp) {
+            len = cp - save_cp;
+            if (len > 0)
+                records[nextRecord].className = strndup(save_cp, len);
+            save_cp = cp + 1;
+            cp = strchr(save_cp, ' ');
+            if (cp == NULL)
+                cp = strchr(save_cp, '\n');
+            if (cp && cp > save_cp) {
+                len = cp - save_cp;
+                records[nextRecord].methodName = strndup(save_cp, len);
+                save_cp = cp + 1;
+                cp = strchr(save_cp, ' ');
+                if (cp == NULL)
+                    cp = strchr(save_cp, '\n');
+                if (cp && cp > save_cp) {
+                    len = cp - save_cp;
+                    records[nextRecord].signature = strndup(save_cp, len);
+                }
+            }
+        }
+
+        if (verbose) {
+            printf("Indent: %d; IndentLevel: %d; Line: %s", indent, indentLevel, buf);
+        }
+
+        action = 0;
+        if (indent == indentLevel + 1) { // Entering a method
+            if (verbose)
+                printf("  Entering %s\n", records[nextRecord].fullName);
+            callStack[threadId].frames[indentLevel] = &records[nextRecord];
+        } else if (indent == indentLevel) { // Exiting a method
+            // Exiting method must be currently on top of stack (unless stack is empty)
+            if (callStack[threadId].frames[indentLevel - 1] == NULL) {
+                if (verbose)
+                    printf("  Exiting %s (past bottom of stack)\n", records[nextRecord].fullName);
+                callStack[threadId].frames[indentLevel - 1] = &records[nextRecord];
+                action = 1;
+            } else {
+                if (indentLevel < 1) {
+                    fprintf(stderr, "Error: line %d: %s", linenum, buf);
+                    fprintf(stderr, "  expected positive (>0) indentation, found %d\n",
+                            indent);
+                    exit(1);
+                }
+                char *name = callStack[threadId].frames[indentLevel - 1]->fullName;
+                if (strcmp(name, records[nextRecord].fullName) == 0) {
+                    if (verbose)
+                        printf("  Exiting %s\n", name);
+                    action = 1;
+                } else { // exiting method doesn't match stack's top method
+                    fprintf(stderr, "Error: line %d: %s", linenum, buf);
+                    fprintf(stderr, "  expected exit from %s\n",
+                            callStack[threadId].frames[indentLevel - 1]->fullName);
+                    exit(1);
+                }
+            }
+        } else {
+            if (nextRecord != 0) {
+                fprintf(stderr, "Error: line %d: %s", linenum, buf);
+                fprintf(stderr, "  expected indentation %d [+1], found %d\n",
+                        indentLevel, indent);
+                exit(1);
+            }
+
+            if (verbose) {
+                printf("  Nonzero indent at first record\n");
+                printf("  Entering %s\n", records[nextRecord].fullName);
+            }
+
+            // This is the first line of data, so we allow a larger
+            // initial indent.  This allows us to test popping off more
+            // frames than we entered.
+            indentLevel = indent - 1;
+            callStack[threadId].frames[indentLevel] = &records[nextRecord];
+        }
+
+        if (action == 0)
+            indentLevel += 1;
+        else
+            indentLevel -= 1;
+        records[nextRecord].action = action;
+        callStack[threadId].indentLevel = indentLevel;
+
+        nextRecord += 1;
+    }
+
+    /* Mark the last record with a sentinel */
+    memset(&records[nextRecord], 0, sizeof(dataRecord));
+}
+
+
+/*
+ * Write values to the binary data file.
+ */
+void write2LE(FILE* fp, unsigned short val)
+{
+    putc(val & 0xff, fp);
+    putc(val >> 8, fp);
+}
+
+void write4LE(FILE* fp, unsigned int val)
+{
+    putc(val & 0xff, fp);
+    putc((val >> 8) & 0xff, fp);
+    putc((val >> 16) & 0xff, fp);
+    putc((val >> 24) & 0xff, fp);
+}
+
+void write8LE(FILE* fp, unsigned long long val)
+{
+    putc(val & 0xff, fp);
+    putc((val >> 8) & 0xff, fp);
+    putc((val >> 16) & 0xff, fp);
+    putc((val >> 24) & 0xff, fp);
+    putc((val >> 32) & 0xff, fp);
+    putc((val >> 40) & 0xff, fp);
+    putc((val >> 48) & 0xff, fp);
+    putc((val >> 56) & 0xff, fp);
+}
+
+void writeDataRecord(FILE *dataFp, int threadId, unsigned int methodVal,
+                   unsigned int elapsedTime)
+{
+    if (versionNumber == 1)
+        putc(threadId, dataFp);
+    else
+        write2LE(dataFp, threadId);
+    write4LE(dataFp, methodVal);
+    write4LE(dataFp, elapsedTime);
+}
+
+void writeDataHeader(FILE *dataFp)
+{
+    struct timeval tv;
+    struct timezone tz;
+
+    gettimeofday(&tv, &tz);
+    unsigned long long startTime = tv.tv_sec;
+    startTime = (startTime << 32) | tv.tv_usec;
+    header.version = versionNumber;
+    write4LE(dataFp, header.magic);
+    write2LE(dataFp, header.version);
+    write2LE(dataFp, header.offsetToData);
+    write8LE(dataFp, startTime);
+}
+
+void writeKeyMethods(FILE *keyFp)
+{
+    dataRecord *pRecord, *pNext;
+    char *methodStr = "*methods\n";
+    fwrite(methodStr, strlen(methodStr), 1, keyFp);
+
+    /* Assign method ids in multiples of 4 */
+    unsigned int methodId = 0;
+    for (pRecord = records; pRecord->fullName; ++pRecord) {
+        if (pRecord->methodId)
+            continue;
+        unsigned int id = ++methodId << 2;
+        pRecord->methodId = id;
+
+        /* Assign this id to all the other records that have the
+         * same name.
+         */
+        for (pNext = pRecord + 1; pNext->fullName; ++pNext) {
+            if (pNext->methodId)
+                continue;
+            if (strcmp(pRecord->fullName, pNext->fullName) == 0)
+                pNext->methodId = id;
+        }
+        if (pRecord->className == NULL || pRecord->methodName == NULL) {
+            fprintf(keyFp, "0x%x        %s      m       ()\n",
+                    pRecord->methodId, pRecord->fullName);
+        } else if (pRecord->signature == NULL) {
+            fprintf(keyFp, "0x%x        %s      %s      ()\n",
+                    pRecord->methodId, pRecord->className,
+                    pRecord->methodName);
+        } else {
+            fprintf(keyFp, "0x%x        %s      %s      %s\n",
+                    pRecord->methodId, pRecord->className,
+                    pRecord->methodName, pRecord->signature);
+        }
+    }
+}
+
+void writeKeys(FILE *keyFp)
+{
+    fprintf(keyFp, "%s%d\n%s", versionHeader, versionNumber, clockDef);
+    fwrite(keyThreads, strlen(keyThreads), 1, keyFp);
+    writeKeyMethods(keyFp);
+    fwrite(keyEnd, strlen(keyEnd), 1, keyFp);
+}
+
+void writeDataRecords(FILE *dataFp)
+{
+    dataRecord *pRecord;
+
+    for (pRecord = records; pRecord->fullName; ++pRecord) {
+        unsigned int val = METHOD_COMBINE(pRecord->methodId, pRecord->action);
+        writeDataRecord(dataFp, pRecord->threadId, val, pRecord->time);
+    }
+}
+
+void writeTrace(const char* traceFileName)
+{
+    FILE *fp = fopen(traceFileName, "w");
+    if (fp == NULL) {
+        perror(traceFileName);
+        exit(1);
+    }
+    writeKeys(fp);
+    writeDataHeader(fp);
+    writeDataRecords(fp);
+    fclose(fp);
+}
+
+int parseOptions(int argc, char **argv)
+{
+    int err = 0;
+    while (1) {
+        int opt = getopt(argc, argv, "v:d");
+        if (opt == -1)
+            break;
+        switch (opt) {
+            case 'v':
+                versionNumber = strtoul(optarg, NULL, 0);
+                if (versionNumber != 1 && versionNumber != 2) {
+                    fprintf(stderr, "Error: version number (%d) must be 1 or 2\n",
+                            versionNumber);
+                    err = 1;
+                }
+                break;
+            case 'd':
+                verbose = 1;
+                break;
+            default:
+                err = 1;
+                break;
+        }
+    }
+    return err;
+}
+
+int main(int argc, char** argv)
+{
+    char *inputFile;
+    char *traceFileName = NULL;
+    int len;
+
+    if (parseOptions(argc, argv) || argc - optind != 2) {
+        fprintf(stderr, "Usage: %s [-v version] [-d] input_file trace_prefix\n",
+                argv[0]);
+        exit(1);
+    }
+
+    inputFile = argv[optind++];
+    parseInputFile(inputFile);
+    traceFileName = argv[optind++];
+
+    writeTrace(traceFileName);
+
+    return 0;
+}
diff --git a/tools/dmtracedump/TraceDump.c b/tools/dmtracedump/TraceDump.c
new file mode 100644
index 0000000..3f80064
--- /dev/null
+++ b/tools/dmtracedump/TraceDump.c
@@ -0,0 +1,3631 @@
+/* //device/tools/dmtracedump/TraceDump.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/*
+ * Process dmtrace output.
+ *
+ * This is the wrong way to go about it -- C is a clumsy language for
+ * shuffling data around.  It'll do for a first pass.
+ */
+#define NOT_VM
+#include "Profile.h"        // from VM header
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+
+/* Version number in the key file.  Version 1 uses one byte for the thread id.
+ * Version 2 uses two bytes for the thread ids.
+ */
+int versionNumber;
+
+/* arbitrarily limit indentation */
+#define MAX_STACK_DEPTH 10000
+
+/* thread list in key file is not reliable, so just max out */
+#define MAX_THREADS     32768
+
+/* Size of temporary buffers for escaping html strings */
+#define HTML_BUFSIZE 10240
+
+/* Size of methodId->method cache */
+#define METHOD_CACHE_SIZE 2048
+#define METHOD_CACHE_SIZE_MASK (METHOD_CACHE_SIZE - 1)
+
+/* Some filter constants */
+#define FILTER_TAG '*'
+#define FILTER_FLAG_THREAD '+'
+#define FILTER_TYPE_CLASS 0
+#define FILTER_TYPE_METHOD 1
+
+#define DEFAULT_ACTIVE_THREADS 8
+
+char *htmlHeader =
+"<html>\n<head>\n<script type=\"text/javascript\" src=\"%ssortable.js\"></script>\n"
+"<script langugage=\"javascript\">\n"
+"function toggle(item) {\n"
+"    obj=document.getElementById(item);\n"
+"    visible=(obj.style.display!=\"none\" && obj.style.display!=\"\");\n"
+"    key=document.getElementById(\"x\" + item);\n"
+"    if (visible) {\n"
+"        obj.style.display=\"none\";\n"
+"        key.innerHTML=\"+\";\n"
+"    } else {\n"
+"        obj.style.display=\"block\";\n"
+"        key.innerHTML=\"-\";\n"
+"    }\n"
+"}\n"
+"function onMouseOver(obj) {\n"
+"    obj.style.background=\"lightblue\";\n"
+"}\n"
+"function onMouseOut(obj) {\n"
+"    obj.style.background=\"white\";\n"
+"}\n"
+"</script>\n"
+"<style type=\"text/css\">\n"
+"div { font-family: courier; font-size: 13 }\n"
+"div.parent { margin-left: 15; display: none }\n"
+"div.leaf { margin-left: 10 }\n"
+"div.header { margin-left: 10 }\n"
+"div.link { margin-left: 10; cursor: move }\n"
+"span.parent { padding-right: 10; }\n"
+"span.leaf { padding-right: 10; }\n"
+"a img { border: 0;}\n"
+"table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}\n"
+"a { text-decoration: none; }\n"
+"a:hover { text-decoration: underline; }\n"
+"table.sortable th, table.sortable td { text-align: left;}"
+"table.sortable tr.odd td { background-color: #ddd; }\n"
+"table.sortable tr.even td { background-color: #fff; }\n"
+"</style>\n"
+"</head><body>\n\n";
+
+char *htmlFooter = "\n</body>\n</html>\n";
+char *profileSeparator =
+    "======================================================================";
+
+const char* tableHeader =
+    "<table class='sortable' id='%s'><tr>\n"
+    "<th>Method</th>\n"
+    "<th>Run 1 (us)</th>\n"
+    "<th>Run 2 (us)</th>\n"
+    "<th>Diff (us)</th>\n"
+    "<th>Diff (%%)</th>\n"
+    "<th>1: # calls</th>\n"
+    "<th>2: # calls</th>\n"
+    "</tr>\n";
+
+const char* tableHeaderMissing =
+    "<table class='sortable' id='%s'>\n"
+    "<th>Method</th>\n"
+    "<th>Exclusive</th>\n"
+    "<th>Inclusive</th>\n"
+    "<th># calls</th>\n";
+
+#define GRAPH_LABEL_VISITED 0x0001
+#define GRAPH_NODE_VISITED  0x0002
+
+/*
+ * Values from the header of the data file.
+ */
+typedef struct DataHeader {
+    unsigned int magic;
+    short version;
+    short offsetToData;
+    long long startWhen;
+} DataHeader;
+
+/*
+ * Entry from the thread list.
+ */
+typedef struct ThreadEntry {
+    int         threadId;
+    const char* threadName;
+    uint64_t    elapsedTime;
+} ThreadEntry;
+
+struct MethodEntry;
+typedef struct TimedMethod {
+    struct TimedMethod *next;
+    uint64_t elapsedInclusive;
+    int numCalls;
+    struct MethodEntry *method;
+} TimedMethod;
+
+typedef struct ClassEntry {
+    const char *className;
+    uint64_t elapsedExclusive;
+    int numMethods;
+    struct MethodEntry **methods;       /* list of methods in this class */
+    int numCalls[2];                    /* 0=normal, 1=recursive */
+} ClassEntry;
+
+typedef struct UniqueMethodEntry {
+    uint64_t elapsedExclusive;
+    int numMethods;
+    struct MethodEntry **methods;       /* list of methods with same name */
+    int numCalls[2];                    /* 0=normal, 1=recursive */
+} UniqueMethodEntry;
+
+/*
+ * Entry from the method list.
+ */
+typedef struct MethodEntry {
+    unsigned int methodId;
+    const char* className;
+    const char* methodName;
+    const char* signature;
+    const char* fileName;
+    int lineNum;
+    uint64_t elapsedExclusive;
+    uint64_t elapsedInclusive;
+    uint64_t topExclusive;              /* non-recursive exclusive time */
+    uint64_t recursiveInclusive;
+    struct TimedMethod *parents[2];     /* 0=normal, 1=recursive */
+    struct TimedMethod *children[2];    /* 0=normal, 1=recursive */
+    int numCalls[2];                    /* 0=normal, 1=recursive */
+    int index;                  /* used after sorting to number methods */
+    int recursiveEntries;       /* number of entries on the stack */
+    int graphState;             /* used when graphing to see if this method has been visited before */
+} MethodEntry;
+
+/*
+ * The parsed contents of the key file.
+ */
+typedef struct DataKeys {
+    char*        fileData;      /* contents of the entire file */
+    long         fileLen;
+    int          numThreads;
+    ThreadEntry* threads;
+    int          numMethods;
+    MethodEntry* methods;       /* 2 extra methods: "toplevel" and "unknown" */
+    int*         methodCache;   /* methodId->methodIndex mapping */
+    // TODO change to map methodId->method itself
+} DataKeys;
+
+#define TOPLEVEL_INDEX 0
+#define UNKNOWN_INDEX 1
+
+typedef struct StackEntry {
+    MethodEntry *method;
+    uint64_t    entryTime;
+} StackEntry;
+
+typedef struct CallStack {
+    int           top;
+    StackEntry    calls[MAX_STACK_DEPTH];
+    uint64_t      lastEventTime;
+    uint64_t      threadStartTime;
+    uint64_t*     remTimes;
+    // Note: remTimes keeps a sum of 'un-allocated' time for each thread, in case
+    // we need to allocate it to one (or many) filter later. This would happen when
+    // we see a method exit that maches a filter, but whose entry we hadn't seen.
+    // TODO: consider moving remTimes into FilterTimes and change logic appropriately
+} CallStack;
+
+typedef struct DiffEntry {
+    MethodEntry* method1;
+    MethodEntry* method2;
+    int64_t differenceExclusive;
+    int64_t differenceInclusive;
+    double differenceExclusivePercentage;
+    double differenceInclusivePercentage;
+} DiffEntry;
+
+// Global options
+typedef struct Options {
+    const char* traceFileName;
+    const char* diffFileName;
+    const char* graphFileName;
+    const char* filterFileName;
+    int keepDotFile;
+    int dump;
+    int outputHtml;
+    const char* sortableUrl;
+    int threshold;
+} Options;
+
+typedef struct TraceData {
+    int numClasses;
+    ClassEntry *classes;
+    CallStack *stacks[MAX_THREADS];
+    int depth[MAX_THREADS];
+    int numUniqueMethods;
+    UniqueMethodEntry *uniqueMethods;
+} TraceData;
+
+typedef struct FilterKey {
+    int       type[2];    /* 0=class, 1=method; 2 needed for start and end keys */
+    uint32_t  flags;      /* 1st bit = include cross-thread time */
+    char*     keys[2];    /* 2 needed for start and end keys */
+} FilterKey;
+
+typedef struct FilterTimes {
+    uint64_t   totalWaitTime;
+    uint64_t*  threadWaitTimes;
+    uint64_t*  threadExecutionTimesWhileWaiting;
+    uint64_t*  threadExecutionTimes;
+} FilterTimes;
+
+typedef struct Filter {
+    char*       filterName;
+    FilterKey*  filterKeys;
+    int         numKeys;
+    int         activeCount;
+    int*        activeThreads;
+    int*        activationKeys;
+    FilterTimes times;
+} Filter;
+
+int numFilters = 0; // global
+
+static Options gOptions;
+
+/* Escapes characters in the source string that are html special entities.
+ * The escaped string is written to "dest" which must be large enough to
+ * hold the result.  A pointer to "dest" is returned.  The characters and
+ * their corresponding escape sequences are:
+ *  '<'  &lt;
+ *  '>'  &gt;
+ *  '&'  &amp;
+ */
+char *htmlEscape(const char *src, char *dest, int len)
+{
+    char *destStart = dest;
+
+    if (src == NULL)
+        return NULL;
+
+    int nbytes = 0;
+    while (*src) {
+        if (*src == '<') {
+            nbytes += 4;
+            if (nbytes >= len)
+                break;
+            *dest++ = '&';
+            *dest++ = 'l';
+            *dest++ = 't';
+            *dest++ = ';';
+        } else if (*src == '>') {
+            nbytes += 4;
+            if (nbytes >= len)
+                break;
+            *dest++ = '&';
+            *dest++ = 'g';
+            *dest++ = 't';
+            *dest++ = ';';
+        } else if (*src == '&') {
+            nbytes += 5;
+            if (nbytes >= len)
+                break;
+            *dest++ = '&';
+            *dest++ = 'a';
+            *dest++ = 'm';
+            *dest++ = 'p';
+            *dest++ = ';';
+        } else {
+            nbytes += 1;
+            if (nbytes >= len)
+                break;
+            *dest++ = *src;
+        }
+        src += 1;
+    }
+    if (nbytes >= len) {
+        fprintf(stderr, "htmlEscape(): buffer overflow\n");
+        exit(1);
+    }
+    *dest = 0;
+
+    return destStart;
+}
+
+/* Initializes a MethodEntry
+ */
+void initMethodEntry(MethodEntry *method, unsigned int methodId,
+                     const char *className, const char *methodName,
+                     const char *signature, const char* fileName,
+                     const char* lineNumStr)
+{
+    method->methodId = methodId;
+    method->className = className;
+    method->methodName = methodName;
+    method->signature = signature;
+    method->fileName = fileName;
+    method->lineNum = (lineNumStr != NULL) ? atoi(lineNumStr) : -1;
+    method->elapsedExclusive = 0;
+    method->elapsedInclusive = 0;
+    method->topExclusive = 0;
+    method->recursiveInclusive = 0;
+    method->parents[0] = NULL;
+    method->parents[1] = NULL;
+    method->children[0] = NULL;
+    method->children[1] = NULL;
+    method->numCalls[0] = 0;
+    method->numCalls[1] = 0;
+    method->index = 0;
+    method->recursiveEntries = 0;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * methods into decreasing order of exclusive elapsed time.
+ */
+int compareElapsedExclusive(const void *a, const void *b) {
+    uint64_t elapsed1, elapsed2;
+    int result;
+
+    const MethodEntry *methodA = *(const MethodEntry**)a;
+    const MethodEntry *methodB = *(const MethodEntry**)b;
+    elapsed1 = methodA->elapsedExclusive;
+    elapsed2 = methodB->elapsedExclusive;
+    if (elapsed1 < elapsed2)
+        return 1;
+    if (elapsed1 > elapsed2)
+        return -1;
+
+    /* If the elapsed times of two methods are equal, then sort them
+     * into alphabetical order.
+     */
+    result = strcmp(methodA->className, methodB->className);
+    if (result == 0) {
+        if (methodA->methodName == NULL || methodB->methodName == NULL) {
+            unsigned int idA = methodA->methodId;
+            unsigned int idB = methodB->methodId;
+            if (idA < idB)
+                return -1;
+            if (idA > idB)
+                return 1;
+            return 0;
+        }
+        result = strcmp(methodA->methodName, methodB->methodName);
+        if (result == 0)
+            result = strcmp(methodA->signature, methodB->signature);
+    }
+    return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * methods into decreasing order of inclusive elapsed time.
+ */
+int compareElapsedInclusive(const void *a, const void *b) {
+    const MethodEntry *methodA, *methodB;
+    uint64_t elapsed1, elapsed2;
+    int result;
+
+    methodA = *(MethodEntry const **)a;
+    methodB = *(MethodEntry const **)b;
+    elapsed1 = methodA->elapsedInclusive;
+    elapsed2 = methodB->elapsedInclusive;
+    if (elapsed1 < elapsed2)
+        return 1;
+    if (elapsed1 > elapsed2)
+        return -1;
+
+    /* If the elapsed times of two methods are equal, then sort them
+     * into alphabetical order.
+     */
+    result = strcmp(methodA->className, methodB->className);
+    if (result == 0) {
+        if (methodA->methodName == NULL || methodB->methodName == NULL) {
+            unsigned int idA = methodA->methodId;
+            unsigned int idB = methodB->methodId;
+            if (idA < idB)
+                return -1;
+            if (idA > idB)
+                return 1;
+            return 0;
+        }
+        result = strcmp(methodA->methodName, methodB->methodName);
+        if (result == 0)
+            result = strcmp(methodA->signature, methodB->signature);
+    }
+    return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * threads into decreasing order of elapsed time.
+ */
+int compareElapsed(const void *a, const void *b) {
+    const ThreadEntry *threadA, *threadB;
+    uint64_t elapsed1, elapsed2;
+    int result = 0;
+
+    threadA = (ThreadEntry const *)a;
+    threadB = (ThreadEntry const *)b;
+    elapsed1 = threadA->elapsedTime;
+    elapsed2 = threadB->elapsedTime;
+    if (elapsed1 < elapsed2)
+        return 1;
+    if (elapsed1 > elapsed2)
+        return -1;
+
+    /* If the elapsed times of two threads are equal, then sort them
+     * by thread id.
+     */
+    int idA = threadA->threadId;
+    int idB = threadB->threadId;
+    if (idA < idB)
+        result = -1;
+    if (idA > idB)
+        result = 1;
+
+    return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * TimedMethods into decreasing order of inclusive elapsed time.
+ */
+int compareTimedMethod(const void *a, const void *b) {
+    const TimedMethod *timedA, *timedB;
+    uint64_t elapsed1, elapsed2;
+    int result;
+
+    timedA = (TimedMethod const *)a;
+    timedB = (TimedMethod const *)b;
+    elapsed1 = timedA->elapsedInclusive;
+    elapsed2 = timedB->elapsedInclusive;
+    if (elapsed1 < elapsed2)
+        return 1;
+    if (elapsed1 > elapsed2)
+        return -1;
+
+    /* If the elapsed times of two methods are equal, then sort them
+     * into alphabetical order.
+     */
+    MethodEntry *methodA = timedA->method;
+    MethodEntry *methodB = timedB->method;
+    result = strcmp(methodA->className, methodB->className);
+    if (result == 0) {
+        if (methodA->methodName == NULL || methodB->methodName == NULL) {
+            unsigned int idA = methodA->methodId;
+            unsigned int idB = methodB->methodId;
+            if (idA < idB)
+                return -1;
+            if (idA > idB)
+                return 1;
+            return 0;
+        }
+        result = strcmp(methodA->methodName, methodB->methodName);
+        if (result == 0)
+            result = strcmp(methodA->signature, methodB->signature);
+    }
+    return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * MethodEntry pointers into alphabetical order of class names.
+ */
+int compareClassNames(const void *a, const void *b) {
+    int result;
+
+    const MethodEntry *methodA = *(const MethodEntry**)a;
+    const MethodEntry *methodB = *(const MethodEntry**)b;
+    result = strcmp(methodA->className, methodB->className);
+    if (result == 0) {
+        unsigned int idA = methodA->methodId;
+        unsigned int idB = methodB->methodId;
+        if (idA < idB)
+            return -1;
+        if (idA > idB)
+            return 1;
+        return 0;
+    }
+    return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * classes into decreasing order of exclusive elapsed time.
+ */
+int compareClassExclusive(const void *a, const void *b) {
+    uint64_t elapsed1, elapsed2;
+    int result;
+
+    const ClassEntry *classA = *(const ClassEntry**)a;
+    const ClassEntry *classB = *(const ClassEntry**)b;
+    elapsed1 = classA->elapsedExclusive;
+    elapsed2 = classB->elapsedExclusive;
+    if (elapsed1 < elapsed2)
+        return 1;
+    if (elapsed1 > elapsed2)
+        return -1;
+
+    /* If the elapsed times of two classs are equal, then sort them
+     * into alphabetical order.
+     */
+    result = strcmp(classA->className, classB->className);
+    if (result == 0) {
+        /* Break ties with the first method id.  This is probably not
+         * needed.
+         */
+        unsigned int idA = classA->methods[0]->methodId;
+        unsigned int idB = classB->methods[0]->methodId;
+        if (idA < idB)
+            return -1;
+        if (idA > idB)
+            return 1;
+        return 0;
+    }
+    return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * MethodEntry pointers into alphabetical order by method name,
+ * then by class name.
+ */
+int compareMethodNames(const void *a, const void *b) {
+    int result;
+
+    const MethodEntry *methodA = *(const MethodEntry**)a;
+    const MethodEntry *methodB = *(const MethodEntry**)b;
+    if (methodA->methodName == NULL || methodB->methodName == NULL) {
+        return compareClassNames(a, b);
+    }
+    result = strcmp(methodA->methodName, methodB->methodName);
+    if (result == 0) {
+        result = strcmp(methodA->className, methodB->className);
+        if (result == 0) {
+            unsigned int idA = methodA->methodId;
+            unsigned int idB = methodB->methodId;
+            if (idA < idB)
+                return -1;
+            if (idA > idB)
+                return 1;
+            return 0;
+        }
+    }
+    return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * unique methods into decreasing order of exclusive elapsed time.
+ */
+int compareUniqueExclusive(const void *a, const void *b) {
+    uint64_t elapsed1, elapsed2;
+    int result;
+
+    const UniqueMethodEntry *uniqueA = *(const UniqueMethodEntry**)a;
+    const UniqueMethodEntry *uniqueB = *(const UniqueMethodEntry**)b;
+    elapsed1 = uniqueA->elapsedExclusive;
+    elapsed2 = uniqueB->elapsedExclusive;
+    if (elapsed1 < elapsed2)
+        return 1;
+    if (elapsed1 > elapsed2)
+        return -1;
+
+    /* If the elapsed times of two methods are equal, then sort them
+     * into alphabetical order.
+     */
+    result = strcmp(uniqueA->methods[0]->className,
+                    uniqueB->methods[0]->className);
+    if (result == 0) {
+        unsigned int idA = uniqueA->methods[0]->methodId;
+        unsigned int idB = uniqueB->methods[0]->methodId;
+        if (idA < idB)
+            return -1;
+        if (idA > idB)
+            return 1;
+        return 0;
+    }
+    return result;
+}
+
+/*
+ * Free a DataKeys struct.
+ */
+void freeDataKeys(DataKeys* pKeys)
+{
+    if (pKeys == NULL)
+        return;
+
+    free(pKeys->fileData);
+    free(pKeys->threads);
+    free(pKeys->methods);
+    free(pKeys->methodCache);
+    free(pKeys);
+}
+
+/*
+ * Find the offset to the next occurrence of the specified character.
+ *
+ * "data" should point somewhere within the current line.  "len" is the
+ * number of bytes left in the buffer.
+ *
+ * Returns -1 if we hit the end of the buffer.
+ */
+int findNextChar(const char* data, int len, char lookFor)
+{
+    const char* start = data;
+
+    while (len > 0) {
+        if (*data == lookFor)
+            return data - start;
+
+        data++;
+        len--;
+    }
+
+    return -1;
+}
+
+int countLinesToChar(const char* data, int len, const char toFind)
+{
+    int count = 0;
+    int next;
+
+    while (*data != toFind) {
+        next = findNextChar(data, len, '\n');
+        if (next < 0)
+	    return count;
+        count++;
+        data += next+1;
+        len -= next+1;
+    }
+
+    return count;
+}
+
+/*
+ * Count the number of lines until the next token.
+ *
+ * Returns 0 if none found before EOF.
+ */
+int countLinesToToken(const char* data, int len)
+{
+    return countLinesToChar(data, len, TOKEN_CHAR);
+}
+
+/*
+ * Make sure we're at the start of the right section.
+ *
+ * Returns the length of the token line, or -1 if something is wrong.
+ */
+int checkToken(const char* data, int len, const char* cmpStr)
+{
+    int cmpLen = strlen(cmpStr);
+    int next;
+
+    if (*data != TOKEN_CHAR) {
+        fprintf(stderr,
+            "ERROR: not at start of %s (found '%.10s')\n", cmpStr, data);
+        return -1;
+    }
+
+    next = findNextChar(data, len, '\n');
+    if (next < cmpLen+1)
+        return -1;
+
+    if (strncmp(data+1, cmpStr, cmpLen) != 0) {
+        fprintf(stderr, "ERROR: '%s' not found (got '%.7s')\n", cmpStr, data+1);
+        return -1;
+    }
+
+    return next+1;
+}
+
+/*
+ * Parse the "*version" section.
+ */
+long parseVersion(DataKeys* pKeys, long offset, int verbose)
+{
+    char* data;
+    char* dataEnd;
+    int i, count, next;
+
+    if (offset < 0)
+        return -1;
+
+    data = pKeys->fileData + offset;
+    dataEnd = pKeys->fileData + pKeys->fileLen;
+    next = checkToken(data, dataEnd - data, "version");
+    if (next <= 0)
+        return -1;
+
+    data += next;
+
+    /*
+     * Count the number of items in the "version" section.
+     */
+    count = countLinesToToken(data, dataEnd - data);
+    if (count <= 0) {
+        fprintf(stderr,
+            "ERROR: failed while reading version (found %d)\n", count);
+        return -1;
+    }
+
+    /* find the end of the line */
+    next = findNextChar(data, dataEnd - data, '\n');
+    if (next < 0)
+        return -1;
+
+    data[next] = '\0';
+    versionNumber = strtoul(data, NULL, 0);
+    if (verbose)
+        printf("VERSION: %d\n", versionNumber);
+
+    data += next+1;
+
+    /* skip over the rest of the stuff, which is "name=value" lines */
+    for (i = 1; i < count; i++) {
+        next = findNextChar(data, dataEnd - data, '\n');
+        if (next < 0)
+            return -1;
+        //data[next] = '\0';
+        //printf("IGNORING: '%s'\n", data);
+        data += next+1;
+    }
+
+    return data - pKeys->fileData;
+}
+
+/*
+ * Parse the "*threads" section.
+ */
+long parseThreads(DataKeys* pKeys, long offset)
+{
+    char* data;
+    char* dataEnd;
+    int i, next, tab, count;
+
+    if (offset < 0)
+        return -1;
+
+    data = pKeys->fileData + offset;
+    dataEnd = pKeys->fileData + pKeys->fileLen;
+    next = checkToken(data, dataEnd - data, "threads");
+
+    data += next;
+
+    /*
+     * Count the number of thread entries (one per line).
+     */
+    count = countLinesToToken(data, dataEnd - data);
+    if (count <= 0) {
+        fprintf(stderr,
+            "ERROR: failed while reading threads (found %d)\n", count);
+        return -1;
+    }
+
+    //printf("+++ found %d threads\n", count);
+    pKeys->threads = (ThreadEntry*) malloc(sizeof(ThreadEntry) * count);
+    if (pKeys->threads == NULL)
+        return -1;
+
+    /*
+     * Extract all entries.
+     */
+    for (i = 0; i < count; i++) {
+        next = findNextChar(data, dataEnd - data, '\n');
+        assert(next > 0);
+        data[next] = '\0';
+
+        tab = findNextChar(data, next, '\t');
+        data[tab] = '\0';
+
+        pKeys->threads[i].threadId = atoi(data);
+        pKeys->threads[i].threadName = data + tab +1;
+
+        data += next+1;
+    }
+
+    pKeys->numThreads = count;
+    return data - pKeys->fileData;
+}
+
+/*
+ * Parse the "*methods" section.
+ */
+long parseMethods(DataKeys* pKeys, long offset)
+{
+    char* data;
+    char* dataEnd;
+    int i, next, count;
+
+    if (offset < 0)
+        return -1;
+
+    data = pKeys->fileData + offset;
+    dataEnd = pKeys->fileData + pKeys->fileLen;
+    next = checkToken(data, dataEnd - data, "methods");
+    if (next < 0)
+        return -1;
+
+    data += next;
+
+    /*
+     * Count the number of method entries (one per line).
+     */
+    count = countLinesToToken(data, dataEnd - data);
+    if (count <= 0) {
+        fprintf(stderr,
+            "ERROR: failed while reading methods (found %d)\n", count);
+        return -1;
+    }
+
+    /* Reserve an extra method at location 0 for the "toplevel" method,
+     * and another extra method for all other "unknown" methods.
+     */
+    count += 2;
+    pKeys->methods = (MethodEntry*) malloc(sizeof(MethodEntry) * count);
+    if (pKeys->methods == NULL)
+        return -1;
+    initMethodEntry(&pKeys->methods[TOPLEVEL_INDEX], 0, "(toplevel)",
+        NULL, NULL, NULL, NULL);
+    initMethodEntry(&pKeys->methods[UNKNOWN_INDEX], 0, "(unknown)",
+        NULL, NULL, NULL, NULL);
+
+    /*
+     * Extract all entries, starting with index 2.
+     */
+    for (i = UNKNOWN_INDEX + 1; i < count; i++) {
+        int tab1, tab2, tab3, tab4, tab5;
+        unsigned int id;
+        char* endptr;
+
+        next = findNextChar(data, dataEnd - data, '\n');
+        assert(next > 0);
+        data[next] = '\0';
+
+        tab1 = findNextChar(data, next, '\t');
+        tab2 = findNextChar(data+(tab1+1), next-(tab1+1), '\t');
+        tab3 = findNextChar(data+(tab1+tab2+2), next-(tab1+tab2+2), '\t');
+        tab4 = findNextChar(data+(tab1+tab2+tab3+3),
+                            next-(tab1+tab2+tab3+3), '\t');
+        tab5 = findNextChar(data+(tab1+tab2+tab3+tab4+4),
+                            next-(tab1+tab2+tab3+tab4+4), '\t');
+        if (tab1 < 0) {
+            fprintf(stderr, "ERROR: missing field on method line: '%s'\n",
+                    data);
+            return -1;
+        }
+        assert(data[tab1] == '\t');
+        data[tab1] = '\0';
+
+        id = strtoul(data, &endptr, 0);
+        if (*endptr != '\0') {
+            fprintf(stderr, "ERROR: bad method ID '%s'\n", data);
+            return -1;
+        }
+
+        // Allow files that specify just a function name, instead of requiring
+        // "class \t method \t signature"
+        if (tab2 > 0 && tab3 > 0) {
+            tab2 += tab1+1;
+            tab3 += tab2+1;
+            assert(data[tab2] == '\t');
+            assert(data[tab3] == '\t');
+            data[tab2] = data[tab3] = '\0';
+
+            // This is starting to get awkward.  Allow filename and line #.
+            if (tab4 > 0 && tab5 > 0) {
+                tab4 += tab3+1;
+                tab5 += tab4+1;
+
+                assert(data[tab4] == '\t');
+                assert(data[tab5] == '\t');
+                data[tab4] = data[tab5] = '\0';
+
+                initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
+                        data + tab2 +1, data + tab3 +1, data + tab4 +1,
+                        data + tab5 +1);
+            } else {
+                initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
+                        data + tab2 +1, data + tab3 +1, NULL, NULL);
+            }
+        } else {
+            initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
+                NULL, NULL, NULL, NULL);
+        }
+
+        data += next+1;
+    }
+
+    pKeys->numMethods = count;
+    return data - pKeys->fileData;
+}
+
+/*
+ * Parse the "*end" section.
+ */
+long parseEnd(DataKeys* pKeys, long offset)
+{
+    char* data;
+    char* dataEnd;
+    int next;
+
+    if (offset < 0)
+        return -1;
+
+    data = pKeys->fileData + offset;
+    dataEnd = pKeys->fileData + pKeys->fileLen;
+    next = checkToken(data, dataEnd - data, "end");
+    if (next < 0)
+        return -1;
+
+    data += next;
+
+    return data - pKeys->fileData;
+}
+
+/*
+ * Sort the thread list entries.
+ */
+static int compareThreads(const void* thread1, const void* thread2)
+{
+    return ((const ThreadEntry*) thread1)->threadId -
+            ((const ThreadEntry*) thread2)->threadId;
+}
+
+void sortThreadList(DataKeys* pKeys)
+{
+    qsort(pKeys->threads, pKeys->numThreads, sizeof(pKeys->threads[0]),
+        compareThreads);
+}
+
+/*
+ * Sort the method list entries.
+ */
+static int compareMethods(const void* meth1, const void* meth2)
+{
+    unsigned int id1, id2;
+
+    id1 = ((const MethodEntry*) meth1)->methodId;
+    id2 = ((const MethodEntry*) meth2)->methodId;
+    if (id1 < id2)
+        return -1;
+    if (id1 > id2)
+        return 1;
+    return 0;
+}
+
+void sortMethodList(DataKeys* pKeys)
+{
+    qsort(pKeys->methods, pKeys->numMethods, sizeof(MethodEntry),
+        compareMethods);
+}
+
+/*
+ * Parse the key section, and return a copy of the parsed contents.
+ */
+DataKeys* parseKeys(FILE *fp, int verbose)
+{
+    DataKeys* pKeys = NULL;
+    long offset;
+    int i;
+
+    pKeys = (DataKeys*) calloc(1, sizeof(DataKeys));
+    if (pKeys == NULL)
+        goto fail;
+
+    /*
+     * We load the entire file into memory.  We do this, rather than memory-
+     * mapping it, because we want to change some whitespace to NULs.
+     */
+    if (fseek(fp, 0L, SEEK_END) != 0) {
+        perror("fseek");
+        goto fail;
+    }
+    pKeys->fileLen = ftell(fp);
+    if (pKeys->fileLen == 0) {
+        fprintf(stderr, "Key file is empty.\n");
+        goto fail;
+    }
+    rewind(fp);
+
+    pKeys->fileData = (char*) malloc(pKeys->fileLen);
+    if (pKeys->fileData == NULL) {
+        fprintf(stderr, "ERROR: unable to alloc %ld bytes\n", pKeys->fileLen);
+        goto fail;
+    }
+
+    if (fread(pKeys->fileData, 1, pKeys->fileLen, fp) != (size_t) pKeys->fileLen)
+    {
+        fprintf(stderr, "ERROR: unable to read %ld bytes from trace file\n",
+            pKeys->fileLen);
+        goto fail;
+    }
+
+    offset = 0;
+
+    offset = parseVersion(pKeys, offset, verbose);
+    offset = parseThreads(pKeys, offset);
+    offset = parseMethods(pKeys, offset);
+    offset = parseEnd(pKeys, offset);
+    if (offset < 0)
+        goto fail;
+
+    /* Leave fp pointing to the beginning of the data section. */
+    fseek(fp, offset, SEEK_SET);
+
+    sortThreadList(pKeys);
+    sortMethodList(pKeys);
+
+    /*
+     * Dump list of threads.
+     */
+    if (verbose) {
+        printf("Threads (%d):\n", pKeys->numThreads);
+        for (i = 0; i < pKeys->numThreads; i++) {
+            printf("%2d %s\n",
+                   pKeys->threads[i].threadId, pKeys->threads[i].threadName);
+        }
+    }
+
+#if 0
+    /*
+     * Dump list of methods.
+     */
+    if (verbose) {
+        printf("Methods (%d):\n", pKeys->numMethods);
+        for (i = 0; i < pKeys->numMethods; i++) {
+            printf("0x%08x %s : %s : %s\n",
+                   pKeys->methods[i].methodId >> 2, pKeys->methods[i].className,
+                   pKeys->methods[i].methodName, pKeys->methods[i].signature);
+        }
+    }
+#endif
+
+    return pKeys;
+
+fail:
+    freeDataKeys(pKeys);
+    return NULL;
+}
+
+
+/*
+ * Read values from the binary data file.
+ */
+
+/* Make the return value "unsigned int" instead of "unsigned short" so that
+ * we can detect EOF.
+ */
+unsigned int read2LE(FILE* fp)
+{
+    unsigned int val;
+
+    val = getc(fp);
+    val |= getc(fp) << 8;
+    return val;
+}
+unsigned int read4LE(FILE* fp)
+{
+    unsigned int val;
+
+    val = getc(fp);
+    val |= getc(fp) << 8;
+    val |= getc(fp) << 16;
+    val |= getc(fp) << 24;
+    return val;
+}
+unsigned long long read8LE(FILE* fp)
+{
+    unsigned long long val;
+
+    val = getc(fp);
+    val |= (unsigned long long) getc(fp) << 8;
+    val |= (unsigned long long) getc(fp) << 16;
+    val |= (unsigned long long) getc(fp) << 24;
+    val |= (unsigned long long) getc(fp) << 32;
+    val |= (unsigned long long) getc(fp) << 40;
+    val |= (unsigned long long) getc(fp) << 48;
+    val |= (unsigned long long) getc(fp) << 56;
+    return val;
+}
+
+/*
+ * Parse the header of the data section.
+ *
+ * Returns with the file positioned at the start of the record data.
+ */
+int parseDataHeader(FILE *fp, DataHeader* pHeader)
+{
+    pHeader->magic = read4LE(fp);
+    pHeader->version = read2LE(fp);
+    pHeader->offsetToData = read2LE(fp);
+    pHeader->startWhen = read8LE(fp);
+
+    if (fseek(fp, pHeader->offsetToData - 16, SEEK_CUR) != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Look up a method by its method ID (using binary search).
+ *
+ * Returns NULL if no matching method was found.
+ */
+MethodEntry* lookupMethod(DataKeys* pKeys, unsigned int methodId)
+{
+    int hi, lo, mid;
+    unsigned int id;
+    int hashedId;
+
+    /* Create cache if it doesn't already exist */
+    if (pKeys->methodCache == NULL) {
+        pKeys->methodCache = (int*) calloc(METHOD_CACHE_SIZE, sizeof(int));
+    }
+
+    // ids are multiples of 4, so shift
+    hashedId = (methodId >> 2) & METHOD_CACHE_SIZE_MASK;
+    if (pKeys->methodCache[hashedId]) /* cache hit */
+        if (pKeys->methods[pKeys->methodCache[hashedId]].methodId == methodId)
+	    return &pKeys->methods[pKeys->methodCache[hashedId]];
+
+    lo = 0;
+    hi = pKeys->numMethods - 1;
+
+    while (hi >= lo) {
+        mid = (hi + lo) / 2;
+
+        id = pKeys->methods[mid].methodId;
+        if (id == methodId) {         /* match, put in cache */
+	    hashedId = (methodId >> 2) & METHOD_CACHE_SIZE_MASK;
+	    pKeys->methodCache[hashedId] = mid;
+	    return &pKeys->methods[mid];
+	} else if (id < methodId)       /* too low */
+            lo = mid + 1;
+        else                          /* too high */
+            hi = mid - 1;
+    }
+
+    return NULL;
+}
+
+/*
+ * Reads the next data record, and assigns the data values to threadId,
+ * methodVal and elapsedTime.  On end-of-file, the threadId, methodVal,
+ * and elapsedTime are unchanged.  Returns 1 on end-of-file, otherwise
+ * returns 0.
+ */
+int readDataRecord(FILE *dataFp, int *threadId, unsigned int *methodVal,
+                   uint64_t *elapsedTime)
+{
+    int id;
+
+    /*
+     * TODO:
+     * This SHOULD NOT be keyed off of the global version number!  Use
+     * a name=value setting in the version area instead!
+     */
+    if (versionNumber == 1) {
+        id = getc(dataFp);
+    } else {
+        id = read2LE(dataFp);
+    }
+    if (id == EOF)
+        return 1;
+    *threadId = id;
+
+    *methodVal = read4LE(dataFp);
+    *elapsedTime = read4LE(dataFp);
+    if (feof(dataFp)) {
+        fprintf(stderr, "WARNING: hit EOF mid-record\n");
+        return 1;
+    }
+    return 0;
+}
+
+/*
+ * Read the key file and use it to produce formatted output from the
+ * data file.
+ */
+void dumpTrace()
+{
+    static const char* actionStr[] = { "ent", "xit", "unr", "???" };
+    MethodEntry bogusMethod = { 0, "???", "???", "???", "???", -1, 0, 0, 0, 0,
+                                {NULL, NULL}, {NULL, NULL}, {0, 0}, 0, 0, -1 };
+    char bogusBuf[80];
+    char spaces[MAX_STACK_DEPTH+1];
+    FILE* dataFp = NULL;
+    DataHeader dataHeader;
+    DataKeys* pKeys = NULL;
+    int i;
+    TraceData traceData;
+
+    //printf("Dumping '%s' '%s'\n", dataFileName, keyFileName);
+
+    memset(spaces, '.', MAX_STACK_DEPTH);
+    spaces[MAX_STACK_DEPTH] = '\0';
+
+    for (i = 0; i < MAX_THREADS; i++)
+        traceData.depth[i] = 2;       // adjust for return from start function
+
+    dataFp = fopen(gOptions.traceFileName, "r");
+    if (dataFp == NULL)
+        goto bail;
+
+    if ((pKeys = parseKeys(dataFp, 1)) == NULL)
+        goto bail;
+
+    if (parseDataHeader(dataFp, &dataHeader) < 0)
+        goto bail;
+
+    printf("Trace (threadID action usecs class.method signature):\n");
+
+    while (1) {
+        MethodEntry* method;
+        int threadId;
+        unsigned int methodVal;
+        uint64_t elapsedTime;
+        int action, printDepth;
+        unsigned int methodId, lastEnter = 0;
+        int mismatch = 0;
+        char depthNote;
+
+        /*
+         * Extract values from file.
+         */
+        if (readDataRecord(dataFp, &threadId, &methodVal, &elapsedTime))
+            break;
+
+        action = METHOD_ACTION(methodVal);
+        methodId = METHOD_ID(methodVal);
+
+        /*
+         * Generate a line of output.
+         */
+        if (action == METHOD_TRACE_ENTER) {
+            traceData.depth[threadId]++;
+            lastEnter = methodId;
+        } else {
+            /* quick test for mismatched adjacent enter/exit */
+            if (lastEnter != 0 && lastEnter != methodId)
+                mismatch = 1;
+        }
+
+        printDepth = traceData.depth[threadId];
+        depthNote = ' ';
+        if (printDepth < 0) {
+            printDepth = 0;
+            depthNote = '-';
+        } else if (printDepth > MAX_STACK_DEPTH) {
+            printDepth = MAX_STACK_DEPTH;
+            depthNote = '+';
+        }
+
+        method = lookupMethod(pKeys, methodId);
+        if (method == NULL) {
+            method = &bogusMethod;
+            sprintf(bogusBuf, "methodId: 0x%x", methodId);
+            method->signature = bogusBuf;
+        }
+
+	if (method->methodName) {
+	    printf("%2d %s%c %8lld%c%s%s.%s %s\n", threadId,
+		   actionStr[action], mismatch ? '!' : ' ',
+		   elapsedTime, depthNote,
+		   spaces + (MAX_STACK_DEPTH - printDepth),
+		   method->className, method->methodName, method->signature);
+	} else {
+	    printf("%2d %s%c %8lld%c%s%s\n", threadId,
+		   actionStr[action], mismatch ? '!' : ' ',
+		   elapsedTime, depthNote,
+		   spaces + (MAX_STACK_DEPTH - printDepth),
+		   method->className);
+	}
+
+        if (action != METHOD_TRACE_ENTER) {
+            traceData.depth[threadId]--;  /* METHOD_TRACE_EXIT or METHOD_TRACE_UNROLL */
+            lastEnter = 0;
+        }
+
+        mismatch = 0;
+    }
+
+bail:
+    if (dataFp != NULL)
+        fclose(dataFp);
+    if (pKeys != NULL)
+        freeDataKeys(pKeys);
+}
+
+/* This routine adds the given time to the parent and child methods.
+ * This is called when the child routine exits, after the child has
+ * been popped from the stack.  The elapsedTime parameter is the
+ * duration of the child routine, including time spent in called routines.
+ */
+void addInclusiveTime(MethodEntry *parent, MethodEntry *child,
+                      uint64_t elapsedTime)
+{
+    TimedMethod *pTimed;
+
+#if 0
+    bool verbose = false;
+    if (strcmp(child->className, debugClassName) == 0)
+        verbose = true;
+#endif
+
+    int childIsRecursive = (child->recursiveEntries > 0);
+    int parentIsRecursive = (parent->recursiveEntries > 1);
+
+    if (child->recursiveEntries == 0) {
+        child->elapsedInclusive += elapsedTime;
+    } else if (child->recursiveEntries == 1) {
+        child->recursiveInclusive += elapsedTime;
+    }
+    child->numCalls[childIsRecursive] += 1;
+
+#if 0
+    if (verbose) {
+        fprintf(stderr,
+                "%s %d elapsedTime: %lld eI: %lld, rI: %lld\n",
+                child->className, child->recursiveEntries,
+                elapsedTime, child->elapsedInclusive,
+                child->recursiveInclusive);
+    }
+#endif
+
+    /* Find the child method in the parent */
+    TimedMethod *children = parent->children[parentIsRecursive];
+    for (pTimed = children; pTimed; pTimed = pTimed->next) {
+        if (pTimed->method == child) {
+            pTimed->elapsedInclusive += elapsedTime;
+            pTimed->numCalls += 1;
+            break;
+        }
+    }
+    if (pTimed == NULL) {
+        /* Allocate a new TimedMethod */
+        pTimed = (TimedMethod *) malloc(sizeof(TimedMethod));
+        pTimed->elapsedInclusive = elapsedTime;
+        pTimed->numCalls = 1;
+        pTimed->method = child;
+
+        /* Add it to the front of the list */
+        pTimed->next = children;
+        parent->children[parentIsRecursive] = pTimed;
+    }
+
+    /* Find the parent method in the child */
+    TimedMethod *parents = child->parents[childIsRecursive];
+    for (pTimed = parents; pTimed; pTimed = pTimed->next) {
+        if (pTimed->method == parent) {
+            pTimed->elapsedInclusive += elapsedTime;
+            pTimed->numCalls += 1;
+            break;
+        }
+    }
+    if (pTimed == NULL) {
+        /* Allocate a new TimedMethod */
+        pTimed = (TimedMethod *) malloc(sizeof(TimedMethod));
+        pTimed->elapsedInclusive = elapsedTime;
+        pTimed->numCalls = 1;
+        pTimed->method = parent;
+
+        /* Add it to the front of the list */
+        pTimed->next = parents;
+        child->parents[childIsRecursive] = pTimed;
+    }
+
+#if 0
+    if (verbose) {
+        fprintf(stderr,
+                "  %s %d eI: %lld\n",
+                parent->className, parent->recursiveEntries,
+                pTimed->elapsedInclusive);
+    }
+#endif
+}
+
+/* Sorts a linked list and returns a newly allocated array containing
+ * the sorted entries.
+ */
+TimedMethod *sortTimedMethodList(TimedMethod *list, int *num)
+{
+    int ii;
+    TimedMethod *pTimed, *sorted;
+
+    /* Count the elements */
+    int num_entries = 0;
+    for (pTimed = list; pTimed; pTimed = pTimed->next)
+        num_entries += 1;
+    *num = num_entries;
+    if (num_entries == 0)
+        return NULL;
+
+    /* Copy all the list elements to a new array and sort them */
+    sorted = (TimedMethod *) malloc(sizeof(TimedMethod) * num_entries);
+    for (ii = 0, pTimed = list; pTimed; pTimed = pTimed->next, ++ii)
+        memcpy(&sorted[ii], pTimed, sizeof(TimedMethod));
+    qsort(sorted, num_entries, sizeof(TimedMethod), compareTimedMethod);
+
+    /* Fix up the "next" pointers so that they work. */
+    for (ii = 0; ii < num_entries - 1; ++ii)
+        sorted[ii].next = &sorted[ii + 1];
+    sorted[num_entries - 1].next = NULL;
+
+    return sorted;
+}
+
+/* Define flag values for printInclusiveMethod() */
+static const int kIsRecursive = 1;
+
+/* This prints the inclusive stats for all the parents or children of a
+ * method, depending on the list that is passed in.
+ */
+void printInclusiveMethod(MethodEntry *method, TimedMethod *list, int numCalls,
+                          int flags)
+{
+    int num;
+    TimedMethod *pTimed;
+    char buf[80];
+    char *anchor_close;
+    char *spaces = "      ";    /* 6 spaces */
+    int num_spaces = strlen(spaces);
+    char *space_ptr = &spaces[num_spaces];
+    char *className, *methodName, *signature;
+    char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+    char signatureBuf[HTML_BUFSIZE];
+
+    anchor_close = "";
+    if (gOptions.outputHtml)
+        anchor_close = "</a>";
+
+    TimedMethod *sorted = sortTimedMethodList(list,  &num);
+    double methodTotal = method->elapsedInclusive;
+    for (pTimed = sorted; pTimed; pTimed = pTimed->next) {
+        MethodEntry *relative = pTimed->method;
+        className = (char*)(relative->className);
+        methodName = (char*)(relative->methodName);
+        signature = (char*)(relative->signature);
+        double per = 100.0 * pTimed->elapsedInclusive / methodTotal;
+        sprintf(buf, "[%d]", relative->index);
+        if (gOptions.outputHtml) {
+            int len = strlen(buf);
+            if (len > num_spaces)
+                len = num_spaces;
+            sprintf(buf, "<a href=\"#m%d\">[%d]",
+                    relative->index, relative->index);
+            space_ptr = &spaces[len];
+            className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+            methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+            signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+        }
+        int nCalls = numCalls;
+        if (nCalls == 0)
+            nCalls = relative->numCalls[0] + relative->numCalls[1];
+        if (relative->methodName) {
+            if (flags & kIsRecursive) {
+                // Don't display percentages for recursive functions
+                printf("%6s %5s   %6s %s%6s%s %6d/%-6d %9llu %s.%s %s\n",
+                       "", "", "",
+                       space_ptr, buf, anchor_close,
+                       pTimed->numCalls, nCalls,
+                       pTimed->elapsedInclusive,
+                       className, methodName, signature);
+            } else {
+                printf("%6s %5s   %5.1f%% %s%6s%s %6d/%-6d %9llu %s.%s %s\n",
+                       "", "", per,
+                       space_ptr, buf, anchor_close,
+                       pTimed->numCalls, nCalls,
+                       pTimed->elapsedInclusive,
+                       className, methodName, signature);
+            }
+        } else {
+            if (flags & kIsRecursive) {
+                // Don't display percentages for recursive functions
+                printf("%6s %5s   %6s %s%6s%s %6d/%-6d %9llu %s\n",
+                       "", "", "",
+                       space_ptr, buf, anchor_close,
+                       pTimed->numCalls, nCalls,
+                       pTimed->elapsedInclusive,
+                       className);
+            } else {
+                printf("%6s %5s   %5.1f%% %s%6s%s %6d/%-6d %9llu %s\n",
+                       "", "", per,
+                       space_ptr, buf, anchor_close,
+                       pTimed->numCalls, nCalls,
+                       pTimed->elapsedInclusive,
+                       className);
+            }
+        }
+    }
+}
+
+void countRecursiveEntries(CallStack *pStack, int top, MethodEntry *method)
+{
+    int ii;
+
+    method->recursiveEntries = 0;
+    for (ii = 0; ii < top; ++ii) {
+        if (pStack->calls[ii].method == method)
+            method->recursiveEntries += 1;
+    }
+}
+
+void stackDump(CallStack *pStack, int top)
+{
+    int ii;
+
+    for (ii = 0; ii < top; ++ii) {
+        MethodEntry *method = pStack->calls[ii].method;
+        uint64_t entryTime = pStack->calls[ii].entryTime;
+        if (method->methodName) {
+            fprintf(stderr, "  %2d: %8llu %s.%s %s\n", ii, entryTime,
+                   method->className, method->methodName, method->signature);
+        } else {
+            fprintf(stderr, "  %2d: %8llu %s\n", ii, entryTime, method->className);
+        }
+    }
+}
+
+void outputTableOfContents()
+{
+    printf("<a name=\"contents\"></a>\n");
+    printf("<h2>Table of Contents</h2>\n");
+    printf("<ul>\n");
+    printf("  <li><a href=\"#exclusive\">Exclusive profile</a></li>\n");
+    printf("  <li><a href=\"#inclusive\">Inclusive profile</a></li>\n");
+    printf("  <li><a href=\"#thread\">Thread profile</a></li>\n");
+    printf("  <li><a href=\"#class\">Class/method profile</a></li>\n");
+    printf("  <li><a href=\"#method\">Method/class profile</a></li>\n");
+    printf("</ul>\n\n");
+}
+
+void outputNavigationBar()
+{
+    printf("<a href=\"#contents\">[Top]</a>\n");
+    printf("<a href=\"#exclusive\">[Exclusive]</a>\n");
+    printf("<a href=\"#inclusive\">[Inclusive]</a>\n");
+    printf("<a href=\"#thread\">[Thread]</a>\n");
+    printf("<a href=\"#class\">[Class]</a>\n");
+    printf("<a href=\"#method\">[Method]</a>\n");
+    printf("<br><br>\n");
+}
+
+void printExclusiveProfile(MethodEntry **pMethods, int numMethods,
+                           uint64_t sumThreadTime)
+{
+    int ii;
+    MethodEntry* method;
+    double total, sum, per, sum_per;
+    char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+    char signatureBuf[HTML_BUFSIZE];
+    char anchor_buf[80];
+    char *anchor_close = "";
+
+    total = sumThreadTime;
+    anchor_buf[0] = 0;
+    if (gOptions.outputHtml) {
+        anchor_close = "</a>";
+        printf("<a name=\"exclusive\"></a>\n");
+        printf("<hr>\n");
+        outputNavigationBar();
+    } else {
+        printf("\n%s\n", profileSeparator);
+    }
+
+    /* First, sort the methods into decreasing order of inclusive
+     * elapsed time so that we can assign the method indices.
+     */
+    qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
+
+    for (ii = 0; ii < numMethods; ++ii)
+        pMethods[ii]->index = ii;
+
+    /* Sort the methods into decreasing order of exclusive elapsed time.
+     */
+    qsort(pMethods, numMethods, sizeof(MethodEntry*),
+          compareElapsedExclusive);
+
+    printf("Total cycles: %llu\n\n", sumThreadTime);
+    if (gOptions.outputHtml) {
+        printf("<br><br>\n");
+    }
+    printf("Exclusive elapsed times for each method, not including time spent in\n");
+    printf("children, sorted by exclusive time.\n\n");
+    if (gOptions.outputHtml) {
+        printf("<br><br>\n<pre>\n");
+    }
+
+    printf("    Usecs  self %%  sum %%  Method\n");
+    sum = 0;
+
+    for (ii = 0; ii < numMethods; ++ii) {
+        char *className, *methodName, *signature;
+
+        method = pMethods[ii];
+        /* Don't show methods with zero cycles */
+        if (method->elapsedExclusive == 0)
+            break;
+        className = (char*)(method->className);
+        methodName = (char*)(method->methodName);
+        signature = (char*)(method->signature);
+        sum += method->elapsedExclusive;
+        per = 100.0 * method->elapsedExclusive / total;
+        sum_per = 100.0 * sum / total;
+        if (gOptions.outputHtml) {
+            sprintf(anchor_buf, "<a href=\"#m%d\">", method->index);
+            className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+            methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+            signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+        }
+        if (method->methodName) {
+            printf("%9llu  %6.2f %6.2f  %s[%d]%s %s.%s %s\n",
+                   method->elapsedExclusive, per, sum_per,
+                   anchor_buf, method->index, anchor_close,
+                   className, methodName, signature);
+        } else {
+            printf("%9llu  %6.2f %6.2f  %s[%d]%s %s\n",
+                   method->elapsedExclusive, per, sum_per,
+                   anchor_buf, method->index, anchor_close,
+                   className);
+        }
+    }
+    if (gOptions.outputHtml) {
+        printf("</pre>\n");
+    }
+}
+
+/* check to make sure that the child method meets the threshold of the parent */
+int checkThreshold(MethodEntry* parent, MethodEntry* child)
+{
+    double parentTime = parent->elapsedInclusive;
+    double childTime = child->elapsedInclusive;
+    int64_t percentage = (childTime / parentTime) * 100.0;
+    return (percentage < gOptions.threshold) ? 0 : 1;
+}
+
+void createLabels(FILE* file, MethodEntry* method)
+{
+    fprintf(file, "node%d[label = \"[%d] %s.%s (%llu, %llu, %d)\"]\n",
+             method->index, method->index, method->className, method->methodName,
+             method->elapsedInclusive / 1000,
+             method->elapsedExclusive / 1000,
+             method->numCalls[0]);
+
+    method->graphState = GRAPH_LABEL_VISITED;
+
+    TimedMethod* child;
+    for (child = method->children[0] ; child ; child = child->next) {
+        MethodEntry* childMethod = child->method;
+
+        if ((childMethod->graphState & GRAPH_LABEL_VISITED) == 0 && checkThreshold(method, childMethod)) {
+            createLabels(file, child->method);
+        }
+    }
+}
+
+void createLinks(FILE* file, MethodEntry* method)
+{
+    method->graphState |= GRAPH_NODE_VISITED;
+
+    TimedMethod* child;
+    for (child = method->children[0] ; child ; child = child->next) {
+        MethodEntry* childMethod = child->method;
+        if (checkThreshold(method, child->method)) {
+            fprintf(file, "node%d -> node%d\n", method->index, child->method->index);
+            // only visit children that haven't been visited before
+            if ((childMethod->graphState & GRAPH_NODE_VISITED) == 0) {
+                createLinks(file, child->method);
+            }
+        }
+    }
+}
+
+void createInclusiveProfileGraphNew(DataKeys* dataKeys)
+{
+    // create a temporary file in /tmp
+    char path[FILENAME_MAX];
+    if (gOptions.keepDotFile) {
+        snprintf(path, FILENAME_MAX, "%s.dot", gOptions.graphFileName);
+    } else {
+        snprintf(path, FILENAME_MAX, "/tmp/dot-%d-%d.dot", (int)time(NULL), rand());
+    }
+
+    FILE* file = fopen(path, "w+");
+
+    fprintf(file, "digraph g {\nnode [shape = record,height=.1];\n");
+
+    createLabels(file, dataKeys->methods);
+    createLinks(file, dataKeys->methods);
+
+    fprintf(file, "}");
+    fclose(file);
+
+    // now that we have the dot file generate the image
+    char command[1024];
+    snprintf(command, 1024, "dot -Tpng -o '%s' '%s'", gOptions.graphFileName, path);
+
+    system(command);
+
+    if (! gOptions.keepDotFile) {
+        remove(path);
+    }
+}
+
+void printInclusiveProfile(MethodEntry **pMethods, int numMethods,
+                           uint64_t sumThreadTime)
+{
+    int ii;
+    MethodEntry* method;
+    double total, sum, per, sum_per;
+    char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+    char signatureBuf[HTML_BUFSIZE];
+    char anchor_buf[80];
+
+    total = sumThreadTime;
+    anchor_buf[0] = 0;
+    if (gOptions.outputHtml) {
+        printf("<a name=\"inclusive\"></a>\n");
+        printf("<hr>\n");
+        outputNavigationBar();
+    } else {
+        printf("\n%s\n", profileSeparator);
+    }
+
+    /* Sort the methods into decreasing order of inclusive elapsed time. */
+    qsort(pMethods, numMethods, sizeof(MethodEntry*),
+          compareElapsedInclusive);
+
+    printf("\nInclusive elapsed times for each method and its parents and children,\n");
+    printf("sorted by inclusive time.\n\n");
+
+    if (gOptions.outputHtml) {
+        printf("<br><br>\n<pre>\n");
+    }
+
+    printf("index  %%/total %%/self  index     calls         usecs name\n");
+    for (ii = 0; ii < numMethods; ++ii) {
+        int num;
+        TimedMethod *pTimed;
+        double excl_per;
+        char buf[40];
+        char *className, *methodName, *signature;
+
+        method = pMethods[ii];
+        /* Don't show methods with zero cycles */
+        if (method->elapsedInclusive == 0)
+            break;
+
+        className = (char*)(method->className);
+        methodName = (char*)(method->methodName);
+        signature = (char*)(method->signature);
+
+        if (gOptions.outputHtml) {
+            printf("<a name=\"m%d\"></a>", method->index);
+            className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+            methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+            signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+        }
+        printf("----------------------------------------------------\n");
+
+        /* Sort and print the parents */
+        int numCalls = method->numCalls[0] + method->numCalls[1];
+        printInclusiveMethod(method, method->parents[0], numCalls, 0);
+        if (method->parents[1]) {
+            printf("               +++++++++++++++++++++++++\n");
+            printInclusiveMethod(method, method->parents[1], numCalls,
+                                 kIsRecursive);
+        }
+
+        per = 100.0 * method->elapsedInclusive / total;
+        sprintf(buf, "[%d]", ii);
+        if (method->methodName) {
+            printf("%-6s %5.1f%%   %5s %6s %6d+%-6d %9llu %s.%s %s\n",
+                   buf,
+                   per, "", "", method->numCalls[0], method->numCalls[1],
+                   method->elapsedInclusive,
+                   className, methodName, signature);
+        } else {
+            printf("%-6s %5.1f%%   %5s %6s %6d+%-6d %9llu %s\n",
+                   buf,
+                   per, "", "", method->numCalls[0], method->numCalls[1],
+                   method->elapsedInclusive,
+                   className);
+        }
+        excl_per = 100.0 * method->topExclusive / method->elapsedInclusive;
+        printf("%6s %5s   %5.1f%% %6s %6s %6s %9llu\n",
+               "", "", excl_per, "excl", "", "", method->topExclusive);
+
+        /* Sort and print the children */
+        printInclusiveMethod(method, method->children[0], 0, 0);
+        if (method->children[1]) {
+            printf("               +++++++++++++++++++++++++\n");
+            printInclusiveMethod(method, method->children[1], 0,
+                                 kIsRecursive);
+        }
+    }
+    if (gOptions.outputHtml) {
+        printf("</pre>\n");
+    }
+}
+
+void printThreadProfile(ThreadEntry *pThreads, int numThreads, uint64_t sumThreadTime, Filter** filters)
+{
+    int ii, jj;
+    ThreadEntry thread;
+    double total, per, sum_per;
+    uint64_t sum;
+    char threadBuf[HTML_BUFSIZE];
+    char anchor_buf[80];
+    int drawTable;
+
+    total = sumThreadTime;
+    anchor_buf[0] = 0;
+    if (gOptions.outputHtml) {
+        printf("<a name=\"thread\"></a>\n");
+        printf("<hr>\n");
+        outputNavigationBar();
+    } else {
+        printf("\n%s\n", profileSeparator);
+    }
+
+    /* Sort the threads into decreasing order of elapsed time. */
+    qsort(pThreads, numThreads, sizeof(ThreadEntry), compareElapsed);
+
+    printf("\nElapsed times for each thread, sorted by elapsed time.\n");
+    printf("Also includes percentage of time spent during the <i>execution</i> of any filters.\n\n");
+
+    if (gOptions.outputHtml) {
+        printf("<br><br>\n<pre>\n");
+    }
+
+    printf("    Usecs   self %%  sum %%");
+    for (ii = 0; ii < numFilters; ++ii) {
+        printf("  %s %%", filters[ii]->filterName);
+    }
+    printf("  tid   ThreadName\n");
+    sum = 0;
+
+    for (ii = 0; ii < numThreads; ++ii) {
+        int threadId;
+        char *threadName;
+        uint64_t time;
+
+        thread = pThreads[ii];
+
+        threadId = thread.threadId;
+        threadName = (char*)(thread.threadName);
+        time = thread.elapsedTime;
+
+        sum += time;
+        per = 100.0 * time / total;
+        sum_per = 100.0 * sum / total;
+
+        if (gOptions.outputHtml) {
+	    threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE);
+        }
+
+	printf("%9llu  %6.2f %6.2f", time, per, sum_per);
+	for (jj = 0; jj < numFilters; jj++) {
+	    printf(" %6.2f", 100.0 * filters[jj]->times.threadExecutionTimes[threadId] / time);
+	}
+	printf("    %3d %s\n", threadId, threadName);
+    }
+
+    if (gOptions.outputHtml)
+        printf("</pre><br />");
+
+    printf("\n\nBreak-down of portion of time spent by each thread while waiting on a filter method.\n");
+
+    for (ii = 0; ii < numFilters; ++ii) {
+        // Draw a table for each filter that measures wait time
+        drawTable = 0;
+	for (jj = 0; jj < filters[ii]->numKeys; jj++)
+	    if (filters[ii]->filterKeys[jj].flags == 1)
+	        drawTable = 1;
+
+	if (drawTable) {
+
+	    if (gOptions.outputHtml)
+	        printf("<br/><br/>\n<pre>\n");
+	    printf("Filter: %s\n", filters[ii]->filterName);
+	    printf("Total waiting cycles: %llu (%6.2f%% of total)\n",
+		   filters[ii]->times.totalWaitTime,
+		   100.0 * filters[ii]->times.totalWaitTime / sum);
+
+	    if (filters[ii]->times.totalWaitTime > 0) {
+
+	        printf("Details: \n\n");
+
+		printf(" Waiting cycles    %% of total waiting time   execution time while waiting    thread name\n");
+
+		for (jj = 0; jj < numThreads; jj++) {
+
+		    thread = pThreads[jj];
+
+		    char *threadName;
+		    threadName = (char*) thread.threadName;
+		    if (gOptions.outputHtml) {
+		        threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE);
+		    }
+
+		    printf(" %9llu                   %6.2f                     %6.2f               %s\n",
+			   filters[ii]->times.threadWaitTimes[thread.threadId],
+			   100.0 * filters[ii]->times.threadWaitTimes[thread.threadId] / filters[ii]->times.totalWaitTime,
+			   100.0 * filters[ii]->times.threadExecutionTimesWhileWaiting[thread.threadId] / filters[ii]->times.totalWaitTime,
+			   threadName);
+		}
+	    }
+
+	    if (gOptions.outputHtml)
+	        printf("</pre>\n");
+
+	}
+    }
+
+}
+
+void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
+{
+    int ii;
+
+    /* Sort the methods into alphabetical order to find the unique class
+     * names.
+     */
+    qsort(pMethods, numMethods, sizeof(MethodEntry*), compareClassNames);
+
+    /* Count the number of unique class names. */
+    const char *currentClassName = "";
+    const char *firstClassName = NULL;
+    traceData->numClasses = 0;
+    for (ii = 0; ii < numMethods; ++ii) {
+        if (pMethods[ii]->methodName == NULL) {
+            continue;
+        }
+        if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
+            // Remember the first one
+            if (firstClassName == NULL) {
+                firstClassName = pMethods[ii]->className;
+            }
+            traceData->numClasses += 1;
+            currentClassName = pMethods[ii]->className;
+        }
+    }
+
+    if (traceData->numClasses == 0) {
+        traceData->classes = NULL;
+        return;
+    }
+
+    /* Allocate space for all of the unique class names */
+    traceData->classes = (ClassEntry *) malloc(sizeof(ClassEntry) * traceData->numClasses);
+
+    /* Initialize the classes array */
+    memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses);
+    ClassEntry *pClass = traceData->classes;
+    pClass->className = currentClassName = firstClassName;
+    int prevNumMethods = 0;
+    for (ii = 0; ii < numMethods; ++ii) {
+        if (pMethods[ii]->methodName == NULL) {
+            continue;
+        }
+        if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
+            pClass->numMethods = prevNumMethods;
+            (++pClass)->className = currentClassName = pMethods[ii]->className;
+            prevNumMethods = 0;
+        }
+        prevNumMethods += 1;
+    }
+    pClass->numMethods = prevNumMethods;
+
+    /* Create the array of MethodEntry pointers for each class */
+    pClass = NULL;
+    currentClassName = "";
+    int nextMethod = 0;
+    for (ii = 0; ii < numMethods; ++ii) {
+        if (pMethods[ii]->methodName == NULL) {
+            continue;
+        }
+        if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
+            currentClassName = pMethods[ii]->className;
+            if (pClass == NULL)
+                pClass = traceData->classes;
+            else
+                pClass++;
+            /* Allocate space for the methods array */
+            int nbytes = sizeof(MethodEntry*) * pClass->numMethods;
+            pClass->methods = (MethodEntry**) malloc(nbytes);
+            nextMethod = 0;
+        }
+        pClass->methods[nextMethod++] = pMethods[ii];
+    }
+}
+
+/* Prints a number of html non-breaking spaces according so that the length
+ * of the string "buf" is at least "width" characters wide.  If width is
+ * negative, then trailing spaces are added instead of leading spaces.
+ */
+void printHtmlField(char *buf, int width)
+{
+    int ii;
+
+    int leadingSpaces = 1;
+    if (width < 0) {
+        width = -width;
+        leadingSpaces = 0;
+    }
+    int len = strlen(buf);
+    int numSpaces = width - len;
+    if (numSpaces <= 0) {
+        printf("%s", buf);
+        return;
+    }
+    if (leadingSpaces == 0)
+        printf("%s", buf);
+    for (ii = 0; ii < numSpaces; ++ii)
+        printf("&nbsp;");
+    if (leadingSpaces == 1)
+        printf("%s", buf);
+}
+
+void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime)
+{
+    int ii, jj;
+    MethodEntry* method;
+    double total, sum, per, sum_per;
+    char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+    char signatureBuf[HTML_BUFSIZE];
+
+    total = sumThreadTime;
+    if (gOptions.outputHtml) {
+        printf("<a name=\"class\"></a>\n");
+        printf("<hr>\n");
+        outputNavigationBar();
+    } else {
+        printf("\n%s\n", profileSeparator);
+    }
+
+    if (traceData->numClasses == 0) {
+        printf("\nNo classes.\n");
+        if (gOptions.outputHtml) {
+            printf("<br><br>\n");
+        }
+        return;
+    }
+
+    printf("\nExclusive elapsed time for each class, summed over all the methods\n");
+    printf("in the class.\n\n");
+    if (gOptions.outputHtml) {
+        printf("<br><br>\n");
+    }
+
+    /* For each class, sum the exclusive times in all of the methods
+     * in that class.  Also sum the number of method calls.  Also
+     * sort the methods so the most expensive appear at the top.
+     */
+    ClassEntry *pClass = traceData->classes;
+    for (ii = 0; ii < traceData->numClasses; ++ii, ++pClass) {
+        //printf("%s %d methods\n", pClass->className, pClass->numMethods);
+        int numMethods = pClass->numMethods;
+        for (jj = 0; jj < numMethods; ++jj) {
+            method = pClass->methods[jj];
+            pClass->elapsedExclusive += method->elapsedExclusive;
+            pClass->numCalls[0] += method->numCalls[0];
+            pClass->numCalls[1] += method->numCalls[1];
+        }
+
+        /* Sort the methods into decreasing order of exclusive time */
+        qsort(pClass->methods, numMethods, sizeof(MethodEntry*),
+              compareElapsedExclusive);
+    }
+
+    /* Allocate an array of pointers to the classes for more efficient
+     * sorting.
+     */
+    ClassEntry **pClasses;
+    pClasses = (ClassEntry**) malloc(sizeof(ClassEntry*) * traceData->numClasses);
+    for (ii = 0; ii < traceData->numClasses; ++ii)
+        pClasses[ii] = &traceData->classes[ii];
+
+    /* Sort the classes into decreasing order of exclusive time */
+    qsort(pClasses, traceData->numClasses, sizeof(ClassEntry*), compareClassExclusive);
+
+    if (gOptions.outputHtml) {
+        printf("<div class=\"header\"><span class=\"parent\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
+        printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; Class</div>\n");
+    } else {
+        printf("   Cycles %%/total Cumul.%%  Calls+Recur  Class\n");
+    }
+
+    sum = 0;
+    for (ii = 0; ii < traceData->numClasses; ++ii) {
+        char *className, *methodName, *signature;
+
+        /* Skip classes with zero cycles */
+        pClass = pClasses[ii];
+        if (pClass->elapsedExclusive == 0)
+            break;
+
+        per = 100.0 * pClass->elapsedExclusive / total;
+        sum += pClass->elapsedExclusive;
+        sum_per = 100.0 * sum / total;
+        className = (char*)(pClass->className);
+        if (gOptions.outputHtml) {
+            char buf[80];
+
+            className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+            printf("<div class=\"link\" onClick=\"javascript:toggle('d%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xd%d\">+</span>", ii, ii);
+            sprintf(buf, "%llu", pClass->elapsedExclusive);
+            printHtmlField(buf, 9);
+            printf(" ");
+            sprintf(buf, "%.1f", per);
+            printHtmlField(buf, 7);
+            printf(" ");
+            sprintf(buf, "%.1f", sum_per);
+            printHtmlField(buf, 7);
+            printf(" ");
+            sprintf(buf, "%d", pClass->numCalls[0]);
+            printHtmlField(buf, 6);
+            printf("+");
+            sprintf(buf, "%d", pClass->numCalls[1]);
+            printHtmlField(buf, -6);
+            printf(" ");
+            printf("%s", className);
+            printf("</div>\n");
+            printf("<div class=\"parent\" id=\"d%d\">\n", ii);
+        } else {
+            printf("---------------------------------------------\n");
+            printf("%9llu %7.1f %7.1f %6d+%-6d %s\n",
+                   pClass->elapsedExclusive, per, sum_per,
+                   pClass->numCalls[0], pClass->numCalls[1],
+                   className);
+        }
+
+        int numMethods = pClass->numMethods;
+        double classExclusive = pClass->elapsedExclusive;
+        double sumMethods = 0;
+        for (jj = 0; jj < numMethods; ++jj) {
+            method = pClass->methods[jj];
+            methodName = (char*)(method->methodName);
+            signature = (char*)(method->signature);
+            per = 100.0 * method->elapsedExclusive / classExclusive;
+            sumMethods += method->elapsedExclusive;
+            sum_per = 100.0 * sumMethods / classExclusive;
+            if (gOptions.outputHtml) {
+                char buf[80];
+
+                methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+                signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+                printf("<div class=\"leaf\"><span class=\"leaf\">&nbsp;</span>");
+                sprintf(buf, "%llu", method->elapsedExclusive);
+                printHtmlField(buf, 9);
+                printf("&nbsp;");
+                sprintf(buf, "%llu", method->elapsedInclusive);
+                printHtmlField(buf, 9);
+                printf("&nbsp;");
+                sprintf(buf, "%.1f", per);
+                printHtmlField(buf, 7);
+                printf("&nbsp;");
+                sprintf(buf, "%.1f", sum_per);
+                printHtmlField(buf, 7);
+                printf("&nbsp;");
+                sprintf(buf, "%d", method->numCalls[0]);
+                printHtmlField(buf, 6);
+                printf("+");
+                sprintf(buf, "%d", method->numCalls[1]);
+                printHtmlField(buf, -6);
+                printf("&nbsp;");
+                printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s&nbsp;%s",
+                       method->index, method->index, methodName, signature);
+                printf("</div>\n");
+            } else {
+                printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s %s\n",
+                       method->elapsedExclusive,
+                       method->elapsedInclusive,
+                       per, sum_per,
+                       method->numCalls[0], method->numCalls[1],
+                       method->index, methodName, signature);
+            }
+        }
+        if (gOptions.outputHtml) {
+            printf("</div>\n");
+        }
+    }
+}
+
+void createUniqueMethodList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
+{
+    int ii;
+
+    /* Sort the methods into alphabetical order of method names
+     * to find the unique method names.
+     */
+    qsort(pMethods, numMethods, sizeof(MethodEntry*), compareMethodNames);
+
+    /* Count the number of unique method names, ignoring class and
+     * signature.
+     */
+    const char *currentMethodName = "";
+    traceData->numUniqueMethods = 0;
+    for (ii = 0; ii < numMethods; ++ii) {
+        if (pMethods[ii]->methodName == NULL)
+            continue;
+        if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
+            traceData->numUniqueMethods += 1;
+            currentMethodName = pMethods[ii]->methodName;
+        }
+    }
+    if (traceData->numUniqueMethods == 0)
+        return;
+
+    /* Allocate space for pointers to all of the unique methods */
+    int nbytes = sizeof(UniqueMethodEntry) * traceData->numUniqueMethods;
+    traceData->uniqueMethods = (UniqueMethodEntry *) malloc(nbytes);
+
+    /* Initialize the uniqueMethods array */
+    memset(traceData->uniqueMethods, 0, nbytes);
+    UniqueMethodEntry *pUnique = traceData->uniqueMethods;
+    currentMethodName = NULL;
+    int prevNumMethods = 0;
+    for (ii = 0; ii < numMethods; ++ii) {
+        if (pMethods[ii]->methodName == NULL)
+            continue;
+        if (currentMethodName == NULL)
+            currentMethodName = pMethods[ii]->methodName;
+        if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
+            currentMethodName = pMethods[ii]->methodName;
+            pUnique->numMethods = prevNumMethods;
+            pUnique++;
+            prevNumMethods = 0;
+        }
+        prevNumMethods += 1;
+    }
+    pUnique->numMethods = prevNumMethods;
+
+    /* Create the array of MethodEntry pointers for each unique method */
+    pUnique = NULL;
+    currentMethodName = "";
+    int nextMethod = 0;
+    for (ii = 0; ii < numMethods; ++ii) {
+        if (pMethods[ii]->methodName == NULL)
+            continue;
+        if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
+            currentMethodName = pMethods[ii]->methodName;
+            if (pUnique == NULL)
+                pUnique = traceData->uniqueMethods;
+            else
+                pUnique++;
+            /* Allocate space for the methods array */
+            int nbytes = sizeof(MethodEntry*) * pUnique->numMethods;
+            pUnique->methods = (MethodEntry**) malloc(nbytes);
+            nextMethod = 0;
+        }
+        pUnique->methods[nextMethod++] = pMethods[ii];
+    }
+}
+
+void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime)
+{
+    int ii, jj;
+    MethodEntry* method;
+    double total, sum, per, sum_per;
+    char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+    char signatureBuf[HTML_BUFSIZE];
+
+    if (traceData->numUniqueMethods == 0)
+        return;
+
+    total = sumThreadTime;
+    if (gOptions.outputHtml) {
+        printf("<a name=\"method\"></a>\n");
+        printf("<hr>\n");
+        outputNavigationBar();
+    } else {
+        printf("\n%s\n", profileSeparator);
+    }
+
+    printf("\nExclusive elapsed time for each method, summed over all the classes\n");
+    printf("that contain a method with the same name.\n\n");
+    if (gOptions.outputHtml) {
+        printf("<br><br>\n");
+    }
+
+    /* For each unique method, sum the exclusive times in all of the methods
+     * with the same name.  Also sum the number of method calls.  Also
+     * sort the methods so the most expensive appear at the top.
+     */
+    UniqueMethodEntry *pUnique = traceData->uniqueMethods;
+    for (ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) {
+        int numMethods = pUnique->numMethods;
+        for (jj = 0; jj < numMethods; ++jj) {
+            method = pUnique->methods[jj];
+            pUnique->elapsedExclusive += method->elapsedExclusive;
+            pUnique->numCalls[0] += method->numCalls[0];
+            pUnique->numCalls[1] += method->numCalls[1];
+        }
+
+        /* Sort the methods into decreasing order of exclusive time */
+        qsort(pUnique->methods, numMethods, sizeof(MethodEntry*),
+              compareElapsedExclusive);
+    }
+
+    /* Allocate an array of pointers to the methods for more efficient
+     * sorting.
+     */
+    UniqueMethodEntry **pUniqueMethods;
+    int nbytes = sizeof(UniqueMethodEntry*) * traceData->numUniqueMethods;
+    pUniqueMethods = (UniqueMethodEntry**) malloc(nbytes);
+    for (ii = 0; ii < traceData->numUniqueMethods; ++ii)
+        pUniqueMethods[ii] = &traceData->uniqueMethods[ii];
+
+    /* Sort the methods into decreasing order of exclusive time */
+    qsort(pUniqueMethods, traceData->numUniqueMethods, sizeof(UniqueMethodEntry*),
+          compareUniqueExclusive);
+
+    if (gOptions.outputHtml) {
+        printf("<div class=\"header\"><span class=\"parent\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
+        printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; Method</div>\n");
+    } else {
+        printf("   Cycles %%/total Cumul.%%  Calls+Recur  Method\n");
+    }
+
+    sum = 0;
+    for (ii = 0; ii < traceData->numUniqueMethods; ++ii) {
+        char *className, *methodName, *signature;
+
+        /* Skip methods with zero cycles */
+        pUnique = pUniqueMethods[ii];
+        if (pUnique->elapsedExclusive == 0)
+            break;
+
+        per = 100.0 * pUnique->elapsedExclusive / total;
+        sum += pUnique->elapsedExclusive;
+        sum_per = 100.0 * sum / total;
+        methodName = (char*)(pUnique->methods[0]->methodName);
+        if (gOptions.outputHtml) {
+            char buf[80];
+
+            methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+            printf("<div class=\"link\" onClick=\"javascript:toggle('e%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xe%d\">+</span>", ii, ii);
+            sprintf(buf, "%llu", pUnique->elapsedExclusive);
+            printHtmlField(buf, 9);
+            printf(" ");
+            sprintf(buf, "%.1f", per);
+            printHtmlField(buf, 7);
+            printf(" ");
+            sprintf(buf, "%.1f", sum_per);
+            printHtmlField(buf, 7);
+            printf(" ");
+            sprintf(buf, "%d", pUnique->numCalls[0]);
+            printHtmlField(buf, 6);
+            printf("+");
+            sprintf(buf, "%d", pUnique->numCalls[1]);
+            printHtmlField(buf, -6);
+            printf(" ");
+            printf("%s", methodName);
+            printf("</div>\n");
+            printf("<div class=\"parent\" id=\"e%d\">\n", ii);
+        } else {
+            printf("---------------------------------------------\n");
+            printf("%9llu %7.1f %7.1f %6d+%-6d %s\n",
+                   pUnique->elapsedExclusive, per, sum_per,
+                   pUnique->numCalls[0], pUnique->numCalls[1],
+                   methodName);
+        }
+        int numMethods = pUnique->numMethods;
+        double methodExclusive = pUnique->elapsedExclusive;
+        double sumMethods = 0;
+        for (jj = 0; jj < numMethods; ++jj) {
+            method = pUnique->methods[jj];
+            className = (char*)(method->className);
+            signature = (char*)(method->signature);
+            per = 100.0 * method->elapsedExclusive / methodExclusive;
+            sumMethods += method->elapsedExclusive;
+            sum_per = 100.0 * sumMethods / methodExclusive;
+            if (gOptions.outputHtml) {
+                char buf[80];
+
+                className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+                signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+                printf("<div class=\"leaf\"><span class=\"leaf\">&nbsp;</span>");
+                sprintf(buf, "%llu", method->elapsedExclusive);
+                printHtmlField(buf, 9);
+                printf("&nbsp;");
+                sprintf(buf, "%llu", method->elapsedInclusive);
+                printHtmlField(buf, 9);
+                printf("&nbsp;");
+                sprintf(buf, "%.1f", per);
+                printHtmlField(buf, 7);
+                printf("&nbsp;");
+                sprintf(buf, "%.1f", sum_per);
+                printHtmlField(buf, 7);
+                printf("&nbsp;");
+                sprintf(buf, "%d", method->numCalls[0]);
+                printHtmlField(buf, 6);
+                printf("+");
+                sprintf(buf, "%d", method->numCalls[1]);
+                printHtmlField(buf, -6);
+                printf("&nbsp;");
+                printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s.%s&nbsp;%s",
+                       method->index, method->index,
+                       className, methodName, signature);
+                printf("</div>\n");
+            } else {
+                printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s.%s %s\n",
+                       method->elapsedExclusive,
+                       method->elapsedInclusive,
+                       per, sum_per,
+                       method->numCalls[0], method->numCalls[1],
+                       method->index, className, methodName, signature);
+            }
+        }
+        if (gOptions.outputHtml) {
+            printf("</div>\n");
+        }
+    }
+}
+
+/*
+ * Determines whether the given FilterKey matches the method. The FilterKey's
+ * key that is used to match against the method is determined by index.
+ */
+int keyMatchesMethod(FilterKey filterKey, MethodEntry* method, int index)
+{
+    if (filterKey.type[index] == 0) { // Class
+#if 0
+        fprintf(stderr, "  class is %s; filter key is %s\n", method->className, filterKey.keys[index]);
+#endif
+        if (strcmp(method->className, filterKey.keys[index]) == 0) {
+	    return 1;
+	}
+    } else { // Method
+        if (method->methodName != NULL) {
+	    // Get fully-qualified name
+            // TODO: parse class name and method name an put them in structure to avoid
+            // allocating memory here
+	    char* str = malloc ((strlen(method->className) + strlen(method->methodName) + 2) * sizeof(char));
+	    strcpy(str, method->className);
+	    strcat(str, ".");
+	    strcat(str, method->methodName);
+#if 0
+	    fprintf(stderr, "  method is %s; filter key is %s\n", str, filterKey.keys[index]);
+#endif
+	    if (strcmp(str, filterKey.keys[index]) == 0) {
+	        free(str);
+	        return 1;
+	    }
+	    free(str);
+	}
+    }
+    return 0;
+}
+
+/*
+ * Adds the appropriate times to the given filter based on the given method. Activates and
+ * de-activates filters as necessary.
+ *
+ * A filter is activated when the given method matches the 'entry' key of one of its FilterKeys.
+ * It is de-activated when the method matches the 'exit' key of the same FilterKey that activated it
+ * in the first place. Thus, a filter may be active more than once on the same thread (activated by
+ * different FilterKeys). A filter may also be active on different threads at the same time.
+ *
+ * While the filter is active on thread 1, elapsed time is allocated to different buckets which
+ * include: thread execution time (i.e., time thread 1 spent executing while filter was active),
+ * thread waiting time (i.e., time thread 1 waited while other threads executed), and execution
+ * time while waiting (i.e., time thread x spent executing while thread 1 was waiting). We also
+ * keep track of the total waiting time for a given filter.
+ *
+ * Lastly, we keep track of remaining (un-allocated) time for cases in which we exit a method we
+ * had not entered before, and that method happens to match the 'exit' key of a FilterKey.
+ */
+int filterMethod(MethodEntry* method, Filter* filter, int entry, int threadId, int numThreads,
+		 uint64_t elapsed, uint64_t remTime)
+{
+    int ii, jj;
+    int activeCount, addedWaitTimeThreadsCount;
+    int* activeThreads;
+    int* activationKeys;
+    int* addedWaitTimeThreads;
+
+    // flags
+    int addWaitTime = 0;
+    int deactivation = 0;
+    int addedExecutionTime = 0;
+    int addedExecutionTimeWhileWaiting = 0;
+    int addedWaitTime;
+    int addedRemTime = 0;
+    int threadKeyPairActive = 0;
+
+    if (filter->times.threadWaitTimes == NULL && filter->times.threadExecutionTimes == NULL &&
+	filter->times.threadExecutionTimesWhileWaiting == NULL) {
+        filter->times.threadWaitTimes = (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
+	filter->times.threadExecutionTimesWhileWaiting =
+          (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
+	filter->times.threadExecutionTimes = (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
+    }
+
+    int verbose = 0;
+
+    if (verbose)
+        fprintf(stderr,
+                "Running %s filter for class %s method %s, thread %d; activeCount: %d time: %llu\n",
+                filter->filterName, method->className, method->methodName, threadId,
+                filter->activeCount, elapsed);
+
+    // If active on some thread
+    if (filter->activeCount > 0) {
+
+        // Initialize active structures in case there are any de-activations
+        activeThreads = (int*) calloc(filter->activeCount, sizeof(int));
+	activationKeys = (int*) calloc(filter->activeCount, sizeof(int));
+	activeCount = 0;
+
+	// Initialize structure to help us determine which threads we've already added wait time to
+	addedWaitTimeThreads = (int*) calloc(filter->activeCount, sizeof(int));
+	addedWaitTimeThreadsCount = 0;
+
+        // Add times to appropriate sums and de-activate (if necessary)
+        for (ii = 0; ii < filter->activeCount; ii++) {
+
+	    if (verbose) {
+	        fprintf(stderr, "  Analyzing active thread with id %d, activated by key [%s, %s]\n",
+			filter->activeThreads[ii],
+                        filter->filterKeys[filter->activationKeys[ii]].keys[0],
+			filter->filterKeys[filter->activationKeys[ii]].keys[1]);
+	    }
+
+	    // If active on THIS thread -> add to execution time (only add once!)
+	    if (filter->activeThreads[ii] == threadId && !addedExecutionTime) {
+	        if (verbose)
+		    fprintf(stderr, "  Adding execution time to this thead\n");
+	        filter->times.threadExecutionTimes[threadId] += elapsed;
+		addedExecutionTime = 1;
+	    }
+
+	    // If active on ANOTHER thread (or this one too) with CROSS_THREAD_FLAG -> add to
+            // both thread's waiting time + total
+	    if (filter->filterKeys[filter->activationKeys[ii]].flags == 1) {
+
+	        // Add time to thread that is waiting (add to each waiting thread at most once!)
+	        addedWaitTime = 0;
+		for (jj = 0; jj < addedWaitTimeThreadsCount; jj++) {
+		    if (addedWaitTimeThreads[jj] == filter->activeThreads[ii])
+		        addedWaitTime = 1;
+		}
+	        if (!addedWaitTime) {
+		    if (verbose)
+		        fprintf(stderr, "  Adding wait time to waiting thread\n");
+		    filter->times.threadWaitTimes[filter->activeThreads[ii]] += elapsed;
+		    addedWaitTimeThreads[addedWaitTimeThreadsCount++] = filter->activeThreads[ii];
+		}
+
+                // Add execution time to this thread while the other is waiting (only add once!)
+                // [Flag is needed only because outside for loop might iterate through same
+                // thread twice?] TODO: verify
+		if (!addedExecutionTimeWhileWaiting) {
+		    if (verbose)
+		        fprintf(stderr, "  Adding exec time to this thread while thread waits\n");
+		    filter->times.threadExecutionTimesWhileWaiting[threadId] += elapsed;
+		    addedExecutionTimeWhileWaiting = 1;
+		}
+
+		addWaitTime = 1;
+	    }
+
+	    // If a method exit matches the EXIT method of an ACTIVE key -> de-activate
+            // the KEY (not the entire filter!!)
+	    if (!entry && keyMatchesMethod(filter->filterKeys[filter->activationKeys[ii]],
+					   method, 1)) {
+	        if (verbose)
+		    fprintf(stderr, "  Exit key matched!\n");
+
+	        // Deactivate by removing (NOT adding) entries from activeThreads and activationKeys
+	        deactivation = 1; // singal that lists should be replaced
+	    } else {
+	        // No de-activation -> copy old entries into new lists
+	        activeThreads[activeCount] = filter->activeThreads[ii];
+		activationKeys[activeCount++] = filter->activationKeys[ii];
+	    }
+	}
+
+	// If waiting on ANY thread, add wait time to total (but only ONCE!)
+	if (addWaitTime) {
+	    filter->times.totalWaitTime += elapsed;
+	}
+
+	// If de-activation occurred, replace lists
+	if (deactivation) {
+	    // TODO: Free memory from old lists
+
+	    // Set new lists
+	    filter->activeThreads = activeThreads;
+	    filter->activationKeys = activationKeys;
+	    filter->activeCount = activeCount;
+	} else {
+	    // TODO: Free memory from new lists
+	}
+
+    }  // Else, continue (we might be activating the filter on a different thread)
+
+
+    if (entry) { // ENTRY
+        if (verbose)
+	    fprintf(stderr, "  Here at the entry\n");
+        // If method matches entry key -> activate thread (do not add time since it's a new entry!)
+        for (ii = 0; ii < filter->numKeys; ii++) {
+	    if (keyMatchesMethod(filter->filterKeys[ii], method, 0)) {
+	        if (verbose)
+		    fprintf(stderr, "  Entry key matched!\n");
+	        // Activate thread only if thread/key pair is not already active
+	        for (jj = 0; jj < filter->activeCount; jj++) {
+		    if (filter->activeThreads[jj] == threadId && filter->activationKeys[jj] == ii)
+		        threadKeyPairActive = 1;
+		}
+	        // TODO: WORRY ABOUT MEMORY WHEN ACTIVE_COUNT > DEFAULT_ACTIVE_THREAD (unlikely)
+	        // TODO: what if the same thread is active multiple times by different keys?
+		// nothing, we just have to make sure we dont double-add, and we dont..
+		if (!threadKeyPairActive) {
+		    filter->activeThreads[filter->activeCount] = threadId;
+		    filter->activationKeys[filter->activeCount++] = ii;
+		}
+	    }
+	}
+    } else { // EXIT
+        // If method matches a terminal key -> add remTime to total (no need to active/de-activate)
+        for (ii = 0; ii < filter->numKeys; ii++) {
+	    if (!deactivation && keyMatchesMethod(filter->filterKeys[ii], method, 1) &&
+		keyMatchesMethod(filter->filterKeys[ii], method, 0)) {
+	        // Add remTime(s)
+	        // TODO: think about how we should add remTimes.. should we add remTime to threads
+	        // that were waiting or being waited on? for now, keep it simple and just add the
+	        // execution time to the current thread.
+	        filter->times.threadExecutionTimes[threadId] += remTime;
+		addedRemTime = 1;
+	    }
+	}
+    }
+
+    return addedExecutionTime | (addedRemTime << 1);
+}
+
+void dumpFilters(Filter** filters) {
+    int i;
+    for (i = 0; i < numFilters; i++) {
+        int j;
+	fprintf(stderr, "FILTER %s\n", filters[i]->filterName);
+	for (j = 0; j < filters[i]->numKeys; j++) {
+	    fprintf(stderr, "Keys: %s, type %d", filters[i]->filterKeys[j].keys[0],
+		    filters[i]->filterKeys[j].type[0]);
+	    if (filters[i]->filterKeys[j].keys[1] != NULL) {
+	        fprintf(stderr, " AND %s, type %d", filters[i]->filterKeys[j].keys[1],
+			filters[i]->filterKeys[j].type[1]);
+	    }
+	    fprintf(stderr, "; flags: %d\n", filters[i]->filterKeys[j].flags);
+	}
+    }
+}
+
+/*
+ * See parseFilters for required data format.
+ * 'data' must point to the beginning of a filter definition.
+ */
+char* parseFilter(char* data, char* dataEnd, Filter** filters, int num) {
+
+    Filter* filter;
+    int next, count, i;
+    int tmpOffset, tmpKeyLen;
+    char* tmpKey;
+    char* key1;
+    char* key2;
+
+    filter = (Filter*) malloc(sizeof(Filter));
+    filter->activeCount = 0;
+    filter->activeThreads = (int*) calloc(DEFAULT_ACTIVE_THREADS, sizeof(int));
+    filter->activationKeys = (int*) calloc(DEFAULT_ACTIVE_THREADS, sizeof(int));
+
+    next = findNextChar(data + 1, dataEnd - data - 1, '\n');
+    if (next < 0) {
+        // TODO: what should we do here?
+        // End of file reached...
+    }
+    data[next+1] = '\0';
+    filter->filterName = data + 1;
+    data += next + 2; // Careful
+
+    /*
+     * Count the number of keys (one per line).
+     */
+    count = countLinesToChar(data, dataEnd - data, FILTER_TAG);
+    if (count <= 0) {
+        fprintf(stderr,
+		"ERROR: failed while parsing filter %s (found %d keys)\n",
+		filter->filterName, count);
+	return NULL; // TODO: Should do something else
+	// Could return filter with 0 keys instead (probably better to avoid random segfaults)
+    }
+
+    filter->filterKeys = (FilterKey*) malloc(sizeof(FilterKey) * count);
+
+    /*
+     * Extract all entries.
+     */
+    tmpOffset = 0;
+    for (i = 0; i < count; i++) {
+        next = findNextChar(data, dataEnd - data, '\n');
+	//        assert(next > 0); // TODO: revise... (skip if next == 0 ?)
+        data[next] = '\0';
+	tmpKey = data;
+
+        if (*data == FILTER_FLAG_THREAD) {
+            filter->filterKeys[i].flags = 1;
+            tmpKey++;
+	} else {
+            filter->filterKeys[i].flags = 0;
+	}
+
+	tmpOffset = findNextChar(tmpKey, next, ',');
+
+        if (tmpOffset < 0) {
+            // No comma, so only 1 key
+            key1 = tmpKey;
+	    key2 = tmpKey;
+
+	    // Get type for key1
+            filter->filterKeys[i].type[0] = FILTER_TYPE_CLASS; // default
+            tmpOffset = findNextChar(key1, next, '(');
+	    if (tmpOffset > 0) {
+	        if (findNextChar(key1, next, ')') == tmpOffset + 1) {
+		    filter->filterKeys[i].type[0] = FILTER_TYPE_METHOD;
+		    filter->filterKeys[i].type[1] = FILTER_TYPE_METHOD;
+		}
+		key1[tmpOffset] = '\0';
+	    }
+	} else {
+	    // Pair of keys
+	    tmpKey[tmpOffset] = '\0';
+	    key1 = tmpKey;
+	    key2 = tmpKey + tmpOffset + 1;
+
+	    // Get type for key1
+	    filter->filterKeys[i].type[0] = FILTER_TYPE_CLASS;
+	    tmpKeyLen = tmpOffset;
+            tmpOffset = findNextChar(key1, tmpKeyLen, '(');
+	    if (tmpOffset > 0) {
+	        if (findNextChar(key1, tmpKeyLen, ')') == tmpOffset + 1) {
+		    filter->filterKeys[i].type[0] = FILTER_TYPE_METHOD;
+		}
+		key1[tmpOffset] = '\0';
+	    }
+
+	    // Get type for key2
+	    filter->filterKeys[i].type[1] = FILTER_TYPE_CLASS;
+            tmpOffset = findNextChar(key2, next - tmpKeyLen, '(');
+	    if (tmpOffset > 0) {
+	        if (findNextChar(key2, next - tmpKeyLen, ')') == tmpOffset + 1) {
+		    filter->filterKeys[i].type[1] = FILTER_TYPE_METHOD;
+		}
+		key2[tmpOffset] = '\0';
+	    }
+	}
+
+	filter->filterKeys[i].keys[0] = key1;
+	filter->filterKeys[i].keys[1] = key2;
+        data += next+1;
+    }
+
+    filter->numKeys = count;
+    filters[num] = filter;
+
+    return data;
+}
+
+/*
+ * Parses filters from given file. The file must follow the following format:
+ *
+ * *FilterName    <- creates a new filter with keys to follow
+ * A.method()     <- key that triggers whenever A.method() enters/exit
+ * Class          <- key that triggers whenever any method from Class enters/exits
+ * +CrossThread   <- same as above, but keeps track of execution times accross threads
+ * B.m(),C.m()    <- key that triggers filter on when B.m() enters and off when C.m() exits
+ *
+ * TODO: add concrete example to make things clear
+ */
+Filter** parseFilters(const char* filterFileName) {
+
+    Filter** filters = NULL;
+    FILE* fp = NULL;
+    long len;
+    char* data;
+    char* dataEnd;
+    char* dataStart;
+    int i, next, count;
+
+    fp = fopen(filterFileName, "r");
+    if (fp == NULL)
+        goto bail;
+
+    if (fseek(fp, 0L, SEEK_END) != 0) {
+        perror("fseek");
+        goto bail;
+    }
+
+    len = ftell(fp);
+    if (len == 0) {
+        fprintf(stderr, "WARNING: Filter file is empty.\n");
+        goto bail;
+    }
+    rewind(fp);
+
+    data = (char*) malloc(len);
+    if (data == NULL) {
+        fprintf(stderr, "ERROR: unable to alloc %ld bytes for filter file\n", len);
+        goto bail;
+    }
+
+    // Read file into memory
+    if (fread(data, 1, len, fp) != (size_t) len) {
+        fprintf(stderr, "ERROR: unable to read %ld bytes from filter file\n", len);
+        goto bail;
+    }
+
+    dataStart = data;
+    dataEnd = data + len;
+
+    // Figure out how many filters there are
+    numFilters = 0;
+    next = -1;
+
+    while (1) {
+        if (*data == FILTER_TAG)
+	    numFilters++;
+        next = findNextChar(data, len, '\n');
+        if (next < 0)
+            break;
+        data += next+1;
+        len -= next+1;
+    }
+
+    if (numFilters == 0) {
+        fprintf(stderr, "WARNING: no filters found. Continuing without filters\n");
+        goto bail;
+    }
+
+    filters = (Filter**) calloc(numFilters, sizeof(Filter *));
+    if (filters == NULL) {
+        fprintf(stderr, "ERROR: unable to alloc memory for filters");
+        goto bail;
+    }
+
+    data = dataStart;
+    for (i = 0; i < numFilters; i++) {
+        data = parseFilter(data, dataEnd, filters, i);
+    }
+
+    return filters;
+
+bail:
+    if (fp != NULL)
+        fclose(fp);
+
+    return NULL;
+
+}
+
+
+/*
+ * Read the key and data files and return the MethodEntries for those files
+ */
+DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName,
+			uint64_t* threadTime, Filter** filters)
+{
+    DataKeys* dataKeys = NULL;
+    MethodEntry **pMethods = NULL;
+    MethodEntry* method;
+    FILE* dataFp = NULL;
+    DataHeader dataHeader;
+    int ii, jj, numThreads;
+    uint64_t currentTime;
+    MethodEntry* caller;
+
+    dataFp = fopen(traceFileName, "r");
+    if (dataFp == NULL)
+        goto bail;
+
+    if ((dataKeys = parseKeys(dataFp, 0)) == NULL)
+       goto bail;
+
+    if (parseDataHeader(dataFp, &dataHeader) < 0)
+        goto bail;
+
+    numThreads = dataKeys->numThreads;
+
+#if 0
+    FILE *dumpStream = fopen("debug", "w");
+#endif
+    while (1) {
+        int threadId;
+        unsigned int methodVal;
+        int action;
+        unsigned int methodId;
+        CallStack *pStack;
+
+        /*
+         * Extract values from file.
+         */
+        if (readDataRecord(dataFp, &threadId, &methodVal, &currentTime))
+            break;
+
+        action = METHOD_ACTION(methodVal);
+        methodId = METHOD_ID(methodVal);
+
+        /* Get the call stack for this thread */
+        pStack = traceData->stacks[threadId];
+
+        /* If there is no call stack yet for this thread, then allocate one */
+        if (pStack == NULL) {
+            pStack = malloc(sizeof(CallStack));
+            pStack->top = 0;
+            pStack->lastEventTime = currentTime;
+            pStack->threadStartTime = currentTime;
+	    pStack->remTimes = (uint64_t*) calloc(numFilters, sizeof(uint64_t));
+            traceData->stacks[threadId] = pStack;
+        }
+
+        /* Lookup the current method */
+        method = lookupMethod(dataKeys, methodId);
+        if (method == NULL)
+            method = &dataKeys->methods[UNKNOWN_INDEX];
+
+#if 0
+        if (method->methodName) {
+	    fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n",
+	           threadId, currentTime, action, pStack->threadStartTime,
+	           method->recursiveEntries,
+	           pStack->top, method->className, method->methodName,
+	           method->signature);
+        } else {
+	    printf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n",
+	           threadId, currentTime, action, pStack->threadStartTime,
+	           method->recursiveEntries,
+	           pStack->top, method->className);
+        }
+#endif
+
+        if (action == METHOD_TRACE_ENTER) {
+            /* This is a method entry */
+            if (pStack->top >= MAX_STACK_DEPTH) {
+                fprintf(stderr, "Stack overflow (exceeded %d frames)\n",
+                        MAX_STACK_DEPTH);
+                exit(1);
+            }
+
+            /* Get the caller method */
+            if (pStack->top >= 1)
+                caller = pStack->calls[pStack->top - 1].method;
+            else
+                caller = &dataKeys->methods[TOPLEVEL_INDEX];
+            countRecursiveEntries(pStack, pStack->top, caller);
+            caller->elapsedExclusive += currentTime - pStack->lastEventTime;
+#if 0
+            if (caller->elapsedExclusive > 10000000)
+                fprintf(dumpStream, "%llu current %llu last %llu diff %llu\n",
+                        caller->elapsedExclusive, currentTime,
+                        pStack->lastEventTime,
+                        currentTime - pStack->lastEventTime);
+#endif
+            if (caller->recursiveEntries <= 1) {
+                caller->topExclusive += currentTime - pStack->lastEventTime;
+            }
+
+            /* Push the method on the stack for this thread */
+            pStack->calls[pStack->top].method = method;
+            pStack->calls[pStack->top++].entryTime = currentTime;
+
+	    // For each filter
+	    int result = 0;
+	    for (ii = 0; ii < numFilters; ii++) {
+	        result = filterMethod(method, filters[ii], 1, threadId, numThreads,
+				       currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
+
+		// TODO: make remTimes work properly
+		// Consider moving remTimes handling together with the rest
+		// of time handling and clean up the return codes
+		/*
+		if (result == 0) { // no time added, no remTime added
+		    pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
+		} else if (result == 3 || result == 4) { // remTime added
+		    // Reset remTime, since it's been added
+		    pStack->remTimes[ii] = 0;
+		}
+		*/
+	    }
+
+        } else {
+            /* This is a method exit */
+            uint64_t entryTime = 0;
+
+            /* Pop the method off the stack for this thread */
+            if (pStack->top > 0) {
+                pStack->top -= 1;
+                entryTime = pStack->calls[pStack->top].entryTime;
+                if (method != pStack->calls[pStack->top].method) {
+                    if (method->methodName) {
+                        fprintf(stderr,
+                            "Exit from method %s.%s %s does not match stack:\n",
+                            method->className, method->methodName,
+                            method->signature);
+                    } else {
+                        fprintf(stderr,
+                            "Exit from method %s does not match stack:\n",
+                            method->className);
+                    }
+                    stackDump(pStack, pStack->top + 1);
+                    exit(1);
+                }
+            }
+
+            /* Get the caller method */
+            if (pStack->top >= 1)
+                caller = pStack->calls[pStack->top - 1].method;
+            else
+                caller = &dataKeys->methods[TOPLEVEL_INDEX];
+            countRecursiveEntries(pStack, pStack->top, caller);
+            countRecursiveEntries(pStack, pStack->top, method);
+            uint64_t elapsed = currentTime - entryTime;
+            addInclusiveTime(caller, method, elapsed);
+            method->elapsedExclusive += currentTime - pStack->lastEventTime;
+            if (method->recursiveEntries == 0) {
+                method->topExclusive += currentTime - pStack->lastEventTime;
+            }
+
+	    // For each filter
+	    int result = 0;
+	    for (ii = 0; ii < numFilters; ii++) {
+	        result = filterMethod(method, filters[ii], 0, threadId, numThreads,
+				       currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
+
+		// TODO: make remTimes work properly
+		/*
+		if (result == 0) { // no time added, no remTime added
+		    pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
+		} else if (result == 3 || result == 4) { // remTime added
+		    // Reset remTime, since it's been added
+		    pStack->remTimes[ii] = 0;
+		}
+		*/
+	    }
+
+        }
+        /* Remember the time of the last entry or exit event */
+        pStack->lastEventTime = currentTime;
+    }
+
+    /* If we have calls on the stack when the trace ends, then clean
+     * up the stack and add time to the callers by pretending that we
+     * are exiting from their methods now.
+     */
+    CallStack *pStack;
+    int threadId;
+    uint64_t elapsedTime = 0;
+    uint64_t sumThreadTime = 0;
+    for (threadId = 0; threadId < MAX_THREADS; ++threadId) {
+
+        pStack = traceData->stacks[threadId];
+
+        /* If this thread never existed, then continue with next thread */
+        if (pStack == NULL)
+            continue;
+
+        /* Calculate times spent in thread, and add it to total time */
+        elapsedTime = pStack->lastEventTime - pStack->threadStartTime;
+        sumThreadTime += elapsedTime;
+
+        for (ii = 0; ii < pStack->top; ++ii) {
+	  //printf("in loop\n");
+
+            if (ii == 0)
+                caller = &dataKeys->methods[TOPLEVEL_INDEX];
+            else
+                caller = pStack->calls[ii - 1].method;
+            method = pStack->calls[ii].method;
+            countRecursiveEntries(pStack, ii, caller);
+            countRecursiveEntries(pStack, ii, method);
+
+            uint64_t entryTime = pStack->calls[ii].entryTime;
+            uint64_t elapsed = pStack->lastEventTime - entryTime;
+            addInclusiveTime(caller, method, elapsed);
+
+	    // For each filter
+	    int result = 0;
+	    for (ii = 0; ii < numFilters; ii++) {
+	        result = filterMethod(method, filters[ii], 0, threadId, numThreads,
+				       currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
+
+		// TODO: make remTimes work properly
+		/*
+		if (result == 0) { // no time added, no remTime added
+		    pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
+		} else if (result == 3 || result == 4) { // remTime added
+		    // Reset remTime, since it's been added
+		    pStack->remTimes[ii] = 0;
+		}
+		*/
+	    }
+        }
+
+	/* Save the per-thread elapsed time in the DataKeys struct */
+	for (ii = 0; ii < dataKeys->numThreads; ++ii) {
+	    if (dataKeys->threads[ii].threadId == threadId) {
+                dataKeys->threads[ii].elapsedTime = elapsedTime;
+	    }
+	}
+
+
+    }
+    caller = &dataKeys->methods[TOPLEVEL_INDEX];
+    caller->elapsedInclusive = sumThreadTime;
+
+#if 0
+    fclose(dumpStream);
+#endif
+
+    if (threadTime != NULL) {
+        *threadTime = sumThreadTime;
+    }
+
+bail:
+    if (dataFp != NULL)
+        fclose(dataFp);
+
+    return dataKeys;
+}
+
+MethodEntry** parseMethodEntries(DataKeys* dataKeys)
+{
+    int ii;
+    /* Create a new array of pointers to the methods and sort the pointers
+     * instead of the actual MethodEntry structs.  We need to do this
+     * because there are other lists that contain pointers to the
+     * MethodEntry structs.
+     */
+    MethodEntry** pMethods = (MethodEntry**) malloc(sizeof(MethodEntry*) * dataKeys->numMethods);
+    for (ii = 0; ii < dataKeys->numMethods; ++ii) {
+        MethodEntry* entry = &dataKeys->methods[ii];
+        pMethods[ii] = entry;
+    }
+
+    return pMethods;
+}
+
+
+/*
+ * Produce a function profile from the following methods
+ */
+void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime,
+                  ThreadEntry *pThreads, int numThreads, Filter** filters)
+{
+   /* Print the html header, if necessary */
+    if (gOptions.outputHtml) {
+        printf(htmlHeader, gOptions.sortableUrl);
+        outputTableOfContents();
+    }
+
+    printExclusiveProfile(pMethods, numMethods, sumThreadTime);
+    printInclusiveProfile(pMethods, numMethods, sumThreadTime);
+
+    printThreadProfile(pThreads, numThreads, sumThreadTime, filters);
+
+    createClassList(traceData, pMethods, numMethods);
+    printClassProfiles(traceData, sumThreadTime);
+
+    createUniqueMethodList(traceData, pMethods, numMethods);
+    printMethodProfiles(traceData, sumThreadTime);
+
+    if (gOptions.outputHtml) {
+        printf("%s", htmlFooter);
+    }
+}
+
+int compareMethodNamesForDiff(const void *a, const void *b)
+{
+    int result;
+
+    const MethodEntry *methodA = *(const MethodEntry**)a;
+    const MethodEntry *methodB = *(const MethodEntry**)b;
+    if (methodA->methodName == NULL || methodB->methodName == NULL) {
+        return compareClassNames(a, b);
+    }
+    result = strcmp(methodA->methodName, methodB->methodName);
+    if (result == 0) {
+        result = strcmp(methodA->signature, methodB->signature);
+        if (result == 0) {
+           return strcmp(methodA->className, methodB->className);
+        }
+    }
+    return result;
+}
+
+int findMatch(MethodEntry** methods, int size, MethodEntry* matchThis)
+{
+    int i;
+
+    for (i = 0 ; i < size ; i++) {
+        MethodEntry* method = methods[i];
+
+        if (method != NULL && !compareMethodNamesForDiff(&method, &matchThis)) {
+//            printf("%s.%s == %s.%s<br>\n", matchThis->className, matchThis->methodName,
+  //              method->className, method->methodName);
+
+            return i;
+/*            if (!compareMethodNames(&method, &matchThis)) {
+                return i;
+            }
+*/        }
+    }
+
+    return -1;
+}
+
+int compareDiffEntriesExculsive(const void *a, const void *b)
+{
+    int result;
+
+    const DiffEntry* entryA = (const DiffEntry*)a;
+    const DiffEntry* entryB = (const DiffEntry*)b;
+
+    if (entryA->differenceExclusive < entryB->differenceExclusive) {
+        return 1;
+    } else if (entryA->differenceExclusive > entryB->differenceExclusive) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int compareDiffEntriesInculsive(const void *a, const void *b)
+{
+    int result;
+
+    const DiffEntry* entryA = (const DiffEntry*)a;
+    const DiffEntry* entryB = (const DiffEntry*)b;
+
+    if (entryA->differenceInclusive < entryB->differenceInclusive) {
+        return 1;
+    } else if (entryA->differenceInclusive > entryB->differenceInclusive) {
+        return -1;
+    }
+
+    return 0;
+}
+
+void printMissingMethod(MethodEntry* method)
+{
+    char classBuf[HTML_BUFSIZE];
+    char methodBuf[HTML_BUFSIZE];
+    char* className;
+    char* methodName;
+
+    className = htmlEscape(method->className, classBuf, HTML_BUFSIZE);
+    methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE);
+
+    if (gOptions.outputHtml) printf("<tr><td>\n");
+
+    printf("%s.%s ", className, methodName);
+    if (gOptions.outputHtml) printf("</td><td>");
+
+    printf("%lld ", method->elapsedExclusive);
+    if (gOptions.outputHtml) printf("</td><td>");
+
+    printf("%lld ", method->elapsedInclusive);
+    if (gOptions.outputHtml) printf("</td><td>");
+
+    printf("%d\n", method->numCalls[0]);
+    if (gOptions.outputHtml) printf("</td><td>\n");
+}
+
+
+void createDiff(DataKeys* d1, uint64_t sum1, DataKeys* d2, uint64_t sum2)
+{
+    MethodEntry** methods1 = parseMethodEntries(d1);
+    MethodEntry** methods2 = parseMethodEntries(d2);
+
+    // sort and assign the indicies
+    int i;
+    qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
+    for (i = 0; i < d1->numMethods; ++i) {
+        methods1[i]->index = i;
+    }
+
+    qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
+    for (i = 0; i < d2->numMethods; ++i) {
+        methods2[i]->index = i;
+    }
+
+    int max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods;
+    max++;
+    DiffEntry* diffs = (DiffEntry*)malloc(max * sizeof(DiffEntry));
+    memset(diffs, 0, max * sizeof(DiffEntry));
+    DiffEntry* ptr = diffs;
+
+//    printf("<br>d1->numMethods: %d d1->numMethods: %d<br>\n", d1->numMethods, d2->numMethods);
+
+    int matches = 0;
+
+    for (i = 0 ; i < d1->numMethods ; i++) {
+        int match = findMatch(methods2, d2->numMethods, methods1[i]);
+        if (match >= 0) {
+            ptr->method1 = methods1[i];
+            ptr->method2 = methods2[match];
+
+            uint64_t e1 = ptr->method1->elapsedExclusive;
+            uint64_t e2 = ptr->method2->elapsedExclusive;
+            if (e1 > 0) {
+                ptr->differenceExclusive = e2 - e1;
+                ptr->differenceExclusivePercentage = ((double)e2 / (double)e1) * 100.0;
+            }
+
+            uint64_t i1 = ptr->method1->elapsedInclusive;
+            uint64_t i2 = ptr->method2->elapsedInclusive;
+            if (i1 > 0) {
+                ptr->differenceInclusive = i2 - i1;
+                ptr->differenceInclusivePercentage = ((double)i2 / (double)i1) * 100.0;
+            }
+
+            // clear these out so we don't find them again and we know which ones
+            // we have left over
+            methods1[i] = NULL;
+            methods2[match] = NULL;
+            ptr++;
+
+            matches++;
+        }
+    }
+    ptr->method1 = NULL;
+    ptr->method2 = NULL;
+
+    qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive);
+    ptr = diffs;
+
+    if (gOptions.outputHtml) {
+        printf(htmlHeader, gOptions.sortableUrl);
+        printf("<h3>Table of Contents</h3>\n");
+        printf("<ul>\n");
+        printf("<li><a href='#exclusive'>Exclusive</a>\n");
+        printf("<li><a href='#inclusive'>Inclusive</a>\n");
+        printf("</ul>\n");
+        printf("Run 1: %s<br>\n", gOptions.diffFileName);
+        printf("Run 2: %s<br>\n", gOptions.traceFileName);
+        printf("<a name=\"exclusive\"></a><h3 id=\"exclusive\">Exclusive</h3>\n");
+        printf(tableHeader, "exclusive_table");
+    }
+
+    char classBuf[HTML_BUFSIZE];
+    char methodBuf[HTML_BUFSIZE];
+    char* className;
+    char* methodName;
+
+    while (ptr->method1 != NULL && ptr->method2 != NULL) {
+        if (gOptions.outputHtml) printf("<tr><td>\n");
+
+        className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
+        methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
+
+        printf("%s.%s ", className, methodName);
+        if (gOptions.outputHtml) printf("</td><td>");
+
+        printf("%lld ", ptr->method1->elapsedExclusive);
+        if (gOptions.outputHtml) printf("</td><td>");
+
+        printf("%llu ", ptr->method2->elapsedExclusive);
+        if (gOptions.outputHtml) printf("</td><td>");
+
+        printf("%lld ", ptr->differenceExclusive);
+        if (gOptions.outputHtml) printf("</td><td>");
+
+        printf("%.2f\n", ptr->differenceExclusivePercentage);
+        if (gOptions.outputHtml) printf("</td><td>\n");
+
+        printf("%d\n", ptr->method1->numCalls[0]);
+        if (gOptions.outputHtml) printf("</td><td>\n");
+
+        printf("%d\n", ptr->method2->numCalls[0]);
+        if (gOptions.outputHtml) printf("</td></tr>\n");
+
+        ptr++;
+    }
+
+    if (gOptions.outputHtml) printf("</table>\n");
+
+    if (gOptions.outputHtml) {
+        printf(htmlHeader, gOptions.sortableUrl);
+        printf("Run 1: %s<br>\n", gOptions.diffFileName);
+        printf("Run 2: %s<br>\n", gOptions.traceFileName);
+        printf("<a name=\"inclusive\"></a><h3 id=\"inculisve\">Inclusive</h3>\n");
+        printf(tableHeader, "inclusive_table");
+    }
+
+    qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesInculsive);
+    ptr = diffs;
+
+    while (ptr->method1 != NULL && ptr->method2 != NULL) {
+        if (gOptions.outputHtml) printf("<tr><td>\n");
+
+        className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
+        methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
+
+        printf("%s.%s ", className, methodName);
+        if (gOptions.outputHtml) printf("</td><td>");
+
+        printf("%lld ", ptr->method1->elapsedInclusive);
+        if (gOptions.outputHtml) printf("</td><td>");
+
+        printf("%llu ", ptr->method2->elapsedInclusive);
+        if (gOptions.outputHtml) printf("</td><td>");
+
+        printf("%lld ", ptr->differenceInclusive);
+        if (gOptions.outputHtml) printf("</td><td>");
+
+        printf("%.2f\n", ptr->differenceInclusivePercentage);
+        if (gOptions.outputHtml) printf("</td><td>\n");
+
+        printf("%d\n", ptr->method1->numCalls[0]);
+        if (gOptions.outputHtml) printf("</td><td>\n");
+
+        printf("%d\n", ptr->method2->numCalls[0]);
+        if (gOptions.outputHtml) printf("</td></tr>\n");
+
+        ptr++;
+    }
+
+    if (gOptions.outputHtml) {
+        printf("</table>\n");
+        printf("<h3>Run 1 methods not found in Run 2</h3>");
+        printf(tableHeaderMissing);
+    }
+
+    for (i = 0; i < d1->numMethods; ++i) {
+        if (methods1[i] != NULL) {
+           printMissingMethod(methods1[i]);
+        }
+    }
+
+    if (gOptions.outputHtml) {
+        printf("</table>\n");
+        printf("<h3>Run 2 methods not found in Run 1</h3>");
+        printf(tableHeaderMissing);
+    }
+
+    for (i = 0; i < d2->numMethods; ++i) {
+        if (methods2[i] != NULL) {
+            printMissingMethod(methods2[i]);
+        }
+    }
+
+    if (gOptions.outputHtml) printf("</body></html\n");
+}
+
+int usage(const char *program)
+{
+    fprintf(stderr, "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] [-f filter-file] trace-file-name\n", program);
+    fprintf(stderr, "  -d trace-file-name  - Diff with this trace\n");
+    fprintf(stderr, "  -g outfile          - Write graph to 'outfile'\n");
+    fprintf(stderr, "  -f filter-file      - Filter functions as specified in file\n");
+    fprintf(stderr, "  -k                  - When writing a graph, keep the intermediate DOT file\n");
+    fprintf(stderr, "  -h                  - Turn on HTML output\n");
+    fprintf(stderr, "  -o                  - Dump the dmtrace file instead of profiling\n");
+    fprintf(stderr, "  -s                  - URL base to where the sortable javascript file\n");
+    fprintf(stderr, "  -t threshold        - Threshold percentage for including nodes in the graph\n");
+    return 2;
+}
+
+// Returns true if there was an error
+int parseOptions(int argc, char **argv)
+{
+    while (1) {
+        int opt = getopt(argc, argv, "d:hg:kos:t:f:");
+        if (opt == -1)
+            break;
+        switch (opt) {
+            case 'd':
+                gOptions.diffFileName = optarg;
+                break;
+            case 'g':
+                gOptions.graphFileName = optarg;
+                break;
+            case 'f':
+	        gOptions.filterFileName = optarg;
+                break;
+            case 'k':
+                gOptions.keepDotFile = 1;
+                break;
+            case 'h':
+                gOptions.outputHtml = 1;
+                break;
+            case 'o':
+                gOptions.dump = 1;
+                break;
+            case 's':
+                gOptions.sortableUrl = optarg;
+                break;
+            case 't':
+                gOptions.threshold = atoi(optarg);
+                break;
+            default:
+                return 1;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char** argv)
+{
+
+    gOptions.threshold = -1;
+
+    // Parse the options
+    if (parseOptions(argc, argv) || argc - optind != 1)
+        return usage(argv[0]);
+
+    gOptions.traceFileName = argv[optind];
+
+    if (gOptions.threshold < 0 || 100 <= gOptions.threshold) {
+        gOptions.threshold = 20;
+    }
+
+    if (gOptions.dump) {
+        dumpTrace();
+        return 0;
+    }
+
+    uint64_t sumThreadTime = 0;
+
+    Filter** filters = NULL;
+    if (gOptions.filterFileName != NULL) {
+        filters = parseFilters(gOptions.filterFileName);
+    }
+
+    TraceData data1;
+    memset(&data1, 0, sizeof(data1));
+    DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName,
+                                       &sumThreadTime, filters);
+    if (dataKeys == NULL) {
+        fprintf(stderr, "Cannot read trace.\n");
+        exit(1);
+    }
+
+    if (gOptions.diffFileName != NULL) {
+        uint64_t sum2;
+        TraceData data2;
+        DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2, filters);
+
+        createDiff(d2, sum2, dataKeys, sumThreadTime);
+
+        freeDataKeys(d2);
+    } else {
+        MethodEntry** methods = parseMethodEntries(dataKeys);
+        profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime,
+                     dataKeys->threads, dataKeys->numThreads, filters);
+        if (gOptions.graphFileName != NULL) {
+            createInclusiveProfileGraphNew(dataKeys);
+        }
+        free(methods);
+    }
+
+    freeDataKeys(dataKeys);
+
+    return 0;
+}
diff --git a/tools/dmtracedump/dmtracedump.pl b/tools/dmtracedump/dmtracedump.pl
new file mode 100755
index 0000000..6e487c6
--- /dev/null
+++ b/tools/dmtracedump/dmtracedump.pl
@@ -0,0 +1,18 @@
+#!/usr/bin/perl
+
+opendir(DIR, ".") || die "can't opendir $some_dir: $!";
+@traces = grep { /.*\.dmtrace\.data/ } readdir(DIR);
+
+foreach (@traces)
+{
+    $input = $_;
+    $input =~ s/\.data$//;
+
+    $output = "$input.html";
+
+    print("dmtracedump -h -p $input > $output\n");
+    system("dmtracedump -h -p '$input' > '$output'");
+
+}
+
+closedir DIR;
diff --git a/tools/dmtracedump/dumpdir.sh b/tools/dmtracedump/dumpdir.sh
new file mode 100644
index 0000000..81992a2
--- /dev/null
+++ b/tools/dmtracedump/dumpdir.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+FILES=`ls $1/*.data | sed "s/^\\(.*\\).data$/\\1/"`
+
+mkdir -p $2
+
+for F in $FILES
+do
+    G=$2/`echo $F | sed "s/.*\\///g"`.html
+    dmtracedump -h -p $F > $G
+done
diff --git a/tools/dmtracedump/filters b/tools/dmtracedump/filters
new file mode 100644
index 0000000..96a041c
--- /dev/null
+++ b/tools/dmtracedump/filters
@@ -0,0 +1,42 @@
+*GC
+dvmGcScanRootClassLoader
+mspace_walk_free_pages
+dvmCollectGarbageInternal
+doHeapWork
+dvmGetNextHeapWorkerObject
+GC
+GC2
+GC3
+*Net
+setsockopt
++sys_setsockopt [kernel]
+socketSelect
+send
+recv
+sendto
+recvfrom
++sys_sendto [kernel]
++sys_recvfrom [kernel]
+org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection
+android.net.http.ConnectionThread
+PlainSocketImpl
+WebCore::HTMLTokenizer
+*IO
+select
++sys_select [kernel]
+*DB
+android.database.sqlite.SQLiteOpenHelper
+android.database.sqlite.SQLiteQueryBuilder
+android.database.sqlite.SQLiteDatabase
+android.database.sqlite.SQLiteDirectCursorDriver
+android.database.sqlite.SQLiteQuery
+android.database.sqlite.SQLiteProgram
+android.database.AbstractCursor
+android.database.sqlite.SQLiteCursor
+*UI
+android.view.View.draw()
+android.view.ViewGroup
+*Sync
++java.lang.Object.wait()
+*Useless
++android.widget.ProgressBar
diff --git a/tools/dmtracedump/tests/filters/run_tests.sh b/tools/dmtracedump/tests/filters/run_tests.sh
new file mode 100755
index 0000000..cdf87cb
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/run_tests.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+failed=0
+for file in $(find $1 -type f -iname 'test*'); do
+  case $file in
+    *testFilters) continue; ;;
+    *Expected) continue; ;;
+    *Trace) continue; ;;
+    *.html) continue; ;;
+  esac
+
+  echo "Running test for $file"
+
+#  create_test_dmtrace $file tmp.trace
+  dmtracedump -f testFilters -h "$file"Trace > tmp.html 2> /dev/null
+
+  output=`diff tmp.html "$file"Expected 2>&1`
+  if [ ${#output} -eq 0 ]
+  then
+    echo "  OK"
+  else
+    echo " Test failed: $output"
+    failed=`expr $failed + 1`
+  fi
+
+done
+
+rm tmp.trace
+rm tmp.html
+
+if [ $failed -gt 0 ]
+then
+  echo "$failed test(s) failed"
+else
+  echo "All tests passed successfully"
+fi
diff --git a/tools/dmtracedump/tests/filters/testFilters b/tools/dmtracedump/tests/filters/testFilters
new file mode 100644
index 0000000..2c3edb6
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testFilters
@@ -0,0 +1,9 @@
+*FirstFilter
++A.m(),B.m()
++C.m()
++R.m(),S.m()
+*SecondFilter
++D.m(),E.m()
++F.m()
+*RepeatedFilter
++R.m(),S.m()
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..b4367c6
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeys
@@ -0,0 +1,19 @@
+#    ____             ____       _________
+# __|A   |___________|B   |_____|Z        |_______
+#
+#         ___________       ____           ____
+# _______|Z          |_____|D   |_________|E   |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+4 2 Z
+2 1 B
+4 1 B
+4 2 D
+6 2 D
+4 1 Z
+8 1 Z
+6 2 E
+8 2 E
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..7fa789a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   12.50  62.50  <a href="#m2">[2]</a> A.m ()
+        2   12.50  75.00  <a href="#m3">[3]</a> B.m ()
+        2   12.50  87.50  <a href="#m4">[4]</a> D.m ()
+        2   12.50 100.00  <a href="#m5">[5]</a> E.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              8 Z.m ()
+                12.5%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                12.5%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+                12.5%    <a href="#m4">[4]</a>      1/1              2 D.m ()
+                12.5%    <a href="#m5">[5]</a>      1/1              2 E.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              8 (toplevel)
+[1]     50.0%                     2+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     12.5%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     12.5%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     12.5%                     1+0              2 D.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     12.5%                     1+0              2 E.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00   0.00  50.00   0.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                      50.00               main
+         0                     0.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         0                     0.00                      50.00               main
+         8                   100.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;E.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..8bc74ff
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeys
new file mode 100644
index 0000000..76cdea7
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeys
@@ -0,0 +1,19 @@
+#    ____             ____       _________
+# __|R   |___________|S   |_____|Z        |_______
+#
+#         ___________       ____           ____
+# _______|Z          |_____|R   |_________|S   |__
+#
+#
+0 1 R
+2 1 R
+0 2 Z
+4 2 Z
+2 1 S
+4 1 S
+4 2 R
+6 2 R
+4 1 Z
+8 1 Z
+6 2 S
+8 2 S
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..5672826
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> R.m ()
+        4   25.00 100.00  <a href="#m3">[3]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              8 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      2/2              4 R.m ()
+                25.0%    <a href="#m3">[3]</a>      2/2              4 S.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              8 (toplevel)
+[1]     50.0%                     2+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     25.0%                     2+0              4 R.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[3]     25.0%                     2+0              4 S.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00  50.00      1 main
+        8   50.00 100.00  50.00   0.00  50.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 16 (100.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                    50.00                      50.00               main
+         8                    50.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 16 (100.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                    50.00                      50.00               main
+         8                    50.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..9ec7378
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeys
new file mode 100644
index 0000000..d1bcdd3
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeys
@@ -0,0 +1,19 @@
+#    ____             ____       _________
+# __|A   |___________|B   |_____|Z        |_______
+#
+#         ___________       ____           ____
+# _______|Z          |_____|R   |_________|S   |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+4 2 Z
+2 1 B
+4 1 B
+4 2 R
+6 2 R
+4 1 Z
+8 1 Z
+6 2 S
+8 2 S
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..ef56af5
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   12.50  62.50  <a href="#m2">[2]</a> A.m ()
+        2   12.50  75.00  <a href="#m3">[3]</a> B.m ()
+        2   12.50  87.50  <a href="#m4">[4]</a> R.m ()
+        2   12.50 100.00  <a href="#m5">[5]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              8 Z.m ()
+                12.5%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                12.5%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+                12.5%    <a href="#m4">[4]</a>      1/1              2 R.m ()
+                12.5%    <a href="#m5">[5]</a>      1/1              2 S.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              8 (toplevel)
+[1]     50.0%                     2+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     12.5%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     12.5%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     12.5%                     1+0              2 R.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     12.5%                     1+0              2 S.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00  50.00   0.00  50.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 16 (100.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                    50.00                      50.00               main
+         8                    50.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         0                     0.00                      50.00               main
+         8                   100.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..0559a6a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeys
new file mode 100644
index 0000000..2bb68d7
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeys
@@ -0,0 +1,19 @@
+#    ____             ____       _________
+# __|A   |___________|B   |_____|Z        |_______
+#
+#         ___________       ____           ____
+# _______|Z          |_____|A   |_________|B   |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+4 2 Z
+2 1 B
+4 1 B
+4 2 A
+6 2 A
+4 1 Z
+8 1 Z
+6 2 B
+8 2 B
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..50b2b98
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> A.m ()
+        4   25.00 100.00  <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              8 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      2/2              4 A.m ()
+                25.0%    <a href="#m3">[3]</a>      2/2              4 B.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              8 (toplevel)
+[1]     50.0%                     2+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     25.0%                     2+0              4 A.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[3]     25.0%                     2+0              4 B.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00  50.00   0.00   0.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 16 (100.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                    50.00                      50.00               main
+         8                    50.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..f113fcf
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..e7456c1
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeys
@@ -0,0 +1,17 @@
+#    ____  ____  ____  ________  ____  ____  ____
+# __|A   ||Z   ||B   ||Z       ||D   ||Z   ||E   |__
+#
+0 1 A
+2 1 A
+2 1 Z
+4 1 Z
+4 1 B
+6 1 B
+6 1 Z
+10 1 Z
+10 1 D
+12 1 D
+12 1 Z
+14 1 Z
+14 1 E
+16 1 E
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..9349375
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   12.50  62.50  <a href="#m2">[2]</a> A.m ()
+        2   12.50  75.00  <a href="#m3">[3]</a> B.m ()
+        2   12.50  87.50  <a href="#m4">[4]</a> D.m ()
+        2   12.50 100.00  <a href="#m5">[5]</a> E.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      3/3              8 Z.m ()
+                12.5%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                12.5%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+                12.5%    <a href="#m4">[4]</a>      1/1              2 D.m ()
+                12.5%    <a href="#m5">[5]</a>      1/1              2 E.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      3/3              8 (toplevel)
+[1]     50.0%                     3+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     12.5%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     12.5%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     12.5%                     1+0              2 D.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     12.5%                     1+0              2 E.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       16  100.00 100.00  37.50  37.50   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 37.50% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 6 ( 37.50% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;E.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..09983ba
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeys
new file mode 100644
index 0000000..b51f81e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeys
@@ -0,0 +1,17 @@
+#    ____  ____  ____  ________  ____  ____  ____
+# __|R   ||Z   ||S   ||Z       ||R   ||Z   ||S   |__
+#
+0 1 R
+2 1 R
+2 1 Z
+4 1 Z
+4 1 S
+6 1 S
+6 1 Z
+10 1 Z
+10 1 R
+12 1 R
+12 1 Z
+14 1 Z
+14 1 S
+16 1 S
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..41f9625
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> R.m ()
+        4   25.00 100.00  <a href="#m3">[3]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      3/3              8 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      2/2              4 R.m ()
+                25.0%    <a href="#m3">[3]</a>      2/2              4 S.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      3/3              8 (toplevel)
+[1]     50.0%                     3+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     25.0%                     2+0              4 R.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[3]     25.0%                     2+0              4 S.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       16  100.00 100.00  75.00   0.00  75.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..2cccf07
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeys
new file mode 100644
index 0000000..d4e41a4
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeys
@@ -0,0 +1,16 @@
+#                                     ____
+#    ____  ____  ____  ________  ____|Z   |____
+# __|A   ||Z   ||B   ||Z       ||C             |__
+#
+0 1 A
+2 1 A
+2 1 Z
+4 1 Z
+4 1 B
+6 1 B
+6 1 Z
+10 1 Z
+10 1 C
+12 1  Z
+14 1  Z
+16 1 C
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..d81cccc
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysExpected
@@ -0,0 +1,216 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> C.m ()
+        2   12.50  87.50  <a href="#m3">[3]</a> A.m ()
+        2   12.50 100.00  <a href="#m4">[4]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                37.5%    <a href="#m2">[2]</a>      1/1              6 C.m ()
+                37.5%    <a href="#m1">[1]</a>      2/3              6 Z.m ()
+                12.5%    <a href="#m3">[3]</a>      1/1              2 A.m ()
+                12.5%    <a href="#m4">[4]</a>      1/1              2 B.m ()
+<a name="m1"></a>----------------------------------------------------
+                75.0%    <a href="#m0">[0]</a>      2/3              6 (toplevel)
+                25.0%    <a href="#m2">[2]</a>      1/3              2 C.m ()
+[1]     50.0%                     3+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              6 (toplevel)
+[2]     37.5%                     1+0              6 C.m ()
+                66.7%   excl                       4
+                33.3%    <a href="#m1">[1]</a>      1/3              2 Z.m ()
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     12.5%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     12.5%                     1+0              2 B.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       16  100.00 100.00  75.00   0.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..3f61656
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeys
new file mode 100644
index 0000000..0b3377d
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeys
@@ -0,0 +1,17 @@
+#    ____  ____  ____  ________  ____  ____  ____
+# __|A   ||Z   ||B   ||Z       ||A   ||Z   ||B   |__
+#
+0 1 A
+2 1 A
+2 1 Z
+4 1 Z
+4 1 B
+6 1 B
+6 1 Z
+10 1 Z
+10 1 A
+12 1 A
+12 1 Z
+14 1 Z
+14 1 B
+16 1 B
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..aa476b3
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> A.m ()
+        4   25.00 100.00  <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      3/3              8 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      2/2              4 A.m ()
+                25.0%    <a href="#m3">[3]</a>      2/2              4 B.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      3/3              8 (toplevel)
+[1]     50.0%                     3+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     25.0%                     2+0              4 A.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[3]     25.0%                     2+0              4 B.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       16  100.00 100.00  75.00   0.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..c6ddbe5
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..d87ac81
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeys
@@ -0,0 +1,19 @@
+#    ____                       ____  ________
+# __|A   |_____________________|B   ||Z       |__
+#
+#         ____  ________  ____
+# _______|D   ||Z       ||E   |______________
+#
+#
+0 1 A
+2 1 A
+0 2 D
+2 2 D
+2 2 Z
+6 2 Z
+6 2 E
+8 2 E
+2 1 B
+4 1 B
+4 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..a97f25c
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   12.50  62.50  <a href="#m2">[2]</a> A.m ()
+        2   12.50  75.00  <a href="#m3">[3]</a> B.m ()
+        2   12.50  87.50  <a href="#m4">[4]</a> D.m ()
+        2   12.50 100.00  <a href="#m5">[5]</a> E.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              8 Z.m ()
+                12.5%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                12.5%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+                12.5%    <a href="#m4">[4]</a>      1/1              2 D.m ()
+                12.5%    <a href="#m5">[5]</a>      1/1              2 E.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              8 (toplevel)
+[1]     50.0%                     2+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     12.5%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     12.5%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     12.5%                     1+0              2 D.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     12.5%                     1+0              2 E.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00   0.00 100.00   0.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                      33.33               main
+         0                     0.00                      66.67               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         0                     0.00                       0.00               main
+         8                   100.00                     100.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;E.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..832bbfc
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeys
new file mode 100644
index 0000000..82ab142
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeys
@@ -0,0 +1,19 @@
+#    ____                       ____  ________
+# __|R   |_____________________|S   ||Z       |__
+#
+#         ____  ________  ____
+# _______|R   ||Z       ||S   |______________
+#
+#
+0 1 R
+2 1 R
+0 2 R
+2 2 R
+2 2 Z
+6 2 Z
+6 2 S
+8 2 S
+2 1 S
+4 1 S
+4 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..623478e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> R.m ()
+        4   25.00 100.00  <a href="#m3">[3]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              8 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      2/2              4 R.m ()
+                25.0%    <a href="#m3">[3]</a>      2/2              4 S.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              8 (toplevel)
+[1]     50.0%                     2+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     25.0%                     2+0              4 R.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[3]     25.0%                     2+0              4 S.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00  50.00      1 main
+        8   50.00 100.00 100.00   0.00 100.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                      33.33               main
+         8                    66.67                      66.67               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                      33.33               main
+         8                    66.67                      66.67               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..371f150
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeys
new file mode 100644
index 0000000..511543f
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeys
@@ -0,0 +1,19 @@
+#    ____                       ____  ________
+# __|A   |_____________________|B   ||Z       |__
+#
+#         ____  ________  ____
+# _______|R   ||Z       ||S   |______________
+#
+#
+0 1 A
+2 1 A
+0 2 R
+2 2 R
+2 2 Z
+6 2 Z
+6 2 S
+8 2 S
+2 1 B
+4 1 B
+4 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..1193f5f
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   12.50  62.50  <a href="#m2">[2]</a> A.m ()
+        2   12.50  75.00  <a href="#m3">[3]</a> B.m ()
+        2   12.50  87.50  <a href="#m4">[4]</a> R.m ()
+        2   12.50 100.00  <a href="#m5">[5]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              8 Z.m ()
+                12.5%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                12.5%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+                12.5%    <a href="#m4">[4]</a>      1/1              2 R.m ()
+                12.5%    <a href="#m5">[5]</a>      1/1              2 S.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              8 (toplevel)
+[1]     50.0%                     2+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     12.5%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     12.5%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     12.5%                     1+0              2 R.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     12.5%                     1+0              2 S.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00 100.00   0.00 100.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                      33.33               main
+         8                    66.67                      66.67               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         0                     0.00                       0.00               main
+         8                   100.00                     100.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..9f87efc
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeys
new file mode 100644
index 0000000..6714ddd
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeys
@@ -0,0 +1,19 @@
+#    ____                       ____  ________
+# __|A   |_____________________|B   ||Z       |__
+#
+#         ____  ________  ____
+# _______|A   ||Z       ||B   |______________
+#
+#
+0 1 A
+2 1 A
+0 2 A
+2 2 A
+2 2 Z
+6 2 Z
+6 2 B
+8 2 B
+2 1 B
+4 1 B
+4 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..79c2e63
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> A.m ()
+        4   25.00 100.00  <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              8 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      2/2              4 A.m ()
+                25.0%    <a href="#m3">[3]</a>      2/2              4 B.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              8 (toplevel)
+[1]     50.0%                     2+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     25.0%                     2+0              4 A.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[3]     25.0%                     2+0              4 B.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00 100.00   0.00   0.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+        12                   100.00                      33.33               main
+         8                    66.67                      66.67               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..74e4c53
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..b92471f
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeys
@@ -0,0 +1,13 @@
+#    ____  ____  ____  ____  ____
+# __|A   ||D   ||E   ||B   ||Z   |__
+#
+0 1 A
+2 1 A
+2 1 D
+4 1 D
+4 1 E
+6 1 E
+6 1 B
+8 1 B
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..3b2ffc8
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        2   20.00  20.00  <a href="#m1">[1]</a> A.m ()
+        2   20.00  40.00  <a href="#m2">[2]</a> B.m ()
+        2   20.00  60.00  <a href="#m3">[3]</a> D.m ()
+        2   20.00  80.00  <a href="#m4">[4]</a> E.m ()
+        2   20.00 100.00  <a href="#m5">[5]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             10 (toplevel)
+                 0.0%   excl                       0
+                20.0%    <a href="#m1">[1]</a>      1/1              2 A.m ()
+                20.0%    <a href="#m2">[2]</a>      1/1              2 B.m ()
+                20.0%    <a href="#m3">[3]</a>      1/1              2 D.m ()
+                20.0%    <a href="#m4">[4]</a>      1/1              2 E.m ()
+                20.0%    <a href="#m5">[5]</a>      1/1              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[1]     20.0%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     20.0%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     20.0%                     1+0              2 D.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     20.0%                     1+0              2 E.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     20.0%                     1+0              2 Z.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       10  100.00 100.00  80.00  40.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 4 ( 40.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         4                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;60.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;60.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;E.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..c9c086c
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeys
new file mode 100644
index 0000000..27b2bf8
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeys
@@ -0,0 +1,13 @@
+#    ____  ____  ____  ____  ____
+# __|R   ||R   ||S   ||S   ||Z   |__
+#
+0 1 R
+2 1 R
+2 1 R
+4 1 R
+4 1 S
+6 1 S
+6 1 S
+8 1 S
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..df55cd4
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   40.00  40.00  <a href="#m1">[1]</a> R.m ()
+        4   40.00  80.00  <a href="#m2">[2]</a> S.m ()
+        2   20.00 100.00  <a href="#m3">[3]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             10 (toplevel)
+                 0.0%   excl                       0
+                40.0%    <a href="#m1">[1]</a>      2/2              4 R.m ()
+                40.0%    <a href="#m2">[2]</a>      2/2              4 S.m ()
+                20.0%    <a href="#m3">[3]</a>      1/1              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[1]     40.0%                     2+0              4 R.m ()
+               100.0%   excl                       4
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     40.0%                     2+0              4 S.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     20.0%                     1+0              2 Z.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       10  100.00 100.00  80.00   0.00  80.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;S.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..0afca4d
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeys
new file mode 100644
index 0000000..a494716
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeys
@@ -0,0 +1,11 @@
+#    ____  ____  ____  ____
+# __|A   ||C   ||B   ||Z   |__
+#
+0 1 A
+2 1 A
+2 1 C
+4 1 C
+4 1 B
+6 1 B
+6 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..720d05a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysExpected
@@ -0,0 +1,214 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        2   25.00  25.00  <a href="#m1">[1]</a> A.m ()
+        2   25.00  50.00  <a href="#m2">[2]</a> B.m ()
+        2   25.00  75.00  <a href="#m3">[3]</a> C.m ()
+        2   25.00 100.00  <a href="#m4">[4]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0              8 (toplevel)
+                 0.0%   excl                       0
+                25.0%    <a href="#m1">[1]</a>      1/1              2 A.m ()
+                25.0%    <a href="#m2">[2]</a>      1/1              2 B.m ()
+                25.0%    <a href="#m3">[3]</a>      1/1              2 C.m ()
+                25.0%    <a href="#m4">[4]</a>      1/1              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[1]     25.0%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     25.0%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     25.0%                     1+0              2 C.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     25.0%                     1+0              2 Z.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8  100.00 100.00  75.00   0.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..c5f9a3e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeys
new file mode 100644
index 0000000..bd645af
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeys
@@ -0,0 +1,13 @@
+#    ____  ____  ____  ____  ____
+# __|A   ||A   ||B   ||B   ||Z   |__
+#
+0 1 A
+2 1 A
+2 1 A
+4 1 A
+4 1 B
+6 1 B
+6 1 B
+8 1 B
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..0e8f300
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   40.00  40.00  <a href="#m1">[1]</a> A.m ()
+        4   40.00  80.00  <a href="#m2">[2]</a> B.m ()
+        2   20.00 100.00  <a href="#m3">[3]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             10 (toplevel)
+                 0.0%   excl                       0
+                40.0%    <a href="#m1">[1]</a>      2/2              4 A.m ()
+                40.0%    <a href="#m2">[2]</a>      2/2              4 B.m ()
+                20.0%    <a href="#m3">[3]</a>      1/1              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[1]     40.0%                     2+0              4 A.m ()
+               100.0%   excl                       4
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     40.0%                     2+0              4 B.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     20.0%                     1+0              2 Z.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       10  100.00 100.00  80.00   0.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..65e381a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairCrossThread b/tools/dmtracedump/tests/filters/testWaitingPairCrossThread
new file mode 100644
index 0000000..6c93bc6
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairCrossThread
@@ -0,0 +1,14 @@
+#    ____        ____  ____
+# __|A   |______|B   ||Z   |__
+#
+#          _____
+# ________|Z    |_________________
+#
+0 1 A
+2 1 A
+0 2 Z
+2 2 Z
+2 1 B
+4 1 B
+4 1 Z
+6 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadExpected b/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadExpected
new file mode 100644
index 0000000..ed45fff
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   25.00  75.00  <a href="#m2">[2]</a> A.m ()
+        2   25.00 100.00  <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0              8 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              4 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                25.0%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[1]     50.0%                     2+0              4 Z.m ()
+               100.0%   excl                       4
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     25.0%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     25.0%                     1+0              2 B.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        6   75.00  75.00  66.67   0.00   0.00      1 main
+        2   25.00 100.00   0.00   0.00   0.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                      66.67               main
+         0                     0.00                      33.33               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadTrace b/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadTrace
new file mode 100644
index 0000000..4e53dfd
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairSingleThread b/tools/dmtracedump/tests/filters/testWaitingPairSingleThread
new file mode 100644
index 0000000..45375ca
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairSingleThread
@@ -0,0 +1,11 @@
+#    ____  ____  ____  ____
+# __|A   ||Z   ||B   ||Z   |__
+#
+0 1 A
+2 1 A
+2 1 Z
+4 1 Z
+4 1 B
+6 1 B
+6 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadExpected b/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadExpected
new file mode 100644
index 0000000..b3e2b3f
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   25.00  75.00  <a href="#m2">[2]</a> A.m ()
+        2   25.00 100.00  <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0              8 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      2/2              4 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                25.0%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[1]     50.0%                     2+0              4 Z.m ()
+               100.0%   excl                       4
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     25.0%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     25.0%                     1+0              2 B.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8  100.00 100.00  75.00   0.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadTrace b/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadTrace
new file mode 100644
index 0000000..3f29843
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..05995f3
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeys
@@ -0,0 +1,23 @@
+#    ____             ____  ____      ____
+# __|A   |___________|B   ||Z   |____|Z   |_______
+#
+#         ____  ____             ____      ____
+# _______|Z   ||D   |___________|E   |____|Z   |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+2 2 Z
+2 2 D
+4 2 D
+2 1 B
+4 1 B
+4 1 Z
+6 1 Z
+4 2 E
+6 2 E
+6 1 Z
+8 1 Z
+6 2 Z
+8 2 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..ba83cee
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   12.50  62.50  <a href="#m2">[2]</a> A.m ()
+        2   12.50  75.00  <a href="#m3">[3]</a> B.m ()
+        2   12.50  87.50  <a href="#m4">[4]</a> D.m ()
+        2   12.50 100.00  <a href="#m5">[5]</a> E.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      4/4              8 Z.m ()
+                12.5%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                12.5%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+                12.5%    <a href="#m4">[4]</a>      1/1              2 D.m ()
+                12.5%    <a href="#m5">[5]</a>      1/1              2 E.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      4/4              8 (toplevel)
+[1]     50.0%                     4+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     12.5%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     12.5%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     12.5%                     1+0              2 D.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     12.5%                     1+0              2 E.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00   0.00  50.00   0.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                      50.00               main
+         0                     0.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         0                     0.00                      50.00               main
+         8                   100.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;E.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..30fbe38
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeys
new file mode 100644
index 0000000..f874464
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeys
@@ -0,0 +1,23 @@
+#    ____             ____  ____      ____
+# __|R   |___________|S   ||Z   |____|Z   |_______
+#
+#         ____  ____             ____      ____
+# _______|Z   ||R   |___________|S   |____|Z   |__
+#
+#
+0 1 R
+2 1 R
+0 2 Z
+2 2 Z
+2 2 R
+4 2 R
+2 1 S
+4 1 S
+4 1 Z
+6 1 Z
+4 2 S
+6 2 S
+6 1 Z
+8 1 Z
+6 2 Z
+8 2 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..93c4a05
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> R.m ()
+        4   25.00 100.00  <a href="#m3">[3]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      4/4              8 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      2/2              4 R.m ()
+                25.0%    <a href="#m3">[3]</a>      2/2              4 S.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      4/4              8 (toplevel)
+[1]     50.0%                     4+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     25.0%                     2+0              4 R.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[3]     25.0%                     2+0              4 S.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00  50.00      1 main
+        8   50.00 100.00  50.00   0.00  50.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                    66.67                      50.00               main
+         8                    66.67                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                    66.67                      50.00               main
+         8                    66.67                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..6dc1826
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeys
new file mode 100644
index 0000000..bdc4373
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeys
@@ -0,0 +1,23 @@
+#    ____             ____  ____      ____
+# __|A   |___________|B   ||Z   |____|Z   |_______
+#
+#         ____  ____             ____      ____
+# _______|Z   ||R   |___________|S   |____|Z   |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+2 2 Z
+2 2 R
+4 2 R
+2 1 B
+4 1 B
+4 1 Z
+6 1 Z
+4 2 S
+6 2 S
+6 1 Z
+8 1 Z
+6 2 Z
+8 2 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..b154ed1
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        2   12.50  62.50  <a href="#m2">[2]</a> A.m ()
+        2   12.50  75.00  <a href="#m3">[3]</a> B.m ()
+        2   12.50  87.50  <a href="#m4">[4]</a> R.m ()
+        2   12.50 100.00  <a href="#m5">[5]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      4/4              8 Z.m ()
+                12.5%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                12.5%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+                12.5%    <a href="#m4">[4]</a>      1/1              2 R.m ()
+                12.5%    <a href="#m5">[5]</a>      1/1              2 S.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      4/4              8 (toplevel)
+[1]     50.0%                     4+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     12.5%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     12.5%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     12.5%                     1+0              2 R.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     12.5%                     1+0              2 S.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00  50.00   0.00  50.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                    66.67                      50.00               main
+         8                    66.67                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         0                     0.00                      50.00               main
+         8                   100.00                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..efb0b1b
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeys
new file mode 100644
index 0000000..552ae40
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeys
@@ -0,0 +1,23 @@
+#    ____             ____  ____      ____
+# __|A   |___________|B   ||Z   |____|Z   |_______
+#
+#         ____  ____             ____      ____
+# _______|Z   ||A   |___________|B   |____|Z   |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+2 2 Z
+2 2 A
+4 2 A
+2 1 B
+4 1 B
+4 1 Z
+6 1 Z
+4 2 B
+6 2 B
+6 1 Z
+8 1 Z
+6 2 Z
+8 2 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..82b0356
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        8   50.00  50.00  <a href="#m1">[1]</a> Z.m ()
+        4   25.00  75.00  <a href="#m2">[2]</a> A.m ()
+        4   25.00 100.00  <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             16 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      4/4              8 Z.m ()
+                25.0%    <a href="#m2">[2]</a>      2/2              4 A.m ()
+                25.0%    <a href="#m3">[3]</a>      2/2              4 B.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      4/4              8 (toplevel)
+[1]     50.0%                     4+0              8 Z.m ()
+               100.0%   excl                       8
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     25.0%                     2+0              4 A.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[3]     25.0%                     2+0              4 B.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8   50.00  50.00  50.00   0.00   0.00      1 main
+        8   50.00 100.00  50.00   0.00   0.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                    66.67                      50.00               main
+         8                    66.67                      50.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..497e925
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..edf03c5
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeys
@@ -0,0 +1,13 @@
+#    ____  ____  ____  ____  ____
+# __|A   ||D   ||B   ||E   ||Z   |__
+#
+0 1 A
+2 1 A
+2 1 D
+4 1 D
+4 1 B
+6 1 B
+6 1 E
+8 1 E
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..2d59720
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        2   20.00  20.00  <a href="#m1">[1]</a> A.m ()
+        2   20.00  40.00  <a href="#m2">[2]</a> B.m ()
+        2   20.00  60.00  <a href="#m3">[3]</a> D.m ()
+        2   20.00  80.00  <a href="#m4">[4]</a> E.m ()
+        2   20.00 100.00  <a href="#m5">[5]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             10 (toplevel)
+                 0.0%   excl                       0
+                20.0%    <a href="#m1">[1]</a>      1/1              2 A.m ()
+                20.0%    <a href="#m2">[2]</a>      1/1              2 B.m ()
+                20.0%    <a href="#m3">[3]</a>      1/1              2 D.m ()
+                20.0%    <a href="#m4">[4]</a>      1/1              2 E.m ()
+                20.0%    <a href="#m5">[5]</a>      1/1              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[1]     20.0%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     20.0%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     20.0%                     1+0              2 D.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     20.0%                     1+0              2 E.m ()
+               100.0%   excl                       2
+<a name="m5"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[5]     20.0%                     1+0              2 Z.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       10  100.00 100.00  60.00  60.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 60.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 6 ( 60.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;60.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;60.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;E.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..b9afef4
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeys
new file mode 100644
index 0000000..27b2bf8
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeys
@@ -0,0 +1,13 @@
+#    ____  ____  ____  ____  ____
+# __|R   ||R   ||S   ||S   ||Z   |__
+#
+0 1 R
+2 1 R
+2 1 R
+4 1 R
+4 1 S
+6 1 S
+6 1 S
+8 1 S
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..df55cd4
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   40.00  40.00  <a href="#m1">[1]</a> R.m ()
+        4   40.00  80.00  <a href="#m2">[2]</a> S.m ()
+        2   20.00 100.00  <a href="#m3">[3]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             10 (toplevel)
+                 0.0%   excl                       0
+                40.0%    <a href="#m1">[1]</a>      2/2              4 R.m ()
+                40.0%    <a href="#m2">[2]</a>      2/2              4 S.m ()
+                20.0%    <a href="#m3">[3]</a>      1/1              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[1]     40.0%                     2+0              4 R.m ()
+               100.0%   excl                       4
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     40.0%                     2+0              4 S.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     20.0%                     1+0              2 Z.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       10  100.00 100.00  80.00   0.00  80.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;S.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..a52929a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeys
new file mode 100644
index 0000000..c53c90d
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeys
@@ -0,0 +1,12 @@
+#               ____
+#    ____  ____|B   |____  ____
+# __|A   ||C             ||Z   |__
+#
+0 1 A
+2 1 A
+2 1 C
+4 1  B
+6 1  B
+8 1 C
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..18ce892
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysExpected
@@ -0,0 +1,214 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   40.00  40.00  <a href="#m1">[1]</a> C.m ()
+        2   20.00  60.00  <a href="#m2">[2]</a> A.m ()
+        2   20.00  80.00  <a href="#m3">[3]</a> B.m ()
+        2   20.00 100.00  <a href="#m4">[4]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             10 (toplevel)
+                 0.0%   excl                       0
+                60.0%    <a href="#m1">[1]</a>      1/1              6 C.m ()
+                20.0%    <a href="#m2">[2]</a>      1/1              2 A.m ()
+                20.0%    <a href="#m4">[4]</a>      1/1              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              6 (toplevel)
+[1]     60.0%                     1+0              6 C.m ()
+                66.7%   excl                       4
+                33.3%    <a href="#m3">[3]</a>      1/1              2 B.m ()
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[2]     20.0%                     1+0              2 A.m ()
+               100.0%   excl                       2
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m1">[1]</a>      1/1              2 C.m ()
+[3]     20.0%                     1+0              2 B.m ()
+               100.0%   excl                       2
+<a name="m4"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[4]     20.0%                     1+0              2 Z.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       10  100.00 100.00  80.00   0.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;60.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;60.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..23f4187
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeys
new file mode 100644
index 0000000..bd645af
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeys
@@ -0,0 +1,13 @@
+#    ____  ____  ____  ____  ____
+# __|A   ||A   ||B   ||B   ||Z   |__
+#
+0 1 A
+2 1 A
+2 1 A
+4 1 A
+4 1 B
+6 1 B
+6 1 B
+8 1 B
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..0e8f300
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   40.00  40.00  <a href="#m1">[1]</a> A.m ()
+        4   40.00  80.00  <a href="#m2">[2]</a> B.m ()
+        2   20.00 100.00  <a href="#m3">[3]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0             10 (toplevel)
+                 0.0%   excl                       0
+                40.0%    <a href="#m1">[1]</a>      2/2              4 A.m ()
+                40.0%    <a href="#m2">[2]</a>      2/2              4 B.m ()
+                20.0%    <a href="#m3">[3]</a>      1/1              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[1]     40.0%                     2+0              4 A.m ()
+               100.0%   excl                       4
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     40.0%                     2+0              4 B.m ()
+               100.0%   excl                       4
+<a name="m3"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              2 (toplevel)
+[3]     20.0%                     1+0              2 Z.m ()
+               100.0%   excl                       2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+       10  100.00 100.00  80.00   0.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         8                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..01e95cd
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloCrossThread b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThread
new file mode 100644
index 0000000..c9dbd1a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThread
@@ -0,0 +1,12 @@
+#    ____       ____  ____
+# __|C              ||Z   |__
+#
+#          ____
+# ________|Z   |_____________
+#
+0 1 C
+0 2 Z
+2 2 Z
+4 1 C
+4 1 Z
+6 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadExpected b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadExpected
new file mode 100644
index 0000000..409b17e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadExpected
@@ -0,0 +1,192 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   50.00  50.00  <a href="#m1">[1]</a> C.m ()
+        4   50.00 100.00  <a href="#m2">[2]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0              8 (toplevel)
+                 0.0%   excl                       0
+                50.0%    <a href="#m1">[1]</a>      1/1              4 C.m ()
+                50.0%    <a href="#m2">[2]</a>      2/2              4 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              4 (toplevel)
+[1]     50.0%                     1+0              4 C.m ()
+               100.0%   excl                       4
+<a name="m2"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      2/2              4 (toplevel)
+[2]     50.0%                     2+0              4 Z.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        6   75.00  75.00  66.67   0.00   0.00      1 main
+        2   25.00 100.00   0.00   0.00   0.00      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                      66.67               main
+         0                     0.00                      33.33               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadTrace b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadTrace
new file mode 100644
index 0000000..e73f040
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloSingleThread b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThread
new file mode 100644
index 0000000..3f0753e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThread
@@ -0,0 +1,10 @@
+#         _____
+#    ____|Z    |____  ____
+# __|C              ||Z   |__
+#
+0 1 C
+2 1  Z
+4 1  Z
+6 1 C
+6 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadExpected b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadExpected
new file mode 100644
index 0000000..8d22b63
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadExpected
@@ -0,0 +1,194 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+    obj=document.getElementById(item);
+    visible=(obj.style.display!="none" && obj.style.display!="");
+    key=document.getElementById("x" + item);
+    if (visible) {
+        obj.style.display="none";
+        key.innerHTML="+";
+    } else {
+        obj.style.display="block";
+        key.innerHTML="-";
+    }
+}
+function onMouseOver(obj) {
+    obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+    obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+  <li><a href="#exclusive">Exclusive profile</a></li>
+  <li><a href="#inclusive">Inclusive profile</a></li>
+  <li><a href="#thread">Thread profile</a></li>
+  <li><a href="#class">Class/method profile</a></li>
+  <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+    Usecs  self %  sum %  Method
+        4   50.00  50.00  <a href="#m1">[1]</a> C.m ()
+        4   50.00 100.00  <a href="#m2">[2]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index  %/total %/self  index     calls         usecs name
+<a name="m0"></a>----------------------------------------------------
+[0]    100.0%                     0+0              8 (toplevel)
+                 0.0%   excl                       0
+                75.0%    <a href="#m1">[1]</a>      1/1              6 C.m ()
+                25.0%    <a href="#m2">[2]</a>      1/2              2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+               100.0%    <a href="#m0">[0]</a>      1/1              6 (toplevel)
+[1]     75.0%                     1+0              6 C.m ()
+                66.7%   excl                       4
+                33.3%    <a href="#m2">[2]</a>      1/2              2 Z.m ()
+<a name="m2"></a>----------------------------------------------------
+                50.0%    <a href="#m0">[0]</a>      1/2              2 (toplevel)
+                50.0%    <a href="#m1">[1]</a>      1/2              2 C.m ()
+[2]     50.0%                     2+0              4 Z.m ()
+               100.0%   excl                       4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+    Usecs   self %  sum %  FirstFilter %  SecondFilter %  RepeatedFilter %  tid   ThreadName
+        8  100.00 100.00  75.00   0.00   0.00      1 main
+        0    0.00 100.00    nan    nan    nan      2 foo
+        0    0.00 100.00    nan    nan    nan      3 bar
+        0    0.00 100.00    nan    nan    nan      4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details: 
+
+ Waiting cycles    % of total waiting time   execution time while waiting    thread name
+         6                   100.00                     100.00               main
+         0                     0.00                       0.00               foo
+         0                     0.00                       0.00               bar
+         0                     0.00                       0.00               blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 (  0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadTrace b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadTrace
new file mode 100644
index 0000000..3a43c46
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadTrace
Binary files differ
diff --git a/tools/gclog.py b/tools/gclog.py
new file mode 100755
index 0000000..d008f6a
--- /dev/null
+++ b/tools/gclog.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python
+#
+# 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.
+
+#
+# Parse event log output, looking for GC events.  Format them for human
+# consumption.
+#
+# ALL OUTPUT VALUES ARE APPROXIMATE.  The event log data format uses a
+# 12-bit floating-point representation, which means there aren't enough
+# bits to accurately represent anything but small integers.  Larger
+# values will be rounded off.
+#
+# The data is generated by dalvik/vm/alloc/HeapDebug.c.
+#
+
+import getopt
+import sys
+import os
+import re
+import time
+
+DEBUG = False       # DEBUG is a global variable
+
+
+def unfloat12(f12):
+    """Unpack a float12 value"""
+    if f12 < 0:
+        raise DataParseError, "bad float12 value %s" % f12
+    return (f12 & 0x1ff) << ((f12 >> 9) * 4)
+
+
+def parseGlobalInfo(value):
+    """Parse event0 (global info)"""
+    value = int(value)
+
+    # Global information:
+    #
+    # [63   ] Must be zero
+    # [62-24] ASCII process identifier
+    # [23-12] GC time in ms
+    # [11- 0] Bytes freed
+    id = (value >> 24) & 0xffffffffff
+    gctime = unfloat12((value >> 12) & 0xfff)
+    bytes_freed = unfloat12(value & 0xfff)
+
+    idstr = "%c%c%c%c%c" % ( \
+            (id >> 32) & 0xff, \
+            (id >> 24) & 0xff, \
+            (id >> 16) & 0xff, \
+            (id >> 8) & 0xff, \
+            id & 0xff )
+
+    return ( idstr, gctime, bytes_freed )
+
+
+def parseAggHeapStats(value):
+    """Parse event1 (aggregated heap stats)"""
+    value = int(value)
+
+    # Aggregated heap stats:
+    #
+    # [63-62] 10
+    # [61-60] Reserved; must be zero
+    # [59-48] Objects freed
+    # [47-36] Actual size (current footprint)
+    # [35-24] Allowed size (current hard max)
+    # [23-12] Objects allocated
+    # [11- 0] Bytes allocated
+    freed = unfloat12((value >> 48) & 0xfff)
+    footprint = unfloat12((value >> 36) & 0xfff)
+    allowed = unfloat12((value >> 24) & 0xfff)
+    objs = unfloat12((value >> 12) & 0xfff)
+    bytes = unfloat12(value & 0xfff)
+
+    return ( freed, footprint, allowed, objs, bytes )
+
+
+def parseZygoteStats(value):
+    """Parse event2 (zygote heap stats)"""
+    value = int(value)
+
+    # Zygote heap stats (except for the soft limit, which belongs to the
+    # active heap):
+    #
+    # [63-62] 11
+    # [61-60] Reserved; must be zero
+    # [59-48] Soft Limit (for the active heap)
+    # [47-36] Actual size (current footprint)
+    # [35-24] Allowed size (current hard max)
+    # [23-12] Objects allocated
+    # [11- 0] Bytes allocated
+    soft_limit = unfloat12((value >> 48) & 0xfff)
+    actual = unfloat12((value >> 36) & 0xfff)
+    allowed = unfloat12((value >> 24) & 0xfff)
+    objs = unfloat12((value >> 12) & 0xfff)
+    bytes = unfloat12(value & 0xfff)
+
+    return ( soft_limit, actual, allowed, objs, bytes )
+
+
+def parseExternalStats(value):
+    """Parse event3 (external allocation stats)"""
+    value = int(value)
+
+    # Report the current external allocation stats and the native heap
+    # summary.
+    #
+    # [63-48] Reserved; must be zero (TODO: put new data in these slots)
+    # [47-36] dlmalloc_footprint
+    # [35-24] mallinfo: total allocated space
+    # [23-12] External byte limit
+    # [11- 0] External bytes allocated
+    footprint = unfloat12((value >> 36) & 0xfff)    # currently disabled
+    total = unfloat12((value >> 24) & 0xfff)        # currently disabled
+    limit = unfloat12((value >> 12) & 0xfff)
+    bytes = unfloat12(value & 0xfff)
+
+    return ( footprint, total, limit, bytes )
+
+
+def handleGcInfo(procFilter, timestamp, pid, values):
+    """Handle a single dvm_gc_info event"""
+
+    pid = int(pid)
+
+    global_info = parseGlobalInfo(values[0])
+
+    if len(procFilter) > 0:
+        if global_info[0] != procFilter:
+            return
+
+    heap_stats = parseAggHeapStats(values[1])
+    zygote = parseZygoteStats(values[2])
+    external = parseExternalStats(values[3])
+
+    print "%s %s(%d) softlim=%dKB, extlim=%dKB, extalloc=%dKB" % \
+            (timestamp, global_info[0], pid, zygote[0]/1024, external[2]/1024, external[3]/1024)
+
+    if DEBUG:
+        # print "RAW: %s %s (%s,%s,%s,%s)" % \
+        #        (timestamp, pid, values[0], values[1], values[2], values[3])
+
+        print "+ id=\"%s\" time=%d freed=%d" % (global_info[0], global_info[1], global_info[2])
+        print "+  freed=%d foot=%d allow=%d objs=%d bytes=%d" % \
+                (heap_stats[0], heap_stats[1], heap_stats[2], heap_stats[3], heap_stats[4])
+        print "+  soft=%d act=%d allow=%d objs=%d bytes=%d" % \
+                (zygote[0], zygote[1], zygote[2], zygote[3], zygote[4])
+        print "+  foot=%d total=%d limit=%d alloc=%d" % \
+                (external[0], external[1], external[2], external[3])
+
+    print "  freed %d objects / %d bytes in %dms" % \
+            (heap_stats[0], global_info[2], global_info[1])
+
+
+def filterInput(logPipe, processFilter):
+    """Loop until EOF, pulling out GC events"""
+
+    # 04-29 20:31:00.334 I/dvm_gc_info(   69): [8320808730292729543,-8916699241518090181,-4006371297196337158,8165229]
+    gc_info_re = re.compile(r"""
+        (\d+-\d+\ \d+:\d+:\d+)\.\d+     # extract the date (#1), ignoring ms
+        .*                              # filler, usually " I/"
+        dvm_gc_info                     # only interested in GC info lines
+        \(\s*(\d+)\)                    # extract the pid (#2)
+        :\ \[                           # filler
+        ([0-9-]+),([0-9-]+),([0-9-]+),([0-9-]+) # four values, may be negative
+        \].*                            # junk to end of line
+        """, re.VERBOSE)
+
+    while True:
+        line = logPipe.readline()
+        if not line:
+            print "EOF hit"
+            return
+
+        match = gc_info_re.match(line)
+        if not match:
+            #print "no match on %s" % line.strip()
+            continue
+        else:
+            handleGcInfo(processFilter, match.group(1), match.group(2), ( match.group(3), \
+                    match.group(4), match.group(5), match.group(6) ) )
+
+def PrintUsage():
+  print "usage: %s [-p procFilter] [-d]" % sys.argv[0]
+
+
+def start():
+    """Entry point"""
+
+    global DEBUG
+
+    procFilter = ""
+
+    opts, args = getopt.getopt(sys.argv[1:], "hdp:")
+
+    for opt, val in opts:
+        if opt == "-h":
+            PrintUsage()
+            sys.exit(2)
+        elif opt == "-p":
+            procFilter = val
+        elif opt == "-d":
+            DEBUG = True
+
+    print "procfilter = %s" % procFilter
+    print "DEBUG = %s" % DEBUG
+
+    # launch a logcat and read from it
+    command = 'adb logcat -v time -b events'
+    logPipe = os.popen(command)
+
+
+    try:
+        filterInput(logPipe, procFilter)
+    except KeyboardInterrupt, err:
+        print "Stopping on keyboard interrupt."
+
+    logPipe.close()
+
+
+start()
diff --git a/tools/gdbjithelper/Android.mk b/tools/gdbjithelper/Android.mk
new file mode 100644
index 0000000..087bc8f
--- /dev/null
+++ b/tools/gdbjithelper/Android.mk
@@ -0,0 +1,22 @@
+# 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
+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/tools/get-hprof b/tools/get-hprof
new file mode 100755
index 0000000..d56d7ad
--- /dev/null
+++ b/tools/get-hprof
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# 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.
+
+# Grab an hprof file using adb. If an argument is specified, grab
+# the so-named file. If no argument is specified, grab the last such file
+# as found by using "ls".
+
+FILE_BASE="$1"
+if [ "x$FILE_BASE" = "x" ]; then
+    # Note: substr() is to get rid of the final carriage return.
+    FILE_BASE=`adb shell ls -l '/data/misc/heap-dump*.hprof' | tail -1 | \
+	awk '{ printf("%s", substr($7, 1, length($7) - 1)); }'`
+    if [ "x$FILE_BASE" = "x" ]; then
+        echo "No file base defined."
+        exit 1
+    fi
+fi
+
+FILE_BASE=/data/misc/${FILE_BASE}
+OUT_FILE=heap-dump.hprof
+
+adb pull "$FILE_BASE" "$OUT_FILE"
+if [ $? -ne 0 ]; then
+    echo "Failed pulling $FILE_BASE."
+    exit 1
+fi
+echo "hat $OUT_FILE"
+exit 0
diff --git a/tools/hprof-conv/Android.mk b/tools/hprof-conv/Android.mk
new file mode 100644
index 0000000..dad399e
--- /dev/null
+++ b/tools/hprof-conv/Android.mk
@@ -0,0 +1,21 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := HprofConv.c
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := hprof-conv
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/hprof-conv/HprofConv.c b/tools/hprof-conv/HprofConv.c
new file mode 100644
index 0000000..02cb7f4
--- /dev/null
+++ b/tools/hprof-conv/HprofConv.c
@@ -0,0 +1,718 @@
+/*
+ * 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.
+ */
+
+/*
+ * Strip Android-specific records out of hprof data, back-converting from
+ * 1.0.3 to 1.0.2.  This removes some useful information, but allows
+ * Android hprof data to be handled by widely-available tools (like "jhat").
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+
+//#define VERBOSE_DEBUG
+#ifdef VERBOSE_DEBUG
+# define DBUG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DBUG(...)
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+# define TRUE (!FALSE)
+#endif
+
+typedef enum HprofBasicType {
+    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,
+} HprofBasicType;
+
+typedef enum HprofTag {
+    /* tags we must handle specially */
+    HPROF_TAG_HEAP_DUMP                 = 0x0c,
+    HPROF_TAG_HEAP_DUMP_SEGMENT         = 0x1c,
+} HprofTag;
+
+typedef enum HprofHeapTag {
+    /* 1.0.2 tags */
+    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 1.0.3 tags */
+    HPROF_HEAP_DUMP_INFO                = 0xfe,
+    HPROF_ROOT_INTERNED_STRING          = 0x89,
+    HPROF_ROOT_FINALIZING               = 0x8a,
+    HPROF_ROOT_DEBUGGER                 = 0x8b,
+    HPROF_ROOT_REFERENCE_CLEANUP        = 0x8c,
+    HPROF_ROOT_VM_INTERNAL              = 0x8d,
+    HPROF_ROOT_JNI_MONITOR              = 0x8e,
+    HPROF_UNREACHABLE                   = 0x90,  /* deprecated */
+    HPROF_PRIMITIVE_ARRAY_NODATA_DUMP   = 0xc3,
+} HprofHeapTag;
+
+#define kIdentSize  4
+#define kRecHdrLen  9
+
+
+/*
+ * ===========================================================================
+ *      Expanding buffer
+ * ===========================================================================
+ */
+
+/* simple struct */
+typedef struct {
+    unsigned char* storage;
+    size_t curLen;
+    size_t maxLen;
+} ExpandBuf;
+
+/*
+ * Create an ExpandBuf.
+ */
+static ExpandBuf* ebAlloc(void)
+{
+    static const int kInitialSize = 64;
+
+    ExpandBuf* newBuf = (ExpandBuf*) malloc(sizeof(ExpandBuf));
+    if (newBuf == NULL)
+        return NULL;
+    newBuf->storage = (unsigned char*) malloc(kInitialSize);
+    newBuf->curLen = 0;
+    newBuf->maxLen = kInitialSize;
+
+    return newBuf;
+}
+
+/*
+ * Release the storage associated with an ExpandBuf.
+ */
+static void ebFree(ExpandBuf* pBuf)
+{
+    if (pBuf != NULL) {
+        free(pBuf->storage);
+        free(pBuf);
+    }
+}
+
+/*
+ * Return a pointer to the data buffer.
+ *
+ * The pointer may change as data is added to the buffer, so this value
+ * should not be cached.
+ */
+static inline unsigned char* ebGetBuffer(ExpandBuf* pBuf)
+{
+    return pBuf->storage;
+}
+
+/*
+ * Get the amount of data currently in the buffer.
+ */
+static inline size_t ebGetLength(ExpandBuf* pBuf)
+{
+    return pBuf->curLen;
+}
+
+/*
+ * Empty the buffer.
+ */
+static void ebClear(ExpandBuf* pBuf)
+{
+    pBuf->curLen = 0;
+}
+
+/*
+ * Ensure that the buffer can hold at least "size" additional bytes.
+ */
+static int ebEnsureCapacity(ExpandBuf* pBuf, int size)
+{
+    assert(size > 0);
+
+    if (pBuf->curLen + size > pBuf->maxLen) {
+        int newSize = pBuf->curLen + size + 128;    /* oversize slightly */
+        unsigned char* newStorage = realloc(pBuf->storage, newSize);
+        if (newStorage == NULL) {
+            fprintf(stderr, "ERROR: realloc failed on size=%d\n", newSize);
+            return -1;
+        }
+
+        pBuf->storage = newStorage;
+        pBuf->maxLen = newSize;
+    }
+
+    assert(pBuf->curLen + size <= pBuf->maxLen);
+    return 0;
+}
+
+/*
+ * Add data to the buffer after ensuring it can hold it.
+ */
+static int ebAddData(ExpandBuf* pBuf, const void* data, size_t count)
+{
+    ebEnsureCapacity(pBuf, count);
+    memcpy(pBuf->storage + pBuf->curLen, data, count);
+    pBuf->curLen += count;
+    return 0;
+}
+
+/*
+ * Read a NULL-terminated string from the input.
+ */
+static int ebReadString(ExpandBuf* pBuf, FILE* in)
+{
+    int ic;
+
+    do {
+        ebEnsureCapacity(pBuf, 1);
+
+        ic = getc(in);
+        if (feof(in) || ferror(in)) {
+            fprintf(stderr, "ERROR: failed reading input\n");
+            return -1;
+        }
+
+        pBuf->storage[pBuf->curLen++] = (unsigned char) ic;
+    } while (ic != 0);
+
+    return 0;
+}
+
+/*
+ * Read some data, adding it to the expanding buffer.
+ *
+ * This will ensure that the buffer has enough space to hold the new data
+ * (plus the previous contents).
+ */
+static int ebReadData(ExpandBuf* pBuf, FILE* in, size_t count, int eofExpected)
+{
+    size_t actual;
+
+    assert(count > 0);
+
+    ebEnsureCapacity(pBuf, count);
+    actual = fread(pBuf->storage + pBuf->curLen, 1, count, in);
+    if (actual != count) {
+        if (eofExpected && feof(in) && !ferror(in)) {
+            /* return without reporting an error */
+        } else {
+            fprintf(stderr, "ERROR: read %d of %d bytes\n", actual, count);
+            return -1;
+        }
+    }
+
+    pBuf->curLen += count;
+    assert(pBuf->curLen <= pBuf->maxLen);
+
+    return 0;
+}
+
+/*
+ * Write the data from the buffer.  Resets the data count to zero.
+ */
+static int ebWriteData(ExpandBuf* pBuf, FILE* out)
+{
+    size_t actual;
+
+    assert(pBuf->curLen > 0);
+    assert(pBuf->curLen <= pBuf->maxLen);
+
+    actual = fwrite(pBuf->storage, 1, pBuf->curLen, out);
+    if (actual != pBuf->curLen) {
+        fprintf(stderr, "ERROR: write %d of %d bytes\n", actual, pBuf->curLen);
+        return -1;
+    }
+
+    pBuf->curLen = 0;
+
+    return 0;
+}
+
+
+/*
+ * ===========================================================================
+ *      Hprof stuff
+ * ===========================================================================
+ */
+
+/*
+ * Get a 2-byte value, in big-endian order, from memory.
+ */
+static uint16_t get2BE(const unsigned char* buf)
+{
+    uint16_t val;
+
+    val = (buf[0] << 8) | buf[1];
+    return val;
+}
+
+/*
+ * Get a 4-byte value, in big-endian order, from memory.
+ */
+static uint32_t get4BE(const unsigned char* buf)
+{
+    uint32_t val;
+
+    val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+    return val;
+}
+
+/*
+ * Set a 4-byte value, in big-endian order.
+ */
+static void set4BE(unsigned char* buf, uint32_t val)
+{
+    buf[0] = val >> 24;
+    buf[1] = val >> 16;
+    buf[2] = val >> 8;
+    buf[3] = val;
+}
+
+/*
+ * Get the size, in bytes, of one of the "basic types".
+ */
+static int computeBasicLen(HprofBasicType basicType)
+{
+    static const int sizes[] = { -1, -1, 4, -1, 1, 2, 4, 8, 1, 2, 4, 8  };
+    static const size_t maxSize = sizeof(sizes) / sizeof(sizes[0]);
+
+    assert(basicType >= 0);
+    if (basicType >= maxSize)
+        return -1;
+    return sizes[basicType];
+}
+
+/*
+ * Compute the length of a HPROF_CLASS_DUMP block.
+ */
+static int computeClassDumpLen(const unsigned char* origBuf, int len)
+{
+    const unsigned char* buf = origBuf;
+    int blockLen = 0;
+    int i, count;
+
+    blockLen += kIdentSize * 7 + 8;
+    buf += blockLen;
+    len -= blockLen;
+
+    if (len < 0)
+        return -1;
+
+    count = get2BE(buf);
+    buf += 2;
+    len -= 2;
+    DBUG("CDL: 1st count is %d\n", count);
+    for (i = 0; i < count; i++) {
+        HprofBasicType basicType;
+        int basicLen;
+
+        basicType = buf[2];
+        basicLen = computeBasicLen(basicType);
+        if (basicLen < 0) {
+            DBUG("ERROR: invalid basicType %d\n", basicType);
+            return -1;
+        }
+
+        buf += 2 + 1 + basicLen;
+        len -= 2 + 1 + basicLen;
+        if (len < 0)
+            return -1;
+    }
+
+    count = get2BE(buf);
+    buf += 2;
+    len -= 2;
+    DBUG("CDL: 2nd count is %d\n", count);
+    for (i = 0; i < count; i++) {
+        HprofBasicType basicType;
+        int basicLen;
+
+        basicType = buf[kIdentSize];
+        basicLen = computeBasicLen(basicType);
+        if (basicLen < 0) {
+            fprintf(stderr, "ERROR: invalid basicType %d\n", basicType);
+            return -1;
+        }
+
+        buf += kIdentSize + 1 + basicLen;
+        len -= kIdentSize + 1 + basicLen;
+        if (len < 0)
+            return -1;
+    }
+
+    count = get2BE(buf);
+    buf += 2;
+    len -= 2;
+    DBUG("CDL: 3rd count is %d\n", count);
+    for (i = 0; i < count; i++) {
+        buf += kIdentSize + 1;
+        len -= kIdentSize + 1;
+        if (len < 0)
+            return -1;
+    }
+
+    DBUG("Total class dump len: %d\n", buf - origBuf);
+    return buf - origBuf;
+}
+
+/*
+ * Compute the length of a HPROF_INSTANCE_DUMP block.
+ */
+static int computeInstanceDumpLen(const unsigned char* origBuf, int len)
+{
+    int extraCount = get4BE(origBuf + kIdentSize * 2 + 4);
+    return kIdentSize * 2 + 8 + extraCount;
+}
+
+/*
+ * Compute the length of a HPROF_OBJECT_ARRAY_DUMP block.
+ */
+static int computeObjectArrayDumpLen(const unsigned char* origBuf, int len)
+{
+    int arrayCount = get4BE(origBuf + kIdentSize + 4);
+    return kIdentSize * 2 + 8 + arrayCount * kIdentSize;
+}
+
+/*
+ * Compute the length of a HPROF_PRIMITIVE_ARRAY_DUMP block.
+ */
+static int computePrimitiveArrayDumpLen(const unsigned char* origBuf, int len)
+{
+    int arrayCount = get4BE(origBuf + kIdentSize + 4);
+    HprofBasicType basicType = origBuf[kIdentSize + 8];
+    int basicLen = computeBasicLen(basicType);
+
+    return kIdentSize + 9 + arrayCount * basicLen;
+}
+
+/*
+ * Crunch through a heap dump record, writing the original or converted
+ * data to "out".
+ */
+static int processHeapDump(ExpandBuf* pBuf, FILE* out)
+{
+    ExpandBuf* pOutBuf = ebAlloc();
+    unsigned char* origBuf = ebGetBuffer(pBuf);
+    unsigned char* buf = origBuf;
+    int len = ebGetLength(pBuf);
+    int result = -1;
+
+    pBuf = NULL;        /* we just use the raw pointer from here forward */
+
+    /* copy the original header to the output buffer */
+    if (ebAddData(pOutBuf, buf, kRecHdrLen) != 0)
+        goto bail;
+
+    buf += kRecHdrLen;      /* skip past record header */
+    len -= kRecHdrLen;
+
+    while (len > 0) {
+        unsigned char subType = buf[0];
+        int justCopy = TRUE;
+        int subLen;
+
+        DBUG("--- 0x%02x  ", subType);
+        switch (subType) {
+        /* 1.0.2 types */
+        case HPROF_ROOT_UNKNOWN:
+            subLen = kIdentSize;
+            break;
+        case HPROF_ROOT_JNI_GLOBAL:
+            subLen = kIdentSize * 2;
+            break;
+        case HPROF_ROOT_JNI_LOCAL:
+            subLen = kIdentSize + 8;
+            break;
+        case HPROF_ROOT_JAVA_FRAME:
+            subLen = kIdentSize + 8;
+            break;
+        case HPROF_ROOT_NATIVE_STACK:
+            subLen = kIdentSize + 4;
+            break;
+        case HPROF_ROOT_STICKY_CLASS:
+            subLen = kIdentSize;
+            break;
+        case HPROF_ROOT_THREAD_BLOCK:
+            subLen = kIdentSize + 4;
+            break;
+        case HPROF_ROOT_MONITOR_USED:
+            subLen = kIdentSize;
+            break;
+        case HPROF_ROOT_THREAD_OBJECT:
+            subLen = kIdentSize + 8;
+            break;
+        case HPROF_CLASS_DUMP:
+            subLen = computeClassDumpLen(buf+1, len-1);
+            break;
+        case HPROF_INSTANCE_DUMP:
+            subLen = computeInstanceDumpLen(buf+1, len-1);
+            break;
+        case HPROF_OBJECT_ARRAY_DUMP:
+            subLen = computeObjectArrayDumpLen(buf+1, len-1);
+            break;
+        case HPROF_PRIMITIVE_ARRAY_DUMP:
+            subLen = computePrimitiveArrayDumpLen(buf+1, len-1);
+            break;
+
+        /* these were added for Android in 1.0.3 */
+        case HPROF_HEAP_DUMP_INFO:
+            justCopy = FALSE;
+            subLen = kIdentSize + 4;
+            // no 1.0.2 equivalent for this
+            break;
+        case HPROF_ROOT_INTERNED_STRING:
+            buf[0] = HPROF_ROOT_UNKNOWN;
+            subLen = kIdentSize;
+            break;
+        case HPROF_ROOT_FINALIZING:
+            buf[0] = HPROF_ROOT_UNKNOWN;
+            subLen = kIdentSize;
+            break;
+        case HPROF_ROOT_DEBUGGER:
+            buf[0] = HPROF_ROOT_UNKNOWN;
+            subLen = kIdentSize;
+            break;
+        case HPROF_ROOT_REFERENCE_CLEANUP:
+            buf[0] = HPROF_ROOT_UNKNOWN;
+            subLen = kIdentSize;
+            break;
+        case HPROF_ROOT_VM_INTERNAL:
+            buf[0] = HPROF_ROOT_UNKNOWN;
+            subLen = kIdentSize;
+            break;
+        case HPROF_ROOT_JNI_MONITOR:
+            /* keep the ident, drop the next 8 bytes */
+            buf[0] = HPROF_ROOT_UNKNOWN;
+            justCopy = FALSE;
+            ebAddData(pOutBuf, buf, 1 + kIdentSize);
+            subLen = kIdentSize + 8;
+            break;
+        case HPROF_UNREACHABLE:
+            buf[0] = HPROF_ROOT_UNKNOWN;
+            subLen = kIdentSize;
+            break;
+        case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
+            buf[0] = HPROF_PRIMITIVE_ARRAY_DUMP;
+            buf[5] = buf[6] = buf[7] = buf[8] = 0;  /* set array len to 0 */
+            subLen = kIdentSize + 9;
+            break;
+
+        /* shouldn't get here */
+        default:
+            fprintf(stderr, "ERROR: unexpected subtype 0x%02x at offset %d\n",
+                subType, buf - origBuf);
+            goto bail;
+        }
+
+        if (justCopy) {
+            /* copy source data */
+            DBUG("(%d)\n", 1 + subLen);
+            ebAddData(pOutBuf, buf, 1 + subLen);
+        } else {
+            /* other data has been written, or the sub-record omitted */
+            DBUG("(adv %d)\n", 1 + subLen);
+        }
+
+        /* advance to next entry */
+        buf += 1 + subLen;
+        len -= 1 + subLen;
+    }
+
+    /*
+     * Update the record length.
+     */
+    set4BE(ebGetBuffer(pOutBuf) + 5, ebGetLength(pOutBuf) - kRecHdrLen);
+
+    if (ebWriteData(pOutBuf, out) != 0)
+        goto bail;
+
+    result = 0;
+
+bail:
+    ebFree(pOutBuf);
+    return result;
+}
+
+/*
+ * Filter an hprof data file.
+ */
+static int filterData(FILE* in, FILE* out)
+{
+    ExpandBuf* pBuf;
+    int result = -1;
+
+    pBuf = ebAlloc();
+    if (pBuf == NULL)
+        goto bail;
+
+    /*
+     * Start with the header.
+     */
+    if (ebReadString(pBuf, in) != 0)
+        goto bail;
+
+    if (strcmp((const char*)ebGetBuffer(pBuf), "JAVA PROFILE 1.0.3") != 0) {
+        fprintf(stderr, "ERROR: expecting 1.0.3\n");
+        goto bail;
+    }
+
+    /* downgrade to 1.0.2 */
+    (ebGetBuffer(pBuf))[17] = '2';
+    if (ebWriteData(pBuf, out) != 0)
+        goto bail;
+
+    /*
+     * Copy:
+     * (4b) identifier size, always 4
+     * (8b) file creation date
+     */
+    if (ebReadData(pBuf, in, 12, FALSE) != 0)
+        goto bail;
+    if (ebWriteData(pBuf, out) != 0)
+        goto bail;
+
+    /*
+     * Read records until we hit EOF.  Each record begins with:
+     * (1b) type
+     * (4b) timestamp
+     * (4b) length of data that follows
+     */
+    while (1) {
+        assert(ebGetLength(pBuf) == 0);
+
+        /* read type char */
+        if (ebReadData(pBuf, in, 1, TRUE) != 0)
+            goto bail;
+        if (feof(in))
+            break;
+
+        /* read the rest of the header */
+        if (ebReadData(pBuf, in, kRecHdrLen-1, FALSE) != 0)
+            goto bail;
+
+        unsigned char* buf = ebGetBuffer(pBuf);
+        unsigned char type;
+        unsigned int timestamp, length;
+
+        type = buf[0];
+        timestamp = get4BE(buf + 1);
+        length = get4BE(buf + 5);
+        buf = NULL;     /* ptr invalid after next read op */
+
+        /* read the record data */
+        if (length != 0) {
+            if (ebReadData(pBuf, in, length, FALSE) != 0)
+                goto bail;
+        }
+
+        if (type == HPROF_TAG_HEAP_DUMP ||
+            type == HPROF_TAG_HEAP_DUMP_SEGMENT)
+        {
+            DBUG("Processing heap dump 0x%02x (%d bytes)\n",
+                type, length);
+            if (processHeapDump(pBuf, out) != 0)
+                goto bail;
+            ebClear(pBuf);
+        } else {
+            /* keep */
+            DBUG("Keeping 0x%02x (%d bytes)\n", type, length);
+            if (ebWriteData(pBuf, out) != 0)
+                goto bail;
+        }
+    }
+
+    result = 0;
+
+bail:
+    ebFree(pBuf);
+    return result;
+}
+
+/*
+ * Get args.
+ */
+int main(int argc, char** argv)
+{
+    FILE* in = stdin;
+    FILE* out = stdout;
+    int cc;
+
+    if (argc != 3) {
+        fprintf(stderr, "Usage: hprof-conf infile outfile\n\n");
+        fprintf(stderr,
+            "Specify '-' for either or both to use stdin/stdout.\n\n");
+
+        fprintf(stderr,
+            "Copyright (C) 2009 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");
+
+        return 2;
+    }
+
+    if (strcmp(argv[1], "-") != 0) {
+        in = fopen(argv[1], "rb");
+        if (in == NULL) {
+            fprintf(stderr, "ERROR: failed to open input '%s': %s\n",
+                argv[1], strerror(errno));
+            return 1;
+        }
+    }
+    if (strcmp(argv[2], "-") != 0) {
+        out = fopen(argv[2], "wb");
+        if (out == NULL) {
+            fprintf(stderr, "ERROR: failed to open output '%s': %s\n",
+                argv[2], strerror(errno));
+            if (in != stdin)
+                fclose(in);
+            return 1;
+        }
+    }
+
+    cc = filterData(in, out);
+
+    if (in != stdin)
+        fclose(in);
+    if (out != stdout)
+        fclose(out);
+    return (cc != 0);
+}
diff --git a/vm/AllocTracker.c b/vm/AllocTracker.c
new file mode 100644
index 0000000..168713c
--- /dev/null
+++ b/vm/AllocTracker.c
@@ -0,0 +1,653 @@
+/*
+ * 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"
+
+#define kMaxAllocRecordStackDepth   16      /* max 255 */
+#define kNumAllocRecords            512     /* 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];
+
+    /*
+     * This was going to be either wall-clock time in seconds or monotonic
+     * time in milliseconds since the VM started, to give a rough sense for
+     * how long ago an allocation happened.  This adds a system call per
+     * allocation, which is too much overhead.
+     */
+    //u4      timestamp;
+};
+
+/*
+ * Initialize a few things.  This gets called early, so keep activity to
+ * a minimum.
+ */
+bool dvmAllocTrackerStartup(void)
+{
+    /* 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(void)
+{
+    free(gDvm.allocRecords);
+    dvmDestroyMutex(&gDvm.allocTrackerLock);
+}
+
+
+/*
+ * ===========================================================================
+ *      Collection
+ * ===========================================================================
+ */
+
+/*
+ * Enable allocation tracking.  Does nothing if tracking is already enabled.
+ *
+ * Returns "true" on success.
+ */
+bool dvmEnableAllocTracker(void)
+{
+    bool result = true;
+    dvmLockMutex(&gDvm.allocTrackerLock);
+
+    if (gDvm.allocRecords == NULL) {
+        LOGI("Enabling alloc tracker (%d entries, %d frames --> %d bytes)\n",
+            kNumAllocRecords, kMaxAllocRecordStackDepth,
+            sizeof(AllocRecord) * kNumAllocRecords);
+        gDvm.allocRecordHead = gDvm.allocRecordCount = 0;
+        gDvm.allocRecords =
+            (AllocRecord*) malloc(sizeof(AllocRecord) * kNumAllocRecords);
+
+        if (gDvm.allocRecords == NULL)
+            result = false;
+    }
+
+    dvmUnlockMutex(&gDvm.allocTrackerLock);
+    return result;
+}
+
+/*
+ * Disable allocation tracking.  Does nothing if tracking is not enabled.
+ */
+void dvmDisableAllocTracker(void)
+{
+    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->curFrame;
+
+    while ((fp != NULL) && (stackDepth < kMaxAllocRecordStackDepth)) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame(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, int size)
+{
+    dvmLockMutex(&gDvm.allocTrackerLock);
+    if (gDvm.allocRecords == NULL)
+        goto bail;
+
+    Thread* self = dvmThreadSelf();
+    if (self == NULL) {
+        LOGW("alloc tracker: no thread\n");
+        goto bail;
+    }
+
+    /* advance and clip */
+    if (++gDvm.allocRecordHead == kNumAllocRecords)
+        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 < kNumAllocRecords)
+        gDvm.allocRecordCount++;
+
+bail:
+    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 * kNumAllocRecords) 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
+ * kNumAllocRecords and then mask it back down.
+ */
+inline static int headIndex(void)
+{
+    return (gDvm.allocRecordHead+1 + kNumAllocRecords - gDvm.allocRecordCount)
+        & (kNumAllocRecords-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) & (kNumAllocRecords-1);
+    }
+
+    LOGI("class %d/%d, method %d/%d, file %d/%d\n",
+        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) & (kNumAllocRecords-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.
+ */
+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) {
+        LOGE("Failed allocating pointer sets\n");
+        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);
+    LOGI("Generated AT, size is %zd/%zd\n", 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) {
+        LOGE("size mismatch (%d vs %zd)\n", 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)
+        goto bail;
+
+    /*
+     * "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;
+
+    LOGI("Tracked allocations, (head=%d count=%d)\n",
+        gDvm.allocRecordHead, count);
+    while (count--) {
+        AllocRecord* pRec = &gDvm.allocRecords[idx];
+        LOGI(" T=%-2d %6d %s\n",
+            pRec->threadId, pRec->size, pRec->clazz->descriptor);
+
+        if (true) {
+            int i;
+            for (i = 0; i < kMaxAllocRecordStackDepth; i++) {
+                if (pRec->stackElem[i].method == NULL)
+                    break;
+
+                const Method* method = pRec->stackElem[i].method;
+                if (dvmIsNativeMethod(method)) {
+                    LOGI("    %s.%s (Native)\n",
+                        method->clazz->descriptor, method->name);
+                } else {
+                    LOGI("    %s.%s +%d\n",
+                        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) & (kNumAllocRecords-1);
+    }
+
+bail:
+    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..84ac9b8
--- /dev/null
+++ b/vm/AllocTracker.h
@@ -0,0 +1,63 @@
+/*
+ * 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
+#define _DALVIK_ALLOCTRACKER
+
+/* initialization */
+bool dvmAllocTrackerStartup(void);
+void dvmAllocTrackerShutdown(void);
+
+struct AllocRecord;
+typedef struct AllocRecord 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, int 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*/
diff --git a/vm/Android.mk b/vm/Android.mk
new file mode 100644
index 0000000..3110639
--- /dev/null
+++ b/vm/Android.mk
@@ -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.
+
+#
+# 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
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+# Overwrite default settings
+ifneq ($(TARGET_ARCH),x86)
+ifeq ($(TARGET_SIMULATOR),false)
+    LOCAL_PRELINK_MODULE := true
+endif
+endif
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libdvm
+LOCAL_CFLAGS += $(target_smp_flag)
+include $(BUILD_SHARED_LIBRARY)
+
+# If WITH_JIT is configured, build multiple versions of libdvm.so to facilitate
+# correctness/performance bugs triage
+ifeq ($(WITH_JIT),true)
+
+    # Derivation #1
+    # Enable assert and JIT tuning
+    include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+    # Enable assertions and JIT-tuning
+    LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
+                    -DWITH_JIT_TUNING $(target_smp_flag)
+    LOCAL_MODULE := libdvm_assert
+    include $(BUILD_SHARED_LIBRARY)
+
+    # Derivation #2
+    # Enable assert and self-verification
+    include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+    # Enable assertions and JIT self-verification
+    LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
+                    -DWITH_SELF_VERIFICATION $(target_smp_flag)
+    LOCAL_MODULE := libdvm_sv
+    include $(BUILD_SHARED_LIBRARY)
+
+    # Devivation #3
+    # Compile out the JIT
+    WITH_JIT := false
+    include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+    LOCAL_CFLAGS += $(target_smp_flag)
+    LOCAL_MODULE := libdvm_interp
+    include $(BUILD_SHARED_LIBRARY)
+
+endif
+
+#
+# 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 := $(HOST_ARCH)
+    # Note: HOST_ARCH_VARIANT isn't defined.
+    dvm_arch_variant := $(HOST_ARCH)
+    dvm_simulator := false
+
+    include $(LOCAL_PATH)/Dvm.mk
+
+    LOCAL_SHARED_LIBRARIES += libcrypto libssl libicuuc libicui18n
+
+    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 libnativehelper libutils libz
+
+    # 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)
+    LOCAL_MODULE_TAGS := optional
+    LOCAL_MODULE := libdvm
+
+    include $(BUILD_HOST_SHARED_LIBRARY)
+
+endif
diff --git a/vm/Atomic.c b/vm/Atomic.c
new file mode 100644
index 0000000..4473c85
--- /dev/null
+++ b/vm/Atomic.c
@@ -0,0 +1,282 @@
+/*
+ * 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>
+
+/*
+ * Quasi-atomic 64-bit operations, for platforms that lack the real thing.
+ *
+ * TODO: unify ARMv6/x86/sh implementations using the to-be-written
+ * spin lock implementation.  We don't want to rely on mutex innards,
+ * and it would be great if all platforms were running the same code.
+ */
+
+#if defined(HAVE_MACOSX_IPC)
+
+#include <libkern/OSAtomic.h>
+
+#if defined(__ppc__)        \
+    || defined(__PPC__)     \
+    || defined(__powerpc__) \
+    || defined(__powerpc)   \
+    || defined(__POWERPC__) \
+    || defined(_M_PPC)      \
+    || defined(__PPC)
+#define NEED_QUASIATOMICS 1
+#else
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue,
+            (int64_t*)addr) == 0;
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+    int64_t oldValue;
+    do {
+        oldValue = *addr;
+    } while (dvmQuasiAtomicCas64(oldValue, value, addr));
+    return oldValue;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+    return OSAtomicAdd64Barrier(0, addr);
+}
+#endif
+
+#elif defined(__i386__) || defined(__x86_64__)
+#define NEED_QUASIATOMICS 1
+
+#elif __arm__
+#include <machine/cpu-features.h>
+
+#ifdef __ARM_HAVE_LDREXD
+int64_t dvmQuasiAtomicSwap64(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;
+}
+
+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;
+}
+
+#else
+
+// on the device, we implement the 64-bit atomic operations through
+// mutex locking. normally, this is bad because we must initialize
+// a pthread_mutex_t before being able to use it, and this means
+// having to do an initialization check on each function call, and
+// that's where really ugly things begin...
+//
+// BUT, as a special twist, we take advantage of the fact that in our
+// pthread library, a mutex is simply a volatile word whose value is always
+// initialized to 0. In other words, simply declaring a static mutex
+// object initializes it !
+//
+// another twist is that we use a small array of mutexes to dispatch
+// the contention locks from different memory addresses
+//
+
+#include <pthread.h>
+
+#define  SWAP_LOCK_COUNT  32U
+static pthread_mutex_t  _swap_locks[SWAP_LOCK_COUNT];
+
+#define  SWAP_LOCK(addr)   \
+   &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT]
+
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+    int64_t oldValue;
+    pthread_mutex_t*  lock = SWAP_LOCK(addr);
+
+    pthread_mutex_lock(lock);
+
+    oldValue = *addr;
+    *addr    = value;
+
+    pthread_mutex_unlock(lock);
+    return oldValue;
+}
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    int result;
+    pthread_mutex_t*  lock = SWAP_LOCK(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 = SWAP_LOCK(addr);
+
+    pthread_mutex_lock(lock);
+    result = *addr;
+    pthread_mutex_unlock(lock);
+    return result;
+}
+
+#endif /*__ARM_HAVE_LDREXD*/
+
+/*****************************************************************************/
+#elif __sh__
+#define NEED_QUASIATOMICS 1
+
+#else
+#error "Unsupported atomic operations for this platform"
+#endif
+
+
+#if NEED_QUASIATOMICS
+
+/* Note that a spinlock is *not* a good idea in general
+ * since they can introduce subtle issues. For example,
+ * a real-time thread trying to acquire a spinlock already
+ * acquired by another thread will never yeld, making the
+ * CPU loop endlessly!
+ *
+ * However, this code is only used on the Linux simulator
+ * so it's probably ok for us.
+ *
+ * The alternative is to use a pthread mutex, but
+ * these must be initialized before being used, and
+ * then you have the problem of lazily initializing
+ * a mutex without any other synchronization primitive.
+ *
+ * TODO: these currently use sched_yield(), which is not guaranteed to
+ * do anything at all.  We need to use dvmIterativeSleep or a wait /
+ * notify mechanism if the initial attempt fails.
+ */
+
+/* global spinlock for all 64-bit quasiatomic operations */
+static int32_t quasiatomic_spinlock = 0;
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+    volatile int64_t* addr)
+{
+    int result;
+
+    while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else
+        sched_yield();
+#endif
+    }
+
+    if (*addr == oldvalue) {
+        *addr = newvalue;
+        result = 0;
+    } else {
+        result = 1;
+    }
+
+    android_atomic_release_store(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+    int64_t result;
+
+    while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else
+        sched_yield();
+#endif
+    }
+
+    result = *addr;
+    android_atomic_release_store(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+    int64_t result;
+
+    while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+        Sleep(0);
+#else
+        sched_yield();
+#endif
+    }
+
+    result = *addr;
+    *addr = value;
+    android_atomic_release_store(0, &quasiatomic_spinlock);
+
+    return result;
+}
+
+#endif /*NEED_QUASIATOMICS*/
diff --git a/vm/Atomic.h b/vm/Atomic.h
new file mode 100644
index 0000000..6c3a66f
--- /dev/null
+++ b/vm/Atomic.h
@@ -0,0 +1,55 @@
+/*
+ * 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
+#define _DALVIK_ATOMIC
+
+#include <cutils/atomic.h>          /* use common Android atomic ops */
+#include <cutils/atomic-inline.h>   /* and some uncommon ones */
+
+/*
+ * 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.
+ *
+ * None of these provide a memory barrier.
+ */
+
+/*
+ * Swap the 64-bit value at "addr" with "value".  Returns the previous
+ * value.
+ */
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr);
+
+/*
+ * Read the 64-bit value at "addr".
+ */
+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*/
diff --git a/vm/AtomicCache.c b/vm/AtomicCache.c
new file mode 100644
index 0000000..a6f48c2
--- /dev/null
+++ b/vm/AtomicCache.c
@@ -0,0 +1,180 @@
+/*
+ * 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)
+        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)
+    {
+        //LOGE("unable to reset the instanceof cache ownership\n");
+        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..686eac5
--- /dev/null
+++ b/vm/AtomicCache.h
@@ -0,0 +1,173 @@
+/*
+ * 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
+#define _DALVIK_ATOMICCACHE
+
+/*
+ * 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.
+ */
+typedef struct AtomicCacheEntry {
+    u4          key1;
+    u4          key2;
+    u4          value;
+    volatile u4 version;    /* version and lock flag */
+} AtomicCacheEntry;
+
+/*
+ * 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.)
+ */
+typedef 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 */
+} AtomicCache;
+
+/*
+ * 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;                                     \
+        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*/
diff --git a/vm/Bits.h b/vm/Bits.h
new file mode 100644
index 0000000..38b016d
--- /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
+#define _DALVIK_BITS
+
+#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*/
diff --git a/vm/CheckJni.c b/vm/CheckJni.c
new file mode 100644
index 0000000..59d34eb
--- /dev/null
+++ b/vm/CheckJni.c
@@ -0,0 +1,2810 @@
+/*
+ * 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.
+ *
+ * TODO: keep a counter on global Get/Release.  Report a warning if some Gets
+ * were not Released.  Do not count explicit Add/DeleteGlobalRef calls (or
+ * count them separately, so we can complain if they exceed a certain
+ * threshold).
+ *
+ * TODO: verify that the methodID passed into the Call functions is for
+ * a method in the specified class.
+ */
+#include "Dalvik.h"
+#include "JniInternal.h"
+
+#include <zlib.h>
+
+static void abortMaybe(void);       // fwd
+
+
+/*
+ * ===========================================================================
+ *      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, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    assert(pResult->l != NULL);
+    Object* resultObj = (Object*) pResult->l;
+    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 */
+        LOGV("Check %s.%s: %s io %s (FAST-OK)\n",
+            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;
+
+        declClazz = dvmFindClassNoInit(declType, method->clazz->classLoader);
+        if (declClazz == NULL) {
+            LOGW("JNI WARNING: method declared to return '%s' returned '%s'\n",
+                declType, objType);
+            LOGW("             failed in %s.%s ('%s' not found)\n",
+                method->clazz->descriptor, method->name, declType);
+            abortMaybe();
+            return;
+        }
+        if (!dvmInstanceof(objClazz, declClazz)) {
+            LOGW("JNI WARNING: method declared to return '%s' returned '%s'\n",
+                declType, objType);
+            LOGW("             failed in %s.%s\n",
+                method->clazz->descriptor, method->name);
+            abortMaybe();
+            return;
+        } else {
+            LOGV("Check %s.%s: %s io %s (SLOW-OK)\n",
+                method->clazz->descriptor, method->name, objType, declType);
+        }
+    }
+}
+
+/*
+ * Determine if we need to check the return type coming out of the call.
+ *
+ * (We don't 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_general(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    dvmCallJNIMethod_general(args, pResult, method, self);
+    if (callNeedsCheck(args, pResult, method, self))
+        checkCallResultCommon(args, pResult, method, self);
+}
+
+/*
+ * Check a synchronized call into native code.
+ */
+void dvmCheckCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    dvmCallJNIMethod_synchronized(args, pResult, method, self);
+    if (callNeedsCheck(args, pResult, method, self))
+        checkCallResultCommon(args, pResult, method, self);
+}
+
+/*
+ * Check a virtual call with no reference arguments (other than "this").
+ */
+void dvmCheckCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    dvmCallJNIMethod_virtualNoRef(args, pResult, method, self);
+    if (callNeedsCheck(args, pResult, method, self))
+        checkCallResultCommon(args, pResult, method, self);
+}
+
+/*
+ * Check a static call with no reference arguments (other than "clazz").
+ */
+void dvmCheckCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    dvmCallJNIMethod_staticNoRef(args, pResult, method, self);
+    if (callNeedsCheck(args, pResult, method, self))
+        checkCallResultCommon(args, pResult, method, self);
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI function helpers
+ * ===========================================================================
+ */
+
+#define JNI_ENTER()     dvmChangeStatus(NULL, THREAD_RUNNING)
+#define JNI_EXIT()      dvmChangeStatus(NULL, THREAD_NATIVE)
+
+#define BASE_ENV(_env)  (((JNIEnvExt*)_env)->baseFuncTable)
+#define BASE_VM(_vm)    (((JavaVMExt*)_vm)->baseFuncTable)
+
+#define kRedundantDirectBufferTest false
+
+/*
+ * Flags passed into checkThread().
+ */
+#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 */
+
+/*
+ * Enter/exit macros for JNI env "check" functions.  These do not change
+ * the thread state within the VM.
+ */
+#define CHECK_ENTER(_env, _flags)                                           \
+    do {                                                                    \
+        JNI_TRACE(true, true);                                              \
+        checkThread(_env, _flags, __FUNCTION__);                            \
+    } while(false)
+
+#define CHECK_EXIT(_env)                                                    \
+    do { JNI_TRACE(false, true); } while(false)
+
+
+/*
+ * Enter/exit macros for JNI invocation interface "check" functions.  These
+ * do not change the thread state within the VM.
+ *
+ * Set "_hasmeth" 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.
+ */
+#define CHECK_VMENTER(_vm, _hasmeth)                                        \
+    do { JNI_TRACE(true, _hasmeth); } while(false)
+#define CHECK_VMEXIT(_vm, _hasmeth)                                         \
+    do { JNI_TRACE(false, _hasmeth); } while(false)
+
+#define CHECK_FIELD_TYPE(_env, _obj, _fieldid, _prim, _isstatic)            \
+    checkFieldType(_env, _obj, _fieldid, _prim, _isstatic, __FUNCTION__)
+#define CHECK_INST_FIELD_ID(_env, _obj, _fieldid)                           \
+    checkInstanceFieldID(_env, _obj, _fieldid, __FUNCTION__)
+#define CHECK_CLASS(_env, _clazz)                                           \
+    checkClass(_env, _clazz, __FUNCTION__)
+#define CHECK_STRING(_env, _str)                                            \
+    checkString(_env, _str, __FUNCTION__)
+#define CHECK_UTF_STRING(_env, _str, _nullok)                               \
+    checkUtfString(_env, _str, _nullok, __FUNCTION__)
+#define CHECK_CLASS_NAME(_env, _str)                                        \
+    checkClassName(_env, _str, __FUNCTION__)
+#define CHECK_OBJECT(_env, _obj)                                            \
+    checkObject(_env, _obj, __FUNCTION__)
+#define CHECK_ARRAY(_env, _array)                                           \
+    checkArray(_env, _array, __FUNCTION__)
+#define CHECK_RELEASE_MODE(_env, _mode)                                     \
+    checkReleaseMode(_env, _mode, __FUNCTION__)
+#define CHECK_LENGTH_POSITIVE(_env, _length)                                \
+    checkLengthPositive(_env, _length, __FUNCTION__)
+#define CHECK_NON_NULL(_env, _ptr)                                          \
+    checkNonNull(_env, _ptr, __FUNCTION__)
+#define CHECK_SIG(_env, _methid, _sigbyte, _isstatic)                       \
+    checkSig(_env, _methid, _sigbyte, _isstatic, __FUNCTION__)
+#define CHECK_VIRTUAL_METHOD(_env, _obj, _methid)                           \
+    checkVirtualMethod(_env, _obj, _methid, __FUNCTION__)
+#define CHECK_STATIC_METHOD(_env, _clazz, _methid)                          \
+    checkStaticMethod(_env, _clazz, _methid, __FUNCTION__)
+#define CHECK_METHOD_ARGS_A(_env, _methid, _args)                           \
+    checkMethodArgsA(_env, _methid, _args, __FUNCTION__)
+#define CHECK_METHOD_ARGS_V(_env, _methid, _args)                           \
+    checkMethodArgsV(_env, _methid, _args, __FUNCTION__)
+
+/*
+ * Prints trace messages when a native method calls a JNI function such as
+ * NewByteArray. Enabled if both "-Xcheck:jni" and "-verbose:jni" are enabled.
+ */
+#define JNI_TRACE(_entry, _hasmeth)                                         \
+    do {                                                                    \
+        if (gDvm.verboseJni && (_entry)) {                                  \
+            static const char* classDescriptor = "???";                     \
+            static const char* methodName = "???";                          \
+            if (_hasmeth) {                                                 \
+                const Method* meth = dvmGetCurrentJNIMethod();              \
+                classDescriptor = meth->clazz->descriptor;                  \
+                methodName = meth->name;                                    \
+            }                                                               \
+            /* use +6 to drop the leading "Check_" */                       \
+            LOGI("JNI: %s (from %s.%s)",                                    \
+                (__FUNCTION__)+6, classDescriptor, methodName);             \
+        }                                                                   \
+    } while(false)
+
+/*
+ * Log the current location.
+ *
+ * "func" looks like "Check_DeleteLocalRef"; we drop the "Check_".
+ */
+static void showLocation(const Method* meth, const char* func)
+{
+    char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+    LOGW("             in %s.%s %s (%s)\n",
+        meth->clazz->descriptor, meth->name, desc, func + 6);
+    free(desc);
+}
+
+/*
+ * Abort if we are configured to bail out on JNI warnings.
+ */
+static void abortMaybe(void)
+{
+    JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+    if (vm->warnError) {
+        dvmDumpThread(dvmThreadSelf(), false);
+        dvmAbort();
+    }
+}
+
+/*
+ * Verify that the current thread is (a) attached and (b) associated with
+ * this particular instance of JNIEnv.
+ *
+ * Verify that, if this thread previously made a critical "get" call, we
+ * do the corresponding "release" call before we try anything else.
+ *
+ * Verify that, if an exception has been raised, the native code doesn't
+ * make any JNI calls other than the Exception* methods.
+ *
+ * TODO? if we add support for non-JNI native calls, make sure that the
+ * method at the top of the interpreted stack is a JNI method call.  (Or
+ * set a flag in the Thread/JNIEnv when the call is made and clear it on
+ * return?)
+ *
+ * NOTE: we are still in THREAD_NATIVE mode.  A GC could happen at any time.
+ */
+static void checkThread(JNIEnv* env, int flags, const char* func)
+{
+    JNIEnvExt* threadEnv;
+    bool printWarn = false;
+    bool printException = false;
+
+    /* get the *correct* JNIEnv by going through our TLS pointer */
+    threadEnv = dvmGetJNIEnvForThread();
+
+    /*
+     * Verify that the JNIEnv we've been handed matches what we expected
+     * to receive.
+     */
+    if (threadEnv == NULL) {
+        LOGE("JNI ERROR: non-VM thread making JNI calls\n");
+        // don't set printWarn -- it'll try to call showLocation()
+        dvmAbort();
+    } else if ((JNIEnvExt*) env != threadEnv) {
+        if (dvmThreadSelf()->threadId != threadEnv->envThreadId) {
+            LOGE("JNI: threadEnv != thread->env?\n");
+            dvmAbort();
+        }
+
+        LOGW("JNI WARNING: threadid=%d using env from threadid=%d\n",
+            threadEnv->envThreadId, ((JNIEnvExt*)env)->envThreadId);
+        printWarn = true;
+
+        /* this is a bad idea -- need to throw as we exit, or abort func */
+        //dvmThrowException("Ljava/lang/RuntimeException;",
+        //    "invalid use of JNI env ptr");
+    } else if (((JNIEnvExt*) env)->self != dvmThreadSelf()) {
+        /* correct JNIEnv*; make sure the "self" pointer is correct */
+        LOGE("JNI ERROR: env->self != thread-self (%p vs. %p)\n",
+            ((JNIEnvExt*) env)->self, dvmThreadSelf());
+        dvmAbort();
+    }
+
+    /*
+     * Check for critical resource misuse.
+     */
+    switch (flags & kFlag_CritMask) {
+    case kFlag_CritOkay:    // okay to call this method
+        break;
+    case kFlag_CritBad:     // not okay to call
+        if (threadEnv->critical) {
+            LOGW("JNI WARNING: threadid=%d using JNI after critical get\n",
+                threadEnv->envThreadId);
+            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) {
+            LOGW("JNI WARNING: threadid=%d called too many crit releases\n",
+                threadEnv->envThreadId);
+            printWarn = true;
+        }
+        break;
+    default:
+        assert(false);
+    }
+
+    /*
+     * Check for raised exceptions.
+     */
+    if ((flags & kFlag_ExcepOkay) == 0 && dvmCheckException(dvmThreadSelf())) {
+        LOGW("JNI WARNING: JNI method called with exception raised\n");
+        printWarn = true;
+        printException = true;
+    }
+
+    if (printWarn)
+        showLocation(dvmGetCurrentJNIMethod(), func);
+    if (printException) {
+        LOGW("Pending exception is:\n");
+        dvmLogExceptionStackTrace();
+    }
+    if (printWarn)
+        abortMaybe();
+}
+
+/*
+ * Verify that the field is of the appropriate type.  If the field has an
+ * object type, "obj" is the object we're trying to assign into it.
+ *
+ * Works for both static and instance fields.
+ */
+static void checkFieldType(JNIEnv* env, jobject jobj, jfieldID fieldID,
+    PrimitiveType prim, bool isStatic, const char* func)
+{
+    static const char* primNameList[] = {
+        "Object/Array", "boolean", "char", "float", "double",
+        "byte", "short", "int", "long", "void"
+    };
+    const char** primNames = &primNameList[1];      // shift up for PRIM_NOT
+    Field* field = (Field*) fieldID;
+    bool printWarn = false;
+
+    if (fieldID == NULL) {
+        LOGE("JNI ERROR: null field ID\n");
+        abortMaybe();
+    }
+
+    if (field->signature[0] == 'L' || field->signature[0] == '[') {
+        Object* obj = dvmDecodeIndirectRef(env, jobj);
+        if (obj != NULL) {
+            ClassObject* fieldClass =
+                dvmFindLoadedClass(field->signature);
+            ClassObject* objClass = obj->clazz;
+
+            assert(fieldClass != NULL);
+            assert(objClass != NULL);
+
+            if (!dvmInstanceof(objClass, fieldClass)) {
+                LOGW("JNI WARNING: field '%s' with type '%s' set with wrong type (%s)\n",
+                    field->name, field->signature, objClass->descriptor);
+                printWarn = true;
+            }
+        }
+    } else if (field->signature[0] != PRIM_TYPE_TO_LETTER[prim]) {
+        LOGW("JNI WARNING: field '%s' with type '%s' set with wrong type (%s)\n",
+            field->name, field->signature, primNames[prim]);
+        printWarn = true;
+    } else if (isStatic && !dvmIsStaticField(field)) {
+        if (isStatic)
+            LOGW("JNI WARNING: accessing non-static field %s as static\n",
+                field->name);
+        else
+            LOGW("JNI WARNING: accessing static field %s as non-static\n",
+                field->name);
+        printWarn = true;
+    }
+
+    if (printWarn) {
+        showLocation(dvmGetCurrentJNIMethod(), func);
+        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.
+ *
+ * Must be in "running" mode before calling here.
+ */
+static void checkObject0(JNIEnv* env, jobject jobj, const char* func)
+{
+    UNUSED_PARAMETER(env);
+    bool printWarn = false;
+
+    if (jobj == NULL)
+        return;
+
+    if (dvmIsWeakGlobalRef(jobj)) {
+        /*
+         * Normalize and continue.  This will tell us if the PhantomReference
+         * object is valid.
+         */
+        jobj = dvmNormalizeWeakGlobalRef((jweak) jobj);
+    }
+
+    if (dvmGetJNIRefType(env, jobj) == JNIInvalidRefType) {
+        LOGW("JNI WARNING: %p is not a valid JNI reference\n", jobj);
+        printWarn = true;
+    } else {
+        Object* obj = dvmDecodeIndirectRef(env, jobj);
+
+        if (obj == NULL || !dvmIsValidObject(obj)) {
+            LOGW("JNI WARNING: native code passing in bad object %p %p (%s)\n",
+                jobj, obj, func);
+            printWarn = true;
+        }
+    }
+
+    if (printWarn) {
+        showLocation(dvmGetCurrentJNIMethod(), func);
+        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.
+ */
+static void checkObject(JNIEnv* env, jobject jobj, const char* func)
+{
+    JNI_ENTER();
+    checkObject0(env, jobj, func);
+    JNI_EXIT();
+}
+
+/*
+ * Verify that "clazz" actually points to a class object.  (Also performs
+ * checkObject.)
+ *
+ * We probably don't need to identify where we're being called from,
+ * because the VM is most likely about to crash and leave a core dump
+ * if something is wrong.
+ *
+ * Because we're looking at an object on the GC heap, we have to switch
+ * to "running" mode before doing the checks.
+ */
+static void checkClass(JNIEnv* env, jclass jclazz, const char* func)
+{
+    JNI_ENTER();
+    bool printWarn = false;
+
+    Object* obj = dvmDecodeIndirectRef(env, jclazz);
+
+    if (obj == NULL) {
+        LOGW("JNI WARNING: received null jclass\n");
+        printWarn = true;
+    } else if (!dvmIsValidObject(obj)) {
+        LOGW("JNI WARNING: jclass points to invalid object %p\n", obj);
+        printWarn = true;
+    } else if (obj->clazz != gDvm.classJavaLangClass) {
+        LOGW("JNI WARNING: jclass arg is not a Class reference "
+             "(%p is instance of %s)\n",
+            jclazz, obj->clazz->descriptor);
+        printWarn = true;
+    }
+    JNI_EXIT();
+
+    if (printWarn)
+        abortMaybe();
+    else
+        checkObject(env, jclazz, func);
+}
+
+/*
+ * Verify that "str" is non-NULL and points to a String object.
+ *
+ * Since we're dealing with objects, switch to "running" mode.
+ */
+static void checkString(JNIEnv* env, jstring jstr, const char* func)
+{
+    JNI_ENTER();
+    bool printWarn = false;
+
+    Object* obj = dvmDecodeIndirectRef(env, jstr);
+
+    if (obj == NULL) {
+        LOGW("JNI WARNING: received null jstring (%s)\n", func);
+        printWarn = true;
+    } else if (obj->clazz != gDvm.classJavaLangString) {
+        /*
+         * TODO: we probably should test dvmIsValidObject first, because
+         * this will crash if "obj" is non-null but pointing to an invalid
+         * memory region.  However, the "is valid" test is a little slow,
+         * we're doing it again over in checkObject().
+         */
+        if (dvmIsValidObject(obj))
+            LOGW("JNI WARNING: jstring %p points to non-string object (%s)\n",
+                jstr, func);
+        else
+            LOGW("JNI WARNING: jstring %p is bogus (%s)\n", jstr, func);
+        printWarn = true;
+    }
+    JNI_EXIT();
+
+    if (printWarn)
+        abortMaybe();
+    else
+        checkObject(env, jstr, func);
+}
+
+/*
+ * Verify that "bytes" points to valid "modified UTF-8" data.
+ */
+static void checkUtfString(JNIEnv* env, const char* bytes, bool nullOk,
+    const char* func)
+{
+    const char* origBytes = bytes;
+
+    if (bytes == NULL) {
+        if (!nullOk) {
+            LOGW("JNI WARNING: unexpectedly null UTF string\n");
+            goto fail;
+        }
+
+        return;
+    }
+
+    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.
+                 */
+                LOGW("JNI WARNING: illegal start byte 0x%x\n", utf8);
+                goto fail;
+            }
+            case 0x0e: {
+                // Bit pattern 1110, so there are two additional bytes.
+                utf8 = *(bytes++);
+                if ((utf8 & 0xc0) != 0x80) {
+                    LOGW("JNI WARNING: illegal continuation byte 0x%x\n", utf8);
+                    goto fail;
+                }
+                // 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) {
+                    LOGW("JNI WARNING: illegal continuation byte 0x%x\n", utf8);
+                    goto fail;
+                }
+                break;
+            }
+        }
+    }
+
+    return;
+
+fail:
+    LOGW("             string: '%s'\n", origBytes);
+    showLocation(dvmGetCurrentJNIMethod(), func);
+    abortMaybe();
+}
+
+/*
+ * 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 "full-qualified" class names, like "java/lang/Thread" or
+ * "[Ljava/lang/Object;".
+ */
+static void checkClassName(JNIEnv* env, const char* className, const char* func)
+{
+    const char* cp;
+
+    /* quick check for illegal chars */
+    cp = className;
+    while (*cp != '\0') {
+        if (*cp == '.')     /* catch "java.lang.String" */
+            goto fail;
+        cp++;
+    }
+    if (*(cp-1) == ';' && *className == 'L')
+        goto fail;         /* catch "Ljava/lang/String;" */
+
+    // TODO: need a more rigorous check here
+
+    return;
+
+fail:
+    LOGW("JNI WARNING: illegal class name '%s' (%s)\n", className, func);
+    LOGW("             (should be formed like 'java/lang/String')\n");
+    abortMaybe();
+}
+
+/*
+ * Verify that "array" is non-NULL and points to an Array object.
+ *
+ * Since we're dealing with objects, switch to "running" mode.
+ */
+static void checkArray(JNIEnv* env, jarray jarr, const char* func)
+{
+    JNI_ENTER();
+    bool printWarn = false;
+
+    Object* obj = dvmDecodeIndirectRef(env, jarr);
+
+    if (obj == NULL) {
+        LOGW("JNI WARNING: received null array (%s)\n", func);
+        printWarn = true;
+    } else if (obj->clazz->descriptor[0] != '[') {
+        if (dvmIsValidObject(obj))
+            LOGW("JNI WARNING: jarray %p points to non-array object (%s)\n",
+                jarr, obj->clazz->descriptor);
+        else
+            LOGW("JNI WARNING: jarray %p is bogus\n", jarr);
+        printWarn = true;
+    }
+
+    JNI_EXIT();
+
+    if (printWarn)
+        abortMaybe();
+    else
+        checkObject(env, jarr, func);
+}
+
+/*
+ * Verify that the "mode" argument passed to a primitive array Release
+ * function is one of the valid values.
+ */
+static void checkReleaseMode(JNIEnv* env, jint mode, const char* func)
+{
+    if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
+        LOGW("JNI WARNING: bad value for mode (%d) (%s)\n", mode, func);
+        abortMaybe();
+    }
+}
+
+/*
+ * Verify that the length argument to array-creation calls is >= 0.
+ */
+static void checkLengthPositive(JNIEnv* env, jsize length, const char* func)
+{
+    if (length < 0) {
+        LOGW("JNI WARNING: negative length for array allocation (%s)\n", func);
+        abortMaybe();
+    }
+}
+
+/*
+ * Verify that the pointer value is non-NULL.
+ */
+static void checkNonNull(JNIEnv* env, const void* ptr, const char* func)
+{
+    if (ptr == NULL) {
+        LOGW("JNI WARNING: invalid null pointer (%s)\n", func);
+        abortMaybe();
+    }
+}
+
+/*
+ * Verify that the method's return type matches the type of call.
+ *
+ * "expectedSigByte" will be 'L' for all objects, including arrays.
+ */
+static void checkSig(JNIEnv* env, jmethodID methodID, char expectedSigByte,
+    bool isStatic, const char* func)
+{
+    const Method* meth = (const Method*) methodID;
+    bool printWarn = false;
+
+    if (expectedSigByte != meth->shorty[0]) {
+        LOGW("JNI WARNING: expected return type '%c'\n", expectedSigByte);
+        printWarn = true;
+    } else if (isStatic && !dvmIsStaticMethod(meth)) {
+        if (isStatic)
+            LOGW("JNI WARNING: calling non-static method with static call\n");
+        else
+            LOGW("JNI WARNING: calling static method with non-static call\n");
+        printWarn = true;
+    }
+
+    if (printWarn) {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        LOGW("             calling %s.%s %s\n",
+            meth->clazz->descriptor, meth->name, desc);
+        free(desc);
+        showLocation(dvmGetCurrentJNIMethod(), func);
+        abortMaybe();
+    }
+}
+
+/*
+ * Verify that this static field ID is valid for this class.
+ */
+static void checkStaticFieldID(JNIEnv* env, jclass jclazz, jfieldID fieldID)
+{
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    StaticField* base = &clazz->sfields[0];
+    int fieldCount = clazz->sfieldCount;
+
+    if ((StaticField*) fieldID < base ||
+        (StaticField*) fieldID >= base + fieldCount)
+    {
+        LOGW("JNI WARNING: static fieldID %p not valid for class %s\n",
+            fieldID, clazz->descriptor);
+        LOGW("             base=%p count=%d\n", base, fieldCount);
+        abortMaybe();
+    }
+}
+
+/*
+ * Verify that this instance field ID is valid for this object.
+ */
+static void checkInstanceFieldID(JNIEnv* env, jobject jobj, jfieldID fieldID,
+    const char* func)
+{
+    JNI_ENTER();
+
+    if (jobj == NULL) {
+        LOGW("JNI WARNING: invalid null object (%s)\n", func);
+        abortMaybe();
+        goto bail;
+    }
+
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    ClassObject* clazz = obj->clazz;
+
+    /*
+     * Check this class and all of its superclasses for a matching field.
+     * Don't need to scan interfaces.
+     */
+    while (clazz != NULL) {
+        if ((InstField*) fieldID >= clazz->ifields &&
+            (InstField*) fieldID < clazz->ifields + clazz->ifieldCount)
+        {
+            goto bail;
+        }
+
+        clazz = clazz->super;
+    }
+
+    LOGW("JNI WARNING: inst fieldID %p not valid for class %s\n",
+        fieldID, obj->clazz->descriptor);
+    abortMaybe();
+
+bail:
+    JNI_EXIT();
+}
+
+/*
+ * 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.)
+ */
+static void checkVirtualMethod(JNIEnv* env, jobject jobj, jmethodID methodID,
+    const char* func)
+{
+    JNI_ENTER();
+
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    const Method* meth = (const Method*) methodID;
+
+    if (!dvmInstanceof(obj->clazz, meth->clazz)) {
+        LOGW("JNI WARNING: can't call %s.%s on instance of %s\n",
+            meth->clazz->descriptor, meth->name, obj->clazz->descriptor);
+        abortMaybe();
+    }
+
+    JNI_EXIT();
+}
+
+/*
+ * Verify that "methodID" is appropriate for "clazz".
+ *
+ * A mismatch isn't dangerous, because the method 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.
+ */
+static void checkStaticMethod(JNIEnv* env, jclass jclazz, jmethodID methodID,
+    const char* func)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    const Method* meth = (const Method*) methodID;
+
+    if (!dvmInstanceof(clazz, meth->clazz)) {
+        LOGW("JNI WARNING: can't call static %s.%s on class %s\n",
+            meth->clazz->descriptor, meth->name, clazz->descriptor);
+        // no abort
+    }
+
+    JNI_EXIT();
+}
+
+/*
+ * Verify that the reference arguments being passed in are appropriate for
+ * this method.
+ *
+ * At a minimum we want to make sure that the argument is a valid
+ * reference.  We can also do a class lookup on the method signature
+ * and verify that the object is an instance of the appropriate class,
+ * but that's more expensive.
+ *
+ * The basic tests are redundant when indirect references are enabled,
+ * since reference arguments must always be converted explicitly.  An
+ * instanceof test would not be redundant, but we're not doing that at
+ * this time.
+ */
+static void checkMethodArgsV(JNIEnv* env, jmethodID methodID, va_list args,
+    const char* func)
+{
+#ifndef USE_INDIRECT_REF
+    JNI_ENTER();
+
+    const Method* meth = (const Method*) methodID;
+    const char* desc = meth->shorty;
+
+    LOGV("V-checking %s.%s:%s...\n", meth->clazz->descriptor, meth->name, desc);
+
+    while (*++desc != '\0') {       /* pre-incr to skip return type */
+        switch (*desc) {
+        case 'L':
+            {     /* 'shorty' descr uses L for all refs, incl array */
+                jobject argObj = va_arg(args, jobject);
+                checkObject0(env, argObj, func);
+            }
+            break;
+        case 'D':       /* 8-byte double */
+        case 'J':       /* 8-byte long */
+        case 'F':       /* floats normalized to doubles */
+            (void) va_arg(args, u8);
+            break;
+        default:        /* Z B C S I -- all passed as 32-bit integers */
+            (void) va_arg(args, u4);
+            break;
+        }
+    }
+
+    JNI_EXIT();
+#endif
+}
+
+/*
+ * Same purpose as checkMethodArgsV, but with arguments in an array of
+ * jvalue structs.
+ */
+static void checkMethodArgsA(JNIEnv* env, jmethodID methodID, jvalue* args,
+    const char* func)
+{
+#ifndef USE_INDIRECT_REF
+    JNI_ENTER();
+
+    const Method* meth = (const Method*) methodID;
+    const char* desc = meth->shorty;
+    int idx = 0;
+
+    LOGV("A-checking %s.%s:%s...\n", meth->clazz->descriptor, meth->name, desc);
+
+    while (*++desc != '\0') {       /* pre-incr to skip return type */
+        if (*desc == 'L') {
+            jobject argObj = args[idx].l;
+            checkObject0(env, argObj, func);
+        }
+
+        idx++;
+    }
+
+    JNI_EXIT();
+#endif
+}
+
+
+/*
+ * ===========================================================================
+ *      Guarded arrays
+ * ===========================================================================
+ */
+
+#define kGuardLen       512         /* must be multiple of 2 */
+#define kGuardPattern   0xd5e3      /* uncommon values; d5e3d5e3 invalid addr */
+#define kGuardMagic     0xffd5aa96
+#define kGuardExtra     sizeof(GuardExtra)
+
+/* this gets tucked in at the start of the buffer; struct size must be even */
+typedef struct GuardExtra {
+    u4          magic;
+    uLong       adler;
+    size_t      originalLen;
+    const void* originalPtr;
+} GuardExtra;
+
+/* find the GuardExtra given the pointer into the "live" data */
+inline static GuardExtra* getGuardExtra(const void* dataBuf)
+{
+    u1* fullBuf = ((u1*) dataBuf) - kGuardLen / 2;
+    return (GuardExtra*) fullBuf;
+}
+
+/*
+ * Create an oversized 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* createGuardedCopy(const void* buf, size_t len, bool modOkay)
+{
+    GuardExtra* pExtra;
+    size_t newLen = (len + kGuardLen +1) & ~0x01;
+    u1* newBuf;
+    u2* pat;
+    int i;
+
+    newBuf = (u1*)malloc(newLen);
+    if (newBuf == NULL) {
+        LOGE("createGuardedCopy failed on alloc of %d bytes\n", newLen);
+        dvmAbort();
+    }
+
+    /* fill it in with a pattern */
+    pat = (u2*) newBuf;
+    for (i = 0; i < (int)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, buf, len);
+        *(uLong*)newBuf = adler;
+    }
+
+    pExtra = (GuardExtra*) newBuf;
+    pExtra->magic = kGuardMagic;
+    pExtra->adler = adler;
+    pExtra->originalPtr = buf;
+    pExtra->originalLen = len;
+
+    return newBuf + kGuardLen / 2;
+}
+
+/*
+ * 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 checkGuardedCopy(const void* dataBuf, bool modOkay)
+{
+    static const u4 kMagicCmp = kGuardMagic;
+    const u1* fullBuf = ((const u1*) dataBuf) - kGuardLen / 2;
+    const GuardExtra* pExtra = getGuardExtra(dataBuf);
+    size_t len;
+    const u2* pat;
+    int i;
+
+    /*
+     * 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);
+        LOGE("JNI: guard magic does not match (found 0x%02x%02x%02x%02x) "
+             "-- incorrect data pointer %p?\n",
+            buf[3], buf[2], buf[1], buf[0], dataBuf); /* assume little endian */
+        return false;
+    }
+
+    len = pExtra->originalLen;
+
+    /* check bottom half of guard; skip over optional checksum storage */
+    pat = (u2*) fullBuf;
+    for (i = kGuardExtra / 2; i < (int) (kGuardLen / 2 - kGuardExtra) / 2; i++)
+    {
+        if (pat[i] != kGuardPattern) {
+            LOGE("JNI: guard pattern(1) disturbed at %p + %d\n",
+                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]) {
+            LOGE("JNI: guard pattern disturbed in odd byte after %p "
+                 "(+%d) 0x%02x 0x%02x\n",
+                fullBuf, offset, fullBuf[offset], ((const u1*) &patSample)[1]);
+            return false;
+        }
+        offset++;
+    }
+
+    /* check top half of guard */
+    pat = (u2*) (fullBuf + offset);
+    for (i = 0; i < kGuardLen / 4; i++) {
+        if (pat[i] != kGuardPattern) {
+            LOGE("JNI: guard pattern(2) disturbed at %p + %d\n",
+                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, dataBuf, len);
+        if (pExtra->adler != adler) {
+            LOGE("JNI: buffer modified (0x%08lx vs 0x%08lx) at addr %p\n",
+                pExtra->adler, adler, dataBuf);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Free up the guard buffer, scrub it, and return the original pointer.
+ */
+static void* freeGuardedCopy(void* dataBuf)
+{
+    u1* fullBuf = ((u1*) dataBuf) - kGuardLen / 2;
+    const GuardExtra* pExtra = getGuardExtra(dataBuf);
+    void* originalPtr = (void*) pExtra->originalPtr;
+    size_t len = pExtra->originalLen;
+
+    memset(dataBuf, 0xdd, len);
+    free(fullBuf);
+    return originalPtr;
+}
+
+/*
+ * Just pull out the original pointer.
+ */
+static void* getGuardedCopyOriginalPtr(const void* dataBuf)
+{
+    const GuardExtra* pExtra = getGuardExtra(dataBuf);
+    return (void*) pExtra->originalPtr;
+}
+
+/*
+ * Grab the data length.
+ */
+static size_t getGuardedCopyOriginalLen(const void* dataBuf)
+{
+    const GuardExtra* pExtra = getGuardExtra(dataBuf);
+    return pExtra->originalLen;
+}
+
+/*
+ * Return the width, in bytes, of a primitive type.
+ */
+static int dvmPrimitiveTypeWidth(PrimitiveType primType)
+{
+    static const int lengths[PRIM_MAX] = {
+        1,      // boolean
+        2,      // char
+        4,      // float
+        8,      // double
+        1,      // byte
+        2,      // short
+        4,      // int
+        8,      // long
+        -1,     // void
+    };
+    assert(primType >= 0 && primType < PRIM_MAX);
+    return lengths[primType];
+}
+
+/*
+ * 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)
+{
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    PrimitiveType primType = arrObj->obj.clazz->elementClass->primitiveType;
+    int len = arrObj->length * dvmPrimitiveTypeWidth(primType);
+    void* result;
+
+    result = createGuardedCopy(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)
+{
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    bool release, copyBack;
+    u1* result;
+
+    if (!checkGuardedCopy(dataBuf, true)) {
+        LOGE("JNI: failed guarded copy check in releaseGuardedPACopy\n");
+        abortMaybe();
+        return NULL;
+    }
+
+    switch (mode) {
+    case 0:
+        release = copyBack = true;
+        break;
+    case JNI_ABORT:
+        release = true;
+        copyBack = false;
+        break;
+    case JNI_COMMIT:
+        release = false;
+        copyBack = true;
+        break;
+    default:
+        LOGE("JNI: bad release mode %d\n", mode);
+        dvmAbort();
+        return NULL;
+    }
+
+    if (copyBack) {
+        size_t len = getGuardedCopyOriginalLen(dataBuf);
+        memcpy(arrObj->contents, dataBuf, len);
+    }
+
+    if (release) {
+        result = (u1*) freeGuardedCopy(dataBuf);
+    } else {
+        result = (u1*) getGuardedCopyOriginalPtr(dataBuf);
+    }
+
+    /* pointer is to the array contents; back up to the array object */
+    result -= offsetof(ArrayObject, contents);
+
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI functions
+ * ===========================================================================
+ */
+
+static jint Check_GetVersion(JNIEnv* env)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    jint result;
+    result = BASE_ENV(env)->GetVersion(env);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jclass Check_DefineClass(JNIEnv* env, const char* name, jobject loader,
+    const jbyte* buf, jsize bufLen)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, loader);
+    CHECK_UTF_STRING(env, name, false);
+    CHECK_CLASS_NAME(env, name);
+    jclass result;
+    result = BASE_ENV(env)->DefineClass(env, name, loader, buf, bufLen);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jclass Check_FindClass(JNIEnv* env, const char* name)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_UTF_STRING(env, name, false);
+    CHECK_CLASS_NAME(env, name);
+    jclass result;
+    result = BASE_ENV(env)->FindClass(env, name);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jclass Check_GetSuperclass(JNIEnv* env, jclass clazz)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    jclass result;
+    result = BASE_ENV(env)->GetSuperclass(env, clazz);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jboolean Check_IsAssignableFrom(JNIEnv* env, jclass clazz1,
+    jclass clazz2)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz1);
+    CHECK_CLASS(env, clazz2);
+    jboolean result;
+    result = BASE_ENV(env)->IsAssignableFrom(env, clazz1, clazz2);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jmethodID Check_FromReflectedMethod(JNIEnv* env, jobject method)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, method);
+    jmethodID result;
+    result = BASE_ENV(env)->FromReflectedMethod(env, method);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jfieldID Check_FromReflectedField(JNIEnv* env, jobject field)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, field);
+    jfieldID result;
+    result = BASE_ENV(env)->FromReflectedField(env, field);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobject Check_ToReflectedMethod(JNIEnv* env, jclass cls,
+    jmethodID methodID, jboolean isStatic)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, cls);
+    jobject result;
+    result = BASE_ENV(env)->ToReflectedMethod(env, cls, methodID, isStatic);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobject Check_ToReflectedField(JNIEnv* env, jclass cls, jfieldID fieldID,
+    jboolean isStatic)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, cls);
+    jobject result;
+    result = BASE_ENV(env)->ToReflectedField(env, cls, fieldID, isStatic);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jint Check_Throw(JNIEnv* env, jthrowable obj)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, obj);
+    jint result;
+    result = BASE_ENV(env)->Throw(env, obj);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jint Check_ThrowNew(JNIEnv* env, jclass clazz, const char* message)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    CHECK_UTF_STRING(env, message, true);
+    jint result;
+    result = BASE_ENV(env)->ThrowNew(env, clazz, message);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jthrowable Check_ExceptionOccurred(JNIEnv* env)
+{
+    CHECK_ENTER(env, kFlag_ExcepOkay);
+    jthrowable result;
+    result = BASE_ENV(env)->ExceptionOccurred(env);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_ExceptionDescribe(JNIEnv* env)
+{
+    CHECK_ENTER(env, kFlag_ExcepOkay);
+    BASE_ENV(env)->ExceptionDescribe(env);
+    CHECK_EXIT(env);
+}
+
+static void Check_ExceptionClear(JNIEnv* env)
+{
+    CHECK_ENTER(env, kFlag_ExcepOkay);
+    BASE_ENV(env)->ExceptionClear(env);
+    CHECK_EXIT(env);
+}
+
+static void Check_FatalError(JNIEnv* env, const char* msg)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_UTF_STRING(env, msg, true);
+    BASE_ENV(env)->FatalError(env, msg);
+    CHECK_EXIT(env);
+}
+
+static jint Check_PushLocalFrame(JNIEnv* env, jint capacity)
+{
+    CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+    jint result;
+    result = BASE_ENV(env)->PushLocalFrame(env, capacity);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobject Check_PopLocalFrame(JNIEnv* env, jobject res)
+{
+    CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+    CHECK_OBJECT(env, res);
+    jobject result;
+    result = BASE_ENV(env)->PopLocalFrame(env, res);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobject Check_NewGlobalRef(JNIEnv* env, jobject obj)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, obj);
+    jobject result;
+    result = BASE_ENV(env)->NewGlobalRef(env, obj);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_DeleteGlobalRef(JNIEnv* env, jobject globalRef)
+{
+    CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+    CHECK_OBJECT(env, globalRef);
+#ifdef USE_INDIRECT_REF
+    if (globalRef != NULL &&
+        dvmGetJNIRefType(env, globalRef) != JNIGlobalRefType)
+    {
+        LOGW("JNI WARNING: DeleteGlobalRef on non-global %p (type=%d)\n",
+            globalRef, dvmGetJNIRefType(env, globalRef));
+        abortMaybe();
+    } else
+#endif
+    {
+        BASE_ENV(env)->DeleteGlobalRef(env, globalRef);
+    }
+    CHECK_EXIT(env);
+}
+
+static jobject Check_NewLocalRef(JNIEnv* env, jobject ref)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, ref);
+    jobject result;
+    result = BASE_ENV(env)->NewLocalRef(env, ref);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_DeleteLocalRef(JNIEnv* env, jobject localRef)
+{
+    CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+    CHECK_OBJECT(env, localRef);
+#ifdef USE_INDIRECT_REF
+    if (localRef != NULL &&
+        dvmGetJNIRefType(env, localRef) != JNILocalRefType)
+    {
+        LOGW("JNI WARNING: DeleteLocalRef on non-local %p (type=%d)\n",
+            localRef, dvmGetJNIRefType(env, localRef));
+        abortMaybe();
+    } else
+#endif
+    {
+        BASE_ENV(env)->DeleteLocalRef(env, localRef);
+    }
+    CHECK_EXIT(env);
+}
+
+static jint Check_EnsureLocalCapacity(JNIEnv *env, jint capacity)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    jint result;
+    result = BASE_ENV(env)->EnsureLocalCapacity(env, capacity);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jboolean Check_IsSameObject(JNIEnv* env, jobject ref1, jobject ref2)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, ref1);
+    CHECK_OBJECT(env, ref2);
+    jboolean result;
+    result = BASE_ENV(env)->IsSameObject(env, ref1, ref2);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobject Check_AllocObject(JNIEnv* env, jclass clazz)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    jobject result;
+    result = BASE_ENV(env)->AllocObject(env, clazz);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobject Check_NewObject(JNIEnv* env, jclass clazz, jmethodID methodID,
+    ...)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    jobject result;
+    va_list args, tmpArgs;
+
+    va_start(args, methodID);
+
+    va_copy(tmpArgs, args);
+    CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);
+    va_end(tmpArgs);
+
+    result = BASE_ENV(env)->NewObjectV(env, clazz, methodID, args);
+    va_end(args);
+
+    CHECK_EXIT(env);
+    return result;
+}
+static jobject Check_NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID,
+    va_list args)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    jobject result;
+
+    va_list tmpArgs;
+    va_copy(tmpArgs, args);
+    CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);
+    va_end(tmpArgs);
+
+    result = BASE_ENV(env)->NewObjectV(env, clazz, methodID, args);
+    CHECK_EXIT(env);
+    return result;
+}
+static jobject Check_NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID,
+    jvalue* args)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    jobject result;
+
+    CHECK_METHOD_ARGS_A(env, methodID, args);
+    result = BASE_ENV(env)->NewObjectA(env, clazz, methodID, args);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jclass Check_GetObjectClass(JNIEnv* env, jobject obj)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, obj);
+    jclass result;
+    result = BASE_ENV(env)->GetObjectClass(env, obj);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jboolean Check_IsInstanceOf(JNIEnv* env, jobject obj, jclass clazz)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, obj);
+    CHECK_CLASS(env, clazz);
+    jboolean result;
+    result = BASE_ENV(env)->IsInstanceOf(env, obj, clazz);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jmethodID Check_GetMethodID(JNIEnv* env, jclass clazz, const char* name,
+    const char* sig)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    CHECK_UTF_STRING(env, name, false);
+    CHECK_UTF_STRING(env, sig, false);
+    jmethodID result;
+    result = BASE_ENV(env)->GetMethodID(env, clazz, name, sig);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jfieldID Check_GetFieldID(JNIEnv* env, jclass clazz,
+    const char* name, const char* sig)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    CHECK_UTF_STRING(env, name, false);
+    CHECK_UTF_STRING(env, sig, false);
+    jfieldID result;
+    result = BASE_ENV(env)->GetFieldID(env, clazz, name, sig);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jmethodID Check_GetStaticMethodID(JNIEnv* env, jclass clazz,
+    const char* name, const char* sig)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    CHECK_UTF_STRING(env, name, false);
+    CHECK_UTF_STRING(env, sig, false);
+    jmethodID result;
+    result = BASE_ENV(env)->GetStaticMethodID(env, clazz, name, sig);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jfieldID Check_GetStaticFieldID(JNIEnv* env, jclass clazz,
+    const char* name, const char* sig)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    CHECK_UTF_STRING(env, name, false);
+    CHECK_UTF_STRING(env, sig, false);
+    jfieldID result;
+    result = BASE_ENV(env)->GetStaticFieldID(env, clazz, name, sig);
+    CHECK_EXIT(env);
+    return result;
+}
+
+#define GET_STATIC_TYPE_FIELD(_ctype, _jname)                               \
+    static _ctype Check_GetStatic##_jname##Field(JNIEnv* env, jclass clazz, \
+        jfieldID fieldID)                                                   \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_CLASS(env, clazz);                                            \
+        _ctype result;                                                      \
+        checkStaticFieldID(env, clazz, fieldID);                            \
+        result = BASE_ENV(env)->GetStatic##_jname##Field(env, clazz,        \
+            fieldID);                                                       \
+        CHECK_EXIT(env);                                                    \
+        return result;                                                      \
+    }
+GET_STATIC_TYPE_FIELD(jobject, Object);
+GET_STATIC_TYPE_FIELD(jboolean, Boolean);
+GET_STATIC_TYPE_FIELD(jbyte, Byte);
+GET_STATIC_TYPE_FIELD(jchar, Char);
+GET_STATIC_TYPE_FIELD(jshort, Short);
+GET_STATIC_TYPE_FIELD(jint, Int);
+GET_STATIC_TYPE_FIELD(jlong, Long);
+GET_STATIC_TYPE_FIELD(jfloat, Float);
+GET_STATIC_TYPE_FIELD(jdouble, Double);
+
+#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _ftype)                       \
+    static void Check_SetStatic##_jname##Field(JNIEnv* env, jclass clazz,   \
+        jfieldID fieldID, _ctype value)                                     \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_CLASS(env, clazz);                                            \
+        checkStaticFieldID(env, clazz, fieldID);                            \
+        /* "value" arg only used when type == ref */                        \
+        CHECK_FIELD_TYPE(env, (jobject)(u4)value, fieldID, _ftype, true);   \
+        BASE_ENV(env)->SetStatic##_jname##Field(env, clazz, fieldID,        \
+            value);                                                         \
+        CHECK_EXIT(env);                                                    \
+    }
+SET_STATIC_TYPE_FIELD(jobject, Object, PRIM_NOT);
+SET_STATIC_TYPE_FIELD(jboolean, Boolean, PRIM_BOOLEAN);
+SET_STATIC_TYPE_FIELD(jbyte, Byte, PRIM_BYTE);
+SET_STATIC_TYPE_FIELD(jchar, Char, PRIM_CHAR);
+SET_STATIC_TYPE_FIELD(jshort, Short, PRIM_SHORT);
+SET_STATIC_TYPE_FIELD(jint, Int, PRIM_INT);
+SET_STATIC_TYPE_FIELD(jlong, Long, PRIM_LONG);
+SET_STATIC_TYPE_FIELD(jfloat, Float, PRIM_FLOAT);
+SET_STATIC_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE);
+
+#define GET_TYPE_FIELD(_ctype, _jname)                                      \
+    static _ctype Check_Get##_jname##Field(JNIEnv* env, jobject obj,        \
+        jfieldID fieldID)                                                   \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_OBJECT(env, obj);                                             \
+        _ctype result;                                                      \
+        CHECK_INST_FIELD_ID(env, obj, fieldID);                             \
+        result = BASE_ENV(env)->Get##_jname##Field(env, obj, fieldID);      \
+        CHECK_EXIT(env);                                                    \
+        return result;                                                      \
+    }
+GET_TYPE_FIELD(jobject, Object);
+GET_TYPE_FIELD(jboolean, Boolean);
+GET_TYPE_FIELD(jbyte, Byte);
+GET_TYPE_FIELD(jchar, Char);
+GET_TYPE_FIELD(jshort, Short);
+GET_TYPE_FIELD(jint, Int);
+GET_TYPE_FIELD(jlong, Long);
+GET_TYPE_FIELD(jfloat, Float);
+GET_TYPE_FIELD(jdouble, Double);
+
+#define SET_TYPE_FIELD(_ctype, _jname, _ftype)                              \
+    static void Check_Set##_jname##Field(JNIEnv* env, jobject obj,          \
+        jfieldID fieldID, _ctype value)                                     \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_OBJECT(env, obj);                                             \
+        CHECK_INST_FIELD_ID(env, obj, fieldID);                             \
+        /* "value" arg only used when type == ref */                        \
+        CHECK_FIELD_TYPE(env, (jobject)(u4) value, fieldID, _ftype, false); \
+        BASE_ENV(env)->Set##_jname##Field(env, obj, fieldID, value);        \
+        CHECK_EXIT(env);                                                    \
+    }
+SET_TYPE_FIELD(jobject, Object, PRIM_NOT);
+SET_TYPE_FIELD(jboolean, Boolean, PRIM_BOOLEAN);
+SET_TYPE_FIELD(jbyte, Byte, PRIM_BYTE);
+SET_TYPE_FIELD(jchar, Char, PRIM_CHAR);
+SET_TYPE_FIELD(jshort, Short, PRIM_SHORT);
+SET_TYPE_FIELD(jint, Int, PRIM_INT);
+SET_TYPE_FIELD(jlong, Long, PRIM_LONG);
+SET_TYPE_FIELD(jfloat, Float, PRIM_FLOAT);
+SET_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE);
+
+#define CALL_VIRTUAL(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig)   \
+    static _ctype Check_Call##_jname##Method(JNIEnv* env, jobject obj,      \
+        jmethodID methodID, ...)                                            \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_OBJECT(env, obj);                                             \
+        CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
+        _retdecl;                                                           \
+        va_list args, tmpArgs;                                              \
+        va_start(args, methodID);                                           \
+        va_copy(tmpArgs, args);                                             \
+        CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);                        \
+        va_end(tmpArgs);                                                    \
+        _retasgn BASE_ENV(env)->Call##_jname##MethodV(env, obj, methodID,   \
+            args);                                                          \
+        va_end(args);                                                       \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Check_Call##_jname##MethodV(JNIEnv* env, jobject obj,     \
+        jmethodID methodID, va_list args)                                   \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_OBJECT(env, obj);                                             \
+        CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
+        _retdecl;                                                           \
+        va_list tmpArgs;                                                    \
+        va_copy(tmpArgs, args);                                             \
+        CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);                        \
+        va_end(tmpArgs);                                                    \
+        _retasgn BASE_ENV(env)->Call##_jname##MethodV(env, obj, methodID,   \
+            args);                                                          \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Check_Call##_jname##MethodA(JNIEnv* env, jobject obj,     \
+        jmethodID methodID, jvalue* args)                                   \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_OBJECT(env, obj);                                             \
+        CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
+        _retdecl;                                                           \
+        CHECK_METHOD_ARGS_A(env, methodID, args);                           \
+        _retasgn BASE_ENV(env)->Call##_jname##MethodA(env, obj, methodID,   \
+            args);                                                          \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }
+CALL_VIRTUAL(jobject, Object, Object* result, result=, result, 'L');
+CALL_VIRTUAL(jboolean, Boolean, jboolean result, result=, result, 'Z');
+CALL_VIRTUAL(jbyte, Byte, jbyte result, result=, result, 'B');
+CALL_VIRTUAL(jchar, Char, jchar result, result=, result, 'C');
+CALL_VIRTUAL(jshort, Short, jshort result, result=, result, 'S');
+CALL_VIRTUAL(jint, Int, jint result, result=, result, 'I');
+CALL_VIRTUAL(jlong, Long, jlong result, result=, result, 'J');
+CALL_VIRTUAL(jfloat, Float, jfloat result, result=, result, 'F');
+CALL_VIRTUAL(jdouble, Double, jdouble result, result=, result, 'D');
+CALL_VIRTUAL(void, Void, , , , 'V');
+
+#define CALL_NONVIRTUAL(_ctype, _jname, _retdecl, _retasgn, _retok,         \
+        _retsig)                                                            \
+    static _ctype Check_CallNonvirtual##_jname##Method(JNIEnv* env,         \
+        jobject obj, jclass clazz, jmethodID methodID, ...)                 \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_CLASS(env, clazz);                                            \
+        CHECK_OBJECT(env, obj);                                             \
+        CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
+        _retdecl;                                                           \
+        va_list args, tmpArgs;                                              \
+        va_start(args, methodID);                                           \
+        va_copy(tmpArgs, args);                                             \
+        CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);                        \
+        va_end(tmpArgs);                                                    \
+        _retasgn BASE_ENV(env)->CallNonvirtual##_jname##MethodV(env, obj,   \
+            clazz, methodID, args);                                         \
+        va_end(args);                                                       \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Check_CallNonvirtual##_jname##MethodV(JNIEnv* env,        \
+        jobject obj, jclass clazz, jmethodID methodID, va_list args)        \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_CLASS(env, clazz);                                            \
+        CHECK_OBJECT(env, obj);                                             \
+        CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
+        _retdecl;                                                           \
+        va_list tmpArgs;                                                    \
+        va_copy(tmpArgs, args);                                             \
+        CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);                        \
+        va_end(tmpArgs);                                                    \
+        _retasgn BASE_ENV(env)->CallNonvirtual##_jname##MethodV(env, obj,   \
+            clazz, methodID, args);                                         \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Check_CallNonvirtual##_jname##MethodA(JNIEnv* env,        \
+        jobject obj, jclass clazz, jmethodID methodID, jvalue* args)        \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_CLASS(env, clazz);                                            \
+        CHECK_OBJECT(env, obj);                                             \
+        CHECK_SIG(env, methodID, _retsig, false);                           \
+        CHECK_VIRTUAL_METHOD(env, obj, methodID);                           \
+        _retdecl;                                                           \
+        CHECK_METHOD_ARGS_A(env, methodID, args);                           \
+        _retasgn BASE_ENV(env)->CallNonvirtual##_jname##MethodA(env, obj,   \
+            clazz, methodID, args);                                         \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }
+CALL_NONVIRTUAL(jobject, Object, Object* result, result=, result, 'L');
+CALL_NONVIRTUAL(jboolean, Boolean, jboolean result, result=, result, 'Z');
+CALL_NONVIRTUAL(jbyte, Byte, jbyte result, result=, result, 'B');
+CALL_NONVIRTUAL(jchar, Char, jchar result, result=, result, 'C');
+CALL_NONVIRTUAL(jshort, Short, jshort result, result=, result, 'S');
+CALL_NONVIRTUAL(jint, Int, jint result, result=, result, 'I');
+CALL_NONVIRTUAL(jlong, Long, jlong result, result=, result, 'J');
+CALL_NONVIRTUAL(jfloat, Float, jfloat result, result=, result, 'F');
+CALL_NONVIRTUAL(jdouble, Double, jdouble result, result=, result, 'D');
+CALL_NONVIRTUAL(void, Void, , , , 'V');
+
+
+#define CALL_STATIC(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig)    \
+    static _ctype Check_CallStatic##_jname##Method(JNIEnv* env,             \
+        jclass clazz, jmethodID methodID, ...)                              \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_CLASS(env, clazz);                                            \
+        CHECK_SIG(env, methodID, _retsig, true);                            \
+        CHECK_STATIC_METHOD(env, clazz, methodID);                          \
+        _retdecl;                                                           \
+        va_list args, tmpArgs;                                              \
+        va_start(args, methodID);                                           \
+        va_copy(tmpArgs, args);                                             \
+        CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);                        \
+        va_end(tmpArgs);                                                    \
+        _retasgn BASE_ENV(env)->CallStatic##_jname##MethodV(env, clazz,     \
+            methodID, args);                                                \
+        va_end(args);                                                       \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Check_CallStatic##_jname##MethodV(JNIEnv* env,            \
+        jclass clazz, jmethodID methodID, va_list args)                     \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_CLASS(env, clazz);                                            \
+        CHECK_SIG(env, methodID, _retsig, true);                            \
+        CHECK_STATIC_METHOD(env, clazz, methodID);                          \
+        _retdecl;                                                           \
+        va_list tmpArgs;                                                    \
+        va_copy(tmpArgs, args);                                             \
+        CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);                        \
+        va_end(tmpArgs);                                                    \
+        _retasgn BASE_ENV(env)->CallStatic##_jname##MethodV(env, clazz,     \
+            methodID, args);                                                \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Check_CallStatic##_jname##MethodA(JNIEnv* env,            \
+        jclass clazz, jmethodID methodID, jvalue* args)                     \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_CLASS(env, clazz);                                            \
+        CHECK_SIG(env, methodID, _retsig, true);                            \
+        CHECK_STATIC_METHOD(env, clazz, methodID);                          \
+        _retdecl;                                                           \
+        CHECK_METHOD_ARGS_A(env, methodID, args);                           \
+        _retasgn BASE_ENV(env)->CallStatic##_jname##MethodA(env, clazz,     \
+            methodID, args);                                                \
+        CHECK_EXIT(env);                                                    \
+        return _retok;                                                      \
+    }
+CALL_STATIC(jobject, Object, Object* result, result=, result, 'L');
+CALL_STATIC(jboolean, Boolean, jboolean result, result=, result, 'Z');
+CALL_STATIC(jbyte, Byte, jbyte result, result=, result, 'B');
+CALL_STATIC(jchar, Char, jchar result, result=, result, 'C');
+CALL_STATIC(jshort, Short, jshort result, result=, result, 'S');
+CALL_STATIC(jint, Int, jint result, result=, result, 'I');
+CALL_STATIC(jlong, Long, jlong result, result=, result, 'J');
+CALL_STATIC(jfloat, Float, jfloat result, result=, result, 'F');
+CALL_STATIC(jdouble, Double, jdouble result, result=, result, 'D');
+CALL_STATIC(void, Void, , , , 'V');
+
+static jstring Check_NewString(JNIEnv* env, const jchar* unicodeChars,
+    jsize len)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    jstring result;
+    result = BASE_ENV(env)->NewString(env, unicodeChars, len);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jsize Check_GetStringLength(JNIEnv* env, jstring string)
+{
+    CHECK_ENTER(env, kFlag_CritOkay);
+    CHECK_STRING(env, string);
+    jsize result;
+    result = BASE_ENV(env)->GetStringLength(env, string);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static const jchar* Check_GetStringChars(JNIEnv* env, jstring string,
+    jboolean* isCopy)
+{
+    CHECK_ENTER(env, kFlag_CritOkay);
+    CHECK_STRING(env, string);
+    const jchar* result;
+    result = BASE_ENV(env)->GetStringChars(env, string, isCopy);
+    if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+        // TODO: fix for indirect
+        int len = dvmStringLen(string) * 2;
+        result = (const jchar*) createGuardedCopy(result, len, false);
+        if (isCopy != NULL)
+            *isCopy = JNI_TRUE;
+    }
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_ReleaseStringChars(JNIEnv* env, jstring string,
+    const jchar* chars)
+{
+    CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+    CHECK_STRING(env, string);
+    CHECK_NON_NULL(env, chars);
+    if (((JNIEnvExt*)env)->forceDataCopy) {
+        if (!checkGuardedCopy(chars, false)) {
+            LOGE("JNI: failed guarded copy check in ReleaseStringChars\n");
+            abortMaybe();
+            return;
+        }
+        chars = (const jchar*) freeGuardedCopy((jchar*)chars);
+    }
+    BASE_ENV(env)->ReleaseStringChars(env, string, chars);
+    CHECK_EXIT(env);
+}
+
+static jstring Check_NewStringUTF(JNIEnv* env, const char* bytes)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_UTF_STRING(env, bytes, true);
+    jstring result;
+    result = BASE_ENV(env)->NewStringUTF(env, bytes);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jsize Check_GetStringUTFLength(JNIEnv* env, jstring string)
+{
+    CHECK_ENTER(env, kFlag_CritOkay);
+    CHECK_STRING(env, string);
+    jsize result;
+    result = BASE_ENV(env)->GetStringUTFLength(env, string);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static const char* Check_GetStringUTFChars(JNIEnv* env, jstring string,
+    jboolean* isCopy)
+{
+    CHECK_ENTER(env, kFlag_CritOkay);
+    CHECK_STRING(env, string);
+    const char* result;
+    result = BASE_ENV(env)->GetStringUTFChars(env, string, isCopy);
+    if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+        // TODO: fix for indirect
+        int len = dvmStringUtf8ByteLen(string) + 1;
+        result = (const char*) createGuardedCopy(result, len, false);
+        if (isCopy != NULL)
+            *isCopy = JNI_TRUE;
+    }
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_ReleaseStringUTFChars(JNIEnv* env, jstring string,
+    const char* utf)
+{
+    CHECK_ENTER(env, kFlag_ExcepOkay);
+    CHECK_STRING(env, string);
+    CHECK_NON_NULL(env, utf);
+    if (((JNIEnvExt*)env)->forceDataCopy) {
+        //int len = dvmStringUtf8ByteLen(string) + 1;
+        if (!checkGuardedCopy(utf, false)) {
+            LOGE("JNI: failed guarded copy check in ReleaseStringUTFChars\n");
+            abortMaybe();
+            return;
+        }
+        utf = (const char*) freeGuardedCopy((char*)utf);
+    }
+    BASE_ENV(env)->ReleaseStringUTFChars(env, string, utf);
+    CHECK_EXIT(env);
+}
+
+static jsize Check_GetArrayLength(JNIEnv* env, jarray array)
+{
+    CHECK_ENTER(env, kFlag_CritOkay);
+    CHECK_ARRAY(env, array);
+    jsize result;
+    result = BASE_ENV(env)->GetArrayLength(env, array);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobjectArray Check_NewObjectArray(JNIEnv* env, jsize length,
+    jclass elementClass, jobject initialElement)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, elementClass);
+    CHECK_OBJECT(env, initialElement);
+    CHECK_LENGTH_POSITIVE(env, length);
+    jobjectArray result;
+    result = BASE_ENV(env)->NewObjectArray(env, length, elementClass,
+                                            initialElement);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobject Check_GetObjectArrayElement(JNIEnv* env, jobjectArray array,
+    jsize index)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_ARRAY(env, array);
+    jobject result;
+    result = BASE_ENV(env)->GetObjectArrayElement(env, array, index);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_SetObjectArrayElement(JNIEnv* env, jobjectArray array,
+    jsize index, jobject value)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_ARRAY(env, array);
+    BASE_ENV(env)->SetObjectArrayElement(env, array, index, value);
+    CHECK_EXIT(env);
+}
+
+#define NEW_PRIMITIVE_ARRAY(_artype, _jname)                                \
+    static _artype Check_New##_jname##Array(JNIEnv* env, jsize length)      \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_LENGTH_POSITIVE(env, length);                                 \
+        _artype result;                                                     \
+        result = BASE_ENV(env)->New##_jname##Array(env, length);            \
+        CHECK_EXIT(env);                                                    \
+        return result;                                                      \
+    }
+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);
+
+
+#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)                        \
+    static _ctype* Check_Get##_jname##ArrayElements(JNIEnv* env,            \
+        _ctype##Array array, jboolean* isCopy)                              \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default);                                    \
+        CHECK_ARRAY(env, array);                                            \
+        _ctype* result;                                                     \
+        result = BASE_ENV(env)->Get##_jname##ArrayElements(env,             \
+            array, isCopy);                                                 \
+        if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {           \
+            result = (_ctype*) createGuardedPACopy(env, array, isCopy);     \
+        }                                                                   \
+        CHECK_EXIT(env);                                                    \
+        return result;                                                      \
+    }
+
+#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)                    \
+    static void Check_Release##_jname##ArrayElements(JNIEnv* env,           \
+        _ctype##Array array, _ctype* elems, jint mode)                      \
+    {                                                                       \
+        CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);                  \
+        CHECK_ARRAY(env, array);                                            \
+        CHECK_NON_NULL(env, elems);                                         \
+        CHECK_RELEASE_MODE(env, mode);                                      \
+        if (((JNIEnvExt*)env)->forceDataCopy) {                             \
+            elems = (_ctype*) releaseGuardedPACopy(env, array, elems, mode);\
+        }                                                                   \
+        BASE_ENV(env)->Release##_jname##ArrayElements(env,                  \
+            array, elems, mode);                                            \
+        CHECK_EXIT(env);                                                    \
+    }
+
+#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_ENTER(env, kFlag_Default);                                    \
+        CHECK_ARRAY(env, array);                                            \
+        BASE_ENV(env)->Get##_jname##ArrayRegion(env, array, start,          \
+            len, buf);                                                      \
+        CHECK_EXIT(env);                                                    \
+    }
+
+#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_ENTER(env, kFlag_Default);                                    \
+        CHECK_ARRAY(env, array);                                            \
+        BASE_ENV(env)->Set##_jname##ArrayRegion(env, array, start,          \
+            len, buf);                                                      \
+        CHECK_EXIT(env);                                                    \
+    }
+
+#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_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    jint result;
+    result = BASE_ENV(env)->RegisterNatives(env, clazz, methods, nMethods);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jint Check_UnregisterNatives(JNIEnv* env, jclass clazz)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_CLASS(env, clazz);
+    jint result;
+    result = BASE_ENV(env)->UnregisterNatives(env, clazz);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jint Check_MonitorEnter(JNIEnv* env, jobject obj)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, obj);
+    jint result;
+    result = BASE_ENV(env)->MonitorEnter(env, obj);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jint Check_MonitorExit(JNIEnv* env, jobject obj)
+{
+    CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+    CHECK_OBJECT(env, obj);
+    jint result;
+    result = BASE_ENV(env)->MonitorExit(env, obj);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jint Check_GetJavaVM(JNIEnv *env, JavaVM **vm)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    jint result;
+    result = BASE_ENV(env)->GetJavaVM(env, vm);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_GetStringRegion(JNIEnv* env, jstring str, jsize start,
+    jsize len, jchar* buf)
+{
+    CHECK_ENTER(env, kFlag_CritOkay);
+    CHECK_STRING(env, str);
+    BASE_ENV(env)->GetStringRegion(env, str, start, len, buf);
+    CHECK_EXIT(env);
+}
+
+static void Check_GetStringUTFRegion(JNIEnv* env, jstring str, jsize start,
+    jsize len, char* buf)
+{
+    CHECK_ENTER(env, kFlag_CritOkay);
+    CHECK_STRING(env, str);
+    BASE_ENV(env)->GetStringUTFRegion(env, str, start, len, buf);
+    CHECK_EXIT(env);
+}
+
+static void* Check_GetPrimitiveArrayCritical(JNIEnv* env, jarray array,
+    jboolean* isCopy)
+{
+    CHECK_ENTER(env, kFlag_CritGet);
+    CHECK_ARRAY(env, array);
+    void* result;
+    result = BASE_ENV(env)->GetPrimitiveArrayCritical(env, array, isCopy);
+    if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+        result = createGuardedPACopy(env, array, isCopy);
+    }
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array,
+    void* carray, jint mode)
+{
+    CHECK_ENTER(env, kFlag_CritRelease | kFlag_ExcepOkay);
+    CHECK_ARRAY(env, array);
+    CHECK_NON_NULL(env, carray);
+    CHECK_RELEASE_MODE(env, mode);
+    if (((JNIEnvExt*)env)->forceDataCopy) {
+        carray = releaseGuardedPACopy(env, array, carray, mode);
+    }
+    BASE_ENV(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
+    CHECK_EXIT(env);
+}
+
+static const jchar* Check_GetStringCritical(JNIEnv* env, jstring string,
+    jboolean* isCopy)
+{
+    CHECK_ENTER(env, kFlag_CritGet);
+    CHECK_STRING(env, string);
+    const jchar* result;
+    result = BASE_ENV(env)->GetStringCritical(env, string, isCopy);
+    if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+        // TODO: fix for indirect
+        int len = dvmStringLen(string) * 2;
+        result = (const jchar*) createGuardedCopy(result, len, false);
+        if (isCopy != NULL)
+            *isCopy = JNI_TRUE;
+    }
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_ReleaseStringCritical(JNIEnv* env, jstring string,
+    const jchar* carray)
+{
+    CHECK_ENTER(env, kFlag_CritRelease | kFlag_ExcepOkay);
+    CHECK_STRING(env, string);
+    CHECK_NON_NULL(env, carray);
+    if (((JNIEnvExt*)env)->forceDataCopy) {
+        if (!checkGuardedCopy(carray, false)) {
+            LOGE("JNI: failed guarded copy check in ReleaseStringCritical\n");
+            abortMaybe();
+            return;
+        }
+        carray = (const jchar*) freeGuardedCopy((jchar*)carray);
+    }
+    BASE_ENV(env)->ReleaseStringCritical(env, string, carray);
+    CHECK_EXIT(env);
+}
+
+static jweak Check_NewWeakGlobalRef(JNIEnv* env, jobject obj)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, obj);
+    jweak result;
+    result = BASE_ENV(env)->NewWeakGlobalRef(env, obj);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void Check_DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
+{
+    CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+    CHECK_OBJECT(env, obj);
+    BASE_ENV(env)->DeleteWeakGlobalRef(env, obj);
+    CHECK_EXIT(env);
+}
+
+static jboolean Check_ExceptionCheck(JNIEnv* env)
+{
+    CHECK_ENTER(env, kFlag_CritOkay | kFlag_ExcepOkay);
+    jboolean result;
+    result = BASE_ENV(env)->ExceptionCheck(env);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobjectRefType Check_GetObjectRefType(JNIEnv* env, jobject obj)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, obj);
+    jobjectRefType result;
+    result = BASE_ENV(env)->GetObjectRefType(env, obj);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static jobject Check_NewDirectByteBuffer(JNIEnv* env, void* address,
+    jlong capacity)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    jobject result;
+    if (address == NULL || capacity < 0) {
+        LOGW("JNI WARNING: invalid values for address (%p) or capacity (%ld)\n",
+            address, (long) capacity);
+        abortMaybe();
+        return NULL;
+    }
+    result = BASE_ENV(env)->NewDirectByteBuffer(env, address, capacity);
+    CHECK_EXIT(env);
+    return result;
+}
+
+static void* Check_GetDirectBufferAddress(JNIEnv* env, jobject buf)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, buf);
+    void* result = BASE_ENV(env)->GetDirectBufferAddress(env, buf);
+    CHECK_EXIT(env);
+
+    /* optional - check result vs. "safe" implementation */
+    if (kRedundantDirectBufferTest) {
+        jobject platformAddr = NULL;
+        void* checkResult = NULL;
+
+        /*
+         * Start by determining if the object supports the DirectBuffer
+         * interfaces.  Note this does not guarantee that it's a direct buffer.
+         */
+        if (JNI_FALSE == (*env)->IsInstanceOf(env, buf,
+                gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer))
+        {
+            goto bail;
+        }
+
+        /*
+         * Get the PlatformAddress object.
+         *
+         * If this isn't a direct buffer, platformAddr will be NULL and/or an
+         * exception will have been thrown.
+         */
+        platformAddr = (*env)->CallObjectMethod(env, buf,
+            (jmethodID) gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
+
+        if ((*env)->ExceptionCheck(env)) {
+            (*env)->ExceptionClear(env);
+            platformAddr = NULL;
+        }
+        if (platformAddr == NULL) {
+            LOGV("Got request for address of non-direct buffer\n");
+            goto bail;
+        }
+
+        jclass platformAddrClass = (*env)->FindClass(env,
+            "org/apache/harmony/luni/platform/PlatformAddress");
+        jmethodID toLongMethod = (*env)->GetMethodID(env, platformAddrClass,
+            "toLong", "()J");
+        checkResult = (void*)(u4)(*env)->CallLongMethod(env, platformAddr,
+                toLongMethod);
+
+    bail:
+        if (platformAddr != NULL)
+            (*env)->DeleteLocalRef(env, platformAddr);
+
+        if (result != checkResult) {
+            LOGW("JNI WARNING: direct buffer result mismatch (%p vs %p)\n",
+                result, checkResult);
+            abortMaybe();
+            /* keep going */
+        }
+    }
+
+    return result;
+}
+
+static jlong Check_GetDirectBufferCapacity(JNIEnv* env, jobject buf)
+{
+    CHECK_ENTER(env, kFlag_Default);
+    CHECK_OBJECT(env, buf);
+    /* TODO: verify "buf" is an instance of java.nio.Buffer */
+    jlong result = BASE_ENV(env)->GetDirectBufferCapacity(env, buf);
+    CHECK_EXIT(env);
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI invocation functions
+ * ===========================================================================
+ */
+
+static jint Check_DestroyJavaVM(JavaVM* vm)
+{
+    CHECK_VMENTER(vm, false);
+    jint result;
+    result = BASE_VM(vm)->DestroyJavaVM(vm);
+    CHECK_VMEXIT(vm, false);
+    return result;
+}
+
+static jint Check_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env,
+    void* thr_args)
+{
+    CHECK_VMENTER(vm, false);
+    jint result;
+    result = BASE_VM(vm)->AttachCurrentThread(vm, p_env, thr_args);
+    CHECK_VMEXIT(vm, true);
+    return result;
+}
+
+static jint Check_AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
+    void* thr_args)
+{
+    CHECK_VMENTER(vm, false);
+    jint result;
+    result = BASE_VM(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args);
+    CHECK_VMEXIT(vm, true);
+    return result;
+}
+
+static jint Check_DetachCurrentThread(JavaVM* vm)
+{
+    CHECK_VMENTER(vm, true);
+    jint result;
+    result = BASE_VM(vm)->DetachCurrentThread(vm);
+    CHECK_VMEXIT(vm, false);
+    return result;
+}
+
+static jint Check_GetEnv(JavaVM* vm, void** env, jint version)
+{
+    CHECK_VMENTER(vm, true);
+    jint result;
+    result = BASE_VM(vm)->GetEnv(vm, env, version);
+    CHECK_VMEXIT(vm, true);
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      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/Common.h b/vm/Common.h
new file mode 100644
index 0000000..ae0938a
--- /dev/null
+++ b/vm/Common.h
@@ -0,0 +1,155 @@
+/*
+ * 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 defines for all Dalvik code.
+ */
+#ifndef _DALVIK_COMMON
+#define _DALVIK_COMMON
+
+#ifndef LOG_TAG
+# define LOG_TAG "dalvikvm"
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+
+#if !defined(NDEBUG) && defined(WITH_DALVIK_ASSERT)
+# undef assert
+# define assert(x) \
+    ((x) ? ((void)0) : (LOGE("ASSERT FAILED (%s:%d): %s\n", \
+        __FILE__, __LINE__, #x), *(int*)39=39, 0) )
+#endif
+
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+#define LIKELY(exp) (__builtin_expect((exp) != 0, true))
+#define UNLIKELY(exp) (__builtin_expect((exp) != 0, false))
+
+/*
+ * If "very verbose" logging is enabled, make it equivalent to LOGV.
+ * Otherwise, make it disappear.
+ *
+ * Define this above the #include "Dalvik.h" to enable for only a
+ * single file.
+ */
+/* #define VERY_VERBOSE_LOG */
+#if defined(VERY_VERBOSE_LOG)
+# define LOGVV      LOGV
+# define IF_LOGVV() IF_LOGV()
+#else
+# define LOGVV(...) ((void)0)
+# define IF_LOGVV() if (false)
+#endif
+
+
+/*
+ * These match the definitions in the VM specification.
+ */
+#ifdef HAVE_STDINT_H
+# include <stdint.h>    /* C99 */
+typedef uint8_t             u1;
+typedef uint16_t            u2;
+typedef uint32_t            u4;
+typedef uint64_t            u8;
+typedef int8_t              s1;
+typedef int16_t             s2;
+typedef int32_t             s4;
+typedef int64_t             s8;
+#else
+typedef unsigned char       u1;
+typedef unsigned short      u2;
+typedef unsigned int        u4;
+typedef unsigned long long  u8;
+typedef signed char         s1;
+typedef signed short        s2;
+typedef signed int          s4;
+typedef signed long long    s8;
+#endif
+
+/*
+ * Storage for primitive types and object references.
+ *
+ * Some parts of the code (notably object field access) assume that values
+ * are "left aligned", i.e. given "JValue jv", "jv.i" and "*((s4*)&jv)"
+ * yield the same result.  This seems to be guaranteed by gcc on big- and
+ * little-endian systems.
+ */
+typedef union JValue {
+    u1      z;
+    s1      b;
+    u2      c;
+    s2      s;
+    s4      i;
+    s8      j;
+    float   f;
+    double  d;
+    void*   l;
+} JValue;
+
+/*
+ * The <stdbool.h> definition uses _Bool, a type known to the compiler.
+ */
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>   /* C99 */
+#else
+# ifndef __bool_true_false_are_defined
+typedef enum { false=0, true=!false } bool;
+# define __bool_true_false_are_defined 1
+# endif
+#endif
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+
+#if defined(HAVE_ENDIAN_H)
+# include <endian.h>
+#else /*not HAVE_ENDIAN_H*/
+# define __BIG_ENDIAN 4321
+# define __LITTLE_ENDIAN 1234
+# if defined(HAVE_LITTLE_ENDIAN)
+#  define __BYTE_ORDER __LITTLE_ENDIAN
+# else
+#  define __BYTE_ORDER __BIG_ENDIAN
+# endif
+#endif /*not HAVE_ENDIAN_H*/
+
+
+#if 0
+/*
+ * Pretend we have the Android logging macros.  These are replaced by the
+ * Android logging implementation.
+ */
+#define ANDROID_LOG_DEBUG 3
+#define LOGV(...)    LOG_PRI(2, 0, __VA_ARGS__)
+#define LOGD(...)    LOG_PRI(3, 0, __VA_ARGS__)
+#define LOGI(...)    LOG_PRI(4, 0, __VA_ARGS__)
+#define LOGW(...)    LOG_PRI(5, 0, __VA_ARGS__)
+#define LOGE(...)    LOG_PRI(6, 0, __VA_ARGS__)
+#define MIN_LOG_LEVEL   2
+
+#define LOG_PRI(priority, tag, ...) do {                            \
+        if (priority >= MIN_LOG_LEVEL) {                            \
+            dvmFprintf(stdout, "%s:%-4d ", __FILE__, __LINE__);     \
+            dvmFprintf(stdout, __VA_ARGS__);                        \
+        }                                                           \
+    } while(0)
+#else
+# include "utils/Log.h"
+#endif
+
+#endif /*_DALVIK_COMMON*/
diff --git a/vm/Dalvik.h b/vm/Dalvik.h
new file mode 100644
index 0000000..5779b08
--- /dev/null
+++ b/vm/Dalvik.h
@@ -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.
+ */
+
+/*
+ * All-inclusive internal header file.  Include this to get everything useful.
+ */
+#ifndef _DALVIK_DALVIK
+#define _DALVIK_DALVIK
+
+#include <pthread.h>
+
+#include "Common.h"
+#include "Inlines.h"
+#include "Misc.h"
+#include "Bits.h"
+#include "libdex/SysUtil.h"
+#include "libdex/DexFile.h"
+#include "libdex/DexProto.h"
+#include "libdex/ZipArchive.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/HeapWorker.h"
+#include "alloc/GC.h"
+#include "alloc/WriteBarrier.h"
+#include "oo/AccessCheck.h"
+#include "JarFile.h"
+#include "Properties.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/OpCode.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*/
diff --git a/vm/DalvikVersion.h b/vm/DalvikVersion.h
new file mode 100644
index 0000000..3bc37b0
--- /dev/null
+++ b/vm/DalvikVersion.h
@@ -0,0 +1,37 @@
+/*
+ * 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 VM version info.
+ */
+#ifndef _DALVIK_VERSION
+#define _DALVIK_VERSION
+
+/*
+ * The version we show to tourists.
+ */
+#define DALVIK_MAJOR_VERSION    1
+#define DALVIK_MINOR_VERSION    4
+#define DALVIK_BUG_VERSION      0
+
+/*
+ * VM build number.  This must change whenever something that affects the
+ * way classes load changes, e.g. field ordering or vtable layout.  Changing
+ * this guarantees that the optimized form of the DEX file is regenerated.
+ */
+#define DALVIK_VM_BUILD         23
+
+#endif /*_DALVIK_VERSION*/
diff --git a/vm/Ddm.c b/vm/Ddm.c
new file mode 100644
index 0000000..774ed3f
--- /dev/null
+++ b/vm/Ddm.c
@@ -0,0 +1,606 @@
+/*
+ * 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;
+    bool result = false;
+
+    assert(dataLen >= 0);
+
+    /*
+     * Prep DdmServer.  We could throw this in gDvm.
+     */
+    ClassObject* ddmServerClass;
+    Method* dispatch;
+
+    ddmServerClass =
+        dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL);
+    if (ddmServerClass == NULL) {
+        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n");
+        goto bail;
+    }
+    dispatch = dvmFindDirectMethodByDescriptor(ddmServerClass, "dispatch",
+                    "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+    if (dispatch == NULL) {
+        LOGW("Unable to find DdmServer.dispatch\n");
+        goto bail;
+    }
+
+    /*
+     * Prep Chunk.
+     */
+    int chunkTypeOff, chunkDataOff, chunkOffsetOff, chunkLengthOff;
+    ClassObject* chunkClass;
+    chunkClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/Chunk;", NULL);
+    if (chunkClass == NULL) {
+        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.Chunk\n");
+        goto bail;
+    }
+    chunkTypeOff = dvmFindFieldOffset(chunkClass, "type", "I");
+    chunkDataOff = dvmFindFieldOffset(chunkClass, "data", "[B");
+    chunkOffsetOff = dvmFindFieldOffset(chunkClass, "offset", "I");
+    chunkLengthOff = dvmFindFieldOffset(chunkClass, "length", "I");
+    if (chunkTypeOff < 0 || chunkDataOff < 0 ||
+        chunkOffsetOff < 0 || chunkLengthOff < 0)
+    {
+        LOGW("Unable to find all chunk fields\n");
+        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) {
+        LOGW("array alloc failed (%d)\n", 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) {
+        LOGW("WARNING: bad chunk found (len=%u pktLen=%d)\n", length, dataLen);
+        goto bail;
+    }
+
+    /*
+     * Call the handler.
+     */
+    JValue callRes;
+    dvmCallMethod(self, dispatch, NULL, &callRes, type, dataArray, offset,
+        length);
+    if (dvmCheckException(self)) {
+        LOGI("Exception thrown by dispatcher for 0x%08x\n", type);
+        dvmLogExceptionStackTrace();
+        dvmClearException(self);
+        goto bail;
+    }
+
+    Object* chunk;
+    ArrayObject* replyData;
+    chunk = (Object*) callRes.l;
+    if (chunk == NULL)
+        goto bail;
+
+    /*
+     * 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.
+     * (If we do an alloc in here, we need to dvmAddTrackedAlloc 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, chunkTypeOff);
+    replyData = (ArrayObject*) dvmGetFieldObject(chunk, chunkDataOff);
+    offset = dvmGetFieldInt(chunk, chunkOffsetOff);
+    length = dvmGetFieldInt(chunk, chunkLengthOff);
+
+    LOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d\n",
+        type, replyData, offset, length);
+
+    if (length == 0 || replyData == NULL)
+        goto bail;
+    if (offset + length > replyData->length) {
+        LOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d\n",
+            offset, length, replyData->length);
+        goto bail;
+    }
+
+    u1* reply;
+    reply = (u1*) malloc(length + kChunkHdrLen);
+    if (reply == NULL) {
+        LOGW("malloc %d failed\n", 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;
+
+    LOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d\n",
+        (char*) reply, reply, length);
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) dataArray, NULL);
+    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)
+{
+    ClassObject* ddmServerClass;
+    Method* bcast;
+
+    ddmServerClass =
+        dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL);
+    if (ddmServerClass == NULL) {
+        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n");
+        goto bail;
+    }
+    bcast = dvmFindDirectMethodByDescriptor(ddmServerClass, "broadcast", "(I)V");
+    if (bcast == NULL) {
+        LOGW("Unable to find DdmServer.broadcast\n");
+        goto bail;
+    }
+
+    Thread* self = dvmThreadSelf();
+
+    if (self->status != THREAD_RUNNING) {
+        LOGE("ERROR: DDM broadcast with thread status=%d\n", self->status);
+        /* try anyway? */
+    }
+
+    JValue unused;
+    dvmCallMethod(self, bcast, NULL, &unused, event);
+    if (dvmCheckException(self)) {
+        LOGI("Exception thrown by broadcast(%d)\n", event);
+        dvmLogExceptionStackTrace();
+        dvmClearException(self);
+        goto bail;
+    }
+
+bail:
+    ;
+}
+
+/*
+ * First DDM packet has arrived over JDWP.  Notify the press.
+ *
+ * We can do some initialization here too.
+ */
+void dvmDdmConnected(void)
+{
+    // TODO: any init
+
+    LOGV("Broadcasting DDM connect\n");
+    broadcast(CONNECTED);
+}
+
+/*
+ * JDWP connection has dropped.
+ *
+ * Do some cleanup.
+ */
+void dvmDdmDisconnected(void)
+{
+    LOGV("Broadcasting DDM disconnect\n");
+    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) {
+            //LOGW("notify %d\n", 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 = dvmStringLen(nameObj);
+            chars = dvmStringChars(nameObj);
+        } 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*) &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 = dvmStringLen(newName);
+    const u2* chars = dvmStringChars(newName);
+
+    /*
+     * 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*) &buf[0x08];
+    while (stringLen--)
+        set2BE((u1*) (outChars++), *chars++);
+
+    dvmDbgDdmSendChunk(CHUNK_TYPE("THNM"), bufLen, buf);
+}
+
+/*
+ * 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.
+ */
+static bool getThreadStats(pid_t pid, pid_t tid, unsigned long* pUtime,
+    unsigned long* pStime)
+{
+    /*
+    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);
+    */
+
+    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/%d/task/%d/stat", (int) pid, (int) tid);
+    fd = open(nameBuf, O_RDONLY);
+    if (fd < 0) {
+        LOGV("Unable to open '%s': %s\n", nameBuf, strerror(errno));
+        return false;
+    }
+
+    char lineBuf[512];      // > 2x typical
+    int cc;
+    cc = read(fd, lineBuf, sizeof(lineBuf)-1);
+    if (cc <= 0) {
+        const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
+        LOGI("Unable to read '%s': %s\n", nameBuf, msg);
+        close(fd);
+        return false;
+    }
+    lineBuf[cc] = '\0';
+
+    /*
+     * Skip whitespace-separated tokens.
+     */
+    static const char* kWhitespace = " ";
+    char* cp = lineBuf;
+    for (i = 0; i < 13; i++) {
+        cp += strcspn(cp, kWhitespace);     // skip token
+        cp += strspn(cp, kWhitespace);      // skip whitespace
+    }
+
+    /*
+     * Grab the values we want.
+     */
+    char* endp;
+    *pUtime = strtoul(cp, &endp, 10);
+    if (endp == cp)
+        LOGI("Warning: strtoul failed on utime ('%.30s...')\n", cp);
+
+    cp += strcspn(cp, kWhitespace);
+    cp += strspn(cp, kWhitespace);
+
+    *pStime = strtoul(cp, &endp, 10);
+    if (endp == cp)
+        LOGI("Warning: strtoul failed on stime ('%.30s...')\n", cp);
+
+    close(fd);
+    return true;
+}
+
+/*
+ * 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(void)
+{
+    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;
+
+    pid_t pid = getpid();
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        unsigned long utime, stime;
+        bool isDaemon = false;
+
+        if (!getThreadStats(pid, thread->systemTid, &utime, &stime)) {
+            // failed; drop in empty values
+            utime = stime = 0;
+        }
+
+        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, utime);
+        set4BE(buf+13, 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) {
+        LOGI("dvmDdmGetStackTraceById: threadid=%d not found\n", 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.
+     */
+    int stackDepth = -1;
+    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(void)
+{
+    u1* data;
+    size_t len;
+
+    if (!dvmGenerateTrackedAllocationReport(&data, &len)) {
+        /* assume OOM */
+        dvmThrowException("Ljava/lang/OutOfMemoryError;","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..01f5d18
--- /dev/null
+++ b/vm/Ddm.h
@@ -0,0 +1,87 @@
+/*
+ * 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
+#define _DALVIK_DDM
+
+/*
+ * 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*/
diff --git a/vm/Debugger.c b/vm/Debugger.c
new file mode 100644
index 0000000..2cea85c
--- /dev/null
+++ b/vm/Debugger.c
@@ -0,0 +1,3071 @@
+/*
+ * 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(void)
+{
+    if (!dvmBreakpointStartup())
+        return false;
+
+    gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
+    return (gDvm.dbgRegistry != NULL);
+}
+
+/*
+ * Free registry storage.
+ */
+void dvmDebuggerShutdown(void)
+{
+    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 */
+typedef enum RegistryType {
+    kObjectId = 0xc1, kRefTypeId
+} RegistryType;
+
+/*
+ * 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? */
+        LOGI("ignoring registerObject request in thread=%d\n",
+            dvmThreadSelf()->threadId);
+        //dvmAbort();
+        goto bail;
+    }
+
+    (void) dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
+                (void*)(u4) id, registryCompare, true);
+
+bail:
+    dvmHashTableUnlock(gDvm.dbgRegistry);
+    return id;
+}
+
+/*
+ * (This is a HashForeachFunc callback.)
+ */
+static int markRef(void* data, void* arg)
+{
+    UNUSED_PARAMETER(arg);
+
+    //LOGI("dbg mark %p\n", data);
+    dvmMarkObjectNonNull(data);
+    return 0;
+}
+
+/* Mark all of the registered debugger references so the
+ * GC doesn't collect them.
+ */
+void dvmGcMarkDebuggerRefs()
+{
+    /* dvmDebuggerStartup() may not have been called before the first GC.
+     */
+    if (gDvm.dbgRegistry != NULL) {
+        dvmHashTableLock(gDvm.dbgRegistry);
+        dvmHashForeach(gDvm.dbgRegistry, markRef, NULL);
+        dvmHashTableUnlock(gDvm.dbgRegistry);
+    }
+}
+
+/*
+ * 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;
+    LOGV("+++ registering %p (%s)\n", 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 void* frameIdToFrame(FrameId id)
+{
+    return (void*)(u4) id;
+}
+
+
+/*
+ * Get the invocation request state.
+ */
+DebugInvokeReq* dvmDbgGetInvokeReq(void)
+{
+    return &dvmThreadSelf()->invokeReq;
+}
+
+/*
+ * Enable the object registry, but don't enable debugging features yet.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgConnected(void)
+{
+    assert(!gDvm.debuggerConnected);
+
+    LOGV("JDWP has attached\n");
+    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(void)
+{
+    if (gDvm.debuggerActive)
+        return;
+
+    LOGI("Debugger is active\n");
+    dvmInitBreakpoints();
+    gDvm.debuggerActive = true;
+#if defined(WITH_JIT)
+    dvmCompilerStateRefresh();
+#endif
+}
+
+/*
+ * Disable debugging features.
+ *
+ * Set "debuggerConnected" to false, which disables use of the object
+ * registry.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgDisconnected(void)
+{
+    assert(gDvm.debuggerConnected);
+
+    gDvm.debuggerActive = false;
+
+    dvmHashTableLock(gDvm.dbgRegistry);
+    gDvm.debuggerConnected = false;
+
+    LOGD("Debugger has detached; object registry had %d entries\n",
+        dvmHashTableNumEntries(gDvm.dbgRegistry));
+    //int i;
+    //for (i = 0; i < gDvm.dbgRegistryNext; i++)
+    //    LOGVV("%4d: 0x%llx\n", i, gDvm.dbgRegistryTable[i]);
+
+    dvmHashTableClear(gDvm.dbgRegistry);
+    dvmHashTableUnlock(gDvm.dbgRegistry);
+#if defined(WITH_JIT)
+    dvmCompilerStateRefresh();
+#endif
+}
+
+/*
+ * Returns "true" if a debugger is connected.
+ *
+ * Does not return "true" if it's just a DDM server.
+ */
+bool dvmDbgIsDebuggerConnected(void)
+{
+    return gDvm.debuggerActive;
+}
+
+/*
+ * Get time since last debugger activity.  Used when figuring out if the
+ * debugger has finished configuring us.
+ */
+s8 dvmDbgLastDebuggerActivity(void)
+{
+    return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
+}
+
+/*
+ * JDWP thread is running, don't allow GC.
+ */
+int dvmDbgThreadRunning(void)
+{
+    return dvmChangeStatus(NULL, THREAD_RUNNING);
+}
+
+/*
+ * JDWP thread is idle, allow GC.
+ */
+int dvmDbgThreadWaiting(void)
+{
+    return dvmChangeStatus(NULL, THREAD_VMWAIT);
+}
+
+/*
+ * Restore state returned by Running/Waiting calls.
+ */
+int dvmDbgThreadContinuing(int status)
+{
+    return dvmChangeStatus(NULL, status);
+}
+
+/*
+ * 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
+    LOGI("GC lifetime allocation: %d bytes\n", gDvm.allocProf.allocCount);
+    if (CALC_CACHE_STATS) {
+        dvmDumpAtomicCacheStats(gDvm.instanceofCache);
+        dvmDumpBootClassPath();
+    }
+#ifdef PROFILE_FIELD_ACCESS
+    dvmDumpFieldAccessCounts();
+#endif
+
+    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 = malloc(sizeof(RefTypeId) * *pNumClasses);
+
+    if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
+        LOGW("Warning: problem getting class list\n");
+        /* 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\n", classLoader);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+
+    /* over-allocate the return buffer */
+    maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
+    *pClassRefBuf = 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'\n", clazz->descriptor);
+            (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
+        }
+    }
+    *pNumClasses = numClasses;
+
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Generate the "JNI signature" for a class, e.g. "Ljava/lang/String;".
+ *
+ * Our class descriptors are in the correct format, so we just copy that.
+ * TODO: figure out if we can avoid the copy now that we're using
+ * descriptors instead of unadorned class names.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* generateJNISignature(ClassObject* clazz)
+{
+    return strdup(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,
+    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 = generateJNISignature(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.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetSignature(RefTypeId refTypeId)
+{
+    ClassObject* clazz;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    return generateJNISignature(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.  Converted to a "JNI signature".
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetObjectTypeName(ObjectId objectId)
+{
+    Object* obj = objectIdToObject(objectId);
+
+    assert(obj != NULL);
+
+    return generateJNISignature(obj->clazz);
+}
+
+/*
+ * Given a type signature (e.g. "Ljava/lang/String;"), return the JDWP
+ * "type tag".
+ *
+ * In many cases this is necessary but not sufficient.  For example, if
+ * we have a NULL String object, we want to return JT_STRING.  If we have
+ * a java/lang/Object that holds a String reference, we also want to
+ * return JT_STRING.  See dvmDbgGetObjectTag().
+ */
+int dvmDbgGetSignatureTag(const char* type)
+{
+    /*
+     * We're not checking the class loader here (to guarantee that JT_STRING
+     * is truly the one and only String), but it probably doesn't matter
+     * for our purposes.
+     */
+    if (strcmp(type, "Ljava/lang/String;") == 0)
+        return JT_STRING;
+    else if (strcmp(type, "Ljava/lang/Class;") == 0)
+        return JT_CLASS_OBJECT;
+    else if (strcmp(type, "Ljava/lang/Thread;") == 0)
+        return JT_THREAD;
+    else if (strcmp(type, "Ljava/lang/ThreadGroup;") == 0)
+        return JT_THREAD_GROUP;
+    else if (strcmp(type, "Ljava/lang/ClassLoader;") == 0)
+        return JT_CLASS_LOADER;
+
+    switch (type[0]) {
+    case '[':       return JT_ARRAY;
+    case 'B':       return JT_BYTE;
+    case 'C':       return JT_CHAR;
+    case 'L':       return JT_OBJECT;
+    case 'F':       return JT_FLOAT;
+    case 'D':       return JT_DOUBLE;
+    case 'I':       return JT_INT;
+    case 'J':       return JT_LONG;
+    case 'S':       return JT_SHORT;
+    case 'V':       return JT_VOID;
+    case 'Z':       return JT_BOOLEAN;
+    default:
+        LOGE("ERROR: unhandled type '%s'\n", type);
+        assert(false);
+        return -1;
+    }
+}
+
+/*
+ * Methods declared to return Object might actually be returning one
+ * of the "refined types".  We need to check the object explicitly.
+ */
+static u1 resultTagFromObject(Object* obj)
+{
+    ClassObject* clazz;
+
+    if (obj == NULL)
+        return JT_OBJECT;
+
+    clazz = obj->clazz;
+
+    /*
+     * Comparing against the known classes is faster than string
+     * comparisons.  It ensures that we only find the classes in the
+     * bootstrap class loader, which may or may not be what we want.
+     */
+    if (clazz == gDvm.classJavaLangString)
+        return JT_STRING;
+    else if (clazz == gDvm.classJavaLangClass)
+        return JT_CLASS_OBJECT;
+    else if (clazz == gDvm.classJavaLangThread)
+        return JT_THREAD;
+    else if (clazz == gDvm.classJavaLangThreadGroup)
+        return JT_THREAD_GROUP;
+    else if (strcmp(clazz->descriptor, "Ljava/lang/ClassLoader;") == 0)
+        return JT_CLASS_LOADER;
+    else if (clazz->descriptor[0] == '[')
+        return JT_ARRAY;
+    else
+        return JT_OBJECT;
+}
+
+/*
+ * Determine the tag for an object with a known type.
+ */
+int dvmDbgGetObjectTag(ObjectId objectId, const char* type)
+{
+    u1 tag;
+
+    tag = dvmDbgGetSignatureTag(type);
+    if (tag == JT_OBJECT && objectId != 0)
+        tag = resultTagFromObject(objectIdToObject(objectId));
+
+    return tag;
+}
+
+/*
+ * 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:
+        LOGE("ERROR: unhandled tag '%c'\n", tag);
+        assert(false);
+        return -1;
+    }
+}
+
+/*
+ * 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:
+        LOGE("ERROR: unhandled tag '%c'\n", tag);
+        assert(false);
+        return false;
+    }
+}
+
+
+/*
+ * 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.
+ */
+int dvmDbgGetArrayElementTag(ObjectId arrayId)
+{
+    ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+
+    assert(dvmIsArray(arrayObj));
+
+    return dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
+}
+
+/*
+ * 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 with, 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) {
+        LOGW("Request for index=%d + count=%d excceds length=%d\n",
+            firstIndex, count, arrayObj->length);
+        return false;
+    }
+
+    tag = dvmDbgGetSignatureTag(arrayObj->obj.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;
+
+        LOGV("    --> copying %d object IDs\n", count);
+        //assert(tag == JT_OBJECT);     // could be object or "refined" type
+
+        for (i = 0; i < count; i++, pObjects++) {
+            u1 thisTag;
+            if (*pObjects != NULL)
+                thisTag = resultTagFromObject(*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) {
+        LOGW("Attempt to set index=%d + count=%d excceds length=%d\n",
+            firstIndex, count, arrayObj->length);
+        return false;
+    }
+
+    tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
+
+    if (isTagPrimitive(tag)) {
+        int width = dvmDbgGetTagWidth(tag);
+
+        LOGV("    --> setting %d '%c' width=%d\n", count, tag, width);
+
+        copyValuesFromBE(data + firstIndex*width, buf, count, width);
+    } else {
+        Object** pObjects;
+        int i;
+
+        pObjects = (Object**) data;
+        pObjects += firstIndex;
+
+        LOGV("    --> 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;
+}
+
+/*
+ * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
+ * output all fields declared by the class.  Inerhited fields are
+ * not included.
+ */
+void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
+    ExpandBuf* pReply)
+{
+    static const u1 genericSignature[1] = "";
+    ClassObject* clazz;
+    Field* field;
+    u4 declared;
+    int i;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    declared = clazz->sfieldCount + clazz->ifieldCount;
+    expandBufAdd4BE(pReply, declared);
+
+    for (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)
+            expandBufAddUtf8String(pReply, genericSignature);
+        expandBufAdd4BE(pReply, field->accessFlags);
+    }
+    for (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)
+            expandBufAddUtf8String(pReply, genericSignature);
+        expandBufAdd4BE(pReply, 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, 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, meth->accessFlags);
+    }
+
+    dexStringCacheRelease(&stringCache);
+}
+
+/*
+ * Output all interfaces directly implemented by the class.
+ */
+void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
+{
+    ClassObject* clazz;
+    int i, start, count;
+
+    clazz = refTypeIdToClassObject(refTypeId);
+    assert(clazz != NULL);
+
+    if (clazz->super == NULL)
+        start = 0;
+    else
+        start = clazz->super->iftableCount;
+
+    count = clazz->iftableCount - start;
+    expandBufAdd4BE(pReply, count);
+    for (i = start; i < clazz->iftableCount; i++) {
+        ClassObject* iface = clazz->iftable[i].clazz;
+        expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
+    }
+}
+
+typedef struct DebugCallbackContext {
+    int numItems;
+    ExpandBuf* pReply;
+    // used by locals table
+    bool withGeneric;
+} DebugCallbackContext;
+
+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;
+
+    LOGV("untweak: %d to %d\n", 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;
+    }
+
+    LOGV("untweak: %d to %d\n", 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);
+
+    LOGV("    %2d: %d(%d) '%s' '%s' slot=%d\n",
+        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 type tag for the field's type.
+ */
+int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId)
+{
+    Object* obj = objectIdToObject(objId);
+    RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+    Field* field = fieldIdToField(classId, fieldId);
+
+    return dvmDbgGetSignatureTag(field->signature);
+}
+
+/*
+ * Get the type tag for the static field's type.
+ */
+int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId)
+{
+    Field* field = fieldIdToField(refTypeId, fieldId);
+    return dvmDbgGetSignatureTag(field->signature);
+}
+
+/*
+ * Copy the value of a field into the specified buffer.
+ */
+void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, u1* buf,
+    int expectedLen)
+{
+    Object* obj = objectIdToObject(objectId);
+    RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+    InstField* field = (InstField*) fieldIdToField(classId, fieldId);
+    Object* objVal;
+    u4 intVal;
+    u8 longVal;
+
+    switch (field->field.signature[0]) {
+    case JT_BOOLEAN:
+        assert(expectedLen == 1);
+        intVal = dvmGetFieldBoolean(obj, field->byteOffset);
+        set1(buf, intVal != 0);
+        break;
+    case JT_BYTE:
+        assert(expectedLen == 1);
+        intVal = dvmGetFieldInt(obj, field->byteOffset);
+        set1(buf, intVal);
+        break;
+    case JT_SHORT:
+    case JT_CHAR:
+        assert(expectedLen == 2);
+        intVal = dvmGetFieldInt(obj, field->byteOffset);
+        set2BE(buf, intVal);
+        break;
+    case JT_INT:
+    case JT_FLOAT:
+        assert(expectedLen == 4);
+        intVal = dvmGetFieldInt(obj, field->byteOffset);
+        set4BE(buf, intVal);
+        break;
+    case JT_ARRAY:
+    case JT_OBJECT:
+        assert(expectedLen == sizeof(ObjectId));
+        objVal = dvmGetFieldObject(obj, field->byteOffset);
+        dvmSetObjectId(buf, objectToObjectId(objVal));
+        break;
+    case JT_DOUBLE:
+    case JT_LONG:
+        assert(expectedLen == 8);
+        longVal = dvmGetFieldLong(obj, field->byteOffset);
+        set8BE(buf, longVal);
+        break;
+    default:
+        LOGE("ERROR: unhandled class type '%s'\n", field->field.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->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:
+        LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
+        assert(false);
+        break;
+    }
+}
+
+/*
+ * Copy the value of a static field into the specified buffer.
+ */
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* buf,
+    int expectedLen)
+{
+    StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
+    Object* objVal;
+    JValue value;
+
+    switch (sfield->field.signature[0]) {
+    case JT_BOOLEAN:
+        assert(expectedLen == 1);
+        set1(buf, dvmGetStaticFieldBoolean(sfield));
+        break;
+    case JT_BYTE:
+        assert(expectedLen == 1);
+        set1(buf, dvmGetStaticFieldByte(sfield));
+        break;
+    case JT_SHORT:
+        assert(expectedLen == 2);
+        set2BE(buf, dvmGetStaticFieldShort(sfield));
+        break;
+    case JT_CHAR:
+        assert(expectedLen == 2);
+        set2BE(buf, dvmGetStaticFieldChar(sfield));
+        break;
+    case JT_INT:
+        assert(expectedLen == 4);
+        set4BE(buf, dvmGetStaticFieldInt(sfield));
+        break;
+    case JT_FLOAT:
+        assert(expectedLen == 4);
+        value.f = dvmGetStaticFieldFloat(sfield);
+        set4BE(buf, value.i);
+        break;
+    case JT_ARRAY:
+    case JT_OBJECT:
+        assert(expectedLen == sizeof(ObjectId));
+        objVal = dvmGetStaticFieldObject(sfield);
+        dvmSetObjectId(buf, objectToObjectId(objVal));
+        break;
+    case JT_LONG:
+        assert(expectedLen == 8);
+        set8BE(buf, dvmGetStaticFieldLong(sfield));
+        break;
+    case JT_DOUBLE:
+        assert(expectedLen == 8);
+        value.d = dvmGetStaticFieldDouble(sfield);
+        set8BE(buf, value.j);
+        break;
+    default:
+        LOGE("ERROR: unhandled class type '%s'\n", sfield->field.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->field.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:
+        LOGE("ERROR: unhandled class type '%s'\n", sfield->field.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;
+}
+
+#if 0
+/*
+ * Wait until a thread suspends.
+ *
+ * We stray from the usual pattern here, and release the thread list lock
+ * before we use the Thread.  This is necessary and should be safe in this
+ * circumstance; see comments in dvmWaitForSuspend().
+ */
+void dvmDbgWaitForSuspend(ObjectId threadId)
+{
+    Object* threadObj;
+    Thread* thread;
+
+    threadObj = objectIdToObject(threadId);
+    assert(threadObj != NULL);
+
+    dvmLockThreadList(NULL);
+    thread = threadObjToThread(threadObj);
+    dvmUnlockThreadList();
+
+    if (thread != NULL)
+        dvmWaitForSuspend(thread);
+}
+#endif
+
+
+/*
+ * Return the ObjectId for the "system" thread group.
+ */
+ObjectId dvmDbgGetSystemThreadGroupId(void)
+{
+    Object* groupObj = dvmGetSystemThreadGroup();
+    return objectToObjectId(groupObj);
+}
+
+/*
+ * Return the ObjectId for the "system" thread group.
+ */
+ObjectId dvmDbgGetMainThreadGroupId(void)
+{
+    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;
+    InstField* nameField;
+    StringObject* nameStr;
+
+    threadGroup = objectIdToObject(threadGroupId);
+    assert(threadGroup != NULL);
+
+    nameField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
+                    "name", "Ljava/lang/String;");
+    if (nameField == NULL) {
+        LOGE("unable to find name field in ThreadGroup\n");
+        return NULL;
+    }
+
+    nameStr = (StringObject*) dvmGetFieldObject(threadGroup,
+                                                nameField->byteOffset);
+    return dvmCreateCstrFromString(nameStr);
+}
+
+/*
+ * Get the parent of a thread group.
+ *
+ * Returns a newly-allocated string.
+ */
+ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
+{
+    Object* threadGroup;
+    InstField* parentField;
+    Object* parent;
+
+    threadGroup = objectIdToObject(threadGroupId);
+    assert(threadGroup != NULL);
+
+    parentField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
+                    "parent", "Ljava/lang/ThreadGroup;");
+    if (parentField == NULL) {
+        LOGE("unable to find parent field in ThreadGroup\n");
+        parent = NULL;
+    } else {
+        parent = dvmGetFieldObject(threadGroup, parentField->byteOffset);
+    }
+    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;
+    InstField* groupField = NULL;
+    Thread* thread;
+    int count;
+
+    if (threadGroupId != THREAD_GROUP_ALL) {
+        targetThreadGroup = objectIdToObject(threadGroupId);
+        assert(targetThreadGroup != NULL);
+    }
+
+    groupField = dvmFindInstanceField(gDvm.classJavaLangThread,
+        "group", "Ljava/lang/ThreadGroup;");
+
+    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, groupField->byteOffset);
+        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,groupField->byteOffset);
+            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;
+    void* framePtr;
+    u4 count = 0;
+
+    threadObj = objectIdToObject(threadId);
+
+    dvmLockThreadList(NULL);
+
+    thread = threadObjToThread(threadObj);
+    if (thread == NULL)
+        goto bail;
+
+    framePtr = thread->curFrame;
+    while (framePtr != NULL) {
+        if (!dvmIsBreakFrame(framePtr))
+            count++;
+
+        framePtr = SAVEAREA_FROM_FP(framePtr)->prevFrame;
+    }
+
+bail:
+    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->curFrame;
+    count = 0;
+    while (framePtr != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame(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(void)
+{
+    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 */
+        LOGW("WARNING: threadid=%llx obj=%p no match\n", 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) {
+        LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
+    } else {
+        dvmResumeThread(thread);
+    }
+
+    dvmUnlockThreadList();
+}
+
+/*
+ * Suspend ourselves after sending an event to the debugger.
+ */
+void dvmDbgSuspendSelf(void)
+{
+    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\n", framePtr);
+    LOGVV("    Method='%s' native=%d static=%d this=%p\n",
+        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 && !dvmIsValidObject(thisObj)) {
+        LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL\n",
+            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.
+ */
+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 == 8);
+        {
+            /* convert to "ObjectId" */
+            objVal = (Object*)framePtr[slot];
+            if (objVal != NULL && !dvmIsValidObject(objVal)) {
+                LOGW("JDWP: slot %d expected to hold array, %p invalid\n",
+                    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 == 8);
+        {
+            /* convert to "ObjectId" */
+            objVal = (Object*)framePtr[slot];
+            //char* name;
+
+            if (objVal != NULL) {
+                if (!dvmIsValidObject(objVal)) {
+                    LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
+                        slot, objVal);
+                    dvmAbort();         // DEBUG: make it obvious
+                    objVal = NULL;
+                }
+                //name = generateJNISignature(objVal->clazz);
+                tag = resultTagFromObject(objVal);
+                //free(name);
+            } else {
+                tag = JT_OBJECT;
+            }
+            dvmSetObjectId(buf+1, objectToObjectId(objVal));
+        }
+        break;
+    case JT_DOUBLE:
+    case JT_LONG:
+        assert(expectedLen == 8);
+        longVal = *(u8*)(&framePtr[slot]);
+        set8BE(buf+1, longVal);
+        break;
+    default:
+        LOGE("ERROR: unhandled tag '%c'\n", tag);
+        assert(false);
+        break;
+    }
+
+    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);
+        *(u8*)(&framePtr[slot]) = value;
+        break;
+    case JT_VOID:
+    case JT_CLASS_OBJECT:
+    case JT_THREAD:
+    case JT_THREAD_GROUP:
+    case JT_CLASS_LOADER:
+    default:
+        LOGE("ERROR: unhandled tag '%c'\n", 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(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)
+{
+    int tag;
+    char* signature;
+
+    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 = generateJNISignature(clazz);
+    dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
+        signature, CS_VERIFIED | CS_PREPARED);
+    free(signature);
+}
+
+/*
+ * 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, enum JdwpStepSize size,
+    enum 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) {
+        LOGE("Thread for single-step not found\n");
+        goto bail;
+    }
+    if (!dvmIsSuspended(thread)) {
+        LOGE("Thread for single-step not suspended\n");
+        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);
+    Thread* targetThread;
+    JdwpError err = ERR_NONE;
+
+    dvmLockThreadList(NULL);
+
+    targetThread = threadObjToThread(threadObj);
+    if (targetThread == NULL) {
+        err = ERR_INVALID_THREAD;       /* thread does not exist */
+        dvmUnlockThreadList();
+        goto bail;
+    }
+    if (!targetThread->invokeReq.ready) {
+        err = ERR_INVALID_THREAD;       /* thread not stopped by event */
+        dvmUnlockThreadList();
+        goto bail;
+    }
+
+    /*
+     * 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) {
+        LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
+             "for method exec\n",
+            dvmThreadSelf()->threadId, targetThread->threadId,
+            targetThread->suspendCount);
+        err = ERR_THREAD_SUSPENDED;     /* probably not expected here */
+        dvmUnlockThreadList();
+        goto bail;
+    }
+
+    /*
+     * 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();
+    int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+
+    LOGV("    Transferring control to event thread\n");
+    dvmLockMutex(&targetThread->invokeReq.lock);
+
+    if ((options & INVOKE_SINGLE_THREADED) == 0) {
+        LOGV("      Resuming all threads\n");
+        dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
+    } else {
+        LOGV("      Resuming event thread only\n");
+        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);
+    LOGV("    Control has returned from event thread\n");
+
+    /* 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) {
+        LOGV("      Suspending all threads\n");
+        dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
+        LOGV("      Resuming event thread to balance the count\n");
+        dvmResumeThread(targetThread);
+    }
+
+    /*
+     * Set up the result.
+     */
+    *pResultTag = targetThread->invokeReq.resultTag;
+    if (isTagPrimitive(targetThread->invokeReq.resultTag))
+        *pResultValue = targetThread->invokeReq.resultValue.j;
+    else
+        *pResultValue = objectToObjectId(targetThread->invokeReq.resultValue.l);
+    *pExceptObj = targetThread->invokeReq.exceptObj;
+    err = targetThread->invokeReq.err;
+
+bail:
+    return err;
+}
+
+/*
+ * Determine the tag type for the return value for this method.
+ */
+static u1 resultTagFromSignature(const Method* method)
+{
+    const char* descriptor = dexProtoGetReturnType(&method->prototype);
+    return dvmDbgGetSignatureTag(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;
+    int 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);
+    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_LOGV() {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        LOGV("JDWP invoking method %p/%p %s.%s:%s\n",
+            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 = resultTagFromSignature(meth);
+    if (pReq->exceptObj != 0) {
+        Object* exc = dvmGetException(self);
+        LOGD("  JDWP invocation returning with exceptObj=%p (%s)\n",
+            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 = resultTagFromObject(pReq->resultValue.l);
+        if (newTag != pReq->resultTag) {
+            LOGVV("  JDWP promoted result from %d to %d\n",
+                pReq->resultTag, newTag);
+            pReq->resultTag = newTag;
+        }
+    }
+
+    if (oldExcept != NULL)
+        dvmSetException(self, oldExcept);
+    dvmChangeStatus(self, oldStatus);
+}
+
+// for dvmAddressSetForLine
+typedef struct AddressSetContext {
+    bool lastAddressValid;
+    u4 lastAddress;
+    u4 lineNum;
+    AddressSet *pSet;
+} AddressSetContext;
+
+// 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 = 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(void)
+{
+    dvmDdmConnected();
+}
+
+/*
+ * JDWP connection has dropped.
+ */
+void dvmDbgDdmDisconnected(void)
+{
+    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) {
+        LOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)\n",
+            type);
+        return;
+    }
+
+    dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
+}
diff --git a/vm/Debugger.h b/vm/Debugger.h
new file mode 100644
index 0000000..d722160
--- /dev/null
+++ b/vm/Debugger.h
@@ -0,0 +1,311 @@
+/*
+ * 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
+#define _DALVIK_DEBUGGER
+
+#include "Common.h"
+#include "Misc.h"
+#include "jdwp/Jdwp.h"
+#include <pthread.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.
+ */
+typedef struct AddressSet {
+    u4 setSize;
+    u1 set[1];
+} AddressSet;
+
+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.
+ */
+typedef struct StepControl {
+    /* request */
+    enum JdwpStepSize   size;
+    enum JdwpStepDepth  depth;
+    struct Thread*      thread;         /* don't deref; for comparison only */
+
+    /* current state */
+    bool                active;
+    const struct Method* method;
+    int                 line;           /* line #; could be -1 */
+    const AddressSet*   pAddressSet;    /* if non-null, address set for line */
+    int                 frameDepth;
+} StepControl;
+
+/*
+ * Invoke-during-breakpoint support.
+ */
+typedef 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 */
+    struct Object*      obj;        /* not used for ClassType.InvokeMethod */
+    struct Object*      thread;
+    struct ClassObject* clazz;
+    struct 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;
+} DebugInvokeReq;
+
+/* 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,
+    char** pSignature);
+bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
+        RefTypeId* pRefTypeId);
+void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
+    RefTypeId* pRefTypeId);
+u1 dvmDbgGetClassObjectType(RefTypeId refTypeId);
+char* dvmDbgGetSignature(RefTypeId refTypeId);
+const char* dvmDbgGetSourceFile(RefTypeId refTypeId);
+char* dvmDbgGetObjectTypeName(ObjectId objectId);
+int dvmDbgGetSignatureTag(const char* signature);
+int dvmDbgGetObjectTag(ObjectId objectId, const char* type);
+int dvmDbgGetTagWidth(int tag);
+
+int dvmDbgGetArrayLength(ObjectId arrayId);
+int 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);
+
+int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId);
+int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId);
+void dvmDbgGetFieldValue(ObjectId objId, FieldId fieldId, u1* ptr, int width);
+void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
+    int width);
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* ptr,
+    int width);
+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 struct Method* method, int pcOffset,
+    struct Object* thisPtr, int eventFlags);
+void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
+    int catchRelPc, struct Object* exception);
+void dvmDbgPostThreadStart(struct Thread* thread);
+void dvmDbgPostThreadDeath(struct Thread* thread);
+void dvmDbgPostClassPrepare(struct ClassObject* clazz);
+// FieldAccess, FieldModification
+
+/* 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, enum JdwpStepSize size,
+    enum 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 struct 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*/
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
new file mode 100644
index 0000000..0867ffa
--- /dev/null
+++ b/vm/Dvm.mk
@@ -0,0 +1,336 @@
+# 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 -fno-align-jumps
+#LOCAL_CFLAGS += -DUSE_INDIRECT_REF
+LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter
+LOCAL_CFLAGS += -DARCH_VARIANT=\"$(dvm_arch_variant)\"
+
+#
+# Optional features.  These may impact the size or performance of the VM.
+#
+
+ifeq ($(WITH_DEADLOCK_PREDICTION),true)
+  LOCAL_CFLAGS += -DWITH_DEADLOCK_PREDICTION
+  WITH_MONITOR_TRACKING := true
+endif
+ifeq ($(WITH_MONITOR_TRACKING),true)
+  LOCAL_CFLAGS += -DWITH_MONITOR_TRACKING
+endif
+
+# Make a debugging version when building the simulator (if not told
+# otherwise) and when explicitly asked.
+dvm_make_debug_vm := false
+ifeq ($(strip $(DEBUG_DALVIK_VM)),)
+  ifeq ($(dvm_simulator),true)
+    dvm_make_debug_vm := true
+  endif
+else
+  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_ALLOC_LIMITS
+  LOCAL_CFLAGS += -DWITH_EXTRA_GC_CHECKS=1
+  #LOCAL_CFLAGS += -DCHECK_MUTEX
+  #LOCAL_CFLAGS += -DPROFILE_FIELD_ACCESS
+  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.c \
+	Atomic.c.arm \
+	AtomicCache.c \
+	CheckJni.c \
+	Ddm.c \
+	Debugger.c \
+	DvmDex.c \
+	Exception.c \
+	Hash.c \
+	IndirectRefTable.c.arm \
+	Init.c \
+	InlineNative.c.arm \
+	Inlines.c \
+	Intern.c \
+	Jni.c \
+	JarFile.c \
+	LinearAlloc.c \
+	Misc.c.arm \
+	Native.c \
+	PointerSet.c \
+	Profile.c \
+	Properties.c \
+	RawDexFile.c \
+	ReferenceTable.c \
+	SignalCatcher.c \
+	StdioConverter.c \
+	Sync.c \
+	TestCompability.c \
+	Thread.c \
+	UtfString.c \
+	alloc/clz.c.arm \
+	alloc/Alloc.c \
+	alloc/CardTable.c \
+	alloc/HeapBitmap.c.arm \
+	alloc/HeapDebug.c \
+	alloc/HeapTable.c \
+	alloc/HeapWorker.c \
+	alloc/Heap.c.arm \
+	alloc/DdmHeap.c \
+	alloc/Verify.c \
+	alloc/Visit.c \
+	analysis/CodeVerify.c \
+	analysis/DexPrepare.c \
+	analysis/DexVerify.c \
+	analysis/Optimize.c \
+	analysis/RegisterMap.c \
+	analysis/VerifySubs.c \
+	interp/Interp.c.arm \
+	interp/Stack.c \
+	jdwp/ExpandBuf.c \
+	jdwp/JdwpAdb.c \
+	jdwp/JdwpConstants.c \
+	jdwp/JdwpEvent.c \
+	jdwp/JdwpHandler.c \
+	jdwp/JdwpMain.c \
+	jdwp/JdwpSocket.c \
+	mterp/Mterp.c.arm \
+	mterp/out/InterpC-portstd.c.arm \
+	mterp/out/InterpC-portdbg.c.arm \
+	native/InternalNative.c \
+	native/dalvik_system_DexFile.c \
+	native/dalvik_system_VMDebug.c \
+	native/dalvik_system_VMRuntime.c \
+	native/dalvik_system_VMStack.c \
+	native/dalvik_system_Zygote.c \
+	native/java_lang_Class.c \
+	native/java_lang_Object.c \
+	native/java_lang_Runtime.c \
+	native/java_lang_String.c \
+	native/java_lang_System.c \
+	native/java_lang_SystemProperties.c \
+	native/java_lang_Throwable.c \
+	native/java_lang_VMClassLoader.c \
+	native/java_lang_VMThread.c \
+	native/java_lang_reflect_AccessibleObject.c \
+	native/java_lang_reflect_Array.c \
+	native/java_lang_reflect_Constructor.c \
+	native/java_lang_reflect_Field.c \
+	native/java_lang_reflect_Method.c \
+	native/java_lang_reflect_Proxy.c \
+	native/java_security_AccessController.c \
+	native/java_util_concurrent_atomic_AtomicLong.c \
+	native/org_apache_harmony_dalvik_NativeTestTarget.c \
+	native/org_apache_harmony_dalvik_ddmc_DdmServer.c \
+	native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c \
+	native/sun_misc_Unsafe.c \
+	oo/AccessCheck.c \
+	oo/Array.c \
+	oo/Class.c \
+	oo/Object.c \
+	oo/Resolve.c \
+	oo/TypeCheck.c \
+	reflect/Annotation.c \
+	reflect/Proxy.c \
+	reflect/Reflect.c \
+	test/AtomicTest.c.arm \
+	test/TestHash.c \
+	test/TestIndirectRefTable.c
+
+WITH_COPYING_GC := $(strip $(WITH_COPYING_GC))
+
+ifeq ($(WITH_COPYING_GC),true)
+  LOCAL_CFLAGS += -DWITH_COPYING_GC
+  LOCAL_SRC_FILES += \
+	alloc/Copying.c.arm
+else
+  LOCAL_SRC_FILES += \
+	alloc/HeapSource.c \
+	alloc/MarkSweep.c.arm
+endif
+
+WITH_JIT := $(strip $(WITH_JIT))
+
+ifeq ($(WITH_JIT),true)
+  LOCAL_CFLAGS += -DWITH_JIT
+  LOCAL_SRC_FILES += \
+	compiler/Compiler.c \
+	compiler/Frontend.c \
+	compiler/Utility.c \
+	compiler/InlineTransformation.c \
+	compiler/IntermediateRep.c \
+	compiler/Dataflow.c \
+	compiler/Loop.c \
+	compiler/Ralloc.c \
+	interp/Jit.c
+endif
+
+WITH_HPROF := $(strip $(WITH_HPROF))
+ifeq ($(WITH_HPROF),)
+  WITH_HPROF := true
+endif
+ifeq ($(WITH_HPROF),true)
+  LOCAL_SRC_FILES += \
+	hprof/Hprof.c \
+	hprof/HprofClass.c \
+	hprof/HprofHeap.c \
+	hprof/HprofOutput.c \
+	hprof/HprofString.c
+  LOCAL_CFLAGS += -DWITH_HPROF=1
+
+  ifeq ($(strip $(WITH_HPROF_STACK)),true)
+    LOCAL_SRC_FILES += \
+	hprof/HprofStack.c \
+	hprof/HprofStackFrame.c
+    LOCAL_CFLAGS += -DWITH_HPROF_STACK=1
+  endif # WITH_HPROF_STACK
+endif   # WITH_HPROF
+
+LOCAL_C_INCLUDES += \
+	$(JNI_H_INCLUDE) \
+	dalvik \
+	dalvik/vm \
+	external/zlib \
+	$(KERNEL_HEADERS)
+
+
+ifeq ($(dvm_simulator),true)
+  LOCAL_LDLIBS += -lpthread -ldl
+  ifeq ($(HOST_OS),linux)
+    # need this for clock_gettime() in profiling
+    LOCAL_LDLIBS += -lrt
+  endif
+endif
+
+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 (armv4t, armv5te etc.)
+  LOCAL_SRC_FILES += \
+		arch/arm/CallOldABI.S \
+		arch/arm/CallEABI.S \
+		arch/arm/HintsEABI.c \
+		mterp/out/InterpC-$(dvm_arch_variant).c.arm \
+		mterp/out/InterpAsm-$(dvm_arch_variant).S
+
+  ifeq ($(WITH_JIT),true)
+    LOCAL_SRC_FILES += \
+		compiler/codegen/arm/RallocUtil.c \
+		compiler/codegen/arm/$(dvm_arch_variant)/Codegen.c \
+		compiler/codegen/arm/$(dvm_arch_variant)/CallingConvention.S \
+		compiler/codegen/arm/Assemble.c \
+		compiler/codegen/arm/ArchUtility.c \
+		compiler/codegen/arm/LocalOptimizations.c \
+		compiler/codegen/arm/GlobalOptimizations.c \
+		compiler/template/out/CompilerTemplateAsm-$(dvm_arch_variant).S
+  endif
+endif
+
+ifeq ($(dvm_arch),x86)
+  ifeq ($(dvm_os),linux)
+    MTERP_ARCH_KNOWN := true
+    LOCAL_SRC_FILES += \
+		arch/$(dvm_arch_variant)/Call386ABI.S \
+		arch/$(dvm_arch_variant)/Hints386ABI.c \
+		mterp/out/InterpC-$(dvm_arch_variant).c \
+		mterp/out/InterpAsm-$(dvm_arch_variant).S
+  endif
+endif
+
+ifeq ($(dvm_arch),sh)
+  MTERP_ARCH_KNOWN := true
+  LOCAL_SRC_FILES += \
+		arch/sh/CallSH4ABI.S \
+		arch/generic/Hints.c \
+		mterp/out/InterpC-allstubs.c \
+		mterp/out/InterpAsm-allstubs.S
+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.c \
+		arch/generic/Hints.c \
+		mterp/out/InterpC-allstubs.c
+
+  # 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
+
+ifeq ($(TEST_VM_IN_ECLAIR),true)
+  LOCAL_CFLAGS += -DTEST_VM_IN_ECLAIR
+endif
diff --git a/vm/DvmDex.c b/vm/DvmDex.c
new file mode 100644
index 0000000..94422b3
--- /dev/null
+++ b/vm/DvmDex.c
@@ -0,0 +1,296 @@
+/*
+ * 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"
+
+
+/*
+ * 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 stringCount, classCount, methodCount, fieldCount;
+
+    pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
+    if (pDvmDex == NULL)
+        return NULL;
+
+    pDvmDex->pDexFile = pDexFile;
+    pDvmDex->pHeader = pDexFile->pHeader;
+
+    pHeader = pDvmDex->pHeader;
+
+    stringCount = pHeader->stringIdsSize;
+    classCount = pHeader->typeIdsSize;
+    methodCount = pHeader->methodIdsSize;
+    fieldCount = pHeader->fieldIdsSize;
+
+    pDvmDex->pResStrings = (struct StringObject**)
+        calloc(stringCount, sizeof(struct StringObject*));
+
+    pDvmDex->pResClasses = (struct ClassObject**)
+        calloc(classCount, sizeof(struct ClassObject*));
+
+    pDvmDex->pResMethods = (struct Method**)
+        calloc(methodCount, sizeof(struct Method*));
+
+    pDvmDex->pResFields = (struct Field**)
+        calloc(fieldCount, sizeof(struct Field*));
+
+    LOGV("+++ DEX %p: allocateAux %d+%d+%d+%d * 4 = %d bytes\n",
+        pDvmDex, stringCount, classCount, methodCount, fieldCount,
+        (stringCount + classCount + methodCount + fieldCount) * 4);
+
+    pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
+
+    if (pDvmDex->pResStrings == NULL ||
+        pDvmDex->pResClasses == NULL ||
+        pDvmDex->pResMethods == NULL ||
+        pDvmDex->pResFields == NULL ||
+        pDvmDex->pInterfaceCache == NULL)
+    {
+        LOGE("Alloc failure in allocateAuxStructures\n");
+        free(pDvmDex->pResStrings);
+        free(pDvmDex->pResClasses);
+        free(pDvmDex->pResMethods);
+        free(pDvmDex->pResFields);
+        free(pDvmDex);
+        return NULL;
+    }
+
+    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) {
+        LOGE("lseek rewind failed\n");
+        goto bail;
+    }
+
+    if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
+        LOGE("Unable to map file\n");
+        goto bail;
+    }
+
+    pDexFile = dexFileParse(memMap.addr, memMap.length, parseFlags);
+    if (pDexFile == NULL) {
+        LOGE("DEX parse failed\n");
+        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);
+    *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(addr, len, parseFlags);
+    if (pDexFile == NULL) {
+        LOGE("DEX parse failed\n");
+        goto bail;
+    }
+    pDvmDex = allocateAuxStructures(pDexFile);
+    if (pDvmDex == NULL) {
+        dexFileFree(pDexFile);
+        goto bail;
+    }
+
+    *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)
+{
+    if (pDvmDex == NULL)
+        return;
+
+    dexFileFree(pDvmDex->pDexFile);
+
+    LOGV("+++ DEX %p: freeing aux structs\n", pDvmDex);
+    free(pDvmDex->pResStrings);
+    free(pDvmDex->pResClasses);
+    free(pDvmDex->pResMethods);
+    free(pDvmDex->pResFields);
+    dvmFreeAtomicCache(pDvmDex->pInterfaceCache);
+
+    sysReleaseShmem(&pDvmDex->memMap);
+    free(pDvmDex);
+}
+
+
+/*
+ * 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) {
+        LOGV("+++ byte at %p is already 0x%02x\n", addr, newVal);
+        return true;
+    }
+
+    /*
+     * We're not holding this for long, so we don't bother with switching
+     * to VMWAIT.
+     */
+    dvmLockMutex(&pDvmDex->modLock);
+
+    LOGV("+++ change byte at %p from 0x%02x to 0x%02x\n", addr, *addr, newVal);
+    if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
+        LOGD("NOTE: DEX page access change (->RW) failed\n");
+        /* expected on files mounted from FAT; keep going (may crash) */
+    }
+
+    *addr = newVal;
+
+    if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
+        LOGD("NOTE: DEX page access change (->RO) failed\n");
+        /* 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) {
+        LOGV("+++ value at %p is already 0x%04x\n", addr, newVal);
+        return true;
+    }
+
+    /*
+     * We're not holding this for long, so we don't bother with switching
+     * to VMWAIT.
+     */
+    dvmLockMutex(&pDvmDex->modLock);
+
+    LOGV("+++ change 2byte at %p from 0x%04x to 0x%04x\n", addr, *addr, newVal);
+    if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
+        LOGD("NOTE: DEX page access change (->RW) failed\n");
+        /* expected on files mounted from FAT; keep going (may crash) */
+    }
+
+    *addr = newVal;
+
+    if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
+        LOGD("NOTE: DEX page access change (->RO) failed\n");
+        /* 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..f36940b
--- /dev/null
+++ b/vm/DvmDex.h
@@ -0,0 +1,160 @@
+/*
+ * 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
+#define _DALVIK_DVMDEX
+
+#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.
+ */
+typedef 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 */
+    MemMapping          memMap;
+
+    /* lock ensuring mutual exclusion during updates */
+    pthread_mutex_t     modLock;
+} DvmDex;
+
+
+/*
+ * 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*/
diff --git a/vm/Exception.c b/vm/Exception.c
new file mode 100644
index 0000000..3a73420
--- /dev/null
+++ b/vm/Exception.c
@@ -0,0 +1,1304 @@
+/*
+ * 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);
+
+
+/*
+ * Cache pointers to some of the exception classes we use locally.
+ *
+ * Note this is NOT called during dexopt optimization.  Some of the fields
+ * are initialized by the verifier (dvmVerifyCodeFlow).
+ */
+bool dvmExceptionStartup(void)
+{
+    gDvm.classJavaLangThrowable =
+        dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
+    gDvm.classJavaLangRuntimeException =
+        dvmFindSystemClassNoInit("Ljava/lang/RuntimeException;");
+    gDvm.classJavaLangStackOverflowError =
+        dvmFindSystemClassNoInit("Ljava/lang/StackOverflowError;");
+    gDvm.classJavaLangError =
+        dvmFindSystemClassNoInit("Ljava/lang/Error;");
+    gDvm.classJavaLangStackTraceElement =
+        dvmFindSystemClassNoInit("Ljava/lang/StackTraceElement;");
+    gDvm.classJavaLangStackTraceElementArray =
+        dvmFindArrayClass("[Ljava/lang/StackTraceElement;", NULL);
+    if (gDvm.classJavaLangThrowable == NULL ||
+        gDvm.classJavaLangStackTraceElement == NULL ||
+        gDvm.classJavaLangStackTraceElementArray == NULL)
+    {
+        LOGE("Could not find one or more essential exception classes\n");
+        return false;
+    }
+
+    /*
+     * Find the constructor.  Note that, unlike other saved method lookups,
+     * we're using a Method* instead of a vtable offset.  This is because
+     * constructors don't have vtable offsets.  (Also, since we're creating
+     * the object in question, it's impossible for anyone to sub-class it.)
+     */
+    Method* meth;
+    meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement,
+        "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
+    if (meth == NULL) {
+        LOGE("Unable to find constructor for StackTraceElement\n");
+        return false;
+    }
+    gDvm.methJavaLangStackTraceElement_init = meth;
+
+    /* grab an offset for the stackData field */
+    gDvm.offJavaLangThrowable_stackState =
+        dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+            "stackState", "Ljava/lang/Object;");
+    if (gDvm.offJavaLangThrowable_stackState < 0) {
+        LOGE("Unable to find Throwable.stackState\n");
+        return false;
+    }
+
+    /* and one for the message field, in case we want to show it */
+    gDvm.offJavaLangThrowable_message =
+        dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+            "detailMessage", "Ljava/lang/String;");
+    if (gDvm.offJavaLangThrowable_message < 0) {
+        LOGE("Unable to find Throwable.detailMessage\n");
+        return false;
+    }
+
+    /* and one for the cause field, just 'cause */
+    gDvm.offJavaLangThrowable_cause =
+        dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+            "cause", "Ljava/lang/Throwable;");
+    if (gDvm.offJavaLangThrowable_cause < 0) {
+        LOGE("Unable to find Throwable.cause\n");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmExceptionShutdown(void)
+{
+    // nothing to do
+}
+
+
+/*
+ * Format the message into a small buffer and pass it along.
+ */
+void dvmThrowExceptionFmtV(const char* exceptionDescriptor, const char* fmt,
+    va_list args)
+{
+    char msgBuf[512];
+
+    vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+    dvmThrowChainedException(exceptionDescriptor, msgBuf, NULL);
+}
+
+/*
+ * 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.
+ *
+ * [Do we want to cache pointers to common exception classes?]
+ */
+void dvmThrowChainedException(const char* exceptionDescriptor, const char* msg,
+    Object* cause)
+{
+    ClassObject* excepClass;
+
+    LOGV("THROW '%s' msg='%s' cause=%s\n",
+        exceptionDescriptor, msg,
+        (cause != NULL) ? cause->clazz->descriptor : "(none)");
+
+    if (gDvm.initializing) {
+        if (++gDvm.initExceptionCount >= 2) {
+            LOGE("Too many exceptions during init (failed on '%s' '%s')\n",
+                exceptionDescriptor, msg);
+            dvmAbort();
+        }
+    }
+
+    excepClass = dvmFindSystemClass(exceptionDescriptor);
+    if (excepClass == NULL) {
+        /*
+         * We couldn't find the exception class.  The attempt to find a
+         * nonexistent class should have raised an exception.  If no
+         * exception is currently raised, then we're pretty clearly unable
+         * to throw ANY sort of exception, and we need to pack it in.
+         *
+         * If we were able to throw the "class load failed" exception,
+         * stick with that.  Ideally we'd stuff the original exception
+         * into the "cause" field, but since we can't find it we can't
+         * do that.  The exception class name should be in the "message"
+         * field.
+         */
+        if (!dvmCheckException(dvmThreadSelf())) {
+            LOGE("FATAL: unable to throw exception (failed on '%s' '%s')\n",
+                exceptionDescriptor, msg);
+            dvmAbort();
+        }
+        return;
+    }
+
+    dvmThrowChainedExceptionByClass(excepClass, msg, cause);
+}
+
+/*
+ * Start/continue throwing process now that we have a class reference.
+ */
+void dvmThrowChainedExceptionByClass(ClassObject* excepClass, const char* msg,
+    Object* cause)
+{
+    Thread* self = dvmThreadSelf();
+    Object* exception;
+
+    /* make sure the exception is initialized */
+    if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) {
+        LOGE("ERROR: unable to initialize exception class '%s'\n",
+            excepClass->descriptor);
+        if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0)
+            dvmAbort();
+        dvmThrowChainedException("Ljava/lang/InternalError;",
+            "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;
+        LOGE("FATAL: unable to allocate exception '%s' '%s'\n",
+            excepClass->descriptor, msg != NULL ? msg : "(no msg)");
+        dvmAbort();
+    }
+
+    /*
+     * Init the exception.
+     */
+    if (gDvm.optimizing) {
+        /* need the exception object, but can't invoke interpreted code */
+        LOGV("Skipping init of exception %s '%s'\n",
+            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);
+}
+
+/*
+ * Throw the named exception using the dotted form of the class
+ * descriptor as the exception message, and with the specified cause.
+ */
+void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor,
+    const char* messageDescriptor, Object* cause)
+{
+    char* message = dvmDescriptorToDot(messageDescriptor);
+
+    dvmThrowChainedException(exceptionDescriptor, message, cause);
+    free(message);
+}
+
+/*
+ * Like dvmThrowExceptionWithMessageFromDescriptor, but take a
+ * class object instead of a name.
+ */
+void dvmThrowExceptionByClassWithClassMessage(ClassObject* exceptionClass,
+    const char* messageDescriptor)
+{
+    char* message = dvmDescriptorToName(messageDescriptor);
+
+    dvmThrowExceptionByClass(exceptionClass, message);
+    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) {
+            LOGW("Could not allocate message string \"%s\" while "
+                    "throwing internal exception (%s)\n",
+                    msg, excepClass->descriptor);
+            goto bail;
+        }
+    }
+
+    if (cause != NULL) {
+        if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) {
+            LOGE("Tried to init exception with cause '%s'\n",
+                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.
+         */
+        LOGW("WARNING: exception class '%s' missing constructor "
+            "(msg='%s' kind=%d)\n",
+            excepClass->descriptor, msg, initKind);
+        assert(strcmp(excepClass->descriptor,
+                      "Ljava/lang/RuntimeException;") != 0);
+        dvmThrowChainedException("Ljava/lang/RuntimeException;",
+            "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)\n", needInitCause);
+        dvmCallMethod(self, initMethod, exception, &unused);
+        break;
+    case kInitMsg:
+        LOGVV("+++ exc msg (ic=%d)\n", 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) {
+        LOGW("Exception thrown (%s) while throwing internal exception (%s)\n",
+            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.
+                 */
+                LOGW("Exception thrown (%s) during initCause() "
+                        "of internal exception (%s)\n",
+                        self->exception->clazz->descriptor,
+                        exception->clazz->descriptor);
+                goto bail;
+            }
+        } else {
+            LOGW("WARNING: couldn't find initCause in '%s'\n",
+                excepClass->descriptor);
+        }
+    }
+
+
+    result = true;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) msgStr, self);     // NULL is ok
+    return result;
+}
+
+
+/*
+ * Clear the pending exception and the "initExceptionCount" counter.  This
+ * is 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.
+ *
+ * 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.  The
+ * reset of initExceptionCount should be harmless in that case.
+ */
+void dvmClearOptException(Thread* self)
+{
+    self->exception = NULL;
+    gDvm.initExceptionCount = 0;
+}
+
+/*
+ * 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.classJavaLangError) ||
+        dvmInstanceof(exception->clazz, gDvm.classJavaLangRuntimeException))
+    {
+        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.classJavaLangThrowable)) {
+        LOGE("Tried to get cause from object of type '%s'\n",
+            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(void)
+{
+    Thread* self = dvmThreadSelf();
+    Object* exception;
+    Method* printMethod;
+
+    exception = self->exception;
+    if (exception == NULL)
+        return;
+
+    self->exception = NULL;
+    printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
+                    "printStackTrace", "()V");
+    if (printMethod != NULL) {
+        JValue unused;
+        dvmCallMethod(self, printMethod, exception, &unused);
+    } else {
+        LOGW("WARNING: could not find printStackTrace in %s\n",
+            exception->clazz->descriptor);
+    }
+
+    if (self->exception != NULL) {
+        LOGW("NOTE: exception thrown while printing stack trace: %s\n",
+            self->exception->clazz->descriptor);
+    }
+
+    self->exception = exception;
+}
+
+/*
+ * 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\n",
+        method->clazz->descriptor, method->name, excepClass->descriptor,
+        dvmComputeExactFrameDepth(self->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 */
+                LOGV("Match on catch-all block at 0x%02x in %s.%s for %s\n",
+                        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.
+                     */
+                    LOGW("Could not resolve class ref'ed in exception "
+                            "catch list (class index %d, exception %s)\n",
+                            handler->typeIdx,
+                            (self->exception != NULL) ?
+                            self->exception->clazz->descriptor : "(none)");
+                    dvmClearException(self);
+                    continue;
+                }
+            }
+
+            //LOGD("ADDR MATCH, check %s instanceof %s\n",
+            //    excepClass->descriptor, pEntry->excepClass->descriptor);
+
+            if (dvmInstanceof(excepClass, throwable)) {
+                LOGV("Match on catch block at 0x%02x in %s.%s for %s\n",
+                        relPc, method->clazz->descriptor,
+                        method->name, excepClass->descriptor);
+                return handler->address;
+            }
+        }
+    }
+
+    LOGV("No matching catch block at 0x%02x in %s for %s\n",
+        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->curFrame is also set to this value.
+ */
+int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+    bool scanOnly, void** newFrame)
+{
+    void* fp = self->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(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(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->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, int* pCount)
+{
+    ArrayObject* stackData = NULL;
+    int* simpleData = NULL;
+    void* fp;
+    void* startFp;
+    int stackDepth;
+    int* intPtr;
+
+    if (pCount != NULL)
+        *pCount = 0;
+    fp = thread->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(fp))
+            break;
+        if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable))
+            break;
+        //LOGD("EXCEP: ignoring %s.%s\n",
+        //         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(fp))
+            stackDepth++;
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    //LOGD("EXCEP: stack depth is %d\n", 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*) 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(fp)) {
+            //LOGD("EXCEP keeping %s.%s\n", 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;
+    const int* intVals;
+    int stackSize;
+
+    stackSize = stackData->length / 2;
+    intVals = (const int*) 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, int stackDepth)
+{
+    ArrayObject* steArray = NULL;
+    int i;
+
+    /* init this if we haven't yet */
+    if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement))
+        dvmInitClass(gDvm.classJavaLangStackTraceElement);
+
+    /* allocate a StackTraceElement array */
+    steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray,
+                    stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT);
+    if (steArray == NULL)
+        goto bail;
+
+    /*
+     * 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;
+        Method* meth;
+        StringObject* className;
+        StringObject* methodName;
+        StringObject* fileName;
+        int lineNumber, pc;
+        const char* sourceFile;
+        char* dotName;
+
+        ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT);
+        if (ste == NULL)
+            goto bail;
+
+        meth = (Method*) *intVals++;
+        pc = *intVals++;
+
+        if (pc == -1)      // broken top frame?
+            lineNumber = 0;
+        else
+            lineNumber = dvmLineNumFromPC(meth, pc);
+
+        dotName = dvmDescriptorToDot(meth->clazz->descriptor);
+        className = dvmCreateStringFromCstr(dotName);
+        free(dotName);
+
+        methodName = dvmCreateStringFromCstr(meth->name);
+        sourceFile = dvmGetMethodSourceFile(meth);
+        if (sourceFile != NULL)
+            fileName = dvmCreateStringFromCstr(sourceFile);
+        else
+            fileName = 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()))
+            goto bail;
+
+        dvmSetObjectArrayElement(steArray, i, ste);
+    }
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) steArray, NULL);
+    return steArray;
+}
+
+/*
+ * Dump the contents of a raw stack trace to the log.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth)
+{
+    int i;
+
+    /*
+     * Run through the array of stack frame data.
+     */
+    for (i = 0; i < stackDepth; i++) {
+        Method* meth;
+        int lineNumber, pc;
+        const char* sourceFile;
+        char* dotName;
+
+        meth = (Method*) *intVals++;
+        pc = *intVals++;
+
+        if (pc == -1)      // broken top frame?
+            lineNumber = 0;
+        else
+            lineNumber = dvmLineNumFromPC(meth, pc);
+
+        // probably don't need to do this, but it looks nicer
+        dotName = dvmDescriptorToDot(meth->clazz->descriptor);
+
+        if (dvmIsNativeMethod(meth)) {
+            LOGI("\tat %s.%s(Native Method)\n", dotName, meth->name);
+        } else {
+            LOGI("\tat %s.%s(%s:%d)\n",
+                dotName, meth->name, dvmGetMethodSourceFile(meth),
+                dvmLineNumFromPC(meth, pc));
+        }
+
+        free(dotName);
+
+        sourceFile = dvmGetMethodSourceFile(meth);
+    }
+}
+
+/*
+ * Print the direct stack trace of the given exception to the log.
+ */
+static void logStackTraceOf(Object* exception)
+{
+    const ArrayObject* stackData;
+    StringObject* messageStr;
+    int stackSize;
+    const int* intVals;
+
+    messageStr = (StringObject*) dvmGetFieldObject(exception,
+                    gDvm.offJavaLangThrowable_message);
+    if (messageStr != NULL) {
+        char* cp = dvmCreateCstrFromString(messageStr);
+        LOGI("%s: %s\n", exception->clazz->descriptor, cp);
+        free(cp);
+    } else {
+        LOGI("%s:\n", exception->clazz->descriptor);
+    }
+
+    stackData = (const ArrayObject*) dvmGetFieldObject(exception,
+                    gDvm.offJavaLangThrowable_stackState);
+    if (stackData == NULL) {
+        LOGI("  (no stack trace data found)\n");
+        return;
+    }
+
+    stackSize = stackData->length / 2;
+    intVals = (const int*) 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(void)
+{
+    Object* exception = dvmThreadSelf()->exception;
+    Object* cause;
+
+    if (exception == NULL) {
+        LOGW("tried to log a null exception?\n");
+        return;
+    }
+
+    for (;;) {
+        logStackTraceOf(exception);
+        cause = dvmGetExceptionCause(exception);
+        if (cause == NULL) {
+            break;
+        }
+        LOGI("Caused by:\n");
+        exception = cause;
+    }
+}
diff --git a/vm/Exception.h b/vm/Exception.h
new file mode 100644
index 0000000..b812f73
--- /dev/null
+++ b/vm/Exception.h
@@ -0,0 +1,203 @@
+/*
+ * 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
+#define _DALVIK_EXCEPTION
+
+/* initialization */
+bool dvmExceptionStartup(void);
+void dvmExceptionShutdown(void);
+
+/*
+ * Throw an exception in the current thread, by class descriptor.
+ */
+void dvmThrowChainedException(const char* exceptionDescriptor, const char* msg,
+    Object* cause);
+INLINE void dvmThrowException(const char* exceptionDescriptor,
+    const char* msg)
+{
+    dvmThrowChainedException(exceptionDescriptor, msg, NULL);
+}
+
+/*
+ * Like dvmThrowChainedException, but takes printf-style args for the message.
+ */
+void dvmThrowExceptionFmtV(const char* exceptionDescriptor, const char* fmt,
+    va_list args);
+void dvmThrowExceptionFmt(const char* exceptionDescriptor, const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+INLINE void dvmThrowExceptionFmt(const char* exceptionDescriptor,
+    const char* fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    dvmThrowExceptionFmtV(exceptionDescriptor, fmt, args);
+    va_end(args);
+}
+
+/*
+ * Throw an exception in the current thread, by class object.
+ */
+void dvmThrowChainedExceptionByClass(ClassObject* exceptionClass,
+    const char* msg, Object* cause);
+INLINE void dvmThrowExceptionByClass(ClassObject* exceptionClass,
+    const char* msg)
+{
+    dvmThrowChainedExceptionByClass(exceptionClass, msg, NULL);
+}
+
+/*
+ * Throw the named exception using the name of a class as the exception
+ * message.
+ */
+void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor,
+    const char* messageDescriptor, Object* cause);
+INLINE void dvmThrowExceptionWithClassMessage(const char* exceptionDescriptor,
+    const char* messageDescriptor)
+{
+    dvmThrowChainedExceptionWithClassMessage(exceptionDescriptor,
+        messageDescriptor, NULL);
+}
+
+/*
+ * Like dvmThrowExceptionWithMessageFromDescriptor, but take a
+ * class object instead of a name.
+ */
+void dvmThrowExceptionByClassWithClassMessage(ClassObject* exceptionClass,
+    const char* messageDescriptor);
+
+/*
+ * 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->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.
+ */
+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, int* 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, int* pCount) {
+    return (int*) dvmFillInStackTraceInternal(thread, false, pCount);
+}
+ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth);
+
+/*
+ * Print a formatted version of a raw stack trace to the log file.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth);
+
+#endif /*_DALVIK_EXCEPTION*/
diff --git a/vm/Globals.h b/vm/Globals.h
new file mode 100644
index 0000000..6ba6d5a
--- /dev/null
+++ b/vm/Globals.h
@@ -0,0 +1,877 @@
+/*
+ * 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
+#define _DALVIK_GLOBALS
+
+#include <stdarg.h>
+#include <pthread.h>
+
+#define MAX_BREAKPOINTS 20      /* used for a debugger optimization */
+
+/* private structures */
+typedef struct GcHeap GcHeap;
+typedef struct BreakpointSet BreakpointSet;
+typedef struct InlineSub InlineSub;
+
+/*
+ * One of these for each -ea/-da/-esa/-dsa on the command line.
+ */
+typedef 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 "..."? */
+} AssertionControl;
+
+/*
+ * Execution mode, e.g. interpreter vs. JIT.
+ */
+typedef enum ExecutionMode {
+    kExecutionModeUnknown = 0,
+    kExecutionModeInterpPortable,
+    kExecutionModeInterpFast,
+#if defined(WITH_JIT)
+    kExecutionModeJit,
+#endif
+} ExecutionMode;
+
+/*
+ * All fields are initialized to zero.
+ *
+ * Storage allocated here must be freed by a subsystem shutdown function or
+ * from within freeGlobals().
+ */
+struct DvmGlobals {
+    /*
+     * Some options from the command line or environment.
+     */
+    char*       bootClassPathStr;
+    char*       classPathStr;
+
+    unsigned int    heapSizeStart;
+    unsigned int    heapSizeMax;
+    unsigned int    stackSize;
+
+    bool        verboseGc;
+    bool        verboseJni;
+    bool        verboseClass;
+    bool        verboseShutdown;
+
+    bool        jdwpAllowed;        // debugging allowed for this process?
+    bool        jdwpConfigured;     // has debugging info been provided?
+    int         jdwpTransport;
+    bool        jdwpServer;
+    char*       jdwpHost;
+    int         jdwpPort;
+    bool        jdwpSuspend;
+
+    /* use wall clock as method profiler clock source? */
+    bool        profilerWallClock;
+
+    /*
+     * 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);
+
+    int         jniGrefLimit;       // 0 means no limit
+    char*       jniTrace;
+    bool        reduceSignals;
+    bool        noQuitHandler;
+    bool        verifyDexChecksum;
+    char*       stackTraceFile;     // for SIGQUIT-inspired output
+
+    bool        logStdio;
+
+    DexOptimizerMode    dexOptMode;
+    DexClassVerifyMode  classVerifyMode;
+
+    bool        dexOptForSmp;
+
+    /*
+     * GC option flags.
+     */
+    bool        preciseGc;
+    bool        preVerify;
+    bool        postVerify;
+    bool        generateRegisterMaps;
+    bool        concurrentMarkSweep;
+    bool        verifyCardTable;
+
+    int         assertionCtrlCount;
+    AssertionControl*   assertionCtrl;
+
+    ExecutionMode   executionMode;
+
+    /*
+     * VM init management.
+     */
+    bool        initializing;
+    int         initExceptionCount;
+    bool        optimizing;
+
+    /*
+     * java.lang.System properties set from the command line.
+     */
+    int         numProps;
+    int         maxProps;
+    char**      propList;
+
+    /*
+     * 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;
+
+    /*
+     * Quick lookups for popular classes used internally.
+     */
+    ClassObject* classJavaLangClass;
+    ClassObject* classJavaLangClassArray;
+    ClassObject* classJavaLangError;
+    ClassObject* classJavaLangObject;
+    ClassObject* classJavaLangObjectArray;
+    ClassObject* classJavaLangRuntimeException;
+    ClassObject* classJavaLangString;
+    ClassObject* classJavaLangThread;
+    ClassObject* classJavaLangVMThread;
+    ClassObject* classJavaLangThreadGroup;
+    ClassObject* classJavaLangThrowable;
+    ClassObject* classJavaLangStackOverflowError;
+    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* classJavaLangExceptionInInitializerError;
+    ClassObject* classJavaLangRefPhantomReference;
+    ClassObject* classJavaLangRefReference;
+    ClassObject* classJavaNioReadWriteDirectByteBuffer;
+    ClassObject* classJavaSecurityAccessController;
+    ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationFactory;
+    ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMember;
+    ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMemberArray;
+    ClassObject* classOrgApacheHarmonyNioInternalDirectBuffer;
+    jclass      jclassOrgApacheHarmonyNioInternalDirectBuffer;
+
+    /* synthetic classes for arrays of primitives */
+    ClassObject* classArrayBoolean;
+    ClassObject* classArrayChar;
+    ClassObject* classArrayFloat;
+    ClassObject* classArrayDouble;
+    ClassObject* classArrayByte;
+    ClassObject* classArrayShort;
+    ClassObject* classArrayInt;
+    ClassObject* classArrayLong;
+
+    /* method offsets - Object */
+    int         voffJavaLangObject_equals;
+    int         voffJavaLangObject_hashCode;
+    int         voffJavaLangObject_toString;
+    int         voffJavaLangObject_finalize;
+
+    /* field offsets - Class */
+    int         offJavaLangClass_pd;
+
+    /* field offsets - String */
+    int         javaLangStringReady;    /* 0=not init, 1=ready, -1=initing */
+    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;
+
+    /* method offsets - Thread */
+    int         voffJavaLangThread_run;
+
+    /* field offsets - VMThread */
+    int         offJavaLangVMThread_thread;
+    int         offJavaLangVMThread_vmData;
+
+    /* method offsets - ThreadGroup */
+    int         voffJavaLangThreadGroup_removeThread;
+
+    /* field offsets - Throwable */
+    int         offJavaLangThrowable_stackState;
+    int         offJavaLangThrowable_message;
+    int         offJavaLangThrowable_cause;
+
+    /* field offsets - java.lang.reflect.* */
+    int         offJavaLangReflectAccessibleObject_flag;
+    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;
+
+    /* method pointers - java.lang.ref.Reference */
+    Method*     methJavaLangRefReference_enqueueInternal;
+
+    /* field offsets - java.nio.Buffer and java.nio.DirectByteBufferImpl */
+    //int         offJavaNioBuffer_capacity;
+    //int         offJavaNioDirectByteBufferImpl_pointer;
+
+    /* method pointers - java.security.AccessController */
+    volatile int javaSecurityAccessControllerReady;
+    Method*     methJavaSecurityAccessController_doPrivileged[4];
+
+    /* constructor method pointers; no vtable involved, so use Method* */
+    Method*     methJavaLangStackTraceElement_init;
+    Method*     methJavaLangExceptionInInitializerError_init;
+    Method*     methJavaLangRefPhantomReference_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;
+
+    /* fake native entry point method */
+    Method*     methFakeNativeEntry;
+
+    /* assorted direct buffer helpers */
+    Method*     methJavaNioReadWriteDirectByteBuffer_init;
+    Method*     methOrgApacheHarmonyLuniPlatformPlatformAddress_on;
+    Method*     methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress;
+    int         offJavaNioBuffer_capacity;
+    int         offJavaNioBuffer_effectiveDirectAddress;
+    int         offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr;
+    int         voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong;
+
+    /*
+     * VM-synthesized primitive classes, for arrays.
+     */
+    ClassObject* volatile primitiveClass[PRIM_MAX];
+
+    /*
+     * 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.  The JIT needs to know if any
+     * thread is suspended.  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_mutex_t vmExitLock;
+    pthread_cond_t  vmExitCond;
+
+    /*
+     * The set of DEX files loaded by custom class loaders.
+     */
+    HashTable*  userDexFiles;
+
+    /*
+     * JNI global reference table.
+     */
+#ifdef USE_INDIRECT_REF
+    IndirectRefTable jniGlobalRefTable;
+#else
+    ReferenceTable  jniGlobalRefTable;
+#endif
+    pthread_mutex_t jniGlobalRefLock;
+    int         jniGlobalRefHiMark;
+    int         jniGlobalRefLoMark;
+
+    /*
+     * 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 */
+
+    /*
+     * JNI allows you to have multiple VMs, but we limit ourselves to 1,
+     * so "vmList" is really just a pointer to the one and only VM.
+     */
+    JavaVM*     vmList;
+
+    /*
+     * Cache results of "A instanceof B".
+     */
+    AtomicCache* instanceofCache;
+
+    /* instruction width table, used for optimization and verification */
+    InstructionWidth*   instrWidth;
+    /* instruction flags table, used for verification */
+    InstructionFlags*   instrFlags;
+    /* instruction format table, used for verification */
+    InstructionFormat*  instrFormat;
+
+    /* inline substitution table, used during optimization */
+    InlineSub*          inlineSubs;
+
+    /*
+     * Bootstrap class loader linear allocator.
+     */
+    LinearAllocHdr* pBootLoaderAlloc;
+
+
+    /*
+     * Heap worker thread.
+     */
+    bool            heapWorkerInitialized;
+    bool            heapWorkerReady;
+    bool            haltHeapWorker;
+    pthread_t       heapWorkerHandle;
+    pthread_mutex_t heapWorkerLock;
+    pthread_cond_t  heapWorkerCond;
+    pthread_cond_t  heapWorkerIdleCond;
+    pthread_mutex_t heapWorkerListLock;
+
+    /*
+     * 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 "debuggerActive" is accessed from mterp, so its storage size and
+     * meaning must not be changed without updating the assembly sources.
+     */
+    bool        debuggerConnected;      /* debugger or DDMS is connected */
+    u1          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 */
+
+#ifdef WITH_ALLOC_LIMITS
+    /* set on first use of an alloc limit, never cleared */
+    bool        checkAllocLimits;
+    /* allocation limit, for setGlobalAllocationLimit() regression testing */
+    int         allocationLimit;
+#endif
+
+#ifdef WITH_DEADLOCK_PREDICTION
+    /* global lock on history tree accesses */
+    pthread_mutex_t deadlockHistoryLock;
+
+    enum { kDPOff=0, kDPWarn, kDPErr, kDPAbort } deadlockPredictMode;
+#endif
+
+    /*
+     * 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 the interpreter
+     * can check to see if any profiling activity is enabled.
+     */
+    volatile int activeProfilers;
+
+    /*
+     * State for method-trace profiling.
+     */
+    MethodTraceState methodTrace;
+
+    /*
+     * 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 (256 entries).
+     */
+    int*        executedInstrCounts;
+    bool        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;
+
+    /*
+     * 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;
+};
+
+extern struct DvmGlobals gDvm;
+
+#if defined(WITH_JIT)
+
+/*
+ * 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.
+ */
+typedef enum NoChainExits {
+    kInlineCacheMiss = 0,
+    kCallsiteInterpreted,
+    kSwitchOverflow,
+    kHeavyweightMonitor,
+    kNoChainExitLast,
+} NoChainExits;
+
+/*
+ * 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 profile threshold counters */
+    unsigned char *pProfTable;
+
+    /* 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;
+
+    /* Bytes allocated for the code cache */
+    unsigned int codeCacheSize;
+
+    /* Trigger for trace selection */
+    unsigned short threshold;
+
+    /* JIT Compiler Control */
+    bool               haltCompilerThread;
+    bool               blockingMode;
+    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;
+
+    /* 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;
+
+    /* Disable JIT for selected opcodes - one bit for each opcode */
+    char opList[32];
+
+    /* Disable JIT for selected methods */
+    HashTable *methodTable;
+
+    /* Flag to dump all compiled code */
+    bool printMe;
+
+    /* Flag to count trace execution */
+    bool profile;
+
+    /* 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;
+    u8                 jitTime;
+    int                codeCachePatches;
+#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
+
+#endif /*_DALVIK_GLOBALS*/
diff --git a/vm/Hash.c b/vm/Hash.c
new file mode 100644
index 0000000..7bdd92f
--- /dev/null
+++ b/vm/Hash.c
@@ -0,0 +1,418 @@
+/*
+ * 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*) malloc(pHashTable->tableSize * sizeof(HashEntry));
+    if (pHashTable->pEntries == NULL) {
+        free(pHashTable);
+        return NULL;
+    }
+
+    memset(pHashTable->pEntries, 0, pHashTable->tableSize * sizeof(HashEntry));
+    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);
+    //LOGI("before: dead=%d\n", 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 */
+            //LOGD("+++ match on entry %d\n", 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;
+        }
+
+        //LOGI("+++ look probing %d...\n", 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 */
+                    LOGE("Dalvik hash resize failure\n");
+                    dvmAbort();
+                }
+                /* note "pEntry" is now invalid */
+            } else {
+                //LOGW("okay %d/%d/%d\n",
+                //    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) {
+            //LOGI("+++ stepping on entry %d\n", 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;
+        }
+
+        //LOGI("+++ del probing %d...\n", 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;
+
+    for (i = 0; i < pHashTable->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;
+
+    for (i = 0; i < pHashTable->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;
+    }
+
+    LOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n",
+        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..cfd7544
--- /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
+#define _DALVIK_HASH
+
+/* 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).
+ */
+typedef struct HashEntry {
+    u4 hashValue;
+    void* data;
+} HashEntry;
+
+#define HASH_TOMBSTONE ((void*) 0xcbcacccd)     // invalid ptr value
+
+/*
+ * Expandable hash table.
+ *
+ * This structure should be considered opaque.
+ */
+typedef 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;
+} HashTable;
+
+/*
+ * 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);
+ *   }
+ */
+typedef struct HashIter {
+    void*       data;
+    HashTable*  pHashTable;
+    int         idx;
+} HashIter;
+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*/
diff --git a/vm/IndirectRefTable.c b/vm/IndirectRefTable.c
new file mode 100644
index 0000000..dadd03f
--- /dev/null
+++ b/vm/IndirectRefTable.c
@@ -0,0 +1,501 @@
+/*
+ * 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"
+
+/*
+ * Initialize an IndirectRefTable structure.
+ */
+bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount,
+    int maxCount, IndirectRefKind kind)
+{
+    assert(initialCount > 0);
+    assert(initialCount <= maxCount);
+    assert(kind == kIndirectKindLocal || kind == kIndirectKindGlobal);
+
+    pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
+    if (pRef->table == NULL)
+        return false;
+#ifndef NDEBUG
+    memset(pRef->table, 0xd1, initialCount * sizeof(Object*));
+#endif
+
+    pRef->slotData =
+        (IndirectRefSlot*) calloc(maxCount, sizeof(IndirectRefSlot));
+    if (pRef->slotData == NULL)
+        return false;
+
+    pRef->segmentState.all = IRT_FIRST_SEGMENT;
+    pRef->allocEntries = initialCount;
+    pRef->maxEntries = maxCount;
+    pRef->kind = kind;
+
+    return true;
+}
+
+/*
+ * Clears out the contents of a IndirectRefTable, freeing allocated storage.
+ */
+void dvmClearIndirectRefTable(IndirectRefTable* pRef)
+{
+    free(pRef->table);
+    pRef->table = NULL;
+    pRef->allocEntries = pRef->maxEntries = -1;
+}
+
+/*
+ * Remove one or more segments from the top.  The table entry identified
+ * by "cookie" becomes the new top-most entry.
+ *
+ * Returns false if "cookie" is invalid or the table has only one segment.
+ */
+bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie)
+{
+    IRTSegmentState sst;
+
+    /*
+     * The new value for "top" must be <= the current value.  Otherwise
+     * this would represent an expansion of the table.
+     */
+    sst.all = cookie;
+    if (sst.parts.topIndex > pRef->segmentState.parts.topIndex) {
+        LOGE("Attempt to expand table with segment pop (%d to %d)\n",
+            pRef->segmentState.parts.topIndex, sst.parts.topIndex);
+        return false;
+    }
+    if (sst.parts.numHoles >= sst.parts.topIndex) {
+        LOGE("Absurd numHoles in cookie (%d bi=%d)\n",
+            sst.parts.numHoles, sst.parts.topIndex);
+        return false;
+    }
+
+    LOGV("IRT %p[%d]: pop, top=%d holes=%d\n",
+        pRef, pRef->kind, sst.parts.topIndex, sst.parts.numHoles);
+
+    return true;
+}
+
+/*
+ * Make sure that the entry at "idx" is correctly paired with "iref".
+ */
+static bool checkEntry(IndirectRefTable* pRef, IndirectRef iref, int idx)
+{
+    Object* obj = pRef->table[idx];
+    IndirectRef checkRef = dvmObjectToIndirectRef(pRef, obj, idx, pRef->kind);
+    if (checkRef != iref) {
+        LOGW("IRT %p[%d]: iref mismatch (req=%p vs cur=%p)\n",
+            pRef, pRef->kind, iref, checkRef);
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Update extended debug info when an entry is added.
+ *
+ * We advance the serial number, invalidating any outstanding references to
+ * this slot.
+ */
+static inline void updateSlotAdd(IndirectRefTable* pRef, Object* obj, int slot)
+{
+    if (pRef->slotData != NULL) {
+        IndirectRefSlot* pSlot = &pRef->slotData[slot];
+        pSlot->serial++;
+        //LOGI("+++ add [%d] slot %d (%p->%p), serial=%d\n",
+        //    pRef->kind, slot, obj, iref, pSlot->serial);
+        pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
+    }
+}
+
+/*
+ * Update extended debug info when an entry is removed.
+ */
+static inline void updateSlotRemove(IndirectRefTable* pRef, int slot)
+{
+    if (pRef->slotData != NULL) {
+        //IndirectRefSlot* pSlot = &pRef->slotData[slot];
+        //LOGI("+++ remove [%d] slot %d, serial now %d\n",
+        //    pRef->kind, slot, pSlot->serial);
+    }
+}
+
+/*
+ * Add "obj" to "pRef".
+ */
+IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
+    Object* obj)
+{
+    IRTSegmentState prevState;
+    prevState.all = cookie;
+    int topIndex = pRef->segmentState.parts.topIndex;
+
+    assert(obj != NULL);
+    assert(dvmIsValidObject(obj));
+    assert(pRef->table != NULL);
+    assert(pRef->allocEntries <= pRef->maxEntries);
+    assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles);
+
+    if (topIndex == pRef->allocEntries) {
+        /* reached end of allocated space; did we hit buffer max? */
+        if (topIndex == pRef->maxEntries) {
+            LOGW("IndirectRefTable overflow (max=%d)\n", pRef->maxEntries);
+            return NULL;
+        }
+
+        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) {
+            LOGE("Unable to expand iref table (from %d to %d, max=%d)\n",
+                pRef->allocEntries, newSize, pRef->maxEntries);
+            return false;
+        }
+        LOGI("Growing ireftab %p from %d to %d (max=%d)\n",
+            pRef, pRef->allocEntries, newSize, pRef->maxEntries);
+
+        /* update entries; adjust "nextEntry" in case memory moved */
+        pRef->table = newTable;
+        pRef->allocEntries = newSize;
+    }
+
+    IndirectRef result;
+
+    /*
+     * 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.
+     */
+    int numHoles = pRef->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 */
+        Object** pScan = &pRef->table[topIndex - 1];
+        assert(*pScan != NULL);
+        while (*--pScan != NULL) {
+            assert(pScan >= pRef->table + prevState.parts.topIndex);
+        }
+        updateSlotAdd(pRef, obj, pScan - pRef->table);
+        result = dvmObjectToIndirectRef(pRef, obj, pScan - pRef->table,
+            pRef->kind);
+        *pScan = obj;
+        pRef->segmentState.parts.numHoles--;
+    } else {
+        /* add to the end */
+        updateSlotAdd(pRef, obj, topIndex);
+        result = dvmObjectToIndirectRef(pRef, obj, topIndex, pRef->kind);
+        pRef->table[topIndex++] = obj;
+        pRef->segmentState.parts.topIndex = topIndex;
+    }
+
+    assert(result != NULL);
+    return result;
+}
+
+/*
+ * Verify that the indirect table lookup is valid.
+ *
+ * Returns "false" if something looks bad.
+ */
+bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref)
+{
+    if (dvmGetIndirectRefType(iref) == kIndirectKindInvalid) {
+        LOGW("Invalid indirect reference 0x%08x\n", (u4) iref);
+        return false;
+    }
+
+    int topIndex = pRef->segmentState.parts.topIndex;
+    int idx = dvmIndirectRefToIndex(iref);
+
+    if (iref == NULL) {
+        LOGI("--- lookup on NULL iref\n");
+        return false;
+    }
+    if (idx >= topIndex) {
+        /* bad -- stale reference? */
+        LOGI("Attempt to access invalid index %d (top=%d)\n",
+            idx, topIndex);
+        return false;
+    }
+
+    Object* obj = pRef->table[idx];
+    if (obj == NULL) {
+        LOGI("Attempt to read from hole, iref=%p\n", iref);
+        return false;
+    }
+    if (!checkEntry(pRef, iref, idx))
+        return false;
+
+    return true;
+}
+
+/*
+ * 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 explict single removals.
+ *
+ * Returns "false" if nothing was removed.
+ */
+bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
+    IndirectRef iref)
+{
+    IRTSegmentState prevState;
+    prevState.all = cookie;
+    int topIndex = pRef->segmentState.parts.topIndex;
+    int bottomIndex = prevState.parts.topIndex;
+
+    assert(pRef->table != NULL);
+    assert(pRef->allocEntries <= pRef->maxEntries);
+    assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles);
+
+    int idx = dvmIndirectRefToIndex(iref);
+    if (idx < bottomIndex) {
+        /* wrong segment */
+        LOGV("Attempt to remove index outside index area (%d vs %d-%d)\n",
+            idx, bottomIndex, topIndex);
+        return false;
+    }
+    if (idx >= topIndex) {
+        /* bad -- stale reference? */
+        LOGI("Attempt to remove invalid index %d (bottom=%d top=%d)\n",
+            idx, bottomIndex, topIndex);
+        return false;
+    }
+
+    if (idx == topIndex-1) {
+        /*
+         * Top-most entry.  Scan up and consume holes.  No need to NULL
+         * out the entry, since the test vs. topIndex will catch it.
+         */
+        if (!checkEntry(pRef, iref, idx))
+            return false;
+        updateSlotRemove(pRef, idx);
+
+#ifndef NDEBUG
+        pRef->table[idx] = (IndirectRef) 0xd3d3d3d3;
+#endif
+
+        int numHoles =
+            pRef->segmentState.parts.numHoles - prevState.parts.numHoles;
+        if (numHoles != 0) {
+            while (--topIndex > bottomIndex && numHoles != 0) {
+                LOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p\n",
+                    topIndex-1, cookie, pRef->table[topIndex-1]);
+                if (pRef->table[topIndex-1] != NULL)
+                    break;
+                LOGV("+++ ate hole at %d\n", topIndex-1);
+                numHoles--;
+            }
+            pRef->segmentState.parts.numHoles =
+                numHoles + prevState.parts.numHoles;
+            pRef->segmentState.parts.topIndex = topIndex;
+        } else {
+            pRef->segmentState.parts.topIndex = topIndex-1;
+            LOGV("+++ ate last entry %d\n", 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.
+         */
+        if (pRef->table[idx] == NULL) {
+            LOGV("--- WEIRD: removing null entry %d\n", idx);
+            return false;
+        }
+        if (!checkEntry(pRef, iref, idx))
+            return false;
+        updateSlotRemove(pRef, idx);
+
+        pRef->table[idx] = NULL;
+        pRef->segmentState.parts.numHoles++;
+        LOGV("+++ left hole at %d, holes=%d\n",
+            idx, pRef->segmentState.parts.numHoles);
+    }
+
+    return true;
+}
+
+/*
+ * 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)
+{
+    Object* obj1 = *((Object**) vobj1);
+    Object* obj2 = *((Object**) vobj2);
+
+    /* ensure null references appear at the end */
+    if (obj1 == NULL) {
+        if (obj2 == NULL) {
+            return 0;
+        } else {
+            return 1;
+        }
+    } else if (obj2 == NULL) {
+        return -1;
+    }
+
+    if (obj1->clazz != obj2->clazz) {
+        return (u1*)obj1->clazz - (u1*)obj2->clazz;
+    } else {
+        int size1 = dvmObjectSizeInHeap(obj1);
+        int size2 = dvmObjectSizeInHeap(obj2);
+        if (size1 != size2) {
+            return size1 - size2;
+        } else {
+            return (u1*)obj1 - (u1*)obj2;
+        }
+    }
+}
+
+/*
+ * Log an object with some additional info.
+ *
+ * Pass in the number of additional elements that are identical to or
+ * equivalent to the original.
+ */
+static void logObject(Object* obj, int size, int identical, int equiv)
+{
+    if (obj == NULL) {
+        LOGW("  NULL reference (count=%d)\n", equiv);
+        return;
+    }
+
+    if (identical + equiv != 0) {
+        LOGW("%5d of %s %dB (%d unique)\n", identical + equiv +1,
+            obj->clazz->descriptor, size, equiv +1);
+    } else {
+        LOGW("%5d of %s %dB\n", identical + equiv +1,
+            obj->clazz->descriptor, size);
+    }
+}
+
+/*
+ * Dump the contents of a IndirectRefTable to the log.
+ */
+void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr)
+{
+    const int kLast = 10;
+    int count = dvmIndirectRefTableEntries(pRef);
+    Object** refs;
+    int i;
+
+    if (count == 0) {
+        LOGW("Reference table has no entries\n");
+        return;
+    }
+    assert(count > 0);
+
+    /*
+     * Dump the most recent N entries.  If there are holes, we will show
+     * fewer than N.
+     */
+    LOGW("Last %d entries in %s reference table:\n", kLast, descr);
+    refs = pRef->table;         // use unsorted list
+    int size;
+    int start = count - kLast;
+    if (start < 0)
+        start = 0;
+
+    for (i = start; i < count; i++) {
+        if (refs[i] == NULL)
+            continue;
+        size = dvmObjectSizeInHeap(refs[i]);
+        Object* ref = refs[i];
+        if (ref->clazz == gDvm.classJavaLangClass) {
+            ClassObject* clazz = (ClassObject*) ref;
+            LOGW("%5d: %p cls=%s '%s' (%d bytes)\n", i, ref,
+                (refs[i] == NULL) ? "-" : ref->clazz->descriptor,
+                clazz->descriptor, size);
+        } else {
+            LOGW("%5d: %p cls=%s (%d bytes)\n", i, ref,
+                (refs[i] == NULL) ? "-" : ref->clazz->descriptor, size);
+        }
+    }
+
+    /*
+     * Make a copy of the table, and sort it.
+     *
+     * The NULL "holes" wind up at the end, so we can strip them off easily.
+     */
+    Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
+    memcpy(tableCopy, pRef->table, sizeof(Object*) * count);
+    qsort(tableCopy, count, sizeof(Object*), compareObject);
+    refs = tableCopy;       // use sorted list
+
+    if (false) {
+        int q;
+        for (q = 0; q < count; q++)
+            LOGI("%d %p\n", q, refs[q]);
+    }
+
+    int holes = 0;
+    while (refs[count-1] == NULL) {
+        count--;
+        holes++;
+    }
+
+    /*
+     * Dump uniquified table summary.  While we're at it, generate a
+     * cumulative total amount of pinned memory based on the unique entries.
+     */
+    LOGW("%s reference table summary (%d entries / %d holes):\n",
+        descr, count, holes);
+    int equiv, identical, total;
+    total = equiv = identical = 0;
+    for (i = 1; i < count; i++) {
+        size = dvmObjectSizeInHeap(refs[i-1]);
+
+        if (refs[i] == refs[i-1]) {
+            /* same reference, added more than once */
+            identical++;
+        } else if (refs[i]->clazz == refs[i-1]->clazz &&
+            (int) dvmObjectSizeInHeap(refs[i]) == size)
+        {
+            /* same class / size, different object */
+            total += size;
+            equiv++;
+        } else {
+            /* different class */
+            total += size;
+            logObject(refs[i-1], size, identical, equiv);
+            equiv = identical = 0;
+        }
+    }
+
+    /* handle the last entry (everything above outputs refs[i-1]) */
+    size = (refs[count-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[count-1]);
+    total += size;
+    logObject(refs[count-1], size, identical, equiv);
+
+    LOGW("Memory held directly by native code is %d bytes\n", total);
+    free(tableCopy);
+}
diff --git a/vm/IndirectRefTable.h b/vm/IndirectRefTable.h
new file mode 100644
index 0000000..6a4db04
--- /dev/null
+++ b/vm/IndirectRefTable.h
@@ -0,0 +1,382 @@
+/*
+ * 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
+#define _DALVIK_INDIRECTREFTABLE
+/*
+ * 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.)
+ * This is only done when CheckJNI is enabled.
+ *
+ * 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.
+ */
+typedef void* IndirectRef;
+
+/*
+ * Indirect reference kind, used as the two low bits of IndirectRef.
+ *
+ * For convenience these match up with enum jobjectRefType from jni.h.
+ */
+typedef enum IndirectRefKind {
+    kIndirectKindInvalid    = 0,
+    kIndirectKindLocal      = 1,
+    kIndirectKindGlobal     = 2,
+    kIndirectKindWeakGlobal = 3
+} IndirectRefKind;
+
+/*
+ * Extended debugging structure.  We keep a parallel array of these, one
+ * per slot in the table.
+ */
+#define kIRTPrevCount   4
+typedef struct IndirectRefSlot {
+    u4          serial;         /* slot serial */
+    Object*     previous[kIRTPrevCount];
+} IndirectRefSlot;
+
+/*
+ * 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 "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, 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.
+ */
+typedef union IRTSegmentState {
+    u4          all;
+    struct {
+        u4      topIndex:16;            /* index of first unused entry */
+        u4      numHoles:16;            /* #of holes in entire table */
+    } parts;
+} IRTSegmentState;
+typedef struct IndirectRefTable {
+    /* semi-public - read/write by interpreter in native call handler */
+    IRTSegmentState segmentState;
+
+    /* semi-public - read-only during GC scan; pointer must not be kept */
+    Object**        table;              /* bottom of the stack */
+
+    /* private */
+    IndirectRefSlot* slotData;          /* extended debugging info */
+    int             allocEntries;       /* #of entries we have space for */
+    int             maxEntries;         /* max #of entries allowed */
+    IndirectRefKind kind;               /* bit mask, ORed into all irefs */
+
+    // TODO: want hole-filling stats (#of holes filled, total entries scanned)
+    //       for performance evaluation.
+} IndirectRefTable;
+
+/* use as initial value for "cookie", and when table has only one segment */
+#define IRT_FIRST_SEGMENT   0
+
+/*
+ * (This is PRIVATE, but we want it inside other inlines in this header.)
+ *
+ * Indirectify the object.
+ *
+ * The object pointer itself is subject to relocation in some GC
+ * implementations, so we shouldn't really be using it here.
+ */
+INLINE IndirectRef dvmObjectToIndirectRef(IndirectRefTable* pRef,
+    Object* obj, u4 tableIndex, IndirectRefKind kind)
+{
+    assert(tableIndex < 65536);
+    //u4 objChunk = (((u4) obj >> 3) ^ ((u4) obj >> 19)) & 0x3fff;
+    //u4 uref = objChunk << 18 | (tableIndex << 2) | kind;
+    u4 serialChunk = pRef->slotData[tableIndex].serial;
+    u4 uref = serialChunk << 20 | (tableIndex << 2) | kind;
+    return (IndirectRef) uref;
+}
+
+/*
+ * (This is PRIVATE, but we want it inside other inlines in this header.)
+ *
+ * Extract the table index from an indirect reference.
+ */
+INLINE u4 dvmIndirectRefToIndex(IndirectRef iref)
+{
+    u4 uref = (u4) iref;
+    return (uref >> 2) & 0xffff;
+}
+
+/*
+ * Determine what kind of indirect reference this is.
+ */
+INLINE IndirectRefKind dvmGetIndirectRefType(IndirectRef iref)
+{
+    return (u4) iref & 0x03;
+}
+
+/*
+ * 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 dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount,
+    int maxCount, IndirectRefKind kind);
+
+/*
+ * Clear out the contents, freeing allocated storage.  Does not free "pRef".
+ *
+ * You must call dvmInitReferenceTable() before you can re-use this table.
+ */
+void dvmClearIndirectRefTable(IndirectRefTable* pRef);
+
+/*
+ * Start a new segment at the top of the table.
+ *
+ * Returns an opaque 32-bit value that must be provided when the segment
+ * is to be removed.
+ *
+ * IMPORTANT: this is implemented as a single instruction in mterp, rather
+ * than a call here.  You can add debugging aids for the C-language
+ * interpreters, but the basic implementation may not change.
+ */
+INLINE u4 dvmPushIndirectRefTableSegment(IndirectRefTable* pRef)
+{
+    return pRef->segmentState.all;
+}
+
+/* extra debugging checks */
+bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie);
+
+/*
+ * Remove one or more segments from the top.  The table entry identified
+ * by "cookie" becomes the new top-most entry.
+ *
+ * IMPORTANT: this is implemented as a single instruction in mterp, rather
+ * than a call here.  You can add debugging aids for the C-language
+ * interpreters, but the basic implementation must not change.
+ */
+INLINE void dvmPopIndirectRefTableSegment(IndirectRefTable* pRef, u4 cookie)
+{
+    dvmPopIndirectRefTableSegmentCheck(pRef, cookie);
+    pRef->segmentState.all = cookie;
+}
+
+/*
+ * Return the #of entries in the entire table.  This includes holes, and
+ * so may be larger than the actual number of "live" entries.
+ */
+INLINE size_t dvmIndirectRefTableEntries(const IndirectRefTable* pRef)
+{
+    return pRef->segmentState.parts.topIndex;
+}
+
+/*
+ * Returns "true" if the table is full.  The table is considered full if
+ * we would need to expand it to add another entry to the current segment.
+ */
+INLINE size_t dvmIsIndirectRefTableFull(const IndirectRefTable* pRef)
+{
+    return dvmIndirectRefTableEntries(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 NULL if the table is full (max entries reached, or alloc
+ * failed during expansion).
+ */
+IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
+    Object* obj);
+
+/*
+ * Add a new entry at the end.  Similar to Add but does not usually attempt
+ * to fill in holes.  This is only appropriate to use right after a new
+ * segment has been pushed.
+ *
+ * (This is intended for use when calling into a native JNI method, so
+ * performance is critical.)
+ */
+INLINE IndirectRef dvmAppendToIndirectRefTable(IndirectRefTable* pRef,
+    u4 cookie, Object* obj)
+{
+    int topIndex = pRef->segmentState.parts.topIndex;
+    if (topIndex == pRef->allocEntries) {
+        /* up against alloc or max limit, call the fancy version */
+        return dvmAddToIndirectRefTable(pRef, cookie, obj);
+    } else {
+        IndirectRef result = dvmObjectToIndirectRef(pRef, obj, topIndex,
+            pRef->kind);
+        pRef->table[topIndex++] = obj;
+        pRef->segmentState.parts.topIndex = topIndex;
+        return result;
+    }
+}
+
+/* extra debugging checks */
+bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref);
+
+/*
+ * Given an IndirectRef in the table, return the Object it refers to.
+ *
+ * Returns NULL if iref is invalid.
+ */
+INLINE Object* dvmGetFromIndirectRefTable(IndirectRefTable* pRef,
+    IndirectRef iref)
+{
+    if (!dvmGetFromIndirectRefTableCheck(pRef, iref))
+        return NULL;
+
+    int idx = dvmIndirectRefToIndex(iref);
+    return pRef->table[idx];
+}
+
+/*
+ * 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 dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
+    IndirectRef iref);
+
+/*
+ * Dump the contents of a reference table to the log file.
+ */
+void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr);
+
+#endif /*_DALVIK_INDIRECTREFTABLE*/
diff --git a/vm/Init.c b/vm/Init.c
new file mode 100644
index 0000000..b82dd39
--- /dev/null
+++ b/vm/Init.c
@@ -0,0 +1,1748 @@
+/*
+ * 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 "Dalvik.h"
+#include "test/Test.h"
+#include "mterp/Mterp.h"
+#include "Hash.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#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 dvmInitJDWP(void);
+static bool dvmInitZygote(void);
+
+
+/* global state */
+struct DvmGlobals gDvm;
+
+/* 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 dvmUsage(const char* progName)
+{
+    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}\n");
+    dvmFprintf(stderr, "  -Xnoquithandler\n");
+    dvmFprintf(stderr,
+                "  -Xjnigreflimit:N  (must be multiple of 100, >= 200)\n");
+    dvmFprintf(stderr, "  -Xjniopts:{warnonly,forcecopy}\n");
+    dvmFprintf(stderr, "  -Xjnitrace:substring (eg NativeClass or nativeMethod)\n");
+    dvmFprintf(stderr, "  -Xdeadlockpredict:{off,warn,err,abort}\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, "  -Xgenregmap\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, "  -Xjitblocking\n");
+    dvmFprintf(stderr, "  -Xjitmethod:signature[,signature]* "
+                       "(eg Ljava/lang/String\\;replace)\n");
+    dvmFprintf(stderr, "  -Xjitcheckcg\n");
+    dvmFprintf(stderr, "  -Xjitverbose\n");
+    dvmFprintf(stderr, "  -Xjitprofile\n");
+    dvmFprintf(stderr, "  -Xjitdisableopt\n");
+#endif
+    dvmFprintf(stderr, "\n");
+    dvmFprintf(stderr, "Configured with:"
+        " debugger"
+        " profiler"
+#ifdef WITH_MONITOR_TRACKING
+        " monitor_tracking"
+#endif
+#ifdef WITH_DEADLOCK_PREDICTION
+        " deadlock_prediction"
+#endif
+#ifdef WITH_HPROF
+        " hprof"
+#endif
+#ifdef WITH_HPROF_STACK
+        " hprof_stack"
+#endif
+#ifdef WITH_ALLOC_LIMITS
+        " alloc_limits"
+#endif
+#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
+#ifdef PROFILE_FIELD_ACCESS
+        " profile_field_access"
+#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(void)
+{
+    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(void)
+{
+    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 unsigned int dvmParseMemOption(const char *s, unsigned int 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;
+        unsigned int val;
+
+        val = (unsigned int)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') {
+                    unsigned int 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 <= UINT_MAX / mul) {
+                        val *= mul;
+                    } else {
+                        /* Clamp to a multiple of 1024.
+                         */
+                        val = UINT_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 {
+            LOGE("JDWP transport '%s' not supported\n", value);
+            return false;
+        }
+    } else if (strcmp(name, "server") == 0) {
+        if (*value == 'n')
+            gDvm.jdwpServer = false;
+        else if (*value == 'y')
+            gDvm.jdwpServer = true;
+        else {
+            LOGE("JDWP option 'server' must be 'y' or 'n'\n");
+            return false;
+        }
+    } else if (strcmp(name, "suspend") == 0) {
+        if (*value == 'n')
+            gDvm.jdwpSuspend = false;
+        else if (*value == 'y')
+            gDvm.jdwpSuspend = true;
+        else {
+            LOGE("JDWP option 'suspend' must be 'y' or 'n'\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') {
+            LOGE("JDWP address missing port\n");
+            return false;
+        }
+        port = strtol(value, &end, 10);
+        if (*end != '\0') {
+            LOGE("JDWP address has junk in port field '%s'\n", 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 */
+        LOGI("Ignoring JDWP option '%s'='%s'\n", name, value);
+    } else {
+        LOGI("Ignoring unrecognized JDWP option '%s'='%s'\n", 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) {
+            LOGE("JDWP opts: garbage at '%s'\n", name);
+            goto bail;
+        }
+
+        comma = strchr(name, ',');      // use name, not value, for safety
+        if (comma != NULL) {
+            if (comma < value) {
+                LOGE("JDWP opts: found comma before '=' in '%s'\n", 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) {
+        LOGE("JDWP opts: must specify transport\n");
+        goto bail;
+    }
+    if (!gDvm.jdwpServer && (gDvm.jdwpHost == NULL || gDvm.jdwpPort == 0)) {
+        LOGE("JDWP opts: when server=n, must specify host and port\n");
+        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 '/' */
+                LOGW("Unable to process assertion arg '%s'\n", 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(void)
+{
+    if (gDvm.assertionCtrl == NULL) {
+        LOGD("Not late-enabling assertions: no assertionCtrl array\n");
+        return;
+    } else if (gDvm.assertionCtrlCount != 0) {
+        LOGD("Not late-enabling assertions: some asserts already configured\n");
+        return;
+    }
+    LOGD("Late-enabling assertions\n");
+
+    /* 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(void)
+{
+    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 &= 0xff;
+
+                if (*endPtr == '-') {
+                    endValue = strtol(endPtr+1, &endPtr, 16);
+                    endValue &= 0xff;
+                } else {
+                    endValue = startValue;
+                }
+
+                for (; startValue <= endValue; startValue++) {
+                    LOGW("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 < 32; i++) {
+            gDvmJit.opList[i] = 0xff;
+        }
+        dvmFprintf(stderr, "Warning: select all opcodes\n");
+    }
+}
+
+/* Parse -Xjitmethod to selectively turn on/off certain methods for JIT */
+static void processXjitmethod(const char *opt)
+{
+    char *buf = strdup(&opt[12]);
+    char *start, *end;
+
+    gDvmJit.methodTable = dvmHashTableCreate(8, NULL);
+
+    start = buf;
+    /*
+     * 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(gDvmJit.methodTable, hashValue,
+                           strdup(start),
+                           (HashCompareFunc) strcmp, true);
+        if (end) {
+            start = end + 1;
+        } else {
+            break;
+        }
+    } while (1);
+    free(buf);
+}
+#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 dvmProcessOptions(int argc, const char* const argv[],
+    bool ignoreUnrecognized)
+{
+    int i;
+
+    LOGV("VM options (%d):\n", argc);
+    for (i = 0; i < argc; i++)
+        LOGV("  %d: '%s'\n", 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 */
+            return -1;
+
+        } 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) {
+            /* set property */
+            dvmAddCommandLineProperty(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) {
+            unsigned int val = dvmParseMemOption(argv[i]+4, 1024);
+            if (val != 0) {
+                if (val >= kMinHeapStartSize && val <= kMaxHeapSize) {
+                    gDvm.heapSizeStart = 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) {
+            unsigned int val = dvmParseMemOption(argv[i]+4, 1024);
+            if (val != 0) {
+                if (val >= kMinHeapSize && val <= kMaxHeapSize) {
+                    gDvm.heapSizeMax = 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], "-Xss", 4) == 0) {
+            unsigned int val = dvmParseMemOption(argv[i]+4, 1);
+            if (val != 0) {
+                if (val >= kMinStackSize && val <= kMaxStackSize) {
+                    gDvm.stackSize = 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 (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 {
+                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) {
+            int lim = atoi(argv[i] + 15);
+            if (lim < 200 || (lim % 100) != 0) {
+                dvmFprintf(stderr, "Bad value for -Xjnigreflimit: '%s'\n",
+                    argv[i]+15);
+                return -1;
+            }
+            gDvm.jniGrefLimit = lim;
+        } 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", 11) == 0) {
+            processXjitmethod(argv[i]);
+        } 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], "-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], "-Xjitverbose", 12) == 0) {
+          gDvmJit.printMe = true;
+        } else if (strncmp(argv[i], "-Xjitprofile", 12) == 0) {
+          gDvmJit.profile = true;
+        } 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;
+          }
+#endif
+
+        } else if (strncmp(argv[i], "-Xdeadlockpredict:", 18) == 0) {
+#ifdef WITH_DEADLOCK_PREDICTION
+            if (strcmp(argv[i] + 18, "off") == 0)
+                gDvm.deadlockPredictMode = kDPOff;
+            else if (strcmp(argv[i] + 18, "warn") == 0)
+                gDvm.deadlockPredictMode = kDPWarn;
+            else if (strcmp(argv[i] + 18, "err") == 0)
+                gDvm.deadlockPredictMode = kDPErr;
+            else if (strcmp(argv[i] + 18, "abort") == 0)
+                gDvm.deadlockPredictMode = kDPAbort;
+            else {
+                dvmFprintf(stderr, "Bad value for -Xdeadlockpredict");
+                return -1;
+            }
+            if (gDvm.deadlockPredictMode != kDPOff)
+                LOGD("Deadlock prediction enabled (%s)\n", argv[i]+18);
+#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;
+            LOGV("Register maps will be generated during verification\n");
+
+        } 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;
+            }
+            LOGV("Precise GC configured %s\n", gDvm.preciseGc ? "ON" : "OFF");
+
+        } else if (strcmp(argv[i], "-Xcheckdexsum") == 0) {
+            gDvm.verifyDexChecksum = true;
+
+        } else if (strcmp(argv[i], "-Xprofile:wallclock") == 0) {
+            gDvm.profilerWallClock = true;
+
+        } else {
+            if (!ignoreUnrecognized) {
+                dvmFprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
+                return -1;
+            }
+        }
+    }
+
+    if (gDvm.heapSizeStart > gDvm.heapSizeMax) {
+        dvmFprintf(stderr, "Heap start size must be <= heap max size\n");
+        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;
+
+    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(".");
+
+    /* Defaults overridden by -Xms and -Xmx.
+     * TODO: base these on a system or application-specific default
+     */
+    gDvm.heapSizeStart = 2 * 1024 * 1024;   // Spec says 16MB; too big for us.
+    gDvm.heapSizeMax = 16 * 1024 * 1024;    // Spec says 75% physical mem
+    gDvm.stackSize = kDefaultStackSize;
+
+    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;
+
+    /*
+     * 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;
+#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);
+}
+
+
+/*
+ * 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;
+
+    LOGE("Caught a SIGBUS (%d), addr=%p\n", 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);
+    }
+}
+
+/*
+ * 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.
+ */
+int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized,
+    JNIEnv* pEnv)
+{
+    int i, cc;
+
+    assert(gDvm.initializing);
+
+    LOGV("VM init args (%d):\n", argc);
+    for (i = 0; i < argc; i++)
+        LOGV("  %d: '%s'\n", i, argv[i]);
+
+    setCommandLineDefaults();
+
+    /* prep properties storage */
+    if (!dvmPropertiesStartup(argc))
+        goto fail;
+
+    /*
+     * Process the option flags (if any).
+     */
+    cc = dvmProcessOptions(argc, argv, ignoreUnrecognized);
+    if (cc != 0) {
+        if (cc < 0) {
+            dvmFprintf(stderr, "\n");
+            dvmUsage("dalvikvm");
+        }
+        goto fail;
+    }
+
+#if WITH_EXTRA_GC_CHECKS > 1
+    /* only "portable" interp has the extra goodies */
+    if (gDvm.executionMode != kExecutionModeInterpPortable) {
+        LOGI("Switching to 'portable' interpreter for GC checks\n");
+        gDvm.executionMode = kExecutionModeInterpPortable;
+    }
+#endif
+
+    /* Configure group scheduling capabilities */
+    if (!access("/dev/cpuctl/tasks", F_OK)) {
+        LOGV("Using kernel group scheduling");
+        gDvm.kernelGroupScheduling = 1;
+    } else {
+        LOGV("Using kernel scheduler policies");
+    }
+
+    /* configure signal handling */
+    if (!gDvm.reduceSignals)
+        blockSignals();
+
+    /* verify system page size */
+    if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
+        LOGE("ERROR: expected page size %d, got %d\n",
+            SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
+        goto fail;
+    }
+
+    /* mterp setup */
+    LOGV("Using executionMode %d\n", gDvm.executionMode);
+    dvmCheckAsmConstants();
+
+    /*
+     * Initialize components.
+     */
+    if (!dvmAllocTrackerStartup())
+        goto fail;
+    if (!dvmGcStartup())
+        goto fail;
+    if (!dvmThreadStartup())
+        goto fail;
+    if (!dvmInlineNativeStartup())
+        goto fail;
+    if (!dvmVerificationStartup())
+        goto fail;
+    if (!dvmRegisterMapStartup())
+        goto fail;
+    if (!dvmInstanceofStartup())
+        goto fail;
+    if (!dvmClassStartup())
+        goto fail;
+    if (!dvmThreadObjStartup())
+        goto fail;
+    if (!dvmExceptionStartup())
+        goto fail;
+    if (!dvmStringInternStartup())
+        goto fail;
+    if (!dvmNativeStartup())
+        goto fail;
+    if (!dvmInternalNativeStartup())
+        goto fail;
+    if (!dvmJniStartup())
+        goto fail;
+    if (!dvmReflectStartup())
+        goto fail;
+    if (!dvmProfilingStartup())
+        goto fail;
+
+    /* make sure we got these [can this go away?] */
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(gDvm.classJavaLangObject != NULL);
+    //assert(gDvm.classJavaLangString != NULL);
+    assert(gDvm.classJavaLangThread != NULL);
+    assert(gDvm.classJavaLangVMThread != NULL);
+    assert(gDvm.classJavaLangThreadGroup != NULL);
+
+    /*
+     * Make sure these exist.  If they don't, we can return a failure out
+     * of main and nip the whole thing in the bud.
+     */
+    static const char* earlyClasses[] = {
+        "Ljava/lang/InternalError;",
+        "Ljava/lang/StackOverflowError;",
+        "Ljava/lang/UnsatisfiedLinkError;",
+        "Ljava/lang/NoClassDefFoundError;",
+        NULL
+    };
+    const char** pClassName;
+    for (pClassName = earlyClasses; *pClassName != NULL; pClassName++) {
+        if (dvmFindSystemClassNoInit(*pClassName) == NULL)
+            goto fail;
+    }
+
+    /*
+     * Miscellaneous class library validation.
+     */
+    if (!dvmValidateBoxClasses())
+        goto fail;
+
+    /*
+     * Do the last bits of Thread struct initialization we need to allow
+     * JNI calls to work.
+     */
+    if (!dvmPrepMainForJni(pEnv))
+        goto fail;
+
+    /*
+     * Register the system native methods, which are registered through JNI.
+     */
+    if (!registerSystemNatives(pEnv))
+        goto fail;
+
+    /*
+     * Do some "late" initialization for the memory allocator.  This may
+     * allocate storage and initialize classes.
+     */
+    if (!dvmCreateStockExceptions())
+        goto fail;
+
+    /*
+     * 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())
+        goto fail;
+
+    /*
+     * Make sure we haven't accumulated any tracked references.  The main
+     * thread should be starting with a clean slate.
+     */
+    if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
+    {
+        LOGW("Warning: tracked references remain post-initialization\n");
+        dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
+    }
+
+    /* general debugging setup */
+    if (!dvmDebuggerStartup())
+        goto fail;
+
+    /*
+     * 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 (!dvmInitZygote())
+            goto fail;
+    } else {
+        if (!dvmInitAfterZygote())
+            goto fail;
+    }
+
+
+#ifndef NDEBUG
+    if (!dvmTestHash())
+        LOGE("dmvTestHash FAILED\n");
+    if (false /*noisy!*/ && !dvmTestIndirectRefTable())
+        LOGE("dvmTestIndirectRefTable FAILED\n");
+#endif
+
+    assert(!dvmCheckException(dvmThreadSelf()));
+    gDvm.initExceptionCount = 0;
+
+    return 0;
+
+fail:
+    dvmShutdown();
+    return 1;
+}
+
+/*
+ * 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)
+{
+    Thread* self;
+
+    /* main thread is always first in list */
+    self = gDvm.threadList;
+
+    /* must set this before allowing JNI-based method registration */
+    self->status = THREAD_NATIVE;
+
+    if (jniRegisterSystemMethods(pEnv) < 0) {
+        LOGW("jniRegisterSystemMethods failed\n");
+        return false;
+    }
+
+    /* back to run mode */
+    self->status = THREAD_RUNNING;
+
+    return true;
+}
+
+
+/*
+ * Do zygote-mode-only initialization.
+ */
+static bool dvmInitZygote(void)
+{
+    /* zygote goes into its own process group */
+    setpgid(0,0);
+
+    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(void)
+{
+    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 (!dvmInitJDWP()) {
+        LOGD("JDWP init failed; continuing anyway\n");
+    }
+
+    endJdwp = dvmGetRelativeTimeUsec();
+
+    LOGV("thread-start heap=%d quit=%d jdwp=%d total=%d usec\n",
+        (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 dvmInitJDWP(void)
+{
+    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) {
+                LOGE("ERROR: hostname too long: '%s'\n", 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) {
+            LOGW("WARNING: debugger thread failed to initialize\n");
+            /* 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)) {
+            LOGW("WARNING: failed to post 'start' message to debugger\n");
+            /* 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.  We check the "initializing" flag anyway when
+ * throwing an exception, so we can insert some code that avoids chucking
+ * an exception when we're optimizing stuff.
+ *
+ * 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 (!dvmVerificationStartup())
+        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(void)
+{
+    LOGV("VM shutting down\n");
+
+    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)
+        LOGD("VM cleaning up\n");
+
+    dvmDebuggerShutdown();
+    dvmReflectShutdown();
+    dvmProfilingShutdown();
+    dvmJniShutdown();
+    dvmStringInternShutdown();
+    dvmExceptionShutdown();
+    dvmThreadShutdown();
+    dvmClassShutdown();
+    dvmVerificationShutdown();
+    dvmRegisterMapShutdown();
+    dvmInstanceofShutdown();
+    dvmInlineNativeShutdown();
+    dvmGcShutdown();
+    dvmAllocTrackerShutdown();
+    dvmPropertiesShutdown();
+
+    /* these must happen AFTER dvmClassShutdown has walked through class data */
+    dvmNativeShutdown();
+    dvmInternalNativeShutdown();
+
+    free(gDvm.bootClassPathStr);
+    free(gDvm.classPathStr);
+
+    freeAssertionCtrl();
+
+    /*
+     * 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;
+}
+
+/*
+ * 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(void)
+{
+    LOGE("VM aborting\n");
+
+    fflush(NULL);       // flush all open file buffers
+
+    /* JNI-supplied abort hook gets right of first refusal */
+    if (gDvm.abortHook != NULL)
+        (*gDvm.abortHook)();
+
+    /*
+     * If we call abort(), all threads in the process receives a SIBABRT.
+     * debuggerd dumps the stack trace of the main thread, whether or not
+     * that was the thread that failed.
+     *
+     * By stuffing a value into a bogus address, we cause a segmentation
+     * fault in the current thread, and get a useful log from debuggerd.
+     * We can also trivially tell the difference between a VM crash and
+     * a deliberate abort by looking at the fault address.
+     */
+    *((char*)0xdeadd00d) = 38;
+    abort();
+
+    /* notreached */
+}
diff --git a/vm/Init.h b/vm/Init.h
new file mode 100644
index 0000000..63051a2
--- /dev/null
+++ b/vm/Init.h
@@ -0,0 +1,54 @@
+/*
+ * 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
+#define _DALVIK_INIT
+
+/*
+ * Standard VM initialization, usually invoked through JNI.
+ */
+int 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);
+
+/*
+ * 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*/
diff --git a/vm/InlineNative.c b/vm/InlineNative.c
new file mode 100644
index 0000000..a3a9ac8
--- /dev/null
+++ b/vm/InlineNative.c
@@ -0,0 +1,857 @@
+/*
+ * 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>
+
+#ifdef HAVE__MEMCMP16
+/* hand-coded assembly implementation, available on some platforms */
+//#warning "trying memcmp16"
+//#define CHECK_MEMCMP16
+/* "count" is in 16-bit units */
+extern u4 __memcmp16(const u2* s0, const u2* s1, size_t count);
+#endif
+
+/*
+ * 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)
+ */
+static bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    int count, offset;
+    ArrayObject* chars;
+
+    /* null reference check on "this" */
+    if (!dvmValidateObject((Object*) arg0))
+        return false;
+
+    //LOGI("String.charAt this=0x%08x index=%d\n", arg0, arg1);
+    count = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    if ((s4) arg1 < 0 || (s4) arg1 >= count) {
+        dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
+        return false;
+    } else {
+        offset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+        chars = (ArrayObject*)
+            dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+
+        pResult->i = ((const u2*) chars->contents)[arg1 + offset];
+        return true;
+    }
+}
+
+#ifdef CHECK_MEMCMP16
+/*
+ * Utility function when we're evaluating alternative implementations.
+ */
+static void badMatch(StringObject* thisStrObj, StringObject* compStrObj,
+    int expectResult, int newResult, const char* compareType)
+{
+    ArrayObject* thisArray;
+    ArrayObject* compArray;
+    const char* thisStr;
+    const char* compStr;
+    int thisOffset, compOffset, thisCount, compCount;
+
+    thisCount =
+        dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_COUNT);
+    compCount =
+        dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_COUNT);
+    thisOffset =
+        dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_OFFSET);
+    compOffset =
+        dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_OFFSET);
+    thisArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) thisStrObj, STRING_FIELDOFF_VALUE);
+    compArray = (ArrayObject*)
+        dvmGetFieldObject((Object*) compStrObj, STRING_FIELDOFF_VALUE);
+
+    thisStr = dvmCreateCstrFromString(thisStrObj);
+    compStr = dvmCreateCstrFromString(compStrObj);
+
+    LOGE("%s expected %d got %d\n", compareType, expectResult, newResult);
+    LOGE(" this (o=%d l=%d) '%s'\n", thisOffset, thisCount, thisStr);
+    LOGE(" comp (o=%d l=%d) '%s'\n", compOffset, compCount, compStr);
+    dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
+        ((const u2*) thisArray->contents) + thisOffset, thisCount*2,
+        kHexDumpLocal);
+    dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
+        ((const u2*) compArray->contents) + compOffset, compCount*2,
+        kHexDumpLocal);
+    dvmAbort();
+}
+#endif
+
+/*
+ * public int compareTo(String s)
+ */
+static 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 (!dvmValidateObject((Object*) arg0) ||
+        !dvmValidateObject((Object*) arg1))
+    {
+        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*) thisArray->contents) + thisOffset;
+    compChars = ((const u2*) compArray->contents) + compOffset;
+
+#ifdef HAVE__MEMCMP16
+    /*
+     * Use assembly version, which returns the difference between the
+     * characters.  The annoying part here is that 0x00e9 - 0xffff != 0x00ea,
+     * because the interpreter converts the characters to 32-bit integers
+     * *without* sign extension before it subtracts them (which makes some
+     * sense since "char" is unsigned).  So what we get is the result of
+     * 0x000000e9 - 0x0000ffff, which is 0xffff00ea.
+     */
+    int otherRes = __memcmp16(thisChars, compChars, minCount);
+# ifdef CHECK_MEMCMP16
+    int i;
+    for (i = 0; i < minCount; i++) {
+        if (thisChars[i] != compChars[i]) {
+            pResult->i = (s4) thisChars[i] - (s4) compChars[i];
+            if (pResult->i != otherRes) {
+                badMatch((StringObject*) arg0, (StringObject*) arg1,
+                    pResult->i, otherRes, "compareTo");
+            }
+            return true;
+        }
+    }
+# endif
+    if (otherRes != 0) {
+        pResult->i = otherRes;
+        return true;
+    }
+
+#else
+    /*
+     * 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;
+        }
+    }
+#endif
+
+    pResult->i = countDiff;
+    return true;
+}
+
+/*
+ * public boolean equals(Object anObject)
+ */
+static bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    /*
+     * Null reference check on "this".
+     */
+    if (!dvmValidateObject((Object*) arg0))
+        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;
+    }
+
+    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*) thisArray->contents) + thisOffset;
+    compChars = ((const u2*) compArray->contents) + compOffset;
+
+#ifdef HAVE__MEMCMP16
+    pResult->i = (__memcmp16(thisChars, compChars, thisCount) == 0);
+# ifdef CHECK_MEMCMP16
+    int otherRes = (memcmp(thisChars, compChars, thisCount * 2) == 0);
+    if (pResult->i != otherRes) {
+        badMatch((StringObject*) arg0, (StringObject*) arg1,
+            otherRes, pResult->i, "equals-1");
+    }
+# endif
+#else
+    /*
+     * 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;
+#endif
+
+    return true;
+}
+
+/*
+ * public int length()
+ */
+static bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    //LOGI("String.length this=0x%08x pResult=%p\n", arg0, pResult);
+
+    /* null reference check on "this" */
+    if (!dvmValidateObject((Object*) arg0))
+        return false;
+
+    pResult->i = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+    return true;
+}
+
+/*
+ * public boolean isEmpty()
+ */
+static bool javaLangString_isEmpty(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    //LOGI("String.isEmpty this=0x%08x pResult=%p\n", arg0, pResult);
+
+    /* null reference check on "this" */
+    if (!dvmValidateObject((Object*) arg0))
+        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*) charArray->contents;
+    int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET);
+    int count = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT);
+    //LOGI("String.indexOf(0x%08x, 0x%04x, %d) off=%d count=%d\n",
+    //    (u4) strObj, ch, start, offset, count);
+
+    /* factor out the offset */
+    chars += offset;
+
+    if (start < 0)
+        start = 0;
+
+#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.
+ */
+static bool javaLangString_fastIndexOf_II(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+    JValue* pResult)
+{
+    /* null reference check on "this" */
+    if (!dvmValidateObject((Object*) arg0))
+        return false;
+
+    pResult->i = indexOfCommon((Object*) arg0, arg1, arg2);
+    return true;
+}
+
+
+/*
+ * ===========================================================================
+ *      java.lang.Math
+ * ===========================================================================
+ */
+
+typedef union {
+    u4 arg;
+    float ff;
+} Convert32;
+
+typedef union {
+    u4 arg[2];
+    s8 ll;
+    double dd;
+} Convert64;
+
+/*
+ * public static int abs(int)
+ */
+static 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)
+ */
+static 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)
+ */
+static 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)
+ */
+static 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)
+ */
+static 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)
+ */
+static 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.
+ */
+static 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)
+ */
+static 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)
+ */
+static 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
+ * ===========================================================================
+ */
+
+static 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;
+}
+
+static bool javaLangFloat_floatToRawIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+    JValue* pResult)
+{
+    pResult->i = arg0;
+    return true;
+}
+
+static 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
+ * ===========================================================================
+ */
+
+static 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;
+}
+
+static 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;
+}
+
+static 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" },
+};
+
+/*
+ * Allocate some tables.
+ */
+bool dvmInlineNativeStartup(void)
+{
+    gDvm.inlinedMethods =
+        (Method**) calloc(NELEM(gDvmInlineOpsTable), sizeof(Method*));
+    if (gDvm.inlinedMethods == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Free generated tables.
+ */
+void dvmInlineNativeShutdown(void)
+{
+    free(gDvm.inlinedMethods);
+}
+
+
+/*
+ * Get a pointer to the inlineops table.
+ */
+const InlineOperation* dvmGetInlineOpsTable(void)
+{
+    return gDvmInlineOpsTable;
+}
+
+/*
+ * Get the number of entries in the inlineops table.
+ */
+int dvmGetInlineOpsTableLength(void)
+{
+    return NELEM(gDvmInlineOpsTable);
+}
+
+/*
+ * 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)
+{
+    Thread* self = dvmThreadSelf();
+    bool result;
+
+    assert(opIndex >= 0 && opIndex < NELEM(gDvmInlineOpsTable));
+
+    /*
+     * 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* method = gDvm.inlinedMethods[opIndex];
+    if (method == NULL) {
+        ClassObject* clazz;
+
+        clazz = dvmFindClassNoInit(
+                gDvmInlineOpsTable[opIndex].classDescriptor, NULL);
+        if (clazz == NULL) {
+            LOGW("Warning: can't find class '%s'\n", clazz->descriptor);
+            goto skip_prof;
+        }
+        method = dvmFindDirectMethodByDescriptor(clazz,
+                    gDvmInlineOpsTable[opIndex].methodName,
+                    gDvmInlineOpsTable[opIndex].methodSignature);
+        if (method == NULL)
+            method = dvmFindVirtualMethodByDescriptor(clazz,
+                        gDvmInlineOpsTable[opIndex].methodName,
+                        gDvmInlineOpsTable[opIndex].methodSignature);
+        if (method == NULL) {
+            LOGW("Warning: can't find method %s.%s %s\n",
+                clazz->descriptor,
+                gDvmInlineOpsTable[opIndex].methodName,
+                gDvmInlineOpsTable[opIndex].methodSignature);
+            goto skip_prof;
+        }
+
+        gDvm.inlinedMethods[opIndex] = method;
+        IF_LOGV() {
+            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+            LOGV("Registered for profile: %s.%s %s\n",
+                method->clazz->descriptor, method->name, desc);
+            free(desc);
+        }
+    }
+
+    TRACE_METHOD_ENTER(self, method);
+    result = (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3,
+                pResult);
+    TRACE_METHOD_EXIT(self, method);
+    return result;
+
+skip_prof:
+    return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult);
+}
diff --git a/vm/InlineNative.h b/vm/InlineNative.h
new file mode 100644
index 0000000..a54d9bd
--- /dev/null
+++ b/vm/InlineNative.h
@@ -0,0 +1,111 @@
+/*
+ * 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
+#define _DALVIK_INLINENATIVE
+
+/* startup/shutdown */
+bool dvmInlineNativeStartup(void);
+void dvmInlineNativeShutdown(void);
+
+/*
+ * 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.
+ */
+typedef struct InlineOperation {
+    InlineOp4Func   func;               /* MUST be first entry */
+    const char*     classDescriptor;
+    const char*     methodName;
+    const char*     methodSignature;
+} InlineOperation;
+
+/* Must be kept in sync w/ gDvmInlineOpsTable in InlineNative.c */
+typedef 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,
+} NativeInlineOps;
+
+/*
+ * 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);
+
+#endif /*_DALVIK_INLINENATIVE*/
diff --git a/vm/Inlines.c b/vm/Inlines.c
new file mode 100644
index 0000000..f1bd621
--- /dev/null
+++ b/vm/Inlines.c
@@ -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.c"
+
+#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.c b/vm/Intern.c
new file mode 100644
index 0000000..8bc38b8
--- /dev/null
+++ b/vm/Intern.c
@@ -0,0 +1,206 @@
+/*
+ * 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(void)
+{
+    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(void)
+{
+    if (gDvm.internedStrings != NULL || gDvm.literalStrings != NULL) {
+        dvmDestroyMutex(&gDvm.internLock);
+    }
+    dvmHashTableFree(gDvm.internedStrings);
+    gDvm.internedStrings = NULL;
+    dvmHashTableFree(gDvm.literalStrings);
+    gDvm.literalStrings = NULL;
+}
+
+static StringObject* lookupInternedString(StringObject* strObj, bool isLiteral)
+{
+    StringObject* found;
+    u4 hash;
+
+    assert(strObj != NULL);
+    hash = dvmComputeStringHash(strObj);
+    dvmLockMutex(&gDvm.internLock);
+    if (isLiteral) {
+        /*
+         * Check the literal table for a match.
+         */
+        StringObject* literal = dvmHashTableLookup(gDvm.literalStrings,
+                                                   hash, strObj,
+                                                   dvmHashcmpStrings,
+                                                   false);
+        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 = dvmHashTableLookup(gDvm.internedStrings,
+                                                        hash, strObj,
+                                                        dvmHashcmpStrings,
+                                                        false);
+            if (interned != NULL) {
+                /*
+                 * A match was found in the interned table.  Move the
+                 * matching string to the literal table.
+                 */
+                dvmHashTableRemove(gDvm.internedStrings, hash, interned);
+                found = dvmHashTableLookup(gDvm.literalStrings,
+                                           hash, interned,
+                                           dvmHashcmpStrings,
+                                           true);
+                assert(found == interned);
+            } else {
+                /*
+                 * No match in the literal table or the interned
+                 * table.  Insert into the literal table.
+                 */
+                found = dvmHashTableLookup(gDvm.literalStrings,
+                                           hash, strObj,
+                                           dvmHashcmpStrings,
+                                           true);
+                assert(found == strObj);
+            }
+        }
+    } else {
+        /*
+         * Check the literal table for a match.
+         */
+        found = dvmHashTableLookup(gDvm.literalStrings,
+                                   hash, strObj,
+                                   dvmHashcmpStrings,
+                                   false);
+        if (found == NULL) {
+            /*
+             * No match was found in the literal table.  Insert into
+             * the intern table.
+             */
+            found = dvmHashTableLookup(gDvm.internedStrings,
+                                       hash, strObj,
+                                       dvmHashcmpStrings,
+                                       true);
+        }
+    }
+    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(const StringObject* strObj)
+{
+    StringObject* found;
+    u4 hash;
+
+    assert(strObj != NULL);
+    if (gDvm.internedStrings == NULL) {
+        return false;
+    }
+    dvmLockMutex(&gDvm.internLock);
+    hash = dvmComputeStringHash(strObj);
+    found = dvmHashTableLookup(gDvm.internedStrings, hash, (void*)strObj,
+                               dvmHashcmpStrings, false);
+    dvmUnlockMutex(&gDvm.internLock);
+    return found == strObj;
+}
+
+static int markStringObject(void* strObj, void* arg)
+{
+    UNUSED_PARAMETER(arg);
+    dvmMarkObjectNonNull(strObj);
+    return 0;
+}
+
+/*
+ * Blacken string references from the literal string table.  The
+ * literal table is a root.
+ */
+void dvmGcScanInternedStrings()
+{
+    /* It's possible for a GC to happen before dvmStringInternStartup()
+     * is called.
+     */
+    if (gDvm.literalStrings != NULL) {
+        dvmHashForeach(gDvm.literalStrings, markStringObject, NULL);
+    }
+}
+
+/*
+ * 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..a378fa5
--- /dev/null
+++ b/vm/Intern.h
@@ -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.
+ */
+/*
+ * Interned strings.
+ */
+#ifndef _DALVIK_INTERN
+#define _DALVIK_INTERN
+
+bool dvmStringInternStartup(void);
+void dvmStringInternShutdown(void);
+StringObject* dvmLookupInternedString(StringObject* strObj);
+StringObject* dvmLookupImmortalInternedString(StringObject* strObj);
+bool dvmIsWeakInternedString(const StringObject* strObj);
+
+#endif /*_DALVIK_INTERN*/
diff --git a/vm/JarFile.c b/vm/JarFile.c
new file mode 100644
index 0000000..39eb8d1
--- /dev/null
+++ b/vm/JarFile.c
@@ -0,0 +1,370 @@
+/*
+ * 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 <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 = 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;
+    }
+    LOGV("Couldn't open %s: %s\n", 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".
+ */
+DexCacheStatus dvmDexCacheStatus(const char *fileName)
+{
+    ZipArchive 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;
+    }
+
+    //TODO: match dvmJarFileOpen()'s logic.  Not super-important
+    //      (the odex-first logic is only necessary for dexpreopt)
+    //      but it would be nice to be consistent.
+
+    /* Try to find the dex file inside of the archive.
+     */
+    if (dexZipOpenArchive(fileName, &archive) != 0) {
+        return DEX_CACHE_BAD_ARCHIVE;
+    }
+    entry = dexZipFindEntry(&archive, kDexInJarName);
+    if (entry != NULL) {
+        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.
+         */
+        LOGV("dvmDexCacheStatus: Checking cache for %s\n", fileName);
+        cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName);
+        if (cachedName == NULL)
+            return -1;
+
+        fd = dvmOpenCachedDexFile(fileName, cachedName,
+                dexGetZipEntryModTime(&archive, entry),
+                dexGetZipEntryCrc32(&archive, entry),
+                /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false);
+        LOGV("dvmOpenCachedDexFile returned fd %d\n", 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 */
+            LOGE("Unable to unlock DEX file\n");
+            goto bail;
+        }
+
+        /* When createIfMissing is false, dvmOpenCachedDexFile() only
+         * returns a valid fd if the cache file is up-to-date.
+         */
+    } else {
+        /*
+         * There's no dex file in the jar file.  See if there's an
+         * optimized dex file living alongside the jar.
+         */
+        fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
+        if (fd < 0) {
+            LOGI("Zip is good, but no %s inside, and no .odex "
+                    "file in the same directory\n", kDexInJarName);
+            result = DEX_CACHE_BAD_ARCHIVE;
+            goto bail;
+        }
+
+        LOGV("Using alternate file (odex) for %s ...\n", fileName);
+        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
+            LOGE("%s odex has stale dependencies\n", fileName);
+            LOGE("odex source not available -- failing\n");
+            result = DEX_CACHE_STALE_ODEX;
+            goto bail;
+        } else {
+            LOGV("%s odex has good dependencies\n", fileName);
+        }
+    }
+    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.
+ */
+int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
+    JarFile** ppJarFile, bool isBootstrap)
+{
+    ZipArchive 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) {
+        LOGV("Using alternate file (odex) for %s ...\n", fileName);
+        if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
+            LOGE("%s odex has stale dependencies\n", fileName);
+            free(cachedName);
+            close(fd);
+            fd = -1;
+            goto tryArchive;
+        } else {
+            LOGV("%s odex has good dependencies\n", 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".
+         */
+        entry = dexZipFindEntry(&archive, kDexInJarName);
+        if (entry != NULL) {
+            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);
+            }
+            LOGV("dvmDexCacheStatus: Checking cache for %s (%s)\n",
+                fileName, cachedName);
+            fd = dvmOpenCachedDexFile(fileName, cachedName,
+                    dexGetZipEntryModTime(&archive, entry),
+                    dexGetZipEntryCrc32(&archive, entry),
+                    isBootstrap, &newFile, /*createIfMissing=*/true);
+            if (fd < 0) {
+                LOGI("Unable to open or create cache for %s (%s)\n",
+                    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,
+                                dexGetZipEntryUncompLen(&archive, entry),
+                                fileName,
+                                dexGetZipEntryModTime(&archive, entry),
+                                dexGetZipEntryCrc32(&archive, entry),
+                                isBootstrap);
+                }
+
+                if (!result) {
+                    LOGE("Unable to extract+optimize DEX from '%s'\n",
+                        fileName);
+                    goto bail;
+                }
+
+                endWhen = dvmGetRelativeTimeUsec();
+                LOGD("DEX prep '%s': unzip in %dms, rewrite %dms\n",
+                    fileName,
+                    (int) (extractWhen - startWhen) / 1000,
+                    (int) (endWhen - extractWhen) / 1000);
+            }
+        } else {
+            LOGI("Zip is good, but no %s inside, and no valid .odex "
+                    "file in the same directory\n", 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) {
+        LOGI("Unable to map %s in %s\n", 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 */
+            LOGE("Unable to unlock DEX file\n");
+            goto bail;
+        }
+        locked = false;
+    }
+
+    LOGV("Successfully opened '%s' in '%s'\n", 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..36849ec
--- /dev/null
+++ b/vm/JarFile.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.
+ */
+/*
+ * Decode jar/apk/zip files.
+ */
+#ifndef _DALVIK_JARFILE
+#define _DALVIK_JARFILE
+
+/*
+ * This represents an open, scanned Jar file.  (It's actually for any Zip
+ * archive that happens to hold a Dex file.)
+ */
+typedef struct JarFile {
+    ZipArchive  archive;
+    //MemMapping  map;
+    char*       cacheFileName;
+    DvmDex*     pDvmDex;
+} JarFile;
+
+/*
+ * 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;
+}
+
+typedef enum DexCacheStatus {
+    DEX_CACHE_ERROR = -2,
+    DEX_CACHE_BAD_ARCHIVE = -1,
+    DEX_CACHE_OK = 0,
+    DEX_CACHE_STALE,
+    DEX_CACHE_STALE_ODEX,
+} DexCacheStatus;
+
+/*
+ * 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*/
diff --git a/vm/Jni.c b/vm/Jni.c
new file mode 100644
index 0000000..ef0749a
--- /dev/null
+++ b/vm/Jni.c
@@ -0,0 +1,4509 @@
+/*
+ * 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 <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, so we should replace JNI_ENTER with JNI_ENTER_NCGC
+or somesuch on the "lite" functions if we want to try this optimization.
+
+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
+
+The table of local references can be stored on the interpreted stack or
+in a parallel data structure (one per thread).
+
+*** Approach #1: use the interpreted stack
+
+The easiest place to tuck it is between the frame ptr and the first saved
+register, which is always in0.  (See the ASCII art in Stack.h.)  We can
+shift the "VM-specific goop" and frame ptr down, effectively inserting
+the JNI local refs in the space normally occupied by local variables.
+
+(Three things are accessed from the frame pointer:
+ (1) framePtr[N] is register vN, used to get at "ins" and "locals".
+ (2) framePtr - sizeof(StackSaveArea) is the VM frame goop.
+ (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go.
+The only thing that isn't determined by an offset from the current FP
+is the previous frame.  However, tucking things below the previous frame
+can be problematic because the "outs" of the previous frame overlap with
+the "ins" of the current frame.  If the "ins" are altered they must be
+restored before we return.  For a native method call, the easiest and
+safest thing to disrupt is #1, because there are no locals and the "ins"
+are all copied to the native stack.)
+
+We can implement Push/PopLocalFrame with the existing stack frame calls,
+making sure we copy some goop from the previous frame (notably the method
+ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
+
+We can pre-allocate the storage at the time the stack frame is first
+set up, but we have to be careful.  When calling from interpreted code
+the frame ptr points directly at the arguments we're passing, but we can
+offset the args pointer when calling the native bridge.
+
+To manage the local ref collection, we need to be able to find three
+things: (1) the start of the region, (2) the end of the region, and (3)
+the next available entry.  The last is only required for quick adds.
+We currently have two easily-accessible pointers, the current FP and the
+previous frame's FP.  (The "stack pointer" shown in the ASCII art doesn't
+actually exist in the interpreted world.)
+
+We can't use the current FP to find the first "in", because we want to
+insert the variable-sized local refs table between them.  It's awkward
+to use the previous frame's FP because native methods invoked via
+dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
+invoked from interpreted code do.  We can either track the local refs
+table size with a field in the stack frame, or insert unnecessary items
+so that all native stack frames have "ins".
+
+Assuming we can find the region bounds, we still need pointer #3
+for an efficient implementation.  This can be stored in an otherwise
+unused-for-native field in the frame goop.
+
+When we run out of room we have to make more space.  If we start allocating
+locals immediately below in0 and grow downward, we will detect end-of-space
+by running into the current frame's FP.  We then memmove() the goop down
+(memcpy if we guarantee the additional size is larger than the frame).
+This is nice because we only have to move sizeof(StackSaveArea) bytes
+each time.
+
+Stack walking should be okay so long as nothing tries to access the
+"ins" by an offset from the FP.  In theory the "ins" could be read by
+the debugger or SIGQUIT handler looking for "this" or other arguments,
+but in practice this behavior isn't expected to work for native methods,
+so we can simply disallow it.
+
+A conservative GC can just scan the entire stack from top to bottom to find
+all references.  An exact GC will need to understand the actual layout.
+
+*** Approach #2: use a parallel stack
+
+Each Thread/JNIEnv points to a reference table.  The struct has
+a system-heap-allocated array of references and a pointer to the
+next-available entry ("nextEntry").
+
+Each stack frame has a pointer to what it sees as the "bottom" element
+in the array (we can double-up the "currentPc" field).  This is set to
+"nextEntry" when the frame is pushed on.  As local references are added
+or removed, "nextEntry" is updated.
+
+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 from the start of the table to the
+"nextEntry" pointer.
+
+*** Comparison
+
+All approaches will return a failure result when they run out of local
+reference space.  For #1 that means blowing out the stack, for #2 it's
+running out of room in the array.
+
+Compared to #1, approach #2:
+ - Needs only one pointer in the stack frame goop.
+ - Makes pre-allocating storage unnecessary.
+ - Doesn't contend with interpreted stack depth for space.  In most
+   cases, if something blows out the local ref storage, it's because the
+   JNI code was misbehaving rather than called from way down.
+ - Allows the GC to do a linear scan per thread in a buffer that is 100%
+   references.  The GC can be slightly less smart when scanning the stack.
+ - Will be easier to work with if we combine native and interpeted stacks.
+
+ - Isn't as clean, especially when popping frames, since we have to do
+   explicit work.  Fortunately we only have to do it when popping native
+   method calls off, so it doesn't add overhead to interpreted code paths.
+ - Is awkward to expand dynamically.  We'll want to pre-allocate the full
+   amount of space; this is fine, since something on the order of 1KB should
+   be plenty.  The JNI spec allows us to limit this.
+ - Requires the GC to scan even more memory.  With the references embedded
+   in the stack we get better locality of reference.
+
+*/
+
+/* fwd */
+static const struct JNINativeInterface gNativeInterface;
+static jobject addGlobalReference(Object* obj);
+
+#ifdef WITH_JNI_STACK_CHECK
+# define COMPUTE_STACK_SUM(_self)   computeStackSum(_self);
+# define CHECK_STACK_SUM(_self)     checkStackSum(_self);
+//static void computeStackSum(Thread* self);
+//static void checkStackSum(Thread* self);
+#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.
+ *
+ * If TRUSTED_JNIENV is set, we get to 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.
+ */
+#define TRUSTED_JNIENV
+#ifdef TRUSTED_JNIENV
+# define JNI_ENTER()                                                        \
+        Thread* _self = ((JNIEnvExt*)env)->self;                            \
+        CHECK_STACK_SUM(_self);                                             \
+        dvmChangeStatus(_self, THREAD_RUNNING)
+#else
+# define JNI_ENTER()                                                        \
+        Thread* _self = dvmThreadSelf();                                    \
+        UNUSED_PARAMETER(env);                                              \
+        CHECK_STACK_SUM(_self);                                             \
+        dvmChangeStatus(_self, THREAD_RUNNING)
+#endif
+#define JNI_EXIT()                                                          \
+        dvmChangeStatus(_self, THREAD_NATIVE);                              \
+        COMPUTE_STACK_SUM(_self)
+
+#define kGlobalRefsTableInitialSize 512
+#define kGlobalRefsTableMaxSize     51200       /* arbitrary, must be < 64K */
+#define kGrefWaterInterval          100
+#define kTrackGrefUsage             true
+
+#define kPinTableInitialSize        16
+#define kPinTableMaxSize            1024
+#define kPinComplainThreshold       10
+
+/*
+ * Allocate the global references table, and look up some classes for
+ * the benefit of direct buffer access.
+ */
+bool dvmJniStartup(void)
+{
+#ifdef USE_INDIRECT_REF
+    if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable,
+            kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize,
+            kIndirectKindGlobal))
+        return false;
+#else
+    if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
+            kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
+        return false;
+#endif
+
+    dvmInitMutex(&gDvm.jniGlobalRefLock);
+    gDvm.jniGlobalRefLoMark = 0;
+    gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
+
+    if (!dvmInitReferenceTable(&gDvm.jniPinRefTable,
+            kPinTableInitialSize, kPinTableMaxSize))
+        return false;
+
+    dvmInitMutex(&gDvm.jniPinRefLock);
+
+    Method* meth;
+
+    /*
+     * Grab the PhantomReference constructor.
+     */
+    gDvm.classJavaLangRefPhantomReference =
+        dvmFindSystemClassNoInit("Ljava/lang/ref/PhantomReference;");
+    if (gDvm.classJavaLangRefPhantomReference == NULL) {
+        LOGE("Unable to find PhantomReference class\n");
+        return false;
+    }
+    meth= dvmFindDirectMethodByDescriptor(gDvm.classJavaLangRefPhantomReference,
+        "<init>", "(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V");
+    if (meth == NULL) {
+        LOGE("Unable to find constructor for PhantomReference\n");
+        return false;
+    }
+    gDvm.methJavaLangRefPhantomReference_init = meth;
+
+
+    /*
+     * Look up and cache pointers to some direct buffer classes, fields,
+     * and methods.
+     */
+    ClassObject* platformAddressClass =
+        dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
+    ClassObject* platformAddressFactoryClass =
+        dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
+    ClassObject* directBufferClass =
+        dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
+    ClassObject* readWriteBufferClass =
+        dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
+    ClassObject* bufferClass =
+        dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
+
+    if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
+        directBufferClass == NULL || readWriteBufferClass == NULL ||
+        bufferClass == NULL)
+    {
+        LOGE("Unable to find internal direct buffer classes\n");
+        return false;
+    }
+    gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
+    gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass;
+    /* need a global reference for extended CheckJNI tests */
+    gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer =
+        addGlobalReference((Object*) directBufferClass);
+
+    /*
+     * We need a Method* here rather than a vtable offset, because
+     * DirectBuffer is an interface class.
+     */
+    meth = dvmFindVirtualMethodByDescriptor(
+                gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
+                "getEffectiveAddress",
+                "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
+    if (meth == NULL) {
+        LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
+        return false;
+    }
+    gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
+
+    meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
+                "toLong", "()J");
+    if (meth == NULL) {
+        LOGE("Unable to find PlatformAddress.toLong\n");
+        return false;
+    }
+    gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
+        meth->methodIndex;
+
+    meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
+                "on",
+                "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
+    if (meth == NULL) {
+        LOGE("Unable to find PlatformAddressFactory.on\n");
+        return false;
+    }
+    gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
+
+    meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
+                "<init>",
+                "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
+    if (meth == NULL) {
+        LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
+        return false;
+    }
+    gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
+
+    gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
+        dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
+    if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
+        LOGE("Unable to find PlatformAddress.osaddr\n");
+        return false;
+    }
+
+    gDvm.offJavaNioBuffer_capacity =
+        dvmFindFieldOffset(bufferClass, "capacity", "I");
+    if (gDvm.offJavaNioBuffer_capacity < 0) {
+        LOGE("Unable to find Buffer.capacity\n");
+        return false;
+    }
+
+    gDvm.offJavaNioBuffer_effectiveDirectAddress =
+        dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
+    if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
+        LOGE("Unable to find Buffer.effectiveDirectAddress\n");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Free the global references table.
+ */
+void dvmJniShutdown(void)
+{
+#ifdef USE_INDIRECT_REF
+    dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
+#else
+    dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
+#endif
+    dvmClearReferenceTable(&gDvm.jniPinRefTable);
+}
+
+
+/*
+ * 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(void)
+{
+    Thread* self = dvmThreadSelf();
+    if (self == NULL)
+        return NULL;
+    return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
+}
+
+/*
+ * 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*) gDvm.vmList;
+    JNIEnvExt* newEnv;
+
+    //if (self != NULL)
+    //    LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
+
+    assert(vm != NULL);
+
+    newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
+    newEnv->funcTable = &gNativeInterface;
+    newEnv->vm = vm;
+    newEnv->forceDataCopy = vm->forceDataCopy;
+    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 (vm->useChecked)
+        dvmUseCheckedJniEnv(newEnv);
+
+    dvmLockMutex(&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;
+
+    dvmUnlockMutex(&vm->envListLock);
+
+    //if (self != NULL)
+    //    LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
+    return (JNIEnv*) newEnv;
+}
+
+/*
+ * Remove a JNIEnv struct from the list and free it.
+ */
+void dvmDestroyJNIEnv(JNIEnv* env)
+{
+    JNIEnvExt* extEnv = (JNIEnvExt*) env;
+    JavaVMExt* vm = extEnv->vm;
+    Thread* self;
+
+    if (env == NULL)
+        return;
+
+    self = dvmThreadSelf();
+    assert(self != NULL);
+
+    //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
+
+    dvmLockMutex(&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;
+
+    dvmUnlockMutex(&extEnv->vm->envListLock);
+
+    free(env);
+    //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
+}
+
+
+/*
+ * Retrieve the ReferenceTable struct for the current thread.
+ *
+ * Going through "env" rather than dvmThreadSelf() is faster but will
+ * get weird if the JNI code is passing the wrong JNIEnv around.
+ */
+#ifdef USE_INDIRECT_REF
+static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
+#else
+static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
+#endif
+{
+    //return &dvmThreadSelf()->jniLocalRefTable;
+    return &((JNIEnvExt*)env)->self->jniLocalRefTable;
+}
+
+#ifdef USE_INDIRECT_REF
+/*
+ * Convert an indirect reference to an Object reference.  The indirect
+ * reference may be local, global, or weak-global.
+ *
+ * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
+ */
+Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
+{
+    if (jobj == NULL)
+        return NULL;
+
+    Object* result;
+
+    switch (dvmGetIndirectRefType(jobj)) {
+    case kIndirectKindLocal:
+        {
+            IndirectRefTable* pRefTable = getLocalRefTable(env);
+            result = dvmGetFromIndirectRefTable(pRefTable, jobj);
+        }
+        break;
+    case kIndirectKindGlobal:
+        {
+            // TODO: find a way to avoid the mutex activity here
+            IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
+            dvmLockMutex(&gDvm.jniGlobalRefLock);
+            result = dvmGetFromIndirectRefTable(pRefTable, jobj);
+            dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+        }
+        break;
+    case kIndirectKindWeakGlobal:
+        {
+            // TODO: implement
+            LOGE("weak-global not yet supported\n");
+            result = NULL;
+            dvmAbort();
+        }
+        break;
+    case kIndirectKindInvalid:
+    default:
+        LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
+        dvmAbort();
+        result = NULL;
+        break;
+    }
+
+    return result;
+}
+#else
+    /* use trivial inline in JniInternal.h for performance */
+#endif
+
+/*
+ * 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.
+ *
+ * Returns the local reference (currently just the same pointer that was
+ * passed in), or NULL on failure.
+ */
+static jobject addLocalReference(JNIEnv* env, Object* obj)
+{
+    if (obj == NULL)
+        return NULL;
+
+    jobject jobj;
+
+#ifdef USE_INDIRECT_REF
+    IndirectRefTable* pRefTable = getLocalRefTable(env);
+    void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
+    u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
+
+    jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
+    if (jobj == NULL) {
+        dvmDumpIndirectRefTable(pRefTable, "JNI local");
+        LOGE("Failed adding to JNI local ref table (has %d entries)\n",
+            (int) dvmIndirectRefTableEntries(pRefTable));
+        dvmDumpThread(dvmThreadSelf(), false);
+        dvmAbort();     // spec says call FatalError; this is equivalent
+    } else {
+        LOGVV("LREF add %p  (%s.%s) (ent=%d)\n", obj,
+            dvmGetCurrentJNIMethod()->clazz->descriptor,
+            dvmGetCurrentJNIMethod()->name,
+            (int) dvmReferenceTableEntries(pRefTable));
+    }
+#else
+    ReferenceTable* pRefTable = getLocalRefTable(env);
+
+    if (!dvmAddToReferenceTable(pRefTable, obj)) {
+        dvmDumpReferenceTable(pRefTable, "JNI local");
+        LOGE("Failed adding to JNI local ref table (has %d entries)\n",
+            (int) dvmReferenceTableEntries(pRefTable));
+        dvmDumpThread(dvmThreadSelf(), false);
+        dvmAbort();     // spec says call FatalError; this is equivalent
+    } else {
+        LOGVV("LREF add %p  (%s.%s) (ent=%d)\n", obj,
+            dvmGetCurrentJNIMethod()->clazz->descriptor,
+            dvmGetCurrentJNIMethod()->name,
+            (int) dvmReferenceTableEntries(pRefTable));
+    }
+
+    jobj = (jobject) obj;
+#endif
+
+    return jobj;
+}
+
+/*
+ * Ensure that at least "capacity" references can be held in the local
+ * refs table of the current thread.
+ */
+static bool ensureLocalCapacity(JNIEnv* env, int capacity)
+{
+#ifdef USE_INDIRECT_REF
+    IndirectRefTable* pRefTable = getLocalRefTable(env);
+    int numEntries = dvmIndirectRefTableEntries(pRefTable);
+    // TODO: this isn't quite right, since "numEntries" includes holes
+    return ((kJniLocalRefMax - numEntries) >= capacity);
+#else
+    ReferenceTable* pRefTable = getLocalRefTable(env);
+
+    return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
+#endif
+}
+
+/*
+ * Explicitly delete a reference from the local list.
+ */
+static void deleteLocalReference(JNIEnv* env, jobject jobj)
+{
+    if (jobj == NULL)
+        return;
+
+#ifdef USE_INDIRECT_REF
+    IndirectRefTable* pRefTable = getLocalRefTable(env);
+    Thread* self = ((JNIEnvExt*)env)->self;
+    u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
+
+    if (!dvmRemoveFromIndirectRefTable(pRefTable, 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.
+         */
+        LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
+    }
+#else
+    ReferenceTable* pRefTable = getLocalRefTable(env);
+    Thread* self = ((JNIEnvExt*)env)->self;
+    Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
+
+    if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) 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.
+         */
+        LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
+            jobj, dvmIsValidObject((Object*) jobj));
+    }
+#endif
+}
+
+/*
+ * 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;
+
+    //LOGI("adding obj=%p\n", obj);
+    //dvmDumpThread(dvmThreadSelf(), false);
+
+    if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
+        ClassObject* clazz = (ClassObject*) obj;
+        LOGI("-------\n");
+        LOGI("Adding global ref on class %s\n", 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) {
+            LOGI("-------\n");
+            LOGI("Adding global ref on string '%s'\n", 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*/)
+        {
+            LOGI("Adding global ref on byte array %p (len=%d)\n",
+                arrayObj, arrayObj->length);
+            dvmDumpThread(dvmThreadSelf(), false);
+        }
+    }
+
+    jobject jobj;
+
+    dvmLockMutex(&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.
+     */
+#ifdef USE_INDIRECT_REF
+    jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
+            obj);
+    if (jobj == NULL) {
+        dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
+        LOGE("Failed adding to JNI global ref table (%d entries)\n",
+            (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
+        dvmAbort();
+    }
+
+    LOGVV("GREF add %p  (%s.%s)\n", obj,
+        dvmGetCurrentJNIMethod()->clazz->descriptor,
+        dvmGetCurrentJNIMethod()->name);
+
+    /* GREF usage tracking; should probably be disabled for production env */
+    if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+        int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
+        // TODO: adjust for "holes"
+        if (count > gDvm.jniGlobalRefHiMark) {
+            LOGD("GREF has increased to %d\n", count);
+            gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
+            gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
+
+            /* watch for "excessive" use; not generally appropriate */
+            if (count >= gDvm.jniGrefLimit) {
+                JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+                if (vm->warnError) {
+                    dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
+                        "JNI global");
+                    LOGE("Excessive JNI global references (%d)\n", count);
+                    dvmAbort();
+                } else {
+                    LOGW("Excessive JNI global references (%d)\n", count);
+                }
+            }
+        }
+    }
+#else
+    if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
+        dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+        LOGE("Failed adding to JNI global ref table (%d entries)\n",
+            (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
+        dvmAbort();
+    }
+    jobj = (jobject) obj;
+
+    LOGVV("GREF add %p  (%s.%s)\n", obj,
+        dvmGetCurrentJNIMethod()->clazz->descriptor,
+        dvmGetCurrentJNIMethod()->name);
+
+    /* GREF usage tracking; should probably be disabled for production env */
+    if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+        int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
+        if (count > gDvm.jniGlobalRefHiMark) {
+            LOGD("GREF has increased to %d\n", count);
+            gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
+            gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
+
+            /* watch for "excessive" use; not generally appropriate */
+            if (count >= gDvm.jniGrefLimit) {
+                JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+                if (vm->warnError) {
+                    dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
+                    LOGE("Excessive JNI global references (%d)\n", count);
+                    dvmAbort();
+                } else {
+                    LOGW("Excessive JNI global references (%d)\n", count);
+                }
+            }
+        }
+    }
+#endif
+
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+    return 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;
+
+    dvmLockMutex(&gDvm.jniGlobalRefLock);
+
+#ifdef USE_INDIRECT_REF
+    if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
+            IRT_FIRST_SEGMENT, jobj))
+    {
+        LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
+        goto bail;
+    }
+
+    if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+        int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
+        // TODO: not quite right, need to subtract holes
+        if (count < gDvm.jniGlobalRefLoMark) {
+            LOGD("GREF has decreased to %d\n", count);
+            gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
+            gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
+        }
+    }
+#else
+    if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
+            gDvm.jniGlobalRefTable.table, jobj))
+    {
+        LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
+            jobj, dvmIsValidObject((Object*) jobj));
+        goto bail;
+    }
+
+    if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+        int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
+        if (count < gDvm.jniGlobalRefLoMark) {
+            LOGD("GREF has decreased to %d\n", count);
+            gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
+            gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
+        }
+    }
+#endif
+
+bail:
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+}
+
+/*
+ * We create a PhantomReference that references the object, add a
+ * global reference to it, and then flip some bits before returning it.
+ * The last step ensures that we detect it as special and that only
+ * appropriate calls will accept it.
+ *
+ * On failure, returns NULL with an exception pending.
+ */
+static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
+{
+    if (jobj == NULL)
+        return NULL;
+
+    Thread* self = ((JNIEnvExt*)env)->self;
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    Object* phantomObj;
+    jobject phantomRef;
+
+    /*
+     * Allocate a PhantomReference, then call the constructor to set
+     * the referent and the reference queue.
+     *
+     * We use a "magic" reference queue that the GC knows about; it behaves
+     * more like a queueless WeakReference, clearing the referent and
+     * not calling enqueue().
+     */
+    if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference))
+        dvmInitClass(gDvm.classJavaLangRefPhantomReference);
+    phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference,
+            ALLOC_DEFAULT);
+    if (phantomObj == NULL) {
+        assert(dvmCheckException(self));
+        LOGW("Failed on WeakGlobalRef alloc\n");
+        return NULL;
+    }
+
+    JValue unused;
+    dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
+        &unused, obj, NULL);
+    dvmReleaseTrackedAlloc(phantomObj, self);
+
+    if (dvmCheckException(self)) {
+        LOGW("PhantomReference init failed\n");
+        return NULL;
+    }
+
+    LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj);
+
+    /*
+     * Add it to the global reference table, and mangle the pointer.
+     */
+    phantomRef = addGlobalReference(phantomObj);
+    return dvmObfuscateWeakGlobalRef(phantomRef);
+}
+
+/*
+ * Delete the global reference that's keeping the PhantomReference around.
+ * The PhantomReference will eventually be discarded by the GC.
+ */
+static void deleteWeakGlobalRef(JNIEnv* env, jweak wref)
+{
+    if (wref == NULL)
+        return;
+
+    jobject phantomRef = dvmNormalizeWeakGlobalRef(wref);
+    deleteGlobalReference(phantomRef);
+}
+
+/*
+ * Extract the referent from a PhantomReference.  Used for weak global
+ * references.
+ *
+ * "jwobj" is a "mangled" WGR pointer.
+ */
+static Object* getPhantomReferent(JNIEnv* env, jweak jwobj)
+{
+    jobject jobj = dvmNormalizeWeakGlobalRef(jwobj);
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+
+    if (obj->clazz != gDvm.classJavaLangRefPhantomReference) {
+        LOGE("%p is not a phantom reference (%s)\n",
+            jwobj, obj->clazz->descriptor);
+        return NULL;
+    }
+
+    return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent);
+}
+
+
+/*
+ * 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;
+
+    dvmLockMutex(&gDvm.jniPinRefLock);
+    if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
+        dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
+        LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
+            (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
+        dvmDumpThread(dvmThreadSelf(), false);
+        dvmAbort();
+    }
+
+    /*
+     * If we're watching global ref usage, also keep an eye on these.
+     *
+     * 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.
+     */
+    if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+        int count = 0;
+        Object** ppObj = gDvm.jniPinRefTable.table;
+        while (ppObj < gDvm.jniPinRefTable.nextEntry) {
+            if (*ppObj++ == (Object*) arrayObj)
+                count++;
+        }
+
+        if (count > kPinComplainThreshold) {
+            LOGW("JNI: pin count on array %p (%s) is now %d\n",
+                arrayObj, arrayObj->obj.clazz->descriptor, count);
+            /* keep going */
+        }
+    }
+
+    dvmUnlockMutex(&gDvm.jniPinRefLock);
+}
+
+/*
+ * 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;
+
+    dvmLockMutex(&gDvm.jniPinRefLock);
+    if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
+            gDvm.jniPinRefTable.table, (Object*) arrayObj))
+    {
+        LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
+            arrayObj, dvmIsValidObject((Object*) arrayObj));
+        goto bail;
+    }
+
+bail:
+    dvmUnlockMutex(&gDvm.jniPinRefLock);
+}
+
+/*
+ * 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(void)
+{
+    Thread* self = dvmThreadSelf();
+    JNIEnv* env = self->jniEnv;
+    ReferenceTable* pLocalRefs = getLocalRefTable(env);
+
+#ifdef USE_INDIRECT_REF
+    dvmDumpIndirectRefTable(pLocalRefs, "JNI local");
+    dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
+#else
+    dvmDumpReferenceTable(pLocalRefs, "JNI local");
+    dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+#endif
+    dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
+}
+
+/*
+ * GC helper function to mark all JNI global references.
+ *
+ * We're currently handling the "pin" table here too.
+ */
+void dvmGcMarkJniGlobalRefs()
+{
+    Object** op;
+
+    dvmLockMutex(&gDvm.jniGlobalRefLock);
+
+#ifdef USE_INDIRECT_REF
+    IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
+    op = pRefTable->table;
+    int numEntries = dvmIndirectRefTableEntries(pRefTable);
+    int i;
+
+    for (i = 0; i < numEntries; i++) {
+        Object* obj = *op;
+        if (obj != NULL)
+            dvmMarkObjectNonNull(obj);
+        op++;
+    }
+#else
+    op = gDvm.jniGlobalRefTable.table;
+    while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
+        dvmMarkObjectNonNull(*(op++));
+    }
+#endif
+
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+
+
+    dvmLockMutex(&gDvm.jniPinRefLock);
+
+    op = gDvm.jniPinRefTable.table;
+    while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
+        dvmMarkObjectNonNull(*(op++));
+    }
+
+    dvmUnlockMutex(&gDvm.jniPinRefLock);
+}
+
+
+#ifndef USE_INDIRECT_REF
+/*
+ * Determine if "obj" appears in the argument list for the native method.
+ *
+ * We use the "shorty" signature to determine which argument slots hold
+ * reference types.
+ */
+static bool findInArgList(Thread* self, Object* obj)
+{
+    const Method* meth;
+    u4* fp;
+    int i;
+
+    fp = self->curFrame;
+    while (1) {
+        /*
+         * Back up over JNI PushLocalFrame frames.  This works because the
+         * previous frame on the interpreted stack is either a break frame
+         * (if we called here via native code) or an interpreted method (if
+         * we called here via the interpreter).  In both cases the method
+         * pointer won't match.
+         */
+        StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        meth = saveArea->method;
+        if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
+            break;
+        fp = saveArea->prevFrame;
+    }
+
+    LOGVV("+++ scanning %d args in %s (%s)\n",
+        meth->insSize, meth->name, meth->shorty);
+    const char* shorty = meth->shorty +1;       /* skip return type char */
+    for (i = 0; i < meth->insSize; i++) {
+        if (i == 0 && !dvmIsStaticMethod(meth)) {
+            /* first arg is "this" ref, not represented in "shorty" */
+            if (fp[i] == (u4) obj)
+                return true;
+        } else {
+            /* if this is a reference type, see if it matches */
+            switch (*shorty) {
+            case 'L':
+                if (fp[i] == (u4) obj)
+                    return true;
+                break;
+            case 'D':
+            case 'J':
+                i++;
+                break;
+            case '\0':
+                LOGE("Whoops! ran off the end of %s (%d)\n",
+                    meth->shorty, meth->insSize);
+                break;
+            default:
+                if (fp[i] == (u4) obj)
+                    LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
+                break;
+            }
+            shorty++;
+        }
+    }
+
+    /*
+     * For static methods, we also pass a class pointer in.
+     */
+    if (dvmIsStaticMethod(meth)) {
+        //LOGI("+++ checking class pointer in %s\n", meth->name);
+        if ((void*)obj == (void*)meth->clazz)
+            return true;
+    }
+    return false;
+}
+#endif
+
+/*
+ * 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.
+ *
+ * NOTE: in the current VM, global and local references are identical.  If
+ * something is both global and local, we can't tell them apart, and always
+ * return "local".
+ */
+jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
+{
+#ifdef USE_INDIRECT_REF
+    /*
+     * 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.
+     */
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+
+    if (obj == NULL) {
+        /* invalid ref, or jobj was NULL */
+        return JNIInvalidRefType;
+    } else {
+        return (jobjectRefType) dvmGetIndirectRefType(jobj);
+    }
+#else
+    ReferenceTable* pRefTable = getLocalRefTable(env);
+    Thread* self = dvmThreadSelf();
+
+    if (dvmIsWeakGlobalRef(jobj)) {
+        return JNIWeakGlobalRefType;
+    }
+
+    /* check args */
+    if (findInArgList(self, jobj)) {
+        //LOGI("--- REF found %p on stack\n", jobj);
+        return JNILocalRefType;
+    }
+
+    /* check locals */
+    if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
+        //LOGI("--- REF found %p in locals\n", jobj);
+        return JNILocalRefType;
+    }
+
+    /* check globals */
+    dvmLockMutex(&gDvm.jniGlobalRefLock);
+    if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
+            gDvm.jniGlobalRefTable.table, jobj))
+    {
+        //LOGI("--- REF found %p in globals\n", jobj);
+        dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+        return JNIGlobalRefType;
+    }
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+
+    /* not found! */
+    return JNIInvalidRefType;
+#endif
+}
+
+/*
+ * Register a method that uses JNI calling conventions.
+ */
+static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
+    const char* signature, void* fnPtr)
+{
+    Method* method;
+    bool result = false;
+
+    if (fnPtr == NULL)
+        goto bail;
+
+    method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
+    if (method == NULL)
+        method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
+    if (method == NULL) {
+        LOGW("ERROR: Unable to find decl for native %s.%s:%s\n",
+            clazz->descriptor, methodName, signature);
+        goto bail;
+    }
+
+    if (!dvmIsNativeMethod(method)) {
+        LOGW("Unable to register: not native: %s.%s:%s\n",
+            clazz->descriptor, methodName, signature);
+        goto bail;
+    }
+
+    if (method->nativeFunc != dvmResolveNativeMethod) {
+        /* this is allowed, but unusual */
+        LOGV("Note: %s.%s:%s was already registered\n",
+            clazz->descriptor, methodName, signature);
+    }
+
+    dvmUseJNIBridge(method, fnPtr);
+
+    LOGV("JNI-registered %s.%s:%s\n", clazz->descriptor, methodName,
+        signature);
+    result = true;
+
+bail:
+    return result;
+}
+
+/*
+ * Returns "true" if CheckJNI is enabled in the VM.
+ */
+static bool dvmIsCheckJNIEnabled(void)
+{
+    JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+    return vm->useChecked;
+}
+
+/*
+ * Returns the appropriate JNI bridge for 'method', also taking into account
+ * the -Xcheck:jni setting.
+ */
+static DalvikBridgeFunc dvmSelectJNIBridge(const Method* method)
+{
+    enum {
+        kJNIGeneral = 0,
+        kJNISync = 1,
+        kJNIVirtualNoRef = 2,
+        kJNIStaticNoRef = 3,
+    } kind;
+    static const DalvikBridgeFunc stdFunc[] = {
+        dvmCallJNIMethod_general,
+        dvmCallJNIMethod_synchronized,
+        dvmCallJNIMethod_virtualNoRef,
+        dvmCallJNIMethod_staticNoRef
+    };
+    static const DalvikBridgeFunc checkFunc[] = {
+        dvmCheckCallJNIMethod_general,
+        dvmCheckCallJNIMethod_synchronized,
+        dvmCheckCallJNIMethod_virtualNoRef,
+        dvmCheckCallJNIMethod_staticNoRef
+    };
+
+    bool hasRefArg = false;
+
+    if (dvmIsSynchronizedMethod(method)) {
+        /* use version with synchronization; calls into general handler */
+        kind = kJNISync;
+    } else {
+        /*
+         * Do a quick scan through the "shorty" signature to see if the method
+         * takes any reference arguments.
+         */
+        const char* cp = method->shorty;
+        while (*++cp != '\0') {     /* pre-incr to skip return type */
+            if (*cp == 'L') {
+                /* 'L' used for both object and array references */
+                hasRefArg = true;
+                break;
+            }
+        }
+
+        if (hasRefArg) {
+            /* use general handler to slurp up reference args */
+            kind = kJNIGeneral;
+        } else {
+            /* virtual methods have a ref in args[0] (not in signature) */
+            if (dvmIsStaticMethod(method))
+                kind = kJNIStaticNoRef;
+            else
+                kind = kJNIVirtualNoRef;
+        }
+    }
+
+    return dvmIsCheckJNIEnabled() ? checkFunc[kind] : stdFunc[kind];
+}
+
+/*
+ * Trace a call into native code.
+ */
+static void dvmTraceCallJNIMethod(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    dvmLogNativeMethodEntry(method, args);
+    DalvikBridgeFunc bridge = dvmSelectJNIBridge(method);
+    (*bridge)(args, pResult, method, self);
+    dvmLogNativeMethodExit(method, self, *pResult);
+}
+
+/**
+ * Returns true if the -Xjnitrace setting implies we should trace 'method'.
+ */
+static bool shouldTrace(Method* method)
+{
+    return gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace);
+}
+
+/*
+ * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
+ * to point at the actual function.
+ */
+void dvmUseJNIBridge(Method* method, void* func)
+{
+    DalvikBridgeFunc bridge = shouldTrace(method)
+        ? dvmTraceCallJNIMethod
+        : dvmSelectJNIBridge(method);
+    dvmSetNativeFunc(method, bridge, func);
+}
+
+/*
+ * Get the method currently being executed by examining the interp stack.
+ */
+const Method* dvmGetCurrentJNIMethod(void)
+{
+    assert(dvmThreadSelf() != NULL);
+
+    void* fp = dvmThreadSelf()->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)) {
+            LOGE("Unable to initialize monitor tracking table\n");
+            dvmAbort();
+        }
+    }
+
+    if (!dvmAddToReferenceTable(refTable, obj)) {
+        /* ran out of memory? could throw exception instead */
+        LOGE("Unable to add entry to monitor tracking table\n");
+        dvmAbort();
+    } else {
+        LOGVV("--- added monitor %p\n", 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)) {
+        LOGE("JNI monitor %p not found in tracking list\n", obj);
+        /* keep going? */
+    } else {
+        LOGVV("--- removed monitor %p\n", 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)) {
+            LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
+        } else {
+            LOGVV("--- detach-releasing monitor %p\n", *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 */
+        dvmThrowExceptionFmt("Ljava/lang/InstantiationException;",
+            "Can't instantiate %s (abstract or interface)", clazz->descriptor);
+        return false;
+    } else if (dvmIsArrayClass(clazz) || clazz == gDvm.classJavaLangClass) {
+        /* spec says "must not" for arrays, ignores Class */
+        dvmThrowExceptionFmt("Ljava/lang/IllegalArgumentException;",
+            "Can't instantiate %s (array or Class) with this JNI function",
+            clazz->descriptor);
+        return false;
+    }
+
+    return true;
+}
+
+#ifdef WITH_JNI_STACK_CHECK
+/*
+ * 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->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->curFrame);
+    u4 stackCrc, crc;
+
+    stackCrc = self->stackCrc;
+    self->stackCrc = 0;
+    crc = dvmInitCrc32();
+    crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
+    if (crc != stackCrc) {
+        const Method* meth = dvmGetCurrentJNIMethod();
+        if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
+            LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
+                stackCrc);
+        } else if (strcmp(meth->name, "nativeLoad") == 0 &&
+                  (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
+        {
+            LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
+                stackCrc);
+        } else {
+            LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
+            dvmAbort();
+        }
+    }
+    self->stackCrc = (u4) -1;       /* make logic errors more noticeable */
+}
+#endif
+
+
+/*
+ * ===========================================================================
+ *      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.
+ */
+static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
+    const Method* method, Thread* self)
+{
+#ifdef USE_INDIRECT_REF
+    if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
+            pResult->l != NULL)
+    {
+        pResult->l = dvmDecodeIndirectRef(env, pResult->l);
+    }
+#endif
+}
+
+/*
+ * General form, handles all cases.
+ */
+void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    int oldStatus;
+    u4* modArgs = (u4*) args;
+    jclass staticMethodClass;
+    JNIEnv* env = self->jniEnv;
+
+    //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
+    //    method->clazz->descriptor, method->name, method->shorty);
+
+#ifdef USE_INDIRECT_REF
+    /*
+     * Walk the argument list, creating local references for appropriate
+     * arguments.
+     */
+    int idx = 0;
+    if (dvmIsStaticMethod(method)) {
+        /* add the class object we pass in */
+        staticMethodClass = addLocalReference(env, (Object*) method->clazz);
+        if (staticMethodClass == NULL) {
+            assert(dvmCheckException(self));
+            return;
+        }
+    } else {
+        /* add "this" */
+        staticMethodClass = NULL;
+        jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
+        if (thisObj == NULL) {
+            assert(dvmCheckException(self));
+            return;
+        }
+        modArgs[idx] = (u4) thisObj;
+        idx = 1;
+    }
+
+    const char* shorty = &method->shorty[1];        /* skip return type */
+    while (*shorty != '\0') {
+        switch (*shorty++) {
+        case 'L':
+            //LOGI("  local %d: 0x%08x\n", idx, modArgs[idx]);
+            if (modArgs[idx] != 0) {
+                //if (!dvmIsValidObject((Object*) modArgs[idx]))
+                //    dvmAbort();
+                jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
+                if (argObj == NULL) {
+                    assert(dvmCheckException(self));
+                    return;
+                }
+                modArgs[idx] = (u4) argObj;
+            }
+            break;
+        case 'D':
+        case 'J':
+            idx++;
+            break;
+        default:
+            /* Z B C S I -- do nothing */
+            break;
+        }
+
+        idx++;
+    }
+#else
+    staticMethodClass = dvmIsStaticMethod(method) ?
+        (jclass) method->clazz : NULL;
+#endif
+
+    oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+    ANDROID_MEMBAR_FULL();      /* guarantee ordering on method->insns */
+    assert(method->insns != NULL);
+
+    COMPUTE_STACK_SUM(self);
+    dvmPlatformInvoke(env, staticMethodClass,
+        method->jniArgInfo, method->insSize, modArgs, method->shorty,
+        (void*)method->insns, pResult);
+    CHECK_STACK_SUM(self);
+
+    dvmChangeStatus(self, oldStatus);
+
+    convertReferenceResult(env, pResult, method, self);
+}
+
+/*
+ * Handler for the unusual case of a synchronized native method.
+ *
+ * Lock the object, then call through the general function.
+ */
+void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    Object* lockObj;
+
+    assert(dvmIsSynchronizedMethod(method));
+
+    if (dvmIsStaticMethod(method))
+        lockObj = (Object*) method->clazz;
+    else
+        lockObj = (Object*) args[0];
+
+    LOGVV("Calling %s.%s: locking %p (%s)\n",
+        method->clazz->descriptor, method->name,
+        lockObj, lockObj->clazz->descriptor);
+
+    dvmLockObject(self, lockObj);
+    dvmCallJNIMethod_general(args, pResult, method, self);
+    dvmUnlockObject(self, lockObj);
+}
+
+/*
+ * Virtual method call, no reference arguments.
+ *
+ * We need to local-ref the "this" argument, found in args[0].
+ */
+void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    u4* modArgs = (u4*) args;
+    int oldStatus;
+
+#ifdef USE_INDIRECT_REF
+    jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
+    if (thisObj == NULL) {
+        assert(dvmCheckException(self));
+        return;
+    }
+    modArgs[0] = (u4) thisObj;
+#endif
+
+    oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+    ANDROID_MEMBAR_FULL();      /* guarantee ordering on method->insns */
+
+    COMPUTE_STACK_SUM(self);
+    dvmPlatformInvoke(self->jniEnv, NULL,
+        method->jniArgInfo, method->insSize, modArgs, method->shorty,
+        (void*)method->insns, pResult);
+    CHECK_STACK_SUM(self);
+
+    dvmChangeStatus(self, oldStatus);
+
+    convertReferenceResult(self->jniEnv, pResult, method, self);
+}
+
+/*
+ * Static method call, no reference arguments.
+ *
+ * We need to local-ref the class reference.
+ */
+void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
+    const Method* method, Thread* self)
+{
+    jclass staticMethodClass;
+    int oldStatus;
+
+#ifdef USE_INDIRECT_REF
+    staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
+    if (staticMethodClass == NULL) {
+        assert(dvmCheckException(self));
+        return;
+    }
+#else
+    staticMethodClass = (jobject) method->clazz;
+#endif
+
+    oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+    ANDROID_MEMBAR_FULL();      /* guarantee ordering on method->insns */
+
+    COMPUTE_STACK_SUM(self);
+    dvmPlatformInvoke(self->jniEnv, staticMethodClass,
+        method->jniArgInfo, method->insSize, args, method->shorty,
+        (void*)method->insns, pResult);
+    CHECK_STACK_SUM(self);
+
+    dvmChangeStatus(self, oldStatus);
+
+    convertReferenceResult(self->jniEnv, pResult, method, self);
+}
+
+/*
+ * Extract the return type enum from the "jniArgInfo" field.
+ */
+DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
+{
+    return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
+}
+
+
+/*
+ * ===========================================================================
+ *      JNI implementation
+ * ===========================================================================
+ */
+
+/*
+ * Return the version of the native method interface.
+ */
+static jint GetVersion(JNIEnv* env)
+{
+    JNI_ENTER();
+    /*
+     * 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.
+     */
+    JNI_EXIT();
+    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);
+
+    JNI_ENTER();
+    LOGW("JNI DefineClass is not supported\n");
+    JNI_EXIT();
+    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)
+{
+    JNI_ENTER();
+
+    const Method* thisMethod;
+    ClassObject* clazz;
+    jclass jclazz = NULL;
+    Object* loader;
+    char* descriptor = NULL;
+
+    thisMethod = dvmGetCurrentJNIMethod();
+    assert(thisMethod != NULL);
+
+    descriptor = dvmNameToDescriptor(name);
+    if (descriptor == NULL) {
+        clazz = NULL;
+        goto bail;
+    }
+
+    //Thread* self = dvmThreadSelf();
+    if (_self->classLoaderOverride != NULL) {
+        /* hack for JNI_OnLoad */
+        assert(strcmp(thisMethod->name, "nativeLoad") == 0);
+        loader = _self->classLoaderOverride;
+    } else if (thisMethod == gDvm.methFakeNativeEntry) {
+        /* start point of invocation interface */
+        if (!gDvm.initializing)
+            loader = dvmGetSystemClassLoader();
+        else
+            loader = NULL;
+    } else {
+        loader = thisMethod->clazz->classLoader;
+    }
+
+    clazz = dvmFindClassNoInit(descriptor, loader);
+    jclazz = addLocalReference(env, (Object*) clazz);
+
+bail:
+    free(descriptor);
+
+    JNI_EXIT();
+    return jclazz;
+}
+
+/*
+ * Return the superclass of a class.
+ */
+static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
+{
+    JNI_ENTER();
+    jclass jsuper = NULL;
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    if (clazz != NULL)
+        jsuper = addLocalReference(env, (Object*)clazz->super);
+    JNI_EXIT();
+    return jsuper;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
+    ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
+
+    jboolean result = dvmInstanceof(clazz1, clazz2);
+
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * Given a java.lang.reflect.Method or .Constructor, return a methodID.
+ */
+static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
+{
+    JNI_ENTER();
+    jmethodID methodID;
+    Object* method = dvmDecodeIndirectRef(env, jmethod);
+    methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
+    JNI_EXIT();
+    return methodID;
+}
+
+/*
+ * Given a java.lang.reflect.Field, return a fieldID.
+ */
+static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
+{
+    JNI_ENTER();
+    jfieldID fieldID;
+    Object* field = dvmDecodeIndirectRef(env, jfield);
+    fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
+    JNI_EXIT();
+    return fieldID;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
+    Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
+    dvmReleaseTrackedAlloc(obj, NULL);
+    jobject jobj = addLocalReference(env, obj);
+    JNI_EXIT();
+    return jobj;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
+    Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID);
+    dvmReleaseTrackedAlloc(obj, NULL);
+    jobject jobj = addLocalReference(env, obj);
+    JNI_EXIT();
+    return jobj;
+}
+
+/*
+ * Take this exception and throw it.
+ */
+static jint Throw(JNIEnv* env, jthrowable jobj)
+{
+    JNI_ENTER();
+
+    jint retval;
+
+    if (jobj != NULL) {
+        Object* obj = dvmDecodeIndirectRef(env, jobj);
+        dvmSetException(_self, obj);
+        retval = JNI_OK;
+    } else {
+        retval = JNI_ERR;
+    }
+
+    JNI_EXIT();
+    return retval;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    dvmThrowExceptionByClass(clazz, message);
+    // TODO: should return failure if this didn't work (e.g. OOM)
+
+    JNI_EXIT();
+    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)
+{
+    JNI_ENTER();
+
+    Object* exception;
+    jobject localException;
+
+    exception = dvmGetException(_self);
+    localException = addLocalReference(env, 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.
+         */
+        LOGW("JNI WARNING: addLocal/exception combo\n");
+    }
+
+    JNI_EXIT();
+    return localException;
+}
+
+/*
+ * Print an exception and stack trace to stderr.
+ */
+static void ExceptionDescribe(JNIEnv* env)
+{
+    JNI_ENTER();
+
+    Object* exception = dvmGetException(_self);
+    if (exception != NULL) {
+        dvmPrintExceptionStackTrace();
+    } else {
+        LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
+    }
+
+    JNI_EXIT();
+}
+
+/*
+ * Clear the exception currently being thrown.
+ *
+ * TODO: we should be able to skip the enter/exit stuff.
+ */
+static void ExceptionClear(JNIEnv* env)
+{
+    JNI_ENTER();
+    dvmClearException(_self);
+    JNI_EXIT();
+}
+
+/*
+ * Kill the VM.  This function does not return.
+ */
+static void FatalError(JNIEnv* env, const char* msg)
+{
+    //dvmChangeStatus(NULL, THREAD_RUNNING);
+    LOGE("JNI posting fatal error: %s\n", msg);
+    dvmAbort();
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+    int result = JNI_OK;
+    if (!ensureLocalCapacity(env, capacity) ||
+        !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
+    {
+        /* yes, OutOfMemoryError, not StackOverflowError */
+        dvmClearException(_self);
+        dvmThrowException("Ljava/lang/OutOfMemoryError;",
+            "out of stack in JNI PushLocalFrame");
+        result = JNI_ERR;
+    }
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * Pop the local frame off.  If "result" is not null, add it as a
+ * local reference on the now-current frame.
+ */
+static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
+{
+    JNI_ENTER();
+    Object* result = dvmDecodeIndirectRef(env, jresult);
+    if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
+        LOGW("JNI WARNING: too many PopLocalFrame calls\n");
+        dvmClearException(_self);
+        dvmThrowException("Ljava/lang/RuntimeException;",
+            "too many PopLocalFrame calls");
+    }
+    jresult = addLocalReference(env, result);
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * Add a reference to the global list.
+ */
+static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
+{
+    Object* obj;
+
+    JNI_ENTER();
+    if (dvmIsWeakGlobalRef(jobj))
+        obj = getPhantomReferent(env, (jweak) jobj);
+    else
+        obj = dvmDecodeIndirectRef(env, jobj);
+    jobject retval = addGlobalReference(obj);
+    JNI_EXIT();
+    return retval;
+}
+
+/*
+ * Delete a reference from the global list.
+ */
+static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
+{
+    JNI_ENTER();
+    deleteGlobalReference(jglobalRef);
+    JNI_EXIT();
+}
+
+
+/*
+ * Add a reference to the local list.
+ */
+static jobject NewLocalRef(JNIEnv* env, jobject jobj)
+{
+    Object* obj;
+
+    JNI_ENTER();
+    if (dvmIsWeakGlobalRef(jobj))
+        obj = getPhantomReferent(env, (jweak) jobj);
+    else
+        obj = dvmDecodeIndirectRef(env, jobj);
+    jobject retval = addLocalReference(env, obj);
+    JNI_EXIT();
+    return retval;
+}
+
+/*
+ * Delete a reference from the local list.
+ */
+static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
+{
+    JNI_ENTER();
+    deleteLocalReference(env, jlocalRef);
+    JNI_EXIT();
+}
+
+/*
+ * Ensure that the local references table can hold at least this many
+ * references.
+ */
+static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
+{
+    JNI_ENTER();
+    bool okay = ensureLocalCapacity(env, capacity);
+    if (!okay) {
+        dvmThrowException("Ljava/lang/OutOfMemoryError;",
+            "can't ensure local reference capacity");
+    }
+    JNI_EXIT();
+    if (okay)
+        return 0;
+    else
+        return -1;
+}
+
+
+/*
+ * Determine whether two Object references refer to the same underlying object.
+ */
+static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
+{
+    JNI_ENTER();
+    Object* obj1 = dvmDecodeIndirectRef(env, jref1);
+    Object* obj2 = dvmDecodeIndirectRef(env, jref2);
+    jboolean result = (obj1 == obj2);
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * Allocate a new object without invoking any constructors.
+ */
+static jobject AllocObject(JNIEnv* env, jclass jclazz)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jobject result;
+
+    if (!canAllocClass(clazz) ||
+        (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+    {
+        assert(dvmCheckException(_self));
+        result = NULL;
+    } else {
+        Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        result = addLocalReference(env, newObj);
+    }
+
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * Allocate a new object and invoke the supplied constructor.
+ */
+static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
+{
+    JNI_ENTER();
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jobject result;
+
+    if (!canAllocClass(clazz) ||
+        (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+    {
+        assert(dvmCheckException(_self));
+        result = NULL;
+    } else {
+        Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        result = addLocalReference(env, newObj);
+        if (newObj != NULL) {
+            JValue unused;
+            va_list args;
+            va_start(args, methodID);
+            dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
+                args);
+            va_end(args);
+        }
+    }
+
+    JNI_EXIT();
+    return result;
+}
+static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
+    va_list args)
+{
+    JNI_ENTER();
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jobject result;
+
+    if (!canAllocClass(clazz) ||
+        (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+    {
+        assert(dvmCheckException(_self));
+        result = NULL;
+    } else {
+        Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        result = addLocalReference(env, newObj);
+        if (newObj != NULL) {
+            JValue unused;
+            dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
+                args);
+        }
+    }
+
+    JNI_EXIT();
+    return result;
+}
+static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
+    jvalue* args)
+{
+    JNI_ENTER();
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jobject result;
+
+    if (!canAllocClass(clazz) ||
+        (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+    {
+        assert(dvmCheckException(_self));
+        result = NULL;
+    } else {
+        Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        result = addLocalReference(env, newObj);
+        if (newObj != NULL) {
+            JValue unused;
+            dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused,
+                args);
+        }
+    }
+
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * Returns the class of an object.
+ *
+ * JNI spec says: obj must not be NULL.
+ */
+static jclass GetObjectClass(JNIEnv* env, jobject jobj)
+{
+    JNI_ENTER();
+
+    assert(jobj != NULL);
+
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
+
+    JNI_EXIT();
+    return jclazz;
+}
+
+/*
+ * Determine whether "obj" is an instance of "clazz".
+ */
+static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
+{
+    JNI_ENTER();
+
+    assert(jclazz != NULL);
+
+    jboolean result;
+
+    if (jobj == NULL) {
+        result = true;
+    } else {
+        Object* obj = dvmDecodeIndirectRef(env, jobj);
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+        result = dvmInstanceof(obj->clazz, clazz);
+    }
+
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * Get a method ID for an instance method.
+ *
+ * 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)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jmethodID id = NULL;
+
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(_self));
+    } else {
+        Method* meth;
+
+        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_LOGD() {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                LOGD("GetMethodID: not returning static method %s.%s %s\n",
+                    clazz->descriptor, meth->name, desc);
+                free(desc);
+            }
+            meth = NULL;
+        }
+        if (meth == NULL) {
+            LOGD("GetMethodID: method not found: %s.%s:%s\n",
+                clazz->descriptor, name, sig);
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
+        }
+
+        /*
+         * 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).
+         */
+        if (meth != NULL) {
+            assert(dvmIsClassInitialized(meth->clazz) ||
+                   dvmIsClassInitializing(meth->clazz));
+        }
+        id = (jmethodID) meth;
+    }
+    JNI_EXIT();
+    return id;
+}
+
+/*
+ * Get a field ID (instance fields).
+ */
+static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
+    const char* name, const char* sig)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jfieldID id;
+
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(_self));
+        id = NULL;
+    } else {
+        id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
+        if (id == NULL) {
+            LOGD("GetFieldID: unable to find field %s.%s:%s\n",
+                clazz->descriptor, name, sig);
+            dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
+        }
+    }
+    JNI_EXIT();
+    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)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jmethodID id;
+
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(_self));
+        id = NULL;
+    } else {
+        Method* meth;
+
+        meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
+
+        /* make sure it's static, not virtual+private */
+        if (meth != NULL && !dvmIsStaticMethod(meth)) {
+            IF_LOGD() {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                LOGD("GetStaticMethodID: "
+                    "not returning nonstatic method %s.%s %s\n",
+                    clazz->descriptor, meth->name, desc);
+                free(desc);
+            }
+            meth = NULL;
+        }
+
+        id = (jmethodID) meth;
+        if (id == NULL)
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
+    }
+
+    JNI_EXIT();
+    return id;
+}
+
+/*
+ * Get a field ID (static fields).
+ */
+static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
+    const char* name, const char* sig)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jfieldID id;
+
+    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+        assert(dvmCheckException(_self));
+        id = NULL;
+    } else {
+        id = (jfieldID) dvmFindStaticField(clazz, name, sig);
+        if (id == NULL)
+            dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
+    }
+    JNI_EXIT();
+    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);                                           \
+        JNI_ENTER();                                                        \
+        StaticField* sfield = (StaticField*) fieldID;                       \
+        _ctype value;                                                       \
+        if (dvmIsVolatileField(&sfield->field)) {                           \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* obj = dvmGetStaticFieldObjectVolatile(sfield);      \
+                value = (_ctype)(u4)addLocalReference(env, obj);            \
+            } else {                                                        \
+                value = dvmGetStaticField##_jname##Volatile(sfield);        \
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* obj = dvmGetStaticFieldObject(sfield);              \
+                value = (_ctype)(u4)addLocalReference(env, obj);            \
+            } else {                                                        \
+                value = dvmGetStaticField##_jname(sfield);                  \
+            }                                                               \
+        }                                                                   \
+        JNI_EXIT();                                                         \
+        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, _jname, _isref)                       \
+    static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz,        \
+        jfieldID fieldID, _ctype value)                                     \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        JNI_ENTER();                                                        \
+        StaticField* sfield = (StaticField*) fieldID;                       \
+        if (dvmIsVolatileField(&sfield->field)) {                           \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj =                                            \
+                    dvmDecodeIndirectRef(env, (jobject)(u4)value);          \
+                dvmSetStaticFieldObjectVolatile(sfield, valObj);            \
+            } else {                                                        \
+                dvmSetStaticField##_jname##Volatile(sfield, value);         \
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj =                                            \
+                    dvmDecodeIndirectRef(env, (jobject)(u4)value);          \
+                dvmSetStaticFieldObject(sfield, valObj);                    \
+            } else {                                                        \
+                dvmSetStaticField##_jname(sfield, value);                   \
+            }                                                               \
+        }                                                                   \
+        JNI_EXIT();                                                         \
+    }
+SET_STATIC_TYPE_FIELD(jobject, Object, true);
+SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
+SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
+SET_STATIC_TYPE_FIELD(jchar, Char, false);
+SET_STATIC_TYPE_FIELD(jshort, Short, false);
+SET_STATIC_TYPE_FIELD(jint, Int, false);
+SET_STATIC_TYPE_FIELD(jlong, Long, false);
+SET_STATIC_TYPE_FIELD(jfloat, Float, false);
+SET_STATIC_TYPE_FIELD(jdouble, 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)                                                   \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        InstField* field = (InstField*) fieldID;                            \
+        _ctype value;                                                       \
+        if (dvmIsVolatileField(&field->field)) {                            \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj =                                            \
+                    dvmGetFieldObjectVolatile(obj, field->byteOffset);      \
+                value = (_ctype)(u4)addLocalReference(env, valObj);         \
+            } else {                                                        \
+                value =                                                     \
+                    dvmGetField##_jname##Volatile(obj, field->byteOffset);  \
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
+                value = (_ctype)(u4)addLocalReference(env, valObj);         \
+            } else {                                                        \
+                value = dvmGetField##_jname(obj, field->byteOffset);        \
+            }                                                               \
+        }                                                                   \
+        JNI_EXIT();                                                         \
+        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, _jname, _isref)                              \
+    static void Set##_jname##Field(JNIEnv* env, jobject jobj,               \
+        jfieldID fieldID, _ctype value)                                     \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        InstField* field = (InstField*) fieldID;                            \
+        if (dvmIsVolatileField(&field->field)) {                            \
+            if (_isref) {   /* only when _ctype==jobject */                 \
+                Object* valObj =                                            \
+                    dvmDecodeIndirectRef(env, (jobject)(u4)value);          \
+                dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj);  \
+            } else {                                                        \
+                dvmSetField##_jname##Volatile(obj,                          \
+                    field->byteOffset, value);                              \
+            }                                                               \
+        } else {                                                            \
+            if (_isref) {                                                   \
+                Object* valObj =                                            \
+                    dvmDecodeIndirectRef(env, (jobject)(u4)value);          \
+                dvmSetFieldObject(obj, field->byteOffset, valObj);          \
+            } else {                                                        \
+                dvmSetField##_jname(obj, field->byteOffset, value);         \
+            }                                                               \
+        }                                                                   \
+        JNI_EXIT();                                                         \
+    }
+SET_TYPE_FIELD(jobject, Object, true);
+SET_TYPE_FIELD(jboolean, Boolean, false);
+SET_TYPE_FIELD(jbyte, Byte, false);
+SET_TYPE_FIELD(jchar, Char, false);
+SET_TYPE_FIELD(jshort, Short, false);
+SET_TYPE_FIELD(jint, Int, false);
+SET_TYPE_FIELD(jlong, Long, false);
+SET_TYPE_FIELD(jfloat, Float, false);
+SET_TYPE_FIELD(jdouble, 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, ...)                                            \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        const Method* meth;                                                 \
+        va_list args;                                                       \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            JNI_EXIT();                                                     \
+            return _retfail;                                                \
+        }                                                                   \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(_self, meth, obj, true, &result, args);              \
+        va_end(args);                                                       \
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj,          \
+        jmethodID methodID, va_list args)                                   \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            JNI_EXIT();                                                     \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodV(_self, meth, obj, true, &result, args);              \
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj,          \
+        jmethodID methodID, jvalue* args)                                   \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
+        if (meth == NULL) {                                                 \
+            JNI_EXIT();                                                     \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodA(_self, meth, obj, true, &result, args);              \
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }
+CALL_VIRTUAL(jobject, Object, NULL, 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, ...)                             \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        ClassObject* clazz =                                                \
+            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
+        const Method* meth;                                                 \
+        va_list args;                                                       \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            JNI_EXIT();                                                     \
+            return _retfail;                                                \
+        }                                                                   \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(_self, meth, obj, true, &result, args);              \
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        va_end(args);                                                       \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
+        jclass jclazz, jmethodID methodID, va_list args)                    \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        ClassObject* clazz =                                                \
+            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            JNI_EXIT();                                                     \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodV(_self, meth, obj, true, &result, args);              \
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
+        jclass jclazz, jmethodID methodID, jvalue* args)                    \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        ClassObject* clazz =                                                \
+            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
+        const Method* meth;                                                 \
+        JValue result;                                                      \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
+        if (meth == NULL) {                                                 \
+            JNI_EXIT();                                                     \
+            return _retfail;                                                \
+        }                                                                   \
+        dvmCallMethodA(_self, meth, obj, true, &result, args);              \
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }
+CALL_NONVIRTUAL(jobject, Object, NULL, 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);                                           \
+        JNI_ENTER();                                                        \
+        JValue result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
+        va_end(args);                                                       \
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz,   \
+        jmethodID methodID, va_list args)                                   \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        JNI_ENTER();                                                        \
+        JValue result;                                                      \
+        dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }                                                                       \
+    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz,   \
+        jmethodID methodID, jvalue* args)                                   \
+    {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
+        JNI_ENTER();                                                        \
+        JValue result;                                                      \
+        dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
+        if (_isref && !dvmCheckException(_self))                            \
+            result.l = addLocalReference(env, result.l);                    \
+        JNI_EXIT();                                                         \
+        return _retok;                                                      \
+    }
+CALL_STATIC(jobject, Object, NULL, 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)
+{
+    JNI_ENTER();
+    jobject retval;
+
+    StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
+    if (jstr == NULL) {
+        retval = NULL;
+    } else {
+        dvmReleaseTrackedAlloc((Object*) jstr, NULL);
+        retval = addLocalReference(env, (Object*) jstr);
+    }
+
+    JNI_EXIT();
+    return retval;
+}
+
+/*
+ * Return the length of a String in Unicode character units.
+ */
+static jsize GetStringLength(JNIEnv* env, jstring jstr)
+{
+    JNI_ENTER();
+
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    jsize len = dvmStringLen(strObj);
+
+    JNI_EXIT();
+    return len;
+}
+
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    ArrayObject* strChars = dvmStringCharArray(strObj);
+
+    pinPrimitiveArray(strChars);
+
+    const u2* data = dvmStringChars(strObj);
+    if (isCopy != NULL)
+        *isCopy = JNI_FALSE;
+
+    JNI_EXIT();
+    return (jchar*)data;
+}
+
+/*
+ * Release our grip on some characters from a string.
+ */
+static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
+{
+    JNI_ENTER();
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    ArrayObject* strChars = dvmStringCharArray(strObj);
+    unpinPrimitiveArray(strChars);
+    JNI_EXIT();
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+
+    jstring result;
+
+    if (bytes == NULL) {
+        result = NULL;
+    } else {
+        /* note newStr could come back NULL on OOM */
+        StringObject* newStr = dvmCreateStringFromCstr(bytes);
+        result = addLocalReference(env, (Object*) newStr);
+        dvmReleaseTrackedAlloc((Object*)newStr, NULL);
+    }
+
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * Return the length in bytes of the modified UTF-8 form of the string.
+ */
+static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
+{
+    JNI_ENTER();
+
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    jsize len = dvmStringUtf8ByteLen(strObj);
+
+    JNI_EXIT();
+    return len;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+    char* newStr;
+
+    if (jstr == NULL) {
+        /* this shouldn't happen; throw NPE? */
+        newStr = NULL;
+    } else {
+        if (isCopy != NULL)
+            *isCopy = JNI_TRUE;
+
+        StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+        newStr = dvmCreateCstrFromString(strObj);
+        if (newStr == NULL) {
+            /* assume memory failure */
+            dvmThrowException("Ljava/lang/OutOfMemoryError;",
+                "native heap string alloc failed");
+        }
+    }
+
+    JNI_EXIT();
+    return newStr;
+}
+
+/*
+ * Release a string created by GetStringUTFChars().
+ */
+static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
+{
+    JNI_ENTER();
+    free((char*)utf);
+    JNI_EXIT();
+}
+
+/*
+ * Return the capacity of the array.
+ */
+static jsize GetArrayLength(JNIEnv* env, jarray jarr)
+{
+    JNI_ENTER();
+
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    jsize length = arrObj->length;
+
+    JNI_EXIT();
+    return length;
+}
+
+/*
+ * Construct a new array that holds objects from class "elementClass".
+ */
+static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
+    jclass jelementClass, jobject jinitialElement)
+{
+    JNI_ENTER();
+
+    jobjectArray newArray = NULL;
+    ClassObject* elemClassObj =
+        (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
+
+    if (elemClassObj == NULL) {
+        dvmThrowException("Ljava/lang/NullPointerException;",
+            "JNI NewObjectArray");
+        goto bail;
+    }
+
+    ArrayObject* newObj =
+        dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
+    if (newObj == NULL) {
+        assert(dvmCheckException(_self));
+        goto bail;
+    }
+    newArray = addLocalReference(env, (Object*) newObj);
+    dvmReleaseTrackedAlloc((Object*) newObj, NULL);
+
+    /*
+     * Initialize the array.  Trashes "length".
+     */
+    if (jinitialElement != NULL) {
+        Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
+        Object** arrayData = (Object**) newObj->contents;
+
+        while (length--)
+            *arrayData++ = initialElement;
+    }
+
+
+bail:
+    JNI_EXIT();
+    return newArray;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    jobject retval = NULL;
+
+    assert(arrayObj != NULL);
+
+    /* check the array bounds */
+    if (index < 0 || index >= (int) arrayObj->length) {
+        dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+            arrayObj->obj.clazz->descriptor);
+        goto bail;
+    }
+
+    Object* value = ((Object**) arrayObj->contents)[index];
+    retval = addLocalReference(env, value);
+
+bail:
+    JNI_EXIT();
+    return retval;
+}
+
+/*
+ * Set one element of an Object array.
+ */
+static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
+    jsize index, jobject jobj)
+{
+    JNI_ENTER();
+
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+
+    assert(arrayObj != NULL);
+
+    /* check the array bounds */
+    if (index < 0 || index >= (int) arrayObj->length) {
+        dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+            arrayObj->obj.clazz->descriptor);
+        goto bail;
+    }
+
+    //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
+
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    dvmSetObjectArrayElement(arrayObj, index, obj);
+
+bail:
+    JNI_EXIT();
+}
+
+/*
+ * Create a new array of primitive elements.
+ */
+#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar)                     \
+    static _artype New##_jname##Array(JNIEnv* env, jsize length)            \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        ArrayObject* arrayObj;                                              \
+        arrayObj = dvmAllocPrimitiveArray(_typechar, length,                \
+            ALLOC_DEFAULT);                                                 \
+        jarray jarr = NULL;                                                 \
+        if (arrayObj != NULL) {                                             \
+            jarr = addLocalReference(env, (Object*) arrayObj);              \
+            dvmReleaseTrackedAlloc((Object*) arrayObj, NULL);               \
+        }                                                                   \
+        JNI_EXIT();                                                         \
+        return (_artype)jarr;                                               \
+    }
+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)                               \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        _ctype* data;                                                       \
+        ArrayObject* arrayObj =                                             \
+            (ArrayObject*) dvmDecodeIndirectRef(env, jarr);                 \
+        pinPrimitiveArray(arrayObj);                                        \
+        data = (_ctype*) arrayObj->contents;                                \
+        if (isCopy != NULL)                                                 \
+            *isCopy = JNI_FALSE;                                            \
+        JNI_EXIT();                                                         \
+        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);                                            \
+        JNI_ENTER();                                                        \
+        if (mode != JNI_COMMIT) {                                           \
+            ArrayObject* arrayObj =                                         \
+                (ArrayObject*) dvmDecodeIndirectRef(env, jarr);             \
+            unpinPrimitiveArray(arrayObj);                                  \
+        }                                                                   \
+        JNI_EXIT();                                                         \
+    }
+
+/*
+ * 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)            \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        ArrayObject* arrayObj =                                             \
+            (ArrayObject*) dvmDecodeIndirectRef(env, jarr);                 \
+        _ctype* data = (_ctype*) arrayObj->contents;                        \
+        if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                arrayObj->obj.clazz->descriptor);                           \
+        } else {                                                            \
+            memcpy(buf, data + start, len * sizeof(_ctype));                \
+        }                                                                   \
+        JNI_EXIT();                                                         \
+    }
+
+/*
+ * 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)      \
+    {                                                                       \
+        JNI_ENTER();                                                        \
+        ArrayObject* arrayObj =                                             \
+            (ArrayObject*) dvmDecodeIndirectRef(env, jarr);                 \
+        _ctype* data = (_ctype*) arrayObj->contents;                        \
+        if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                arrayObj->obj.clazz->descriptor);                           \
+        } else {                                                            \
+            memcpy(data + start, buf, len * sizeof(_ctype));                \
+        }                                                                   \
+        JNI_EXIT();                                                         \
+    }
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jint retval = JNI_OK;
+    int i;
+
+    if (gDvm.verboseJni) {
+        LOGI("[Registering JNI native methods for class %s]\n",
+            clazz->descriptor);
+    }
+
+    for (i = 0; i < nMethods; i++) {
+        if (!dvmRegisterJNIMethod(clazz, methods[i].name,
+                methods[i].signature, methods[i].fnPtr))
+        {
+            retval = JNI_ERR;
+        }
+    }
+
+    JNI_EXIT();
+    return retval;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    if (gDvm.verboseJni) {
+        LOGI("[Unregistering JNI native methods for class %s]\n",
+            clazz->descriptor);
+    }
+    dvmUnregisterJNINativeMethods(clazz);
+
+    JNI_EXIT();
+    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)
+{
+    JNI_ENTER();
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    dvmLockObject(_self, obj);
+    trackMonitorEnter(_self, obj);
+    JNI_EXIT();
+    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)
+{
+    JNI_ENTER();
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    bool success = dvmUnlockObject(_self, obj);
+    if (success)
+        trackMonitorExit(_self, obj);
+    JNI_EXIT();
+    return success ? JNI_OK : JNI_ERR;
+}
+
+/*
+ * Return the JavaVM interface associated with the current thread.
+ */
+static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
+{
+    JNI_ENTER();
+    //*vm = gDvm.vmList;
+    *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
+    JNI_EXIT();
+    if (*vm == NULL)
+        return JNI_ERR;
+    else
+        return JNI_OK;
+}
+
+/*
+ * Copies "len" Unicode characters, from offset "start".
+ */
+static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
+    jchar* buf)
+{
+    JNI_ENTER();
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    if (start + len > dvmStringLen(strObj))
+        dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
+    else
+        memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
+    JNI_EXIT();
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    if (start + len > dvmStringLen(strObj))
+        dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
+    else
+        dvmCreateCstrFromStringRegion(strObj, start, len, buf);
+    JNI_EXIT();
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+    void* data;
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    pinPrimitiveArray(arrayObj);
+    data = arrayObj->contents;
+    if (isCopy != NULL)
+        *isCopy = JNI_FALSE;
+    JNI_EXIT();
+    return data;
+}
+
+/*
+ * Release an array obtained with GetPrimitiveArrayCritical.
+ */
+static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
+    void* carray, jint mode)
+{
+    JNI_ENTER();
+    if (mode != JNI_COMMIT) {
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+        unpinPrimitiveArray(arrayObj);
+    }
+    JNI_EXIT();
+}
+
+/*
+ * Like GetStringChars, but with restricted use.
+ */
+static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
+    jboolean* isCopy)
+{
+    JNI_ENTER();
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    ArrayObject* strChars = dvmStringCharArray(strObj);
+
+    pinPrimitiveArray(strChars);
+
+    const u2* data = dvmStringChars(strObj);
+    if (isCopy != NULL)
+        *isCopy = JNI_FALSE;
+
+    JNI_EXIT();
+    return (jchar*)data;
+}
+
+/*
+ * Like ReleaseStringChars, but with restricted use.
+ */
+static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
+    const jchar* carray)
+{
+    JNI_ENTER();
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    ArrayObject* strChars = dvmStringCharArray(strObj);
+    unpinPrimitiveArray(strChars);
+    JNI_EXIT();
+}
+
+/*
+ * Create a new weak global reference.
+ */
+static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
+{
+    JNI_ENTER();
+    jweak wref = createWeakGlobalRef(env, obj);
+    JNI_EXIT();
+    return wref;
+}
+
+/*
+ * Delete the specified weak global reference.
+ */
+static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
+{
+    JNI_ENTER();
+    deleteWeakGlobalRef(env, wref);
+    JNI_EXIT();
+}
+
+/*
+ * Quick check for pending exceptions.
+ *
+ * TODO: we should be able to skip the enter/exit macros here.
+ */
+static jboolean ExceptionCheck(JNIEnv* env)
+{
+    JNI_ENTER();
+    bool result = dvmCheckException(_self);
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+    jobjectRefType type = dvmGetJNIRefType(env, jobj);
+    JNI_EXIT();
+    return type;
+}
+
+/*
+ * Allocate and return a new java.nio.ByteBuffer for this block of memory.
+ *
+ * "address" may not be NULL, and "capacity" must be > 0.  (These are only
+ * verified when CheckJNI is enabled.)
+ */
+static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
+{
+    JNI_ENTER();
+
+    Thread* self = _self /*dvmThreadSelf()*/;
+    Object* platformAddress = NULL;
+    JValue callResult;
+    jobject result = NULL;
+    ClassObject* tmpClazz;
+
+    tmpClazz = gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on->clazz;
+    if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
+        goto bail;
+
+    /* get an instance of PlatformAddress that wraps the provided address */
+    dvmCallMethod(self,
+        gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
+        NULL, &callResult, address);
+    if (dvmGetException(self) != NULL || callResult.l == NULL)
+        goto bail;
+
+    /* don't let the GC discard it */
+    platformAddress = (Object*) callResult.l;
+    dvmAddTrackedAlloc(platformAddress, self);
+    LOGV("tracking %p for address=%p\n", platformAddress, address);
+
+    /* create an instance of java.nio.ReadWriteDirectByteBuffer */
+    tmpClazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
+    if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
+        goto bail;
+    Object* newObj = dvmAllocObject(tmpClazz, ALLOC_DONT_TRACK);
+    if (newObj != NULL) {
+        /* call the (PlatformAddress, int, int) constructor */
+        result = addLocalReference(env, newObj);
+        dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
+            newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
+        if (dvmGetException(self) != NULL) {
+            deleteLocalReference(env, result);
+            result = NULL;
+            goto bail;
+        }
+    }
+
+bail:
+    if (platformAddress != NULL)
+        dvmReleaseTrackedAlloc(platformAddress, self);
+    JNI_EXIT();
+    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)
+{
+    JNI_ENTER();
+
+    Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
+    Thread* self = _self /*dvmThreadSelf()*/;
+    void* result;
+
+    /*
+     * All Buffer objects have an effectiveDirectAddress field.  If it's
+     * nonzero, we can just return that value.  If not, we have to call
+     * through DirectBuffer.getEffectiveAddress(), which as a side-effect
+     * will set the effectiveDirectAddress field for direct buffers (and
+     * things that wrap direct buffers).
+     */
+    result = (void*) dvmGetFieldInt(bufObj,
+            gDvm.offJavaNioBuffer_effectiveDirectAddress);
+    if (result != NULL) {
+        //LOGI("fast path for %p\n", buf);
+        goto bail;
+    }
+
+    /*
+     * Start by determining if the object supports the DirectBuffer
+     * interfaces.  Note this does not guarantee that it's a direct buffer.
+     */
+    if (!dvmInstanceof(bufObj->clazz,
+            gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
+    {
+        goto bail;
+    }
+
+    /*
+     * Get a PlatformAddress object with the effective address.
+     *
+     * If this isn't a direct buffer, the result will be NULL and/or an
+     * exception will have been thrown.
+     */
+    JValue callResult;
+    const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
+        gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
+    dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
+    if (dvmGetException(self) != NULL) {
+        dvmClearException(self);
+        callResult.l = NULL;
+    }
+
+    Object* platformAddr = callResult.l;
+    if (platformAddr == NULL) {
+        LOGV("Got request for address of non-direct buffer\n");
+        goto bail;
+    }
+
+    /*
+     * Extract the address from the PlatformAddress object.  Instead of
+     * calling the toLong() method, just grab the field directly.  This
+     * is faster but more fragile.
+     */
+    result = (void*) dvmGetFieldInt(platformAddr,
+                gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
+
+    //LOGI("slow path for %p --> %p\n", buf, result);
+
+bail:
+    JNI_EXIT();
+    return result;
+}
+
+/*
+ * 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)
+{
+    JNI_ENTER();
+
+    /*
+     * 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(env, jbuf);
+    jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
+
+    JNI_EXIT();
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      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;
+    Thread* self;
+    bool result = false;
+
+    /*
+     * Return immediately if we're already one with the VM.
+     */
+    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
+        LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
+            (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 = dvmGetMainThreadGroup();
+    } else {
+        assert(args->version >= JNI_VERSION_1_2);
+
+        argsCopy.version = args->version;
+        argsCopy.name = args->name;
+        if (args->group != NULL)
+            argsCopy.group = args->group;
+        else
+            argsCopy.group = dvmGetMainThreadGroup();
+    }
+
+    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();
+
+    if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
+        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);
+    }
+    if (*env == NULL)
+        return JNI_EDETACHED;
+    else
+        return JNI_OK;
+}
+
+/*
+ * 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;
+    Thread* self;
+
+    if (ext == NULL)
+        return JNI_ERR;
+
+    if (gDvm.verboseShutdown)
+        LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
+
+    /*
+     * Sleep on a condition variable until it's okay to exit.
+     */
+    self = dvmThreadSelf();
+    if (self == NULL) {
+        JNIEnv* tmpEnv;
+        if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
+            LOGV("Unable to reattach main for Destroy; assuming VM is "
+                 "shutting down (count=%d)\n",
+                gDvm.nonDaemonThreadCount);
+            goto shutdown;
+        } else {
+            LOGV("Attached to wait for shutdown in Destroy\n");
+        }
+    }
+    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)
+        LOGD("DestroyJavaVM shutting VM down\n");
+    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
+ * ===========================================================================
+ */
+
+/*
+ * 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(void)
+{
+    JNIEnvExt* extEnv;
+    JavaVMExt* extVm;
+
+    extEnv = dvmGetJNIEnvForThread();
+    if (extEnv == NULL) {
+        LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
+        return;
+    }
+    extVm = extEnv->vm;
+    assert(extVm != NULL);
+
+    if (!extVm->useChecked) {
+        LOGD("Late-enabling CheckJNI\n");
+        dvmUseCheckedJniVm(extVm);
+        extVm->useChecked = true;
+        dvmUseCheckedJniEnv(extEnv);
+
+        /* currently no way to pick up jniopts features */
+    } else {
+        LOGD("Not late-enabling CheckJNI (already on)\n");
+    }
+}
+
+/*
+ * 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 (gDvm.vmList != NULL) {
+        *nVMs = 1;
+
+        if (bufLen > 0)
+            *vmBuf++ = gDvm.vmList;
+    } 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;
+    JNIEnvExt* pEnv = NULL;
+    JavaVMExt* pVM = NULL;
+    const char** argv;
+    int argc = 0;
+    int i, curOpt;
+    int result = JNI_ERR;
+    bool checkJni = false;
+    bool warnError = true;
+    bool forceDataCopy = false;
+
+    if (args->version < JNI_VERSION_1_2)
+        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.
+     */
+    //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
+    pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
+
+    //memset(pEnv, 0, sizeof(JNIEnvExt));
+    //pEnv->funcTable = &gNativeInterface;
+    //pEnv->vm = pVM;
+    memset(pVM, 0, sizeof(JavaVMExt));
+    pVM->funcTable = &gInvokeInterface;
+    pVM->envList = pEnv;
+    dvmInitMutex(&pVM->envListLock);
+
+    argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
+    memset(argv, 0, sizeof(char*) * (args->nOptions));
+
+    curOpt = 0;
+
+    /*
+     * 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.
+     */
+    for (i = 0; i < args->nOptions; i++) {
+        const char* optStr = args->options[i].optionString;
+
+        if (optStr == NULL) {
+            fprintf(stderr, "ERROR: arg %d string was null\n", i);
+            goto bail;
+        } else if (strcmp(optStr, "vfprintf") == 0) {
+            gDvm.vfprintfHook = args->options[i].extraInfo;
+        } else if (strcmp(optStr, "exit") == 0) {
+            gDvm.exitHook = args->options[i].extraInfo;
+        } else if (strcmp(optStr, "abort") == 0) {
+            gDvm.abortHook = args->options[i].extraInfo;
+        } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
+            checkJni = true;
+        } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
+            const char* jniOpts = optStr + 9;
+            while (jniOpts != NULL) {
+                jniOpts++;      /* skip past ':' or ',' */
+                if (strncmp(jniOpts, "warnonly", 8) == 0) {
+                    warnError = false;
+                } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
+                    forceDataCopy = true;
+                } else {
+                    LOGW("unknown jni opt starting at '%s'\n", jniOpts);
+                }
+                jniOpts = strchr(jniOpts, ',');
+            }
+        } else {
+            /* regular option */
+            argv[curOpt++] = optStr;
+        }
+    }
+    argc = curOpt;
+
+    if (checkJni) {
+        dvmUseCheckedJniVm(pVM);
+        pVM->useChecked = true;
+    }
+    pVM->warnError = warnError;
+    pVM->forceDataCopy = forceDataCopy;
+
+    /* set this up before initializing VM, so it can create some JNIEnvs */
+    gDvm.vmList = (JavaVM*) pVM;
+
+    /*
+     * Create an env for 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.
+     */
+    pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
+
+    /* initialize VM */
+    gDvm.initializing = true;
+    if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
+        free(pEnv);
+        free(pVM);
+        goto bail;
+    }
+
+    /*
+     * Success!  Return stuff to caller.
+     */
+    dvmChangeStatus(NULL, THREAD_NATIVE);
+    *p_env = (JNIEnv*) pEnv;
+    *p_vm = (JavaVM*) pVM;
+    result = JNI_OK;
+
+bail:
+    gDvm.initializing = false;
+    if (result == JNI_OK)
+        LOGV("JNI_CreateJavaVM succeeded\n");
+    else
+        LOGW("JNI_CreateJavaVM failed\n");
+    free(argv);
+    return result;
+}
diff --git a/vm/JniInternal.h b/vm/JniInternal.h
new file mode 100644
index 0000000..302dcb0
--- /dev/null
+++ b/vm/JniInternal.h
@@ -0,0 +1,248 @@
+/*
+ * 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
+#define _DALVIK_JNIINTERNAL
+
+#include "jni.h"
+
+/* system init/shutdown */
+bool dvmJniStartup(void);
+void dvmJniShutdown(void);
+
+/*
+ * Our data structures for JNIEnv and JavaVM.
+ *
+ * Native code thinks it has a pointer to a pointer.  We know better.
+ */
+struct JavaVMExt;
+
+typedef struct JNIEnvExt {
+    const struct JNINativeInterface* funcTable;     /* must be first */
+
+    const struct JNINativeInterface* baseFuncTable;
+
+    /* pointer to the VM we are a part of */
+    struct JavaVMExt* vm;
+
+    u4      envThreadId;
+    Thread* self;
+
+    /* if nonzero, we are in a "critical" JNI call */
+    int     critical;
+
+    /* keep a copy of this here for speed */
+    bool    forceDataCopy;
+
+    struct JNIEnvExt* prev;
+    struct JNIEnvExt* next;
+} JNIEnvExt;
+
+typedef struct JavaVMExt {
+    const struct JNIInvokeInterface* funcTable;     /* must be first */
+
+    const struct JNIInvokeInterface* baseFuncTable;
+
+    /* if multiple VMs are desired, add doubly-linked list stuff here */
+
+    /* per-VM feature flags */
+    bool    useChecked;
+    bool    warnError;
+    bool    forceDataCopy;
+
+    /* head of list of JNIEnvs associated with this VM */
+    JNIEnvExt*      envList;
+    pthread_mutex_t envListLock;
+} JavaVMExt;
+
+/*
+ * 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.
+ */
+typedef 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
+} DalvikJniReturnType;
+
+#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)
+{
+#ifdef USE_INDIRECT_REF
+    self->jniLocalRefTable.segmentState.all = saveArea->xtra.localRefCookie;
+#else
+    self->jniLocalRefTable.nextEntry = saveArea->xtra.localRefCookie;
+#endif
+}
+
+/*
+ * Set the envThreadId field.
+ */
+INLINE void dvmSetJniEnvThreadId(JNIEnv* pEnv, Thread* self)
+{
+    ((JNIEnvExt*)pEnv)->envThreadId = self->threadId;
+    ((JNIEnvExt*)pEnv)->self = self;
+}
+
+/*
+ * JNI call bridges.  Not called directly.
+ *
+ * The "Check" versions are used when CheckJNI is enabled.
+ */
+void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCheckCallJNIMethod_general(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCheckCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCheckCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+    const Method* method, Thread* self);
+void dvmCheckCallJNIMethod_staticNoRef(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.
+ */
+#ifdef USE_INDIRECT_REF
+Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj);
+#else
+/* use an inline to ensure this is a no-op */
+INLINE Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj) {
+    return (Object*) jobj;
+}
+#endif
+
+/*
+ * Verify that a reference passed in from native code is valid.  Returns
+ * an indication of local/global/invalid.
+ */
+jobjectRefType dvmGetJNIRefType(JNIEnv* env, 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);
+
+/*
+ * Extract the return type enum from the "jniArgInfo" value.
+ */
+DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo);
+
+/*
+ * 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);
+
+/*
+ * This mask is applied to weak global reference values returned to
+ * native code.  The goal is to create an invalid pointer that will cause
+ * a crash if misused.  The mmap region for the virtual heap is typically
+ * around 0x40xxxxxx.
+ *
+ * To make weak global references easily distinguishable from other kinds
+ * of references when !USE_INDIRECT_REF, we XOR the low bits.  Assuming >=
+ * 64-bit alignment of objects, this changes the low 3 bits from all clear
+ * to all set.
+ */
+#define WEAK_GLOBAL_XOR 0x9e0fffff
+
+/*
+ * "Obfuscate" a weak global reference pointer.
+ */
+INLINE jweak dvmObfuscateWeakGlobalRef(jobject jobj) {
+    return (jweak) ((u4) jobj ^ WEAK_GLOBAL_XOR);
+}
+
+/*
+ * Undo the obfuscation.
+ */
+INLINE jobject dvmNormalizeWeakGlobalRef(jweak ref) {
+    return (jobject) ((u4) ref ^ WEAK_GLOBAL_XOR);
+}
+
+/*
+ * Returns "true" if this looks like a weak global reference.
+ *
+ * Relies on the low 3 bits being set instead of clear (the latter is
+ * guaranteed by 64-bit alignment of objects).
+ */
+INLINE bool dvmIsWeakGlobalRef(jobject jobj) {
+    return (((u4) jobj & 0x07) == 0x07);
+}
+
+#endif /*_DALVIK_JNIINTERNAL*/
diff --git a/vm/LinearAlloc.c b/vm/LinearAlloc.c
new file mode 100644
index 0000000..65db79e
--- /dev/null
+++ b/vm/LinearAlloc.c
@@ -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  (5*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) {
+        LOGE("ashmem LinearAlloc failed %s", strerror(errno));
+        free(pHdr);
+        return NULL;
+    }
+
+    pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+        MAP_PRIVATE, fd, 0);
+    if (pHdr->mapAddr == MAP_FAILED) {
+        LOGE("LinearAlloc mmap(%d) failed: %s\n", 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) {
+        LOGE("LinearAlloc mmap(%d) failed: %s\n", 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) {
+        LOGW("LinearAlloc init mprotect failed: %s\n", 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)
+    {
+        LOGW("LinearAlloc init mprotect #2 failed: %s\n", 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 = calloc(numPages, sizeof(short));
+        if (pHdr->writeRefCount == NULL) {
+            free(pHdr);
+            return NULL;
+        }
+    }
+
+    dvmInitMutex(&pHdr->lock);
+
+    LOGV("LinearAlloc: created region at %p-%p\n",
+        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) {
+        LOGV("Unmapping linear allocator base=%p\n", pHdr->mapAddr);
+        LOGD("LinearAlloc %p used %d of %d (%d%%)\n",
+            classLoader, pHdr->curOffset, pHdr->mapLength,
+            (pHdr->curOffset * 100) / pHdr->mapLength);
+    }
+
+    if (munmap(pHdr->mapAddr, pHdr->mapLength) != 0) {
+        LOGW("LinearAlloc munmap(%p, %d) failed: %s\n",
+            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)\n", 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\n", 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.
+         */
+        LOGE("LinearAlloc exceeded capacity (%d), last=%d\n",
+            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)\n", 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\n",
+        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)\n", start, len);
+        cc = mprotect(pHdr->mapAddr + start, len, PROT_READ | PROT_WRITE);
+        if (cc != 0) {
+            LOGE("LinearAlloc mprotect (+%d %d) failed: %s\n",
+                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)\n",
+            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);
+    LOGV("--- LinearRealloc(%d) old=%d\n", 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)\n", 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) {
+                    LOGW("Double RO on %p\n", mem);
+                    dvmAbort();
+                } else
+                    *pLen &= ~LENGTHFLAG_RW;
+            }
+
+            if (pHdr->writeRefCount[i] == 0) {
+                LOGE("Can't make page %d any less writable\n", i);
+                dvmAbort();
+            }
+            pHdr->writeRefCount[i]--;
+            if (pHdr->writeRefCount[i] == 0) {
+                LOGVV("---  prot page %d RO\n", 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) {
+                LOGE("Can't make page %d any more writable\n", i);
+                dvmAbort();
+            }
+            if (pHdr->writeRefCount[i] == 0) {
+                LOGVV("---  prot page %d RW\n", 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) {
+                    LOGW("Double RW on %p\n", 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);
+
+    LOGI("LinearAlloc classLoader=%p\n", classLoader);
+    LOGI("  mapAddr=%p mapLength=%d firstOffset=%d\n",
+        pHdr->mapAddr, pHdr->mapLength, pHdr->firstOffset);
+    LOGI("  curOffset=%d\n", 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));
+
+        LOGI("  %p (%3d): %clen=%d%s\n", 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) {
+        LOGI("writeRefCount map:\n");
+
+        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);
+    }
+
+    LOGD("LinearAlloc %p using %d of %d (%d%%)\n",
+        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) {
+            LOGW("LinearAlloc %p not freed: %p len=%d\n", 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..aa33fe1
--- /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
+#define _DALVIK_LINEARALLOC
+
+/*
+ * 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.
+ */
+typedef 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 */
+} LinearAllocHdr;
+
+
+/*
+ * 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*/
diff --git a/vm/Misc.c b/vm/Misc.c
new file mode 100644
index 0000000..93bc7f4
--- /dev/null
+++ b/vm/Misc.c
@@ -0,0 +1,738 @@
+/*
+ * 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>
+
+#define ALIGN_UP_TO_PAGE_SIZE(p) \
+    (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
+
+/*
+ * 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 = 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:
+        LOGE("unexpected 'which' %d\n", target->which);
+        break;
+    }
+
+    va_end(args);
+}
+
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ */
+BitVector* dvmAllocBitVector(int startBits, bool expandable)
+{
+    BitVector* bv;
+    int count;
+
+    assert(sizeof(bv->storage[0]) == 4);        /* assuming 32-bit units */
+    assert(startBits >= 0);
+
+    bv = (BitVector*) malloc(sizeof(BitVector));
+
+    count = (startBits + 31) >> 5;
+
+    bv->storageSize = count;
+    bv->expandable = expandable;
+    bv->storage = (u4*) malloc(count * sizeof(u4));
+    memset(bv->storage, 0x00, 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)
+{
+    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 >= 0 && 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 = 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".
+ *
+ * Returns "false" if the bit is outside the range of the vector and we're
+ * not allowed to expand.
+ */
+bool dvmSetBit(BitVector* pBits, int num)
+{
+    assert(num >= 0);
+    if (num >= pBits->storageSize * (int)sizeof(u4) * 8) {
+        if (!pBits->expandable)
+            return false;
+
+        int newSize = (num + 31) >> 5;
+        assert(newSize > pBits->storageSize);
+        pBits->storage = realloc(pBits->storage, newSize * sizeof(u4));
+        memset(&pBits->storage[pBits->storageSize], 0x00,
+            (newSize - pBits->storageSize) * sizeof(u4));
+        pBits->storageSize = newSize;
+    }
+
+    pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+    return true;
+}
+
+/*
+ * Mark the specified bit as "clear".
+ */
+void dvmClearBit(BitVector* pBits, int num)
+{
+    assert(num >= 0 && num < (int) pBits->storageSize * (int)sizeof(u4) * 8);
+
+    pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
+}
+
+/*
+ * Mark all bits bit as "clear".
+ */
+void dvmClearAllBits(BitVector* pBits)
+{
+    int count = pBits->storageSize;
+    memset(pBits->storage, 0, count * sizeof(u4));
+}
+
+/*
+ * Determine whether or not the specified bit is set.
+ */
+bool dvmIsBitSet(const BitVector* pBits, int num)
+{
+    assert(num >= 0 && num < (int) pBits->storageSize * (int)sizeof(u4) * 8);
+
+    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)
+{
+    int word;
+    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;
+}
+
+/*
+ * Copy a whole vector to the other. Only do that when the both vectors have
+ * the same size and attribute.
+ */
+bool dvmCopyBitVector(BitVector *dest, const BitVector *src)
+{
+    if (dest->storageSize != src->storageSize ||
+        dest->expandable != src->expandable)
+        return false;
+    memcpy(dest->storage, src->storage, sizeof(u4) * dest->storageSize);
+    return true;
+}
+
+/*
+ * Intersect two bit vectores and merge the result on top of the pre-existing
+ * value in 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;
+
+    int i;
+    for (i = 0; i < dest->storageSize; i++) {
+        dest->storage[i] |= src1->storage[i] & src2->storage[i];
+    }
+    return true;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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 '.'.
+ */
+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 = 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 '.' are changed to '/'.
+ */
+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 = 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 = 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 = 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(void)
+{
+#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(void)
+{
+#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)
+{
+    const int minSleep = 10000;
+    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)\n",
+            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\n",
+            curDelay, (int) ((relStartTime + maxTotalSleep) - curTime));
+        curDelay = (int) ((relStartTime + maxTotalSleep) - curTime);
+    }
+
+    if (iteration == 0) {
+        LOGVV("exsl: yield\n");
+        sched_yield();
+    } else {
+        LOGVV("exsl: sleep for %d\n", 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) {
+        LOGW("Unable to get fd flags for fd %d\n", fd);
+        return false;
+    }
+    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
+        LOGW("Unable to set close-on-exec for fd %d\n", 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 size, int prot, const char *name) {
+    void *base;
+    int fd, ret;
+
+    size = ALIGN_UP_TO_PAGE_SIZE(size);
+    fd = ashmem_create_region(name, size);
+    if (fd == -1) {
+        return NULL;
+    }
+    base = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0);
+    ret = close(fd);
+    if (base == MAP_FAILED) {
+        return NULL;
+    }
+    if (ret == -1) {
+        return NULL;
+    }
+    return base;
+}
+
+/* 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;
+}
diff --git a/vm/Misc.h b/vm/Misc.h
new file mode 100644
index 0000000..fd3c531
--- /dev/null
+++ b/vm/Misc.h
@@ -0,0 +1,328 @@
+/*
+ * 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
+#define _DALVIK_MISC
+
+#include "Inlines.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.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.
+ */
+typedef enum { kHexDumpLocal, kHexDumpMem } HexDumpMode;
+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
+}
+
+/*
+ * 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).
+ */
+typedef struct DebugOutputTarget {
+    /* where to? */
+    enum {
+        kDebugTargetUnknown = 0,
+        kDebugTargetLog,
+        kDebugTargetFile,
+    } which;
+
+    /* additional bits */
+    union {
+        struct {
+            int priority;
+            const char* tag;
+        } log;
+        struct {
+            FILE* fp;
+        } file;
+    } data;
+} DebugOutputTarget;
+
+/*
+ * 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
+    ;
+
+
+/*
+ * Expanding bitmap, used for tracking resources.  Bits are numbered starting
+ * from zero.
+ *
+ * All operations on a BitVector are unsynchronized.
+ */
+typedef struct BitVector {
+    bool    expandable;     /* expand bitmap if we run out? */
+    int     storageSize;    /* current size, in 32-bit words */
+    u4*     storage;
+} BitVector;
+
+/* allocate a bit vector with enough space to hold "startBits" bits */
+BitVector* dvmAllocBitVector(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).
+ *
+ * dvmIsBitSet returns "true" if the bit is set.
+ */
+int dvmAllocBit(BitVector* pBits);
+bool dvmSetBit(BitVector* pBits, int num);
+void dvmClearBit(BitVector* pBits, int num);
+void dvmClearAllBits(BitVector* pBits);
+bool dvmIsBitSet(const BitVector* pBits, int num);
+
+/* count the number of bits that have been set */
+int dvmCountSetBits(const BitVector* pBits);
+
+/* copy one vector to the other compatible one */
+bool dvmCopyBitVector(BitVector *dest, const BitVector *src);
+
+/*
+ * Intersect two bit vectores and merge the result on top of the pre-existing
+ * value in the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+                            const BitVector *src2);
+
+#define kBitVectorGrowth    4   /* increase by 4 u4s when limit hit */
+
+
+/*
+ * 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 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 '.'.
+ */
+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 surrounde by "L" and ";", and all
+ * occurrences of '.' have been changed to '/'.
+ */
+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.
+ */
+void dvmAbort(void);
+
+#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);
+#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);
+
+/*
+ * 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);
+
+#endif /*_DALVIK_MISC*/
diff --git a/vm/Native.c b/vm/Native.c
new file mode 100644
index 0000000..1ebef2e
--- /dev/null
+++ b/vm/Native.c
@@ -0,0 +1,874 @@
+/*
+ * 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(void)
+{
+    gDvm.nativeLibs = dvmHashTableCreate(4, freeSharedLibEntry);
+    if (gDvm.nativeLibs == NULL)
+        return false;
+
+    return true;
+}
+
+/*
+ * Free up our tables.
+ */
+void dvmNativeShutdown(void)
+{
+    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;
+    void* func;
+
+    /*
+     * 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 */
+    func = dvmLookupInternalNativeMethod(method);
+    if (func != 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\n",
+                clazz->descriptor, method->name, desc);
+            free(desc);
+        }
+        if (dvmIsSynchronizedMethod(method)) {
+            LOGE("ERROR: internal-native can't be declared 'synchronized'\n");
+            LOGE("Failing on %s.%s\n", method->clazz->descriptor, method->name);
+            dvmAbort();     // harsh, but this is VM-internal problem
+        }
+        DalvikBridgeFunc dfunc = (DalvikBridgeFunc) func;
+        dvmSetNativeFunc((Method*) method, dfunc, NULL);
+        dfunc(args, pResult, method, self);
+        return;
+    }
+
+    /* now scan any DLLs we have loaded for JNI signatures */
+    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_LOGW() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        LOGW("No implementation found for native %s.%s %s\n",
+            clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    dvmThrowException("Ljava/lang/UnsatisfiedLinkError;", method->name);
+}
+
+
+/*
+ * ===========================================================================
+ *      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.)
+
+typedef enum OnLoadState {
+    kOnLoadPending = 0,     /* initial state, must be zero */
+    kOnLoadFailed,
+    kOnLoadOkay,
+} OnLoadState;
+
+/*
+ * We add one of these to the hash table for every library we load.  The
+ * hash is on the "pathName" field.
+ */
+typedef 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 */
+} SharedLib;
+
+/*
+ * (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;
+
+    LOGD("--- comparing %p '%s' %p '%s'\n",
+        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 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 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.
+         */
+        LOGI("threadid=%d: recursive native library load attempt (%s)\n",
+            self->threadId, pEntry->pathName);
+        return true;
+    }
+
+    LOGV("+++ retrieving %s OnLoad status\n", pEntry->pathName);
+    bool result;
+
+    dvmLockMutex(&pEntry->onLoadLock);
+    while (pEntry->onLoadResult == kOnLoadPending) {
+        LOGD("threadid=%d: waiting for %s OnLoad status\n",
+            self->threadId, pEntry->pathName);
+        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        pthread_cond_wait(&pEntry->onLoadCond, &pEntry->onLoadLock);
+        dvmChangeStatus(self, oldStatus);
+    }
+    if (pEntry->onLoadResult == kOnLoadOkay) {
+        LOGV("+++ earlier OnLoad(%s) okay\n", pEntry->pathName);
+        result = true;
+    } else {
+        LOGV("+++ earlier OnLoad(%s) failed\n", 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)
+        LOGD("Trying to load lib %s %p\n", 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) {
+            LOGW("Shared lib '%s' already opened by CL %p; can't open in %p\n",
+                pathName, pEntry->classLoader, classLoader);
+            return false;
+        }
+        if (verbose) {
+            LOGD("Shared lib '%s' already loaded in same CL %p\n",
+                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());
+        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) {
+        LOGI("WOW: we lost a race to add a shared lib (%s CL=%p)\n",
+            pathName, classLoader);
+        freeSharedLibEntry(pNewEntry);
+        return checkOnLoadResult(pActualEntry);
+    } else {
+        if (verbose)
+            LOGD("Added shared lib %s %p\n", pathName, classLoader);
+
+        bool result = true;
+        void* vonLoad;
+        int version;
+
+        vonLoad = dlsym(handle, "JNI_OnLoad");
+        if (vonLoad == NULL) {
+            LOGD("No JNI_OnLoad found in %s %p, skipping init\n",
+                pathName, classLoader);
+        } 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 = vonLoad;
+            Object* prevOverride = self->classLoaderOverride;
+
+            self->classLoaderOverride = classLoader;
+            oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+            LOGV("+++ calling JNI_OnLoad(%s)\n", pathName);
+            version = (*func)(gDvm.vmList, NULL);
+            dvmChangeStatus(self, oldStatus);
+            self->classLoaderOverride = prevOverride;
+
+            if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 &&
+                version != JNI_VERSION_1_6)
+            {
+                LOGW("JNI_OnLoad returned bad version (%d) in %s %p\n",
+                    version, pathName, classLoader);
+                /*
+                 * 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.
+                 */
+                result = false;
+            } else {
+                LOGV("+++ finished JNI_OnLoad %s\n", 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.
+         */
+
+        LOGD("Unregistering JNI method %s.%s:%s\n",
+            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 = 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)
+{
+    u2* utf16 = NULL;
+    char* mangle = NULL;
+    int charLen;
+
+    //LOGI("mangling '%s' %d\n", str, len);
+
+    assert(str[len] == '\0');
+
+    charLen = dvmUtf8Len(str);
+    utf16 = (u2*) malloc(sizeof(u2) * charLen);
+    if (utf16 == NULL)
+        goto bail;
+
+    dvmConvertUtf8ToUtf16(utf16, str);
+
+    /*
+     * Compute the length of the mangled string.
+     */
+    int i, mangleLen = 0;
+
+    for (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* cp;
+
+    mangle = (char*) malloc(mangleLen +1);
+    if (mangle == NULL)
+        goto bail;
+
+    for (i = 0, cp = mangle; 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';
+
+bail:
+    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) {
+        LOGV("+++ not scanning '%s' for '%s' (wrong CL)\n",
+            pLib->pathName, meth->name);
+        return 0;
+    } else
+        LOGV("+++ scanning '%s' for '%s'\n", 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;
+
+    LOGV("+++ calling dlsym(%s)\n", 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);
+
+        LOGV("+++ calling dlsym(%s)\n", mangleCMSig);
+        func = dlsym(pLib->handle, mangleCMSig);
+        if (func != NULL) {
+            LOGV("Found '%s' with dlsym\n", mangleCMSig);
+        }
+    } else {
+        LOGV("Found '%s' with dlsym\n", 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) {
+        LOGE("Unexpected init state: nativeLibs not ready\n");
+        dvmAbort();
+    }
+    return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib,
+        (void*) method);
+}
+
+
+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, "0x%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, "0x%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, ", ");
+    }
+}
+
+#define LOGI_NATIVE(...) LOG(LOG_INFO, LOG_TAG "-native", __VA_ARGS__)
+
+void dvmLogNativeMethodEntry(const Method* method, const u4* args)
+{
+    char thisString[32] = { 0 };
+    const u4* sp = args; // &args[method->registersSize - method->insSize];
+    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');
+    }
+
+    char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+    LOGI_NATIVE("-> %s.%s%s %s(%s)", method->clazz->descriptor, method->name,
+            signature, thisString, argsString);
+    free(signature);
+}
+
+void dvmLogNativeMethodExit(const Method* method, Thread* self,
+        const JValue returnValue)
+{
+    char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+    if (dvmCheckException(self)) {
+        Object* exception = dvmGetException(self);
+        LOGI_NATIVE("<- %s.%s%s threw %s", method->clazz->descriptor,
+                method->name, signature, exception->clazz->descriptor);
+    } else {
+        char returnValueString[128] = { 0 };
+        char returnType = method->shorty[0];
+        appendValue(returnType, returnValue,
+                returnValueString, sizeof(returnValueString), false);
+        LOGI_NATIVE("<- %s.%s%s returned %s", method->clazz->descriptor,
+                method->name, signature, returnValueString);
+    }
+    free(signature);
+}
diff --git a/vm/Native.h b/vm/Native.h
new file mode 100644
index 0000000..f60ccce
--- /dev/null
+++ b/vm/Native.h
@@ -0,0 +1,122 @@
+/*
+ * 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
+#define _DALVIK_NATIVE
+
+/*
+ * Method description; equivalent to a JNI struct.
+ */
+typedef struct DalvikNativeMethod {
+    const char* name;
+    const char* signature;
+    DalvikNativeFunc  fnPtr;
+} DalvikNativeMethod;
+
+/*
+ * All methods for one class.  The last "methodInfo" has a NULL "name".
+ */
+typedef struct DalvikNativeClass {
+    const char* classDescriptor;
+    const DalvikNativeMethod* methodInfo;
+    u4          classDescriptorHash;          /* initialized at runtime */
+} DalvikNativeClass;
+
+
+/* init/shutdown */
+bool dvmNativeStartup(void);
+void dvmNativeShutdown(void);
+
+
+/*
+ * Convert argc/argv into a function call.  This is platform-specific.
+ */
+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);
+//void dvmLoadNativeLibrary(StringObject* libNameObj, Object* classLoader);
+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)          (*(s8*)(&(_args)[_elem]))
+#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)
+{
+#if 0
+    union { u4 parts[2]; s8 whole; } conv;
+    conv.parts[0] = args[elem];
+    conv.parts[1] = args[elem+1];
+    return conv.whole;
+#else
+    /* with gcc's optimizer, memcpy() turns into simpler assignments */
+    s8 val;
+    memcpy(&val, &args[elem], 8);
+    return val;
+#endif
+}
+
+/*
+ * Used to implement -Xjnitrace.
+ */
+struct Thread;
+void dvmLogNativeMethodEntry(const Method* method, const u4* newFp);
+void dvmLogNativeMethodExit(const Method* method, struct Thread* self,
+        const JValue retval);
+
+#endif /*_DALVIK_NATIVE*/
diff --git a/vm/PointerSet.c b/vm/PointerSet.c
new file mode 100644
index 0000000..1d2e814
--- /dev/null
+++ b/vm/PointerSet.c
@@ -0,0 +1,273 @@
+/*
+ * 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 = calloc(1, sizeof(PointerSet));
+    if (pSet != NULL) {
+        if (initialSize > 0) {
+            pSet->list = malloc(sizeof(const 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\n", pSet, pSet->alloc);
+        newList = realloc(pSet->list, pSet->alloc * sizeof(const void*));
+        if (newList == NULL) {
+            LOGE("Failed expanding ptr set (alloc=%d)\n", 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]) {
+            //LOGD("nearby-1=%d %p, inserting %p at -1\n",
+            //    nearby-1, pSet->list[nearby-1], ptr);
+            nearby--;
+        } else if (ptr < pSet->list[nearby]) {
+            //LOGD("nearby=%d %p, inserting %p at +0\n",
+            //    nearby, pSet->list[nearby], ptr);
+        } else {
+            //LOGD("nearby+1=%d %p, inserting %p at +1\n",
+            //    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)
+{
+    int i;
+    for (i = 0; i < pSet->count; i++)
+        printf(" %p", pSet->list[i]);
+}
diff --git a/vm/PointerSet.h b/vm/PointerSet.h
new file mode 100644
index 0000000..ffc0635
--- /dev/null
+++ b/vm/PointerSet.h
@@ -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.
+ */
+/*
+ * Maintain an expanding set of unique pointer values.  The set is
+ * kept in sorted order.
+ */
+#ifndef _DALVIK_POINTERSET
+#define _DALVIK_POINTERSET
+
+struct PointerSet;   /* private */
+typedef struct PointerSet PointerSet;
+
+/*
+ * 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*/
diff --git a/vm/Profile.c b/vm/Profile.c
new file mode 100644
index 0000000..d5dcc36
--- /dev/null
+++ b/vm/Profile.c
@@ -0,0 +1,895 @@
+/*
+ * 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 <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
+ *
+ * Record format:
+ *  u1  thread ID
+ *  u4  method ID | method action
+ *  u4  time delta since start, in usec
+ *
+ * 32 bits of microseconds is 70 minutes.
+ *
+ * All values are stored in little-endian order.
+ */
+#define TRACE_REC_SIZE      9
+#define TRACE_MAGIC         0x574f4c53
+#define TRACE_HEADER_LEN    32
+
+#define FILL_PATTERN        0xeeeeeeee
+
+
+/*
+ * Get the wall-clock date/time, in usec.
+ */
+static inline u8 getTimeInUsec()
+{
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec * 1000000LL + tv.tv_usec;
+}
+
+/*
+ * Get the current time, in microseconds.
+ *
+ * This can mean one of two things.  In "global clock" mode, we get the
+ * same time across all threads.  If we use CLOCK_THREAD_CPUTIME_ID, we
+ * get a per-thread CPU usage timer.  The latter is better, but a bit
+ * more complicated to implement.
+ */
+static inline u8 getClock()
+{
+#if defined(HAVE_POSIX_CLOCKS)
+    if (!gDvm.profilerWallClock) {
+        struct timespec tm;
+
+        clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
+        if (!(tm.tv_nsec >= 0 && tm.tv_nsec < 1*1000*1000*1000)) {
+            LOGE("bad nsec: %ld\n", tm.tv_nsec);
+            dvmAbort();
+        }
+
+        return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
+    } else
+#endif
+    {
+        struct timeval tv;
+
+        gettimeofday(&tv, NULL);
+        return tv.tv_sec * 1000000LL + tv.tv_usec;
+    }
+}
+
+/*
+ * 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);
+}
+
+/*
+ * Boot-time init.
+ */
+bool dvmProfilingStartup(void)
+{
+    /*
+     * Initialize "dmtrace" method profiling.
+     */
+    memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
+    dvmInitMutex(&gDvm.methodTrace.startStopLock);
+    pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
+
+    ClassObject* clazz =
+        dvmFindClassNoInit("Ldalvik/system/VMDebug;", NULL);
+    assert(clazz != NULL);
+    gDvm.methodTrace.gcMethod =
+        dvmFindDirectMethodByDescriptor(clazz, "startGC", "()V");
+    gDvm.methodTrace.classPrepMethod =
+        dvmFindDirectMethodByDescriptor(clazz, "startClassPrep", "()V");
+    if (gDvm.methodTrace.gcMethod == NULL ||
+        gDvm.methodTrace.classPrepMethod == NULL)
+    {
+        LOGE("Unable to find startGC or startClassPrep\n");
+        return false;
+    }
+
+    assert(!dvmCheckException(dvmThreadSelf()));
+
+    /*
+     * Allocate storage for instruction counters.
+     */
+    gDvm.executedInstrCounts = (int*) malloc(kNumDalvikInstructions * sizeof(int));
+    if (gDvm.executedInstrCounts == NULL)
+        return false;
+    memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int));
+
+#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) {
+        LOGV("Unable to open /dev/qemu_trace\n");
+    } else {
+        gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
+                                      MAP_SHARED, fd, 0);
+        close(fd);
+        if (gDvm.emulatorTracePage == MAP_FAILED) {
+            LOGE("Unable to mmap /dev/qemu_trace\n");
+            gDvm.emulatorTracePage = NULL;
+        } else {
+            *(u4*) gDvm.emulatorTracePage = 0;
+        }
+    }
+#else
+    assert(gDvm.emulatorTracePage == NULL);
+#endif
+
+    return true;
+}
+
+/*
+ * Free up profiling resources.
+ */
+void dvmProfilingShutdown(void)
+{
+#ifdef UPDATE_MAGIC_PAGE
+    if (gDvm.emulatorTracePage != NULL)
+        munmap(gDvm.emulatorTracePage, SYSTEM_PAGE_SIZE);
+#endif
+    free(gDvm.executedInstrCounts);
+}
+
+/*
+ * Update the "active profilers" count.
+ *
+ * "count" should be +1 or -1.
+ */
+static void updateActiveProfilers(int count)
+{
+    int oldValue, newValue;
+
+    do {
+        oldValue = gDvm.activeProfilers;
+        newValue = oldValue + count;
+        if (newValue < 0) {
+            LOGE("Can't have %d active profilers\n", newValue);
+            dvmAbort();
+        }
+    } while (android_atomic_release_cas(oldValue, newValue,
+            &gDvm.activeProfilers) != 0);
+
+    LOGD("+++ active profiler count now %d\n", newValue);
+#if defined(WITH_JIT)
+    dvmCompilerStateRefresh();
+#endif
+}
+
+
+/*
+ * Reset the "cpuClockBase" field in all threads.
+ */
+static void resetCpuClockBase(void)
+{
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        thread->cpuClockBaseSet = false;
+        thread->cpuClockBase = 0;
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Dump the thread list to the specified file.
+ */
+static void dumpThreadList(FILE* fp)
+{
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        char* name = dvmGetThreadName(thread);
+
+        fprintf(fp, "%d\t%s\n", thread->threadId, name);
+        free(name);
+    }
+    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)
+{
+    MethodTraceState* state = &gDvm.methodTrace;
+
+    assert(bufferSize > 0);
+
+    dvmLockMutex(&state->startStopLock);
+    while (state->traceEnabled != 0) {
+        LOGI("TRACE start requested, but already in progress; stopping\n");
+        dvmUnlockMutex(&state->startStopLock);
+        dvmMethodTraceStop();
+        dvmLockMutex(&state->startStopLock);
+    }
+    updateActiveProfilers(1);
+    LOGI("TRACE STARTED: '%s' %dKB\n", 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) {
+        dvmThrowException("Ljava/lang/InternalError;", "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;
+            LOGE("Unable to open trace file '%s': %s\n",
+                traceFileName, strerror(err));
+            dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
+                "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 = getTimeInUsec();
+
+    /*
+     * Output the header.
+     */
+    memset(state->buf, 0, TRACE_HEADER_LEN);
+    storeIntLE(state->buf + 0, TRACE_MAGIC);
+    storeShortLE(state->buf + 4, TRACE_VERSION);
+    storeShortLE(state->buf + 6, TRACE_HEADER_LEN);
+    storeLongLE(state->buf + 8, state->startWhen);
+    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);
+    dvmUnlockMutex(&state->startStopLock);
+    return;
+
+fail:
+    updateActiveProfilers(-1);
+    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;
+    unsigned int methodVal;
+    Method* method;
+
+    while (ptr < end) {
+        methodVal = *(ptr+1) | (*(ptr+2) << 8) | (*(ptr+3) << 16)
+                    | (*(ptr+4) << 24);
+        method = (Method*) METHOD_ID(methodVal);
+
+        method->inProfile = true;
+        ptr += TRACE_REC_SIZE;
+    }
+}
+
+/*
+ * 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(void)
+{
+    u8 calStart, calElapsed;
+    int i;
+
+    calStart = getClock();
+    for (i = 1000 * 4; i > 0; i--) {
+        getClock();
+        getClock();
+        getClock();
+        getClock();
+        getClock();
+        getClock();
+        getClock();
+        getClock();
+    }
+
+    calElapsed = getClock() - calStart;
+    return (int) (calElapsed / (8*4));
+}
+
+/*
+ * Returns "true" if method tracing is currently active.
+ */
+bool dvmIsMethodTraceActive(void)
+{
+    const MethodTraceState* state = &gDvm.methodTrace;
+    return state->traceEnabled;
+}
+
+/*
+ * Stop method tracing.  We write the buffer to disk and generate a key
+ * file so we can interpret it.
+ */
+void dvmMethodTraceStop(void)
+{
+    MethodTraceState* state = &gDvm.methodTrace;
+    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 */
+        LOGD("TRACE stop requested, but not running\n");
+        dvmUnlockMutex(&state->startStopLock);
+        return;
+    } else {
+        updateActiveProfilers(-1);
+    }
+
+    /* compute elapsed time */
+    elapsed = getTimeInUsec() - 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;
+
+    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[1] | (scanPtr[2] << 8) | (scanPtr[3] << 16)
+                        | (scanPtr[4] << 24);
+            if (METHOD_ID(methodVal) == fillVal) {
+                u1* scanBase = state->buf + TRACE_HEADER_LEN;
+                LOGW("Found unfilled record at %d (of %d)\n",
+                    (scanPtr - scanBase) / TRACE_REC_SIZE,
+                    (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
+                finalCurOffset = scanPtr - state->buf;
+                break;
+            }
+
+            scanPtr += TRACE_REC_SIZE;
+        }
+    }
+
+    LOGI("TRACE STOPPED%s: writing %d records\n",
+        state->overflow ? " (NOTE: overflowed buffer)" : "",
+        (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
+    if (gDvm.debuggerActive) {
+        LOGW("WARNING: a debugger is active; method-tracing results "
+             "will be skewed\n");
+    }
+
+    /*
+     * 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 */
+            LOGE("Unable to open memstream\n");
+            dvmAbort();
+        }
+    }
+    assert(state->traceFile != NULL);
+
+    fprintf(state->traceFile, "%cversion\n", TOKEN_CHAR);
+    fprintf(state->traceFile, "%d\n", TRACE_VERSION);
+    fprintf(state->traceFile, "data-file-overflow=%s\n",
+        state->overflow ? "true" : "false");
+#if defined(HAVE_POSIX_CLOCKS)
+    if (!gDvm.profilerWallClock) {
+        fprintf(state->traceFile, "clock=thread-cpu\n");
+    } else
+#endif
+    {
+        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) / TRACE_REC_SIZE);
+    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;
+            LOGE("trace fwrite(%d) failed: %s\n",
+                finalCurOffset, strerror(err));
+            dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
+                "Trace data write failed: %s", strerror(err));
+        }
+    }
+
+    /* done! */
+    free(state->buf);
+    state->buf = NULL;
+    fclose(state->traceFile);
+    state->traceFile = NULL;
+
+    /* wake any threads that were waiting for profiling to complete */
+    dvmBroadcastCond(&state->threadExitCond);
+    dvmUnlockMutex(&state->startStopLock);
+}
+
+
+/*
+ * 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)
+{
+    MethodTraceState* state = &gDvm.methodTrace;
+    u4 clockDiff, methodVal;
+    int oldOffset, newOffset;
+    u1* ptr;
+
+    /*
+     * We can only access the per-thread CPU clock from within the
+     * thread, so we have to initialize the base time on the first use.
+     * (Looks like pthread_getcpuclockid(thread, &id) will do what we
+     * want, but it doesn't appear to be defined on the device.)
+     */
+    if (!self->cpuClockBaseSet) {
+        self->cpuClockBase = getClock();
+        self->cpuClockBaseSet = true;
+        //LOGI("thread base id=%d 0x%llx\n",
+        //    self->threadId, self->cpuClockBase);
+    }
+
+    /*
+     * Advance "curOffset" atomically.
+     */
+    do {
+        oldOffset = state->curOffset;
+        newOffset = oldOffset + TRACE_REC_SIZE;
+        if (newOffset > state->bufferSize) {
+            state->overflow = true;
+            return;
+        }
+    } while (android_atomic_release_cas(oldOffset, newOffset,
+            &state->curOffset) != 0);
+
+    //assert(METHOD_ACTION((u4) method) == 0);
+
+    u8 now = getClock();
+    clockDiff = (u4) (now - self->cpuClockBase);
+
+    methodVal = METHOD_COMBINE((u4) method, action);
+
+    /*
+     * Write data into "oldOffset".
+     */
+    ptr = state->buf + oldOffset;
+    *ptr++ = self->threadId;
+    *ptr++ = (u1) methodVal;
+    *ptr++ = (u1) (methodVal >> 8);
+    *ptr++ = (u1) (methodVal >> 16);
+    *ptr++ = (u1) (methodVal >> 24);
+    *ptr++ = (u1) clockDiff;
+    *ptr++ = (u1) (clockDiff >> 8);
+    *ptr++ = (u1) (clockDiff >> 16);
+    *ptr++ = (u1) (clockDiff >> 24);
+}
+
+/*
+ * 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 Java 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 Java 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)\n",
+        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(void)
+{
+    TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTrace.gcMethod);
+}
+void dvmMethodTraceGCEnd(void)
+{
+    TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTrace.gcMethod);
+}
+
+/*
+ * The class loader calls this when it's loading or initializing a class.
+ */
+void dvmMethodTraceClassPrepBegin(void)
+{
+    TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTrace.classPrepMethod);
+}
+void dvmMethodTraceClassPrepEnd(void)
+{
+    TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTrace.classPrepMethod);
+}
+
+
+/*
+ * Enable emulator trace info.
+ */
+void dvmEmulatorTraceStart(void)
+{
+    /* If we could not map the emulator trace page, then do not enable tracing */
+    if (gDvm.emulatorTracePage == NULL)
+        return;
+
+    updateActiveProfilers(1);
+
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.emulatorTraceEnableCount++;
+    if (gDvm.emulatorTraceEnableCount == 1)
+        LOGD("--- emulator method traces enabled\n");
+}
+
+/*
+ * Disable emulator trace info.
+ */
+void dvmEmulatorTraceStop(void)
+{
+    if (gDvm.emulatorTraceEnableCount == 0) {
+        LOGE("ERROR: emulator tracing not enabled\n");
+        return;
+    }
+    updateActiveProfilers(-1);
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.emulatorTraceEnableCount--;
+    if (gDvm.emulatorTraceEnableCount == 0)
+        LOGD("--- emulator method traces disabled\n");
+}
+
+
+/*
+ * Start instruction counting.
+ */
+void dvmStartInstructionCounting()
+{
+    updateActiveProfilers(1);
+    /* in theory we should make this an atomic inc; in practice not important */
+    gDvm.instructionCountEnableCount++;
+}
+
+/*
+ * Start instruction counting.
+ */
+void dvmStopInstructionCounting()
+{
+    if (gDvm.instructionCountEnableCount == 0) {
+        LOGE("ERROR: instruction counting not enabled\n");
+        dvmAbort();
+    }
+    updateActiveProfilers(-1);
+    gDvm.instructionCountEnableCount--;
+}
+
+
+/*
+ * Start alloc counting.  Note this doesn't affect the "active profilers"
+ * count, since the interpreter loop is not involved.
+ */
+void dvmStartAllocCounting(void)
+{
+    gDvm.allocProf.enabled = true;
+}
+
+/*
+ * Stop alloc counting.
+ */
+void dvmStopAllocCounting(void)
+{
+    gDvm.allocProf.enabled = false;
+}
diff --git a/vm/Profile.h b/vm/Profile.h
new file mode 100644
index 0000000..08bbf61
--- /dev/null
+++ b/vm/Profile.h
@@ -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.
+ */
+
+/*
+ * Android's method call profiling goodies.
+ */
+#ifndef _DALVIK_PROFILE
+#define _DALVIK_PROFILE
+
+#ifndef NOT_VM      /* for utilities that sneakily include this file */
+
+#include <stdio.h>
+
+/* External allocations are hackish enough that it's worthwhile
+ * separating them for possible removal later.
+ */
+#define PROFILE_EXTERNAL_ALLOCATIONS 1
+
+struct Thread;      // extern
+
+
+/* boot init */
+bool dvmProfilingStartup(void);
+void dvmProfilingShutdown(void);
+
+/*
+ * Method trace state.  This is currently global.  In theory we could make
+ * most of this per-thread.
+ */
+typedef struct MethodTraceState {
+    /* these are set during VM init */
+    Method* gcMethod;
+    Method* classPrepMethod;
+
+    /* active state */
+    pthread_mutex_t startStopLock;
+    pthread_cond_t  threadExitCond;
+    FILE*   traceFile;
+    bool    directToDdms;
+    int     bufferSize;
+    int     flags;
+
+    int     traceEnabled;
+    u1*     buf;
+    volatile int curOffset;
+    u8      startWhen;
+    int     overflow;
+} MethodTraceState;
+
+/*
+ * Memory allocation profiler state.  This is used both globally and
+ * per-thread.
+ *
+ * If you add a field here, zero it out in dvmStartAllocCounting().
+ */
+typedef struct AllocProfState {
+    bool    enabled;            // is allocation tracking enabled?
+
+    int     allocCount;         // #of objects allocated
+    int     allocSize;          // cumulative size of objects
+
+    int     failedAllocCount;   // #of times an allocation failed
+    int     failedAllocSize;    // cumulative size of failed allocations
+
+    int     freeCount;          // #of objects freed
+    int     freeSize;           // cumulative size of freed objects
+
+    int     gcCount;            // #of times an allocation triggered a GC
+
+    int     classInitCount;     // #of initialized classes
+    u8      classInitTime;      // cumulative time spent in class init (nsec)
+
+#if PROFILE_EXTERNAL_ALLOCATIONS
+    int     externalAllocCount; // #of calls to dvmTrackExternalAllocation()
+    int     externalAllocSize;  // #of bytes passed to ...ExternalAllocation()
+
+    int     failedExternalAllocCount; // #of times an allocation failed
+    int     failedExternalAllocSize;  // cumulative size of failed allocations
+
+    int     externalFreeCount;  // #of calls to dvmTrackExternalFree()
+    int     externalFreeSize;   // #of bytes passed to ...ExternalFree()
+#endif  // PROFILE_EXTERNAL_ALLOCATIONS
+} AllocProfState;
+
+
+/*
+ * Start/stop method tracing.
+ */
+void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
+        int flags, bool directToDdms);
+bool dvmIsMethodTraceActive(void);
+void dvmMethodTraceStop(void);
+
+/*
+ * Start/stop emulator tracing.
+ */
+void dvmEmulatorTraceStart(void);
+void dvmEmulatorTraceStop(void);
+
+/*
+ * Start/stop Dalvik instruction counting.
+ */
+void dvmStartInstructionCounting();
+void dvmStopInstructionCounting();
+
+/*
+ * Bit flags for dvmMethodTraceStart "flags" argument.  These must match
+ * the values in android.os.Debug.
+ */
+enum {
+    TRACE_ALLOC_COUNTS      = 0x01,
+};
+
+/*
+ * Call these when a method enters or exits.
+ */
+#define TRACE_METHOD_ENTER(_self, _method)                                 \
+    do {                                                                    \
+        if (gDvm.activeProfilers != 0) {                                    \
+            if (gDvm.methodTrace.traceEnabled)                              \
+                dvmMethodTraceAdd(_self, _method, METHOD_TRACE_ENTER);      \
+            if (gDvm.emulatorTraceEnableCount != 0)                         \
+                dvmEmitEmulatorTrace(_method, METHOD_TRACE_ENTER);          \
+        }                                                                   \
+    } while(0);
+#define TRACE_METHOD_EXIT(_self, _method)                                  \
+    do {                                                                    \
+        if (gDvm.activeProfilers != 0) {                                    \
+            if (gDvm.methodTrace.traceEnabled)                              \
+                dvmMethodTraceAdd(_self, _method, METHOD_TRACE_EXIT);       \
+            if (gDvm.emulatorTraceEnableCount != 0)                         \
+                dvmEmitEmulatorTrace(_method, METHOD_TRACE_EXIT);           \
+        }                                                                   \
+    } while(0);
+#define TRACE_METHOD_UNROLL(_self, _method)                                \
+    do {                                                                    \
+        if (gDvm.activeProfilers != 0) {                                    \
+            if (gDvm.methodTrace.traceEnabled)                              \
+                dvmMethodTraceAdd(_self, _method, METHOD_TRACE_UNROLL);     \
+            if (gDvm.emulatorTraceEnableCount != 0)                         \
+                dvmEmitEmulatorTrace(_method, METHOD_TRACE_UNROLL);         \
+        }                                                                   \
+    } while(0);
+
+void dvmMethodTraceAdd(struct Thread* self, const Method* method, int action);
+void dvmEmitEmulatorTrace(const Method* method, int action);
+
+void dvmMethodTraceGCBegin(void);
+void dvmMethodTraceGCEnd(void);
+void dvmMethodTraceClassPrepBegin(void);
+void dvmMethodTraceClassPrepEnd(void);
+
+/*
+ * Start/stop alloc counting.
+ */
+void dvmStartAllocCounting(void);
+void dvmStopAllocCounting(void);
+
+#endif
+
+
+/*
+ * Enumeration for the two "action" bits.
+ */
+enum {
+    METHOD_TRACE_ENTER = 0x00,      // method entry
+    METHOD_TRACE_EXIT = 0x01,       // method exit
+    METHOD_TRACE_UNROLL = 0x02,     // method exited by exception unrolling
+    // 0x03 currently unused
+};
+
+#define TOKEN_CHAR      '*'
+#define TRACE_VERSION   1
+
+/*
+ * Common definitions, shared with the dump tool.
+ */
+#define METHOD_ACTION_MASK      0x03            /* two bits */
+#define METHOD_ID(_method)      ((_method) & (~METHOD_ACTION_MASK))
+#define METHOD_ACTION(_method)  (((unsigned int)(_method)) & METHOD_ACTION_MASK)
+#define METHOD_COMBINE(_method, _action)    ((_method) | (_action))
+
+#endif /*_DALVIK_PROFILE*/
diff --git a/vm/Properties.c b/vm/Properties.c
new file mode 100644
index 0000000..288085b
--- /dev/null
+++ b/vm/Properties.c
@@ -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.
+ */
+/*
+ * Set up values for System.getProperties().
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <sys/utsname.h>
+#include <limits.h>
+#include <unistd.h>
+
+/*
+ * Create some storage for properties read from the command line.
+ */
+bool dvmPropertiesStartup(int maxProps)
+{
+    gDvm.maxProps = maxProps;
+    if (maxProps > 0) {
+        gDvm.propList = (char**) malloc(maxProps * sizeof(char*));
+        if (gDvm.propList == NULL)
+            return false;
+    }
+    gDvm.numProps = 0;
+
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmPropertiesShutdown(void)
+{
+    int i;
+
+    for (i = 0; i < gDvm.numProps; i++)
+        free(gDvm.propList[i]);
+    free(gDvm.propList);
+    gDvm.propList = NULL;
+}
+
+/*
+ * Add a property specified on the command line.  "argStr" has the form
+ * "name=value".  "name" must have nonzero length.
+ *
+ * Returns "true" if argStr appears valid.
+ */
+bool dvmAddCommandLineProperty(const char* argStr)
+{
+    char* mangle;
+    char* equals;
+
+    mangle = strdup(argStr);
+    equals = strchr(mangle, '=');
+    if (equals == NULL || equals == mangle) {
+        free(mangle);
+        return false;
+    }
+    *equals = '\0';
+
+    assert(gDvm.numProps < gDvm.maxProps);
+    gDvm.propList[gDvm.numProps++] = mangle;
+
+    return true;
+}
+
+
+/*
+ * Find the "put" method for this class.
+ *
+ * Returns NULL and throws an exception if not found.
+ */
+static Method* getPut(ClassObject* clazz)
+{
+    Method* put;
+
+    put = dvmFindVirtualMethodHierByDescriptor(clazz, "setProperty",
+            "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
+    if (put == NULL) {
+        dvmThrowException("Ljava/lang/RuntimeException;",
+            "could not find setProperty(String,String) in Properties");
+        /* fall through to return */
+    }
+    return put;
+}
+
+/*
+ * Set the value of the property.
+ */
+static void setProperty(Object* propObj, Method* put, const char* key,
+    const char* value)
+{
+    StringObject* keyStr;
+    StringObject* valueStr;
+
+    if (value == NULL) {
+        /* unclear what to do; probably want to create prop w/ empty string */
+        value = "";
+    }
+
+    keyStr = dvmCreateStringFromCstr(key);
+    valueStr = dvmCreateStringFromCstr(value);
+    if (keyStr == NULL || valueStr == NULL) {
+        LOGW("setProperty string creation failed\n");
+        goto bail;
+    }
+
+    JValue unused;
+    dvmCallMethod(dvmThreadSelf(), put, propObj, &unused, keyStr, valueStr);
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) keyStr, NULL);
+    dvmReleaseTrackedAlloc((Object*) valueStr, NULL);
+}
+
+/*
+ * Create the VM-default system properties.
+ *
+ * We can do them here, or do them in interpreted code with lots of native
+ * methods to get bits and pieces.  This is a bit smaller.
+ */
+void dvmCreateDefaultProperties(Object* propObj)
+{
+    Method* put = getPut(propObj->clazz);
+
+    if (put == NULL)
+        return;
+
+    struct utsname info;
+    uname(&info);
+
+    /* constant strings that are used multiple times below */
+    const char *projectUrl = "http://www.android.com/";
+    const char *projectName = "The Android Project";
+
+    /*
+     * These are listed in the docs.
+     */
+
+    setProperty(propObj, put, "java.boot.class.path", gDvm.bootClassPathStr);
+    setProperty(propObj, put, "java.class.path", gDvm.classPathStr);
+    setProperty(propObj, put, "java.class.version", "46.0");
+    setProperty(propObj, put, "java.compiler", "");
+    setProperty(propObj, put, "java.ext.dirs", "");
+
+    if (getenv("JAVA_HOME") != NULL) {
+        setProperty(propObj, put, "java.home", getenv("JAVA_HOME"));
+    } else {
+        setProperty(propObj, put, "java.home", "/system");
+    }
+
+    setProperty(propObj, put, "java.io.tmpdir", "/tmp");
+    setProperty(propObj, put, "java.library.path", getenv("LD_LIBRARY_PATH"));
+
+    setProperty(propObj, put, "java.net.preferIPv6Addresses", "true");
+
+    setProperty(propObj, put, "java.vendor", projectName);
+    setProperty(propObj, put, "java.vendor.url", projectUrl);
+    setProperty(propObj, put, "java.version", "0");
+    setProperty(propObj, put, "java.vm.name", "Dalvik");
+    setProperty(propObj, put, "java.vm.specification.name",
+            "Dalvik Virtual Machine Specification");
+    setProperty(propObj, put, "java.vm.specification.vendor", projectName);
+    setProperty(propObj, put, "java.vm.specification.version", "0.9");
+    setProperty(propObj, put, "java.vm.vendor", projectName);
+
+    char tmpBuf[64];
+    sprintf(tmpBuf, "%d.%d.%d",
+        DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+    setProperty(propObj, put, "java.vm.version", tmpBuf);
+
+    setProperty(propObj, put, "java.specification.name",
+            "Dalvik Core Library");
+    setProperty(propObj, put, "java.specification.vendor", projectName);
+    setProperty(propObj, put, "java.specification.version", "0.9");
+
+    setProperty(propObj, put, "os.arch", info.machine);
+    setProperty(propObj, put, "os.name", info.sysname);
+    setProperty(propObj, put, "os.version", info.release);
+    setProperty(propObj, put, "user.home", getenv("HOME"));
+    setProperty(propObj, put, "user.name", getenv("USER"));
+
+    char path[PATH_MAX];
+    setProperty(propObj, put, "user.dir", getcwd(path, sizeof(path)));
+
+    setProperty(propObj, put, "file.separator", "/");
+    setProperty(propObj, put, "line.separator", "\n");
+    setProperty(propObj, put, "path.separator", ":");
+
+    /*
+     * These show up elsewhere, so do them here too.
+     */
+    setProperty(propObj, put, "java.runtime.name", "Android Runtime");
+    setProperty(propObj, put, "java.runtime.version", "0.9");
+    setProperty(propObj, put, "java.vm.vendor.url", projectUrl);
+
+    setProperty(propObj, put, "file.encoding", "UTF-8");
+    setProperty(propObj, put, "user.language", "en");
+    setProperty(propObj, put, "user.region", "US");
+
+    /*
+     * These are unique to Android/Dalvik.
+     */
+    setProperty(propObj, put, "android.vm.dexfile", "true");
+}
+
+/*
+ * Add anything specified on the command line.
+ */
+void dvmSetCommandLineProperties(Object* propObj)
+{
+    Method* put = getPut(propObj->clazz);
+    int i;
+
+    if (put == NULL)
+        return;
+
+    for (i = 0; i < gDvm.numProps; i++) {
+        const char* value;
+
+        /* value starts after the end of the key string */
+        for (value = gDvm.propList[i]; *value != '\0'; value++)
+            ;
+        setProperty(propObj, put, gDvm.propList[i], value+1);
+    }
+}
+
+/*
+ * Get a property by calling System.getProperty(key).
+ *
+ * Returns a newly-allocated string, or NULL on failure or key not found.
+ * (Unexpected failures will also raise an exception.)
+ */
+char* dvmGetProperty(const char* key)
+{
+    ClassObject* system;
+    Method* getProp;
+    StringObject* keyObj = NULL;
+    StringObject* valueObj;
+    char* result = NULL;
+
+    assert(key != NULL);
+
+    system = dvmFindSystemClass("Ljava/lang/System;");
+    if (system == NULL)
+        goto bail;
+
+    getProp = dvmFindDirectMethodByDescriptor(system, "getProperty",
+        "(Ljava/lang/String;)Ljava/lang/String;");
+    if (getProp == NULL) {
+        LOGW("Could not find getProperty(String) in java.lang.System\n");
+        goto bail;
+    }
+
+    keyObj = dvmCreateStringFromCstr(key);
+    if (keyObj == NULL)
+        goto bail;
+
+    JValue val;
+    dvmCallMethod(dvmThreadSelf(), getProp, NULL, &val, keyObj);
+    valueObj = (StringObject*) val.l;
+    if (valueObj == NULL)
+        goto bail;
+
+    result = dvmCreateCstrFromString(valueObj);
+    /* fall through with result */
+
+bail:
+    dvmReleaseTrackedAlloc((Object*)keyObj, NULL);
+    return result;
+}
diff --git a/vm/Properties.h b/vm/Properties.h
new file mode 100644
index 0000000..f7f2f03
--- /dev/null
+++ b/vm/Properties.h
@@ -0,0 +1,37 @@
+/*
+ * 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 System.getProperties().
+ */
+#ifndef _DALVIK_PROPERTIES
+#define _DALVIK_PROPERTIES
+
+/*
+ * Initialization.
+ */
+bool dvmPropertiesStartup(int maxProps);
+void dvmPropertiesShutdown(void);
+
+/* add "-D" option to list */
+bool dvmAddCommandLineProperty(const char* argStr);
+
+/* called during property initialization */
+void dvmCreateDefaultProperties(Object* propObj);
+void dvmSetCommandLineProperties(Object* propObj);
+
+char* dvmGetProperty(const char* key);
+
+#endif /*_DALVIK_PROPERTIES*/
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.c b/vm/RawDexFile.c
new file mode 100644
index 0000000..3402ff4
--- /dev/null
+++ b/vm/RawDexFile.c
@@ -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.
+ */
+/*
+ * Open an unoptimized DEX file.
+ */
+#include "Dalvik.h"
+
+/*
+ * Open an unoptimized DEX file.  This finds the optimized version in the
+ * cache, constructing it if necessary.
+ */
+int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
+    RawDexFile** ppRawDexFile, bool isBootstrap)
+{
+    // TODO - should be very similar to what JarFile does
+    return -1;
+}
+
+/*
+ * 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..b7c5b33
--- /dev/null
+++ b/vm/RawDexFile.h
@@ -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.
+ */
+/*
+ * 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
+#define _DALVIK_RAWDEXFILE
+
+/*
+ * Structure representing a "raw" DEX file, in its unswapped unoptimized
+ * state.
+ */
+typedef struct RawDexFile {
+    char*       cacheFileName;
+    DvmDex*     pDvmDex;
+} RawDexFile;
+
+/*
+ * 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);
+
+/*
+ * 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*/
diff --git a/vm/ReconfigureDvm.mk b/vm/ReconfigureDvm.mk
new file mode 100644
index 0000000..13138f8
--- /dev/null
+++ b/vm/ReconfigureDvm.mk
@@ -0,0 +1,36 @@
+# 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)
+dvm_arch := $(TARGET_ARCH)
+dvm_arch_variant := $(TARGET_ARCH_VARIANT)
+dvm_simulator := $(TARGET_SIMULATOR)
+
+include $(LOCAL_PATH)/Dvm.mk
+
+LOCAL_SHARED_LIBRARIES += liblog libcutils libnativehelper libz
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libdl
+endif  # !TARGET_SIMULATOR
+
+LOCAL_STATIC_LIBRARIES += libdex
+
+# Don't prelink by default
+LOCAL_PRELINK_MODULE := false
+
+# Don't install on any build by default
+LOCAL_MODULE_TAGS := optional
diff --git a/vm/ReferenceTable.c b/vm/ReferenceTable.c
new file mode 100644
index 0000000..8984d5f
--- /dev/null
+++ b/vm/ReferenceTable.c
@@ -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.
+ */
+
+/*
+ * 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(dvmIsValidObject(obj));
+    assert(obj != NULL);
+    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) {
+            LOGW("ReferenceTable overflow (max=%d)\n", 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) {
+            LOGE("Unable to expand ref table (from %d to %d %d-byte entries)\n",
+                pRef->allocEntries, newSize, sizeof(Object*));
+            return false;
+        }
+        LOGVV("Growing %p from %d to %d\n", 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*));
+        //LOGV("LREF delete %p, shift %d down\n", obj, moveCount);
+    } else {
+        /* last entry, falls off the end */
+        //LOGV("LREF delete %p from end\n", obj);
+    }
+
+    return true;
+}
+
+/*
+ * 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)
+{
+    Object* obj1 = *((Object**) vobj1);
+    Object* obj2 = *((Object**) vobj2);
+
+    if (obj1 == NULL || obj2 == NULL)
+        return (u1*)obj1 - (u1*)obj2;
+
+    if (obj1->clazz != obj2->clazz) {
+        return (u1*)obj1->clazz - (u1*)obj2->clazz;
+    } else {
+        int size1 = dvmObjectSizeInHeap(obj1);
+        int size2 = dvmObjectSizeInHeap(obj2);
+        if (size1 != size2) {
+            return size1 - size2;
+        } else {
+            return (u1*)obj1 - (u1*)obj2;
+        }
+    }
+}
+
+/*
+ * Log an object with some additional info.
+ *
+ * Pass in the number of additional elements that are identical to or
+ * equivalent to the original.
+ */
+static void logObject(Object* obj, int size, int identical, int equiv)
+{
+    if (obj == NULL) {
+        LOGW("  NULL reference (count=%d)\n", equiv);
+        return;
+    }
+
+    /* handle "raw" dvmMalloc case */
+    const char* descriptor =
+        (obj->clazz != NULL) ? obj->clazz->descriptor : "(raw)";
+
+    if (identical + equiv != 0) {
+        LOGW("%5d of %s %dB (%d unique)\n", identical + equiv +1,
+            descriptor, size, equiv +1);
+    } else {
+        LOGW("%5d of %s %dB\n", identical + equiv +1, descriptor, size);
+    }
+}
+
+/*
+ * Dump the contents of a ReferenceTable to the log.
+ *
+ * The caller should lock any external sync before calling.
+ *
+ * (This was originally written to be tolerant of null entries in the table.
+ * I don't think that can happen anymore.)
+ */
+void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr)
+{
+    const int kLast = 10;
+    int count = dvmReferenceTableEntries(pRef);
+    Object** refs;
+    int i;
+
+    if (count == 0) {
+        LOGW("%s reference table has no entries\n", descr);
+        return;
+    }
+    assert(count > 0);
+
+    /*
+     * Dump the most recent N entries.
+     */
+    LOGW("Last %d entries in %s reference table:\n", kLast, descr);
+    refs = pRef->table;         // use unsorted list
+    int size;
+    int start = count - kLast;
+    if (start < 0)
+        start = 0;
+
+    for (i = start; i < count; i++) {
+        size = (refs[i] == NULL) ? 0 : dvmObjectSizeInHeap(refs[i]);
+        Object* ref = refs[i];
+        if (ref->clazz == gDvm.classJavaLangClass) {
+            ClassObject* clazz = (ClassObject*) ref;
+            LOGW("%5d: %p cls=%s '%s' (%d bytes)\n", i, ref,
+                (refs[i] == NULL) ? "-" : ref->clazz->descriptor,
+                clazz->descriptor, size);
+        } else if (ref->clazz == NULL) {
+            /* should only be possible right after a plain dvmMalloc() */
+            LOGW("%5d: %p cls=(raw) (%d bytes)\n", i, ref, size);
+        } else {
+            LOGW("%5d: %p cls=%s (%d bytes)\n", i, ref,
+                (refs[i] == NULL) ? "-" : ref->clazz->descriptor, size);
+        }
+    }
+
+    /*
+     * Make a copy of the table, and sort it.
+     */
+    Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
+    memcpy(tableCopy, pRef->table, sizeof(Object*) * count);
+    qsort(tableCopy, count, sizeof(Object*), compareObject);
+    refs = tableCopy;       // use sorted list
+
+    /*
+     * Dump uniquified table summary.  While we're at it, generate a
+     * cumulative total amount of pinned memory based on the unique entries.
+     */
+    LOGW("%s reference table summary (%d entries):\n", descr, count);
+    int equiv, identical, total;
+    total = equiv = identical = 0;
+    for (i = 1; i < count; i++) {
+        size = (refs[i-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[i-1]);
+
+        if (refs[i] == refs[i-1]) {
+            /* same reference, added more than once */
+            identical++;
+        } else if (refs[i]->clazz == refs[i-1]->clazz &&
+            (int) dvmObjectSizeInHeap(refs[i]) == size)
+        {
+            /* same class / size, different object */
+            total += size;
+            equiv++;
+        } else {
+            /* different class */
+            total += size;
+            logObject(refs[i-1], size, identical, equiv);
+            equiv = identical = 0;
+        }
+    }
+
+    /* handle the last entry (everything above outputs refs[i-1]) */
+    size = (refs[count-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[count-1]);
+    total += size;
+    logObject(refs[count-1], size, identical, equiv);
+
+    LOGW("Memory held directly by tracked refs is %d bytes\n", total);
+    free(tableCopy);
+}
diff --git a/vm/ReferenceTable.h b/vm/ReferenceTable.h
new file mode 100644
index 0000000..d6e2d70
--- /dev/null
+++ b/vm/ReferenceTable.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.
+ */
+
+/*
+ * Maintain a table of references.  Used for internal local references,
+ * JNI locals, JNI globals, and GC heap references.
+ *
+ * None of the table functions are synchronized.
+ */
+#ifndef _DALVIK_REFERENCETABLE
+#define _DALVIK_REFERENCETABLE
+
+/*
+ * 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.)
+ */
+typedef 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 */
+} ReferenceTable;
+
+/*
+ * 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.
+ */
+void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr);
+
+#endif /*_DALVIK_REFERENCETABLE*/
diff --git a/vm/SignalCatcher.c b/vm/SignalCatcher.c
new file mode 100644
index 0000000..d270b6f
--- /dev/null
+++ b/vm/SignalCatcher.c
@@ -0,0 +1,317 @@
+/*
+ * 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(void)
+{
+    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(void)
+{
+    gDvm.haltSignalCatcher = true;
+    if (gDvm.signalCatcherHandle == 0)      // not started yet
+        return;
+
+    pthread_kill(gDvm.signalCatcherHandle, SIGQUIT);
+
+    pthread_join(gDvm.signalCatcherHandle, NULL);
+    LOGV("signal catcher has shut down\n");
+}
+
+
+/*
+ * 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");
+    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(void)
+{
+    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);
+        dvmDumpAllThreadsEx(&target, true);
+    } else {
+        /* write to memory buffer */
+        FILE* memfp = open_memstream(&traceBuf, &traceLen);
+        if (memfp == NULL) {
+            LOGE("Unable to create memstream for stack traces\n");
+            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) {
+        dvmLockMutex(&gDvm.jniGlobalRefLock);
+        dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+        dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+    }
+    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.
+         */
+        int 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) {
+            LOGE("Unable to open stack trace file '%s': %s\n",
+                gDvm.stackTraceFile, strerror(errno));
+        } else {
+            ssize_t actual = write(fd, traceBuf, traceLen);
+            if (actual != (ssize_t) traceLen) {
+                LOGE("Failed to write stack traces to %s (%d of %zd): %s\n",
+                    gDvm.stackTraceFile, (int) actual, traceLen,
+                    strerror(errno));
+            } else {
+                LOGI("Wrote stack traces to '%s'\n", gDvm.stackTraceFile);
+            }
+            close(fd);
+        }
+
+        free(traceBuf);
+        dvmChangeStatus(dvmThreadSelf(), oldStatus);
+    }
+}
+
+/*
+ * Respond to a SIGUSR1 by forcing a GC.
+ */
+static void handleSigUsr1(void)
+{
+    LOGI("SIGUSR1 forcing GC (no HPROF)\n");
+    dvmCollectGarbage(false);
+}
+
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+/*
+ * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
+ * the code cache.
+ */
+static void handleSigUsr2(void)
+{
+    static int codeCacheResetCount = 0;
+    if ((--codeCacheResetCount & 7) == 0) {
+        gDvmJit.codeCacheFull = true;
+    } else {
+        dvmCompilerDumpStats();
+        /* Stress-test unchain all */
+        dvmJitUnchainAll();
+        LOGD("Send %d more signals to rest the code cache",
+             codeCacheResetCount & 7);
+    }
+}
+#endif
+
+/*
+ * Sleep in sigwait() until a signal arrives.
+ */
+static void* signalCatcherThreadStart(void* arg)
+{
+    Thread* self = dvmThreadSelf();
+    sigset_t mask;
+    int cc;
+
+    UNUSED_PARAMETER(arg);
+
+    LOGV("Signal catcher thread started (threadid=%d)\n", 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) {
+                //LOGV("sigwait: EINTR\n");
+                goto loop;
+            }
+            assert(!"bad result from sigwait");
+        }
+
+        if (!gDvm.haltSignalCatcher) {
+            LOGI("threadid=%d: reacting to signal %d\n",
+                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:
+            LOGE("unexpected signal %d\n", rcvd);
+            break;
+        }
+    }
+
+    return NULL;
+}
diff --git a/vm/SignalCatcher.h b/vm/SignalCatcher.h
new file mode 100644
index 0000000..ece052c
--- /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
+#define _DALVIK_SIGNALCATCHER
+
+bool dvmSignalCatcherStartup(void);
+void dvmSignalCatcherShutdown(void);
+
+#endif /*_DALVIK_SIGNALCATCHER*/
diff --git a/vm/StdioConverter.c b/vm/StdioConverter.c
new file mode 100644
index 0000000..6a4d845
--- /dev/null
+++ b/vm/StdioConverter.c
@@ -0,0 +1,286 @@
+/*
+ * 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
+
+/*
+ * Hold our replacement stdout/stderr.
+ */
+typedef struct StdPipes {
+    int stdoutPipe[2];
+    int stderrPipe[2];
+} StdPipes;
+
+#define kMaxLine    512
+
+/*
+ * Hold some data.
+ */
+typedef struct BufferedData {
+    char    buf[kMaxLine+1];
+    int     count;
+} BufferedData;
+
+// 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(void)
+{
+    StdPipes* pipeStorage;
+
+    gDvm.haltStdioConverter = false;
+
+    dvmInitMutex(&gDvm.stdioConverterLock);
+    pthread_cond_init(&gDvm.stdioConverterCond, NULL);
+
+    pipeStorage = (StdPipes*) malloc(sizeof(StdPipes));
+    if (pipeStorage == NULL)
+        return false;
+
+    if (pipe(pipeStorage->stdoutPipe) != 0) {
+        LOGW("pipe failed: %s\n", strerror(errno));
+        return false;
+    }
+    if (pipe(pipeStorage->stderrPipe) != 0) {
+        LOGW("pipe failed: %s\n", strerror(errno));
+        return false;
+    }
+
+    if (dup2(pipeStorage->stdoutPipe[1], kFilenoStdout) != kFilenoStdout) {
+        LOGW("dup2(1) failed: %s\n", strerror(errno));
+        return false;
+    }
+    close(pipeStorage->stdoutPipe[1]);
+    pipeStorage->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(pipeStorage->stderrPipe[1], kFilenoStderr) != kFilenoStderr) {
+        LOGW("dup2(2) failed: %d %s\n", errno, strerror(errno));
+        return false;
+    }
+    close(pipeStorage->stderrPipe[1]);
+    pipeStorage->stderrPipe[1] = -1;
+#endif
+
+
+    /*
+     * Create the thread.
+     */
+    dvmLockMutex(&gDvm.stdioConverterLock);
+
+    if (!dvmCreateInternalThread(&gDvm.stdioConverterHandle,
+                "Stdio Converter", stdioConverterThreadStart, pipeStorage))
+    {
+        free(pipeStorage);
+        return false;
+    }
+    /* new thread owns pipeStorage */
+
+    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(void)
+{
+    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);
+
+    LOGD("Joining stdio converter...\n");
+    pthread_join(gDvm.stdioConverterHandle, NULL);
+}
+
+/*
+ * Select on stdout/stderr pipes, waiting for activity.
+ *
+ * DO NOT use printf from here.
+ */
+static void* stdioConverterThreadStart(void* arg)
+{
+    StdPipes* pipeStorage = (StdPipes*) arg;
+    BufferedData* stdoutData;
+    BufferedData* stderrData;
+    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.
+     */
+    stdoutData = (BufferedData*) malloc(sizeof(*stdoutData));
+    stderrData = (BufferedData*) malloc(sizeof(*stderrData));
+    stdoutData->count = stderrData->count = 0;
+
+    /*
+     * Read until shutdown time.
+     */
+    while (!gDvm.haltStdioConverter) {
+        fd_set readfds;
+        int maxFd, fdCount;
+
+        FD_ZERO(&readfds);
+        FD_SET(pipeStorage->stdoutPipe[0], &readfds);
+        FD_SET(pipeStorage->stderrPipe[0], &readfds);
+        maxFd = MAX(pipeStorage->stdoutPipe[0], pipeStorage->stderrPipe[0]);
+
+        fdCount = select(maxFd+1, &readfds, NULL, NULL, NULL);
+
+        if (fdCount < 0) {
+            if (errno != EINTR) {
+                LOGE("select on stdout/stderr failed\n");
+                break;
+            }
+            LOGD("Got EINTR, ignoring\n");
+        } else if (fdCount == 0) {
+            LOGD("WEIRD: select returned zero\n");
+        } else {
+            bool err = false;
+            if (FD_ISSET(pipeStorage->stdoutPipe[0], &readfds)) {
+                err |= !readAndLog(pipeStorage->stdoutPipe[0], stdoutData,
+                    "stdout");
+            }
+            if (FD_ISSET(pipeStorage->stderrPipe[0], &readfds)) {
+                err |= !readAndLog(pipeStorage->stderrPipe[0], stderrData,
+                    "stderr");
+            }
+
+            /* probably EOF; give up */
+            if (err) {
+                LOGW("stdio converter got read error; shutting it down\n");
+                break;
+            }
+        }
+    }
+
+    close(pipeStorage->stdoutPipe[0]);
+    close(pipeStorage->stderrPipe[0]);
+
+    free(pipeStorage);
+    free(stdoutData);
+    free(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) {
+        LOGW("read %s: (%d,%d) failed (%d): %s\n",
+            tag, fd, want, (int)actual, strerror(errno));
+        return false;
+    } else {
+        //LOGI("read %s: %d at %d\n", 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';
+            //LOGW("GOT %d at %d '%s'\n", cp - start, start - data->buf, start);
+            LOG(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';
+        LOG(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..ffbf807
--- /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
+#define _DALVIK_STDOUTCONVERTER
+
+bool dvmStdioConverterStartup(void);
+void dvmStdioConverterShutdown(void);
+
+#endif /*_DALVIK_STDOUTCONVERTER*/
diff --git a/vm/Sync.c b/vm/Sync.c
new file mode 100644
index 0000000..b771b1c
--- /dev/null
+++ b/vm/Sync.c
@@ -0,0 +1,2129 @@
+/*
+ * 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.
+ */
+
+/*
+ * Fundamental synchronization mechanisms.
+ *
+ * The top part of the file has operations on "monitor" structs; the
+ * next part has the native calls on objects.
+ *
+ * The current implementation uses "thin locking" to avoid allocating
+ * an Object's full Monitor struct until absolutely necessary (i.e.,
+ * during contention or a call to wait()).
+ *
+ * TODO: make improvements to thin locking
+ * We may be able to improve performance and reduce memory requirements by:
+ *  - reverting to a thin lock once the Monitor is no longer necessary
+ *  - using a pool of monitor objects, with some sort of recycling scheme
+ *
+ * TODO: recycle native-level monitors when objects are garbage collected.
+ */
+#include "Dalvik.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#define LOG_THIN    LOGV
+
+#ifdef WITH_DEADLOCK_PREDICTION     /* fwd */
+static const char* kStartBanner =
+    "<-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#";
+static const char* kEndBanner =
+    "#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#->";
+
+/*
+ * Unsorted, expanding list of objects.
+ *
+ * This is very similar to PointerSet (which came into existence after this),
+ * but these are unsorted, uniqueness is not enforced by the "add" function,
+ * and the base object isn't allocated on the heap.
+ */
+typedef struct ExpandingObjectList {
+    u2          alloc;
+    u2          count;
+    Object**    list;
+} ExpandingObjectList;
+
+/* fwd */
+static void updateDeadlockPrediction(Thread* self, Object* obj);
+static void removeCollectedObject(Object* obj);
+static void expandObjClear(ExpandingObjectList* pList);
+#endif
+
+/*
+ * 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, ownerFileName may be NULL.
+     */
+    char*       ownerFileName;
+    u4          ownerLineNumber;
+
+#ifdef WITH_DEADLOCK_PREDICTION
+    /*
+     * Objects that have been locked immediately after this one in the
+     * past.  We use an expanding flat array, allocated on first use, to
+     * minimize allocations.  Deletions from the list, expected to be
+     * infrequent, are crunched down.
+     */
+    ExpandingObjectList historyChildren;
+
+    /*
+     * We also track parents.  This isn't strictly necessary, but it makes
+     * the cleanup at GC time significantly faster.
+     */
+    ExpandingObjectList historyParents;
+
+    /* used during cycle detection */
+    bool        historyMark;
+
+    /* stack trace, established the first time we locked the object */
+    int         historyStackDepth;
+    int*        historyRawStackTrace;
+#endif
+};
+
+
+/*
+ * Create and initialize a monitor.
+ */
+Monitor* dvmCreateMonitor(Object* obj)
+{
+    Monitor* mon;
+
+    mon = (Monitor*) calloc(1, sizeof(Monitor));
+    if (mon == NULL) {
+        LOGE("Unable to allocate monitor\n");
+        dvmAbort();
+    }
+    if (((u4)mon & 7) != 0) {
+        LOGE("Misaligned monitor: %p\n", mon);
+        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(void)
+{
+    Monitor* mon;
+    Monitor* nextMon;
+
+    mon = gDvm.monitorList;
+    while (mon != NULL) {
+        nextMon = mon->next;
+
+#ifdef WITH_DEADLOCK_PREDICTION
+        expandObjClear(&mon->historyChildren);
+        expandObjClear(&mon->historyParents);
+        free(mon->historyRawStackTrace);
+#endif
+        free(mon);
+        mon = nextMon;
+    }
+}
+
+/*
+ * Log some info about our monitors.
+ */
+void dvmDumpMonitorInfo(const char* msg)
+{
+#if QUIET_ZYGOTE_MONITOR
+    if (gDvm.zygote) {
+        return;
+    }
+#endif
+
+    int totalCount;
+    int liveCount;
+
+    totalCount = liveCount = 0;
+    Monitor* mon = gDvm.monitorList;
+    while (mon != NULL) {
+        totalCount++;
+        if (mon->obj != NULL)
+            liveCount++;
+        mon = mon->next;
+    }
+
+    LOGD("%s: monitor list has %d entries (%d live)\n",
+        msg, totalCount, liveCount);
+}
+
+/*
+ * 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 freeObjectMonitor(Object* obj)
+{
+    Monitor *mon;
+
+    assert(LW_SHAPE(obj->lock) == LW_SHAPE_FAT);
+
+#ifdef WITH_DEADLOCK_PREDICTION
+    if (gDvm.deadlockPredictMode != kDPOff)
+        removeCollectedObject(obj);
+#endif
+
+    mon = LW_MONITOR(obj->lock);
+    obj->lock = DVM_LOCK_INITIAL_THIN_VALUE;
+
+    /* 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);
+#ifdef WITH_DEADLOCK_PREDICTION
+    expandObjClear(&mon->historyChildren);
+    expandObjClear(&mon->historyParents);
+    free(mon->historyRawStackTrace);
+#endif
+    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);
+#ifdef WITH_DEADLOCK_PREDICTION
+    dvmDumpMonitorInfo("before monitor sweep");
+#endif
+    prev = &handle;
+    prev->next = curr = *mon;
+    while (curr != NULL) {
+        obj = curr->obj;
+        if (obj != NULL && (*isUnmarkedObject)(obj) != 0) {
+            prev->next = curr = curr->next;
+            freeObjectMonitor(obj);
+        } else {
+            prev = curr;
+            curr = curr->next;
+        }
+    }
+    *mon = handle.next;
+#ifdef WITH_DEADLOCK_PREDICTION
+    dvmDumpMonitorInfo("after monitor sweep");
+#endif
+}
+
+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], *selfName;
+    char *cp;
+    size_t len;
+    int fd;
+
+    saveArea = SAVEAREA_FROM_FP(self->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 main thread status, 5 bytes. */
+    bool isMainThread = (self->systemTid == getpid());
+    cp = logWriteInt(cp, isMainThread);
+
+    /* Emit self thread name string, <= 37 bytes. */
+    selfName = dvmGetThreadName(self);
+    cp = logWriteString(cp, selfName, strlen(selfName));
+    free(selfName);
+
+    /* 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 char* currentOwnerFileName = mon->ownerFileName;
+        u4 currentOwnerLineNumber = mon->ownerLineNumber;
+
+        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)) {
+                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) {
+        const StackSaveArea *saveArea;
+        const Method *meth;
+        mon->ownerLineNumber = 0;
+        if (self->curFrame == NULL) {
+            mon->ownerFileName = "no_frame";
+        } else if ((saveArea = SAVEAREA_FROM_FP(self->curFrame)) == NULL) {
+            mon->ownerFileName = "no_save_area";
+        } else if ((meth = saveArea->method) == NULL) {
+            mon->ownerFileName = "no_method";
+        } else {
+            u4 relativePc = saveArea->xtra.currentPc - saveArea->method->insns;
+            mon->ownerFileName = (char*) dvmGetMethodSourceFile(meth);
+            if (mon->ownerFileName == NULL) {
+                mon->ownerFileName = "no_method_file";
+            } else {
+                mon->ownerLineNumber = dvmLineNumFromPC(meth, relativePc);
+            }
+        }
+    }
+}
+
+/*
+ * 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->ownerFileName = "unlocked";
+            mon->ownerLineNumber = 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.
+         */
+        dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+                          "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.
+ */
+void absoluteTime(s8 msec, s4 nsec, struct timespec *ts)
+{
+    s8 endSec;
+
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+    clock_gettime(CLOCK_MONOTONIC, ts);
+#else
+    {
+        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) {
+        LOGV("NOTE: end time exceeds epoch\n");
+        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;
+    }
+}
+
+int dvmRelativeCondWait(pthread_cond_t* cond, pthread_mutex_t* mutex,
+                        s8 msec, s4 nsec)
+{
+    int ret;
+    struct timespec ts;
+    absoluteTime(msec, nsec, &ts);
+#if defined(HAVE_TIMEDWAIT_MONOTONIC)
+    ret = pthread_cond_timedwait_monotonic(cond, mutex, &ts);
+#else
+    ret = pthread_cond_timedwait(cond, mutex, &ts);
+#endif
+    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;
+    char *savedFileName;
+    u4 savedLineNumber;
+
+    assert(self != NULL);
+    assert(mon != NULL);
+
+    /* Make sure that we hold the lock. */
+    if (mon->owner != self) {
+        dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+            "object not locked by thread before wait()");
+        return;
+    }
+
+    /*
+     * Enforce the timeout range.
+     */
+    if (msec < 0 || nsec < 0 || nsec > 999999) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "timeout arguments out of range");
+        return;
+    }
+
+    /*
+     * Compute absolute wakeup time, if necessary.
+     */
+    if (msec == 0 && nsec == 0) {
+        timed = false;
+    } else {
+        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;
+    savedFileName = mon->ownerFileName;
+    mon->ownerFileName = NULL;
+    savedLineNumber = mon->ownerLineNumber;
+    mon->ownerLineNumber = 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 {
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+        ret = pthread_cond_timedwait_monotonic(&self->waitCond, &self->waitMutex, &ts);
+#else
+        ret = pthread_cond_timedwait(&self->waitCond, &self->waitMutex, &ts);
+#endif
+        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->ownerFileName = savedFileName;
+    mon->ownerLineNumber = savedLineNumber;
+    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)
+            dvmThrowException("Ljava/lang/InterruptedException;", 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) {
+        dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+            "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) {
+        dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+            "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;
+    useconds_t sleepDelay;
+    const useconds_t maxSleepDelay = 1 << 20;
+    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 {
+            LOG_THIN("(%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.
+             */
+            sleepDelay = 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 (sleepDelay == 0) {
+                            sched_yield();
+                            sleepDelay = 1000;
+                        } else {
+                            usleep(sleepDelay);
+                            if (sleepDelay < maxSleepDelay / 2) {
+                                sleepDelay *= 2;
+                            }
+                        }
+                    }
+                } else {
+                    /*
+                     * The thin lock was inflated by another thread.
+                     * Let the VM know we are no longer waiting and
+                     * try again.
+                     */
+                    LOG_THIN("(%d) lock %p surprise-fattened",
+                             threadId, &obj->lock);
+                    dvmChangeStatus(self, oldStatus);
+                    goto retry;
+                }
+            }
+            LOG_THIN("(%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);
+            LOG_THIN("(%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));
+    }
+#ifdef WITH_DEADLOCK_PREDICTION
+    /*
+     * See if we were allowed to grab the lock at this time.  We do it
+     * *after* acquiring the lock, rather than before, so that we can
+     * freely update the Monitor struct.  This seems counter-intuitive,
+     * but our goal is deadlock *prediction* not deadlock *prevention*.
+     * (If we actually deadlock, the situation is easy to diagnose from
+     * a thread dump, so there's no point making a special effort to do
+     * the checks before the lock is held.)
+     *
+     * This needs to happen before we add the object to the thread's
+     * monitor list, so we can tell the difference between first-lock and
+     * re-lock.
+     *
+     * It's also important that we do this while in THREAD_RUNNING, so
+     * that we don't interfere with cleanup operations in the GC.
+     */
+    if (gDvm.deadlockPredictMode != kDPOff) {
+        if (self->status != THREAD_RUNNING) {
+            LOGE("Bad thread status (%d) in DP\n", self->status);
+            dvmDumpThread(self, false);
+            dvmAbort();
+        }
+        assert(!dvmCheckException(self));
+        updateDeadlockPrediction(self, obj);
+        if (dvmCheckException(self)) {
+            /*
+             * If we're throwing an exception here, we need to free the
+             * lock.  We add the object to the thread's monitor list so the
+             * "unlock" code can remove it.
+             */
+            dvmAddToMonitorList(self, obj, false);
+            dvmUnlockObject(self, obj);
+            LOGV("--- unlocked, pending is '%s'\n",
+                dvmGetException(self)->clazz->descriptor);
+        }
+    }
+
+    /*
+     * Add the locked object, and the current stack trace, to the list
+     * held by the Thread object.  If deadlock prediction isn't on,
+     * don't capture the stack trace.
+     */
+    dvmAddToMonitorList(self, obj, gDvm.deadlockPredictMode != kDPOff);
+#elif defined(WITH_MONITOR_TRACKING)
+    /*
+     * Add the locked object to the list held by the Thread object.
+     */
+    dvmAddToMonitorList(self, obj, false);
+#endif
+}
+
+/*
+ * 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 = 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.
+                 */
+                obj->lock &= (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT);
+            } 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.
+             */
+            dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+                              "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;
+        }
+    }
+
+#ifdef WITH_MONITOR_TRACKING
+    /*
+     * Remove the object from the Thread's list.
+     */
+    dvmRemoveFromMonitorList(self, obj);
+#endif
+
+    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 = 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) {
+            dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+                "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);
+        LOG_THIN("(%d) lock %p fattened by wait() to count %d",
+                 self->threadId, &obj->lock, mon->lockCount);
+    }
+    mon = LW_MONITOR(obj->lock);
+    waitMonitor(self, mon, msec, nsec, interruptShouldThrow);
+}
+
+/*
+ * Object.notify().
+ */
+void dvmObjectNotify(Thread* self, Object *obj)
+{
+    u4 thin = 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) {
+            dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+                "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 = 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) {
+            dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+                "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(obj->clazz != gDvm.classJavaLangClass);
+        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 = DVM_LOCK_INITIAL_THIN_VALUE;
+            lock |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+            if (android_atomic_acquire_cas(
+                                (int32_t)DVM_LOCK_INITIAL_THIN_VALUE,
+                                (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;
+    }
+    LOGE("object %p has an unknown hash state %#x", obj, hashState);
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+    return 0;  /* Quiet the compiler. */
+}
+#endif  /* WITH_COPYING_GC */
+
+#ifdef WITH_DEADLOCK_PREDICTION
+/*
+ * ===========================================================================
+ *      Deadlock prediction
+ * ===========================================================================
+ */
+/*
+The idea is to predict the possibility of deadlock by recording the order
+in which monitors are acquired.  If we see an attempt to acquire a lock
+out of order, we can identify the locks and offending code.
+
+To make this work, we need to keep track of the locks held by each thread,
+and create history trees for each lock.  When a thread tries to acquire
+a new lock, we walk through the "history children" of the lock, looking
+for a match with locks the thread already holds.  If we find a match,
+it means the thread has made a request that could result in a deadlock.
+
+To support recursive locks, we always allow re-locking a currently-held
+lock, and maintain a recursion depth count.
+
+An ASCII-art example, where letters represent Objects:
+
+        A
+       /|\
+      / | \
+     B  |  D
+      \ |
+       \|
+        C
+
+The above is the tree we'd have after handling Object synchronization
+sequences "ABC", "AC", "AD".  A has three children, {B, C, D}.  C is also
+a child of B.  (The lines represent pointers between parent and child.
+Every node can have multiple parents and multiple children.)
+
+If we hold AC, and want to lock B, we recursively search through B's
+children to see if A or C appears.  It does, so we reject the attempt.
+(A straightforward way to implement it: add a link from C to B, then
+determine whether the graph starting at B contains a cycle.)
+
+If we hold AC and want to lock D, we would succeed, creating a new link
+from C to D.
+
+The lock history and a stack trace is attached to the Object's Monitor
+struct, which means we need to fatten every Object we lock (thin locking
+is effectively disabled).  If we don't need the stack trace we can
+avoid fattening the leaf nodes, only fattening objects that need to hold
+history trees.
+
+Updates to Monitor structs are only allowed for the thread that holds
+the Monitor, so we actually do most of our deadlock prediction work after
+the lock has been acquired.
+
+When an object with a monitor is GCed, we need to remove it from the
+history trees.  There are two basic approaches:
+ (1) For through the entire set of known monitors, search all child
+     lists for the object in question.  This is rather slow, resulting
+     in GC passes that take upwards of 10 seconds to complete.
+ (2) Maintain "parent" pointers in each node.  Remove the entries as
+     required.  This requires additional storage and maintenance for
+     every operation, but is significantly faster at GC time.
+For each GCed object, we merge all of the object's children into each of
+the object's parents.
+*/
+
+#if !defined(WITH_MONITOR_TRACKING)
+# error "WITH_DEADLOCK_PREDICTION requires WITH_MONITOR_TRACKING"
+#endif
+
+/*
+ * Clear out the contents of an ExpandingObjectList, freeing any
+ * dynamic allocations.
+ */
+static void expandObjClear(ExpandingObjectList* pList)
+{
+    if (pList->list != NULL) {
+        free(pList->list);
+        pList->list = NULL;
+    }
+    pList->alloc = pList->count = 0;
+}
+
+/*
+ * Get the number of objects currently stored in the list.
+ */
+static inline int expandBufGetCount(const ExpandingObjectList* pList)
+{
+    return pList->count;
+}
+
+/*
+ * Get the Nth entry from the list.
+ */
+static inline Object* expandBufGetEntry(const ExpandingObjectList* pList,
+    int i)
+{
+    return pList->list[i];
+}
+
+/*
+ * Add a new entry to the list.
+ *
+ * We don't check for or try to enforce uniqueness.  It's expected that
+ * the higher-level code does this for us.
+ */
+static void expandObjAddEntry(ExpandingObjectList* pList, Object* obj)
+{
+    if (pList->count == pList->alloc) {
+        /* time to expand */
+        Object** newList;
+
+        if (pList->alloc == 0)
+            pList->alloc = 4;
+        else
+            pList->alloc *= 2;
+        LOGVV("expanding %p to %d\n", pList, pList->alloc);
+        newList = realloc(pList->list, pList->alloc * sizeof(Object*));
+        if (newList == NULL) {
+            LOGE("Failed expanding DP object list (alloc=%d)\n", pList->alloc);
+            dvmAbort();
+        }
+        pList->list = newList;
+    }
+
+    pList->list[pList->count++] = obj;
+}
+
+/*
+ * Returns "true" if the element was successfully removed.
+ */
+static bool expandObjRemoveEntry(ExpandingObjectList* pList, Object* obj)
+{
+    int i;
+
+    for (i = pList->count-1; i >= 0; i--) {
+        if (pList->list[i] == obj)
+            break;
+    }
+    if (i < 0)
+        return false;
+
+    if (i != pList->count-1) {
+        /*
+         * The order of elements is not important, so we just copy the
+         * last entry into the new slot.
+         */
+        //memmove(&pList->list[i], &pList->list[i+1],
+        //    (pList->count-1 - i) * sizeof(pList->list[0]));
+        pList->list[i] = pList->list[pList->count-1];
+    }
+
+    pList->count--;
+    pList->list[pList->count] = (Object*) 0xdecadead;
+    return true;
+}
+
+/*
+ * Returns "true" if "obj" appears in the list.
+ */
+static bool expandObjHas(const ExpandingObjectList* pList, Object* obj)
+{
+    int i;
+
+    for (i = 0; i < pList->count; i++) {
+        if (pList->list[i] == obj)
+            return true;
+    }
+    return false;
+}
+
+/*
+ * Print the list contents to stdout.  For debugging.
+ */
+static void expandObjDump(const ExpandingObjectList* pList)
+{
+    int i;
+    for (i = 0; i < pList->count; i++)
+        printf(" %p", pList->list[i]);
+}
+
+/*
+ * Check for duplicate entries.  Returns the index of the first instance
+ * of the duplicated value, or -1 if no duplicates were found.
+ */
+static int expandObjCheckForDuplicates(const ExpandingObjectList* pList)
+{
+    int i, j;
+    for (i = 0; i < pList->count-1; i++) {
+        for (j = i + 1; j < pList->count; j++) {
+            if (pList->list[i] == pList->list[j]) {
+                return i;
+            }
+        }
+    }
+
+    return -1;
+}
+
+
+/*
+ * Determine whether "child" appears in the list of objects associated
+ * with the Monitor in "parent".  If "parent" is a thin lock, we return
+ * false immediately.
+ */
+static bool objectInChildList(const Object* parent, Object* child)
+{
+    u4 lock = parent->lock;
+    if (!IS_LOCK_FAT(&lock)) {
+        //LOGI("on thin\n");
+        return false;
+    }
+
+    return expandObjHas(&LW_MONITOR(lock)->historyChildren, child);
+}
+
+/*
+ * Print the child list.
+ */
+static void dumpKids(Object* parent)
+{
+    Monitor* mon = LW_MONITOR(parent->lock);
+
+    printf("Children of %p:", parent);
+    expandObjDump(&mon->historyChildren);
+    printf("\n");
+}
+
+/*
+ * Add "child" to the list of children in "parent", and add "parent" to
+ * the list of parents in "child".
+ */
+static void linkParentToChild(Object* parent, Object* child)
+{
+    //assert(LW_MONITOR(parent->lock)->owner == dvmThreadSelf());   // !owned for merge
+    assert(IS_LOCK_FAT(&parent->lock));
+    assert(IS_LOCK_FAT(&child->lock));
+    assert(parent != child);
+    Monitor* mon;
+
+    mon = LW_MONITOR(parent->lock);
+    assert(!expandObjHas(&mon->historyChildren, child));
+    expandObjAddEntry(&mon->historyChildren, child);
+
+    mon = LW_MONITOR(child->lock);
+    assert(!expandObjHas(&mon->historyParents, parent));
+    expandObjAddEntry(&mon->historyParents, parent);
+}
+
+
+/*
+ * Remove "child" from the list of children in "parent".
+ */
+static void unlinkParentFromChild(Object* parent, Object* child)
+{
+    //assert(LW_MONITOR(parent->lock)->owner == dvmThreadSelf());   // !owned for GC
+    assert(IS_LOCK_FAT(&parent->lock));
+    assert(IS_LOCK_FAT(&child->lock));
+    assert(parent != child);
+    Monitor* mon;
+
+    mon = LW_MONITOR(parent->lock);
+    if (!expandObjRemoveEntry(&mon->historyChildren, child)) {
+        LOGW("WARNING: child %p not found in parent %p\n", child, parent);
+    }
+    assert(!expandObjHas(&mon->historyChildren, child));
+    assert(expandObjCheckForDuplicates(&mon->historyChildren) < 0);
+
+    mon = LW_MONITOR(child->lock);
+    if (!expandObjRemoveEntry(&mon->historyParents, parent)) {
+        LOGW("WARNING: parent %p not found in child %p\n", parent, child);
+    }
+    assert(!expandObjHas(&mon->historyParents, parent));
+    assert(expandObjCheckForDuplicates(&mon->historyParents) < 0);
+}
+
+
+/*
+ * Log the monitors held by the current thread.  This is done as part of
+ * flagging an error.
+ */
+static void logHeldMonitors(Thread* self)
+{
+    char* name = NULL;
+
+    name = dvmGetThreadName(self);
+    LOGW("Monitors currently held by thread (threadid=%d '%s')\n",
+        self->threadId, name);
+    LOGW("(most-recently-acquired on top):\n");
+    free(name);
+
+    LockedObjectData* lod = self->pLockedObjects;
+    while (lod != NULL) {
+        LOGW("--- object %p[%d] (%s)\n",
+            lod->obj, lod->recursionCount, lod->obj->clazz->descriptor);
+        dvmLogRawStackTrace(lod->rawStackTrace, lod->stackDepth);
+
+        lod = lod->next;
+    }
+}
+
+/*
+ * Recursively traverse the object hierarchy starting at "obj".  We mark
+ * ourselves on entry and clear the mark on exit.  If we ever encounter
+ * a marked object, we have a cycle.
+ *
+ * Returns "true" if all is well, "false" if we found a cycle.
+ */
+static bool traverseTree(Thread* self, const Object* obj)
+{
+    assert(IS_LOCK_FAT(&obj->lock));
+    Monitor* mon = LW_MONITOR(obj->lock);
+
+    /*
+     * Have we been here before?
+     */
+    if (mon->historyMark) {
+        int* rawStackTrace;
+        int stackDepth;
+
+        LOGW("%s\n", kStartBanner);
+        LOGW("Illegal lock attempt:\n");
+        LOGW("--- object %p (%s)\n", obj, obj->clazz->descriptor);
+
+        rawStackTrace = dvmFillInStackTraceRaw(self, &stackDepth);
+        dvmLogRawStackTrace(rawStackTrace, stackDepth);
+        free(rawStackTrace);
+
+        LOGW(" ");
+        logHeldMonitors(self);
+
+        LOGW(" ");
+        LOGW("Earlier, the following lock order (from last to first) was\n");
+        LOGW("established -- stack trace is from first successful lock):\n");
+        return false;
+    }
+    mon->historyMark = true;
+
+    /*
+     * Examine the children.  We do NOT hold these locks, so they might
+     * very well transition from thin to fat or change ownership while
+     * we work.
+     *
+     * NOTE: we rely on the fact that they cannot revert from fat to thin
+     * while we work.  This is currently a safe assumption.
+     *
+     * We can safely ignore thin-locked children, because by definition
+     * they have no history and are leaf nodes.  In the current
+     * implementation we always fatten the locks to provide a place to
+     * hang the stack trace.
+     */
+    ExpandingObjectList* pList = &mon->historyChildren;
+    int i;
+    for (i = expandBufGetCount(pList)-1; i >= 0; i--) {
+        const Object* child = expandBufGetEntry(pList, i);
+        u4 lock = child->lock;
+        if (!IS_LOCK_FAT(&lock))
+            continue;
+        if (!traverseTree(self, child)) {
+            LOGW("--- object %p (%s)\n", obj, obj->clazz->descriptor);
+            dvmLogRawStackTrace(mon->historyRawStackTrace,
+                mon->historyStackDepth);
+            mon->historyMark = false;
+            return false;
+        }
+    }
+
+    mon->historyMark = false;
+
+    return true;
+}
+
+/*
+ * Update the deadlock prediction tree, based on the current thread
+ * acquiring "acqObj".  This must be called before the object is added to
+ * the thread's list of held monitors.
+ *
+ * If the thread already holds the lock (recursion), or this is a known
+ * lock configuration, we return without doing anything.  Otherwise, we add
+ * a link from the most-recently-acquired lock in this thread to "acqObj"
+ * after ensuring that the parent lock is "fat".
+ *
+ * This MUST NOT be called while a GC is in progress in another thread,
+ * because we assume exclusive access to history trees in owned monitors.
+ */
+static void updateDeadlockPrediction(Thread* self, Object* acqObj)
+{
+    LockedObjectData* lod;
+    LockedObjectData* mrl;
+
+    /*
+     * Quick check for recursive access.
+     */
+    lod = dvmFindInMonitorList(self, acqObj);
+    if (lod != NULL) {
+        LOGV("+++ DP: recursive %p\n", acqObj);
+        return;
+    }
+
+    /*
+     * Make the newly-acquired object's monitor "fat".  In some ways this
+     * isn't strictly necessary, but we need the GC to tell us when
+     * "interesting" objects go away, and right now the only way to make
+     * an object look interesting is to give it a monitor.
+     *
+     * This also gives us a place to hang a stack trace.
+     *
+     * Our thread holds the lock, so we're allowed to rewrite the lock
+     * without worrying that something will change out from under us.
+     */
+    if (!IS_LOCK_FAT(&acqObj->lock)) {
+        LOGVV("fattening lockee %p (recur=%d)\n",
+            acqObj, LW_LOCK_COUNT(acqObj->lock.thin));
+        inflateMonitor(self, acqObj);
+    }
+
+    /* if we don't have a stack trace for this monitor, establish one */
+    if (LW_MONITOR(acqObj->lock)->historyRawStackTrace == NULL) {
+        Monitor* mon = LW_MONITOR(acqObj->lock);
+        mon->historyRawStackTrace = dvmFillInStackTraceRaw(self,
+            &mon->historyStackDepth);
+    }
+
+    /*
+     * We need to examine and perhaps modify the most-recently-locked
+     * monitor.  We own that, so there's no risk of another thread
+     * stepping on us.
+     *
+     * Retrieve the most-recently-locked entry from our thread.
+     */
+    mrl = self->pLockedObjects;
+    if (mrl == NULL)
+        return;         /* no other locks held */
+
+    /*
+     * Do a quick check to see if "acqObj" is a direct descendant.  We can do
+     * this without holding the global lock because of our assertion that
+     * a GC is not running in parallel -- nobody except the GC can
+     * modify a history list in a Monitor they don't own, and we own "mrl".
+     * (There might be concurrent *reads*, but no concurrent *writes.)
+     *
+     * If we find it, this is a known good configuration, and we're done.
+     */
+    if (objectInChildList(mrl->obj, acqObj))
+        return;
+
+    /*
+     * "mrl" is going to need to have a history tree.  If it's currently
+     * a thin lock, we make it fat now.  The thin lock might have a
+     * nonzero recursive lock count, which we need to carry over.
+     *
+     * Our thread holds the lock, so we're allowed to rewrite the lock
+     * without worrying that something will change out from under us.
+     */
+    if (!IS_LOCK_FAT(&mrl->obj->lock)) {
+        LOGVV("fattening parent %p f/b/o child %p (recur=%d)\n",
+            mrl->obj, acqObj, LW_LOCK_COUNT(mrl->obj->lock));
+        inflateMonitor(self, mrl->obj);
+    }
+
+    /*
+     * We haven't seen this configuration before.  We need to scan down
+     * acqObj's tree to see if any of the monitors in self->pLockedObjects
+     * appear.  We grab a global lock before traversing or updating the
+     * history list.
+     *
+     * If we find a match for any of our held locks, we know that the lock
+     * has previously been acquired *after* acqObj, and we throw an error.
+     *
+     * The easiest way to do this is to create a link from "mrl" to "acqObj"
+     * and do a recursive traversal, marking nodes as we cross them.  If
+     * we cross one a second time, we have a cycle and can throw an error.
+     * (We do the flag-clearing traversal before adding the new link, so
+     * that we're guaranteed to terminate.)
+     *
+     * If "acqObj" is a thin lock, it has no history, and we can create a
+     * link to it without additional checks.  [ We now guarantee that it's
+     * always fat. ]
+     */
+    bool failed = false;
+    dvmLockMutex(&gDvm.deadlockHistoryLock);
+    linkParentToChild(mrl->obj, acqObj);
+    if (!traverseTree(self, acqObj)) {
+        LOGW("%s\n", kEndBanner);
+        failed = true;
+
+        /* remove the entry so we're still okay when in "warning" mode */
+        unlinkParentFromChild(mrl->obj, acqObj);
+    }
+    dvmUnlockMutex(&gDvm.deadlockHistoryLock);
+
+    if (failed) {
+        switch (gDvm.deadlockPredictMode) {
+        case kDPErr:
+            dvmThrowException("Ldalvik/system/PotentialDeadlockError;", NULL);
+            break;
+        case kDPAbort:
+            LOGE("Aborting due to potential deadlock\n");
+            dvmAbort();
+            break;
+        default:
+            /* warn only */
+            break;
+        }
+    }
+}
+
+/*
+ * We're removing "child" from existence.  We want to pull all of
+ * child's children into "parent", filtering out duplicates.  This is
+ * called during the GC.
+ *
+ * This does not modify "child", which might have multiple parents.
+ */
+static void mergeChildren(Object* parent, const Object* child)
+{
+    Monitor* mon;
+    int i;
+
+    assert(IS_LOCK_FAT(&child->lock));
+    mon = LW_MONITOR(child->lock);
+    ExpandingObjectList* pList = &mon->historyChildren;
+
+    for (i = expandBufGetCount(pList)-1; i >= 0; i--) {
+        Object* grandChild = expandBufGetEntry(pList, i);
+
+        if (!objectInChildList(parent, grandChild)) {
+            LOGVV("+++  migrating %p link to %p\n", grandChild, parent);
+            linkParentToChild(parent, grandChild);
+        } else {
+            LOGVV("+++  parent %p already links to %p\n", parent, grandChild);
+        }
+    }
+}
+
+/*
+ * An object with a fat lock is being collected during a GC pass.  We
+ * want to remove it from any lock history trees that it is a part of.
+ *
+ * This may require updating the history trees in several monitors.  The
+ * monitor semantics guarantee that no other thread will be accessing
+ * the history trees at the same time.
+ */
+static void removeCollectedObject(Object* obj)
+{
+    Monitor* mon;
+
+    LOGVV("+++ collecting %p\n", obj);
+
+    /*
+     * For every parent of this object:
+     *  - merge all of our children into the parent's child list (creates
+     *    a two-way link between parent and child)
+     *  - remove ourselves from the parent's child list
+     */
+    ExpandingObjectList* pList;
+    int i;
+
+    assert(IS_LOCK_FAT(&obj->lock));
+    mon = LW_MONITOR(obj->lock);
+    pList = &mon->historyParents;
+    for (i = expandBufGetCount(pList)-1; i >= 0; i--) {
+        Object* parent = expandBufGetEntry(pList, i);
+        Monitor* parentMon = LW_MONITOR(parent->lock);
+
+        if (!expandObjRemoveEntry(&parentMon->historyChildren, obj)) {
+            LOGW("WARNING: child %p not found in parent %p\n", obj, parent);
+        }
+        assert(!expandObjHas(&parentMon->historyChildren, obj));
+
+        mergeChildren(parent, obj);
+    }
+
+    /*
+     * For every child of this object:
+     *  - remove ourselves from the child's parent list
+     */
+    pList = &mon->historyChildren;
+    for (i = expandBufGetCount(pList)-1; i >= 0; i--) {
+        Object* child = expandBufGetEntry(pList, i);
+        Monitor* childMon = LW_MONITOR(child->lock);
+
+        if (!expandObjRemoveEntry(&childMon->historyParents, obj)) {
+            LOGW("WARNING: parent %p not found in child %p\n", obj, child);
+        }
+        assert(!expandObjHas(&childMon->historyParents, obj));
+    }
+}
+
+#endif /*WITH_DEADLOCK_PREDICTION*/
diff --git a/vm/Sync.h b/vm/Sync.h
new file mode 100644
index 0000000..1a168b9
--- /dev/null
+++ b/vm/Sync.h
@@ -0,0 +1,167 @@
+/*
+ * 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
+#define _DALVIK_SYNC
+
+/*
+ * 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;
+typedef struct Monitor Monitor;
+
+#define QUIET_ZYGOTE_MONITOR 1
+
+/*
+ * Initialize a Lock to the proper starting value.
+ * This is necessary for thin locking.
+ */
+#define DVM_LOCK_INITIAL_THIN_VALUE (0)
+
+#define DVM_LOCK_INIT(lock) \
+    do { *(lock) = DVM_LOCK_INITIAL_THIN_VALUE; } while (0)
+
+/*
+ * Returns true if the lock has been fattened.
+ */
+#define IS_LOCK_FAT(lock)   (LW_SHAPE(*(lock)) == LW_SHAPE_FAT)
+
+/*
+ * Acquire the object's monitor.
+ */
+void dvmLockObject(struct Thread* self, struct Object* obj);
+
+/* Returns true if the unlock succeeded.
+ * If the unlock failed, an exception will be pending.
+ */
+bool dvmUnlockObject(struct Thread* self, struct Object* obj);
+
+/*
+ * Implementations of some java/lang/Object calls.
+ */
+void dvmObjectWait(struct Thread* self, struct Object* obj,
+    s8 timeout, s4 nanos, bool interruptShouldThrow);
+void dvmObjectNotify(struct Thread* self, struct Object* obj);
+void dvmObjectNotifyAll(struct Thread* self, struct Object* obj);
+
+/*
+ * Implementation of System.identityHashCode().
+ */
+u4 dvmIdentityHashCode(struct 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(struct Thread* thread);
+
+/* create a new Monitor struct */
+Monitor* dvmCreateMonitor(struct 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).
+ */
+struct 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.
+ */
+struct Thread* dvmGetObjectLockHolder(struct Object* obj);
+
+/*
+ * Checks whether the object is held by the specified thread.
+ */
+bool dvmHoldsLock(struct Thread* thread, struct Object* obj);
+
+/*
+ * Relative timed wait on condition
+ */
+int dvmRelativeCondWait(pthread_cond_t* cond, pthread_mutex_t* mutex,
+                         s8 msec, s4 nsec);
+
+/*
+ * Debug.
+ */
+void dvmDumpMonitorInfo(const char* msg);
+
+#endif /*_DALVIK_SYNC*/
diff --git a/vm/TestCompability.c b/vm/TestCompability.c
new file mode 100644
index 0000000..d447db3
--- /dev/null
+++ b/vm/TestCompability.c
@@ -0,0 +1,26 @@
+/*
+ * 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>
+
+#if defined(TEST_VM_IN_ECLAIR)
+FILE *open_memstream(char **ptr, size_t *sizeloc)
+{
+    LOGE("Fake open_memstream entered");
+    dvmAbort();
+    return NULL;
+}
+#endif
diff --git a/vm/Thread.c b/vm/Thread.c
new file mode 100644
index 0000000..c9b6311
--- /dev/null
+++ b/vm/Thread.c
@@ -0,0 +1,4219 @@
+/*
+ * 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 support.
+ */
+#include "Dalvik.h"
+
+#include "utils/threads.h"      // need Android thread priorities
+
+#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 <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
+
+
+/* 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 LOGV/LOGD 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);
+static int getThreadPriorityFromSystem(void);
+
+/*
+ * The JIT needs to know if any thread is suspended.  We do this by
+ * maintaining a global sum of all threads' suspend counts.  All suspendCount
+ * updates should go through this after aquiring threadSuspendCountLock.
+ */
+static inline void dvmAddToThreadSuspendCount(int *pSuspendCount, int delta)
+{
+    *pSuspendCount += delta;
+    gDvm.sumThreadSuspendCount += delta;
+}
+
+/*
+ * 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(void)
+{
+    Thread* thread;
+
+    /* allocate a TLS slot */
+    if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) {
+        LOGE("ERROR: pthread_key_create failed\n");
+        return false;
+    }
+
+    /* test our pthread lib */
+    if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL)
+        LOGW("WARNING: newly-created pthread TLS slot is not NULL\n");
+
+    /* prep thread-related locks and conditions */
+    dvmInitMutex(&gDvm.threadListLock);
+    pthread_cond_init(&gDvm.threadStartCond, NULL);
+    //dvmInitMutex(&gDvm.vmExitLock);
+    pthread_cond_init(&gDvm.vmExitCond, NULL);
+    dvmInitMutex(&gDvm._threadSuspendLock);
+    dvmInitMutex(&gDvm.threadSuspendCountLock);
+    pthread_cond_init(&gDvm.threadSuspendCountCond, NULL);
+#ifdef WITH_DEADLOCK_PREDICTION
+    dvmInitMutex(&gDvm.deadlockHistoryLock);
+#endif
+
+    /*
+     * 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.stackSize);
+    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;
+}
+
+/*
+ * We're a little farther up now, and can load some basic classes.
+ *
+ * We're far enough along that we can poke at java.lang.Thread and friends,
+ * but should not assume that static initializers have run (or cause them
+ * to do so).  That means no object allocations yet.
+ */
+bool dvmThreadObjStartup(void)
+{
+    /*
+     * Cache the locations of these classes.  It's likely that we're the
+     * first to reference them, so they're being loaded now.
+     */
+    gDvm.classJavaLangThread =
+        dvmFindSystemClassNoInit("Ljava/lang/Thread;");
+    gDvm.classJavaLangVMThread =
+        dvmFindSystemClassNoInit("Ljava/lang/VMThread;");
+    gDvm.classJavaLangThreadGroup =
+        dvmFindSystemClassNoInit("Ljava/lang/ThreadGroup;");
+    if (gDvm.classJavaLangThread == NULL ||
+        gDvm.classJavaLangThreadGroup == NULL ||
+        gDvm.classJavaLangThreadGroup == NULL)
+    {
+        LOGE("Could not find one or more essential thread classes\n");
+        return false;
+    }
+
+    /*
+     * Cache field offsets.  This makes things a little faster, at the
+     * expense of hard-coding non-public field names into the VM.
+     */
+    gDvm.offJavaLangThread_vmThread =
+        dvmFindFieldOffset(gDvm.classJavaLangThread,
+            "vmThread", "Ljava/lang/VMThread;");
+    gDvm.offJavaLangThread_group =
+        dvmFindFieldOffset(gDvm.classJavaLangThread,
+            "group", "Ljava/lang/ThreadGroup;");
+    gDvm.offJavaLangThread_daemon =
+        dvmFindFieldOffset(gDvm.classJavaLangThread, "daemon", "Z");
+    gDvm.offJavaLangThread_name =
+        dvmFindFieldOffset(gDvm.classJavaLangThread,
+            "name", "Ljava/lang/String;");
+    gDvm.offJavaLangThread_priority =
+        dvmFindFieldOffset(gDvm.classJavaLangThread, "priority", "I");
+
+    if (gDvm.offJavaLangThread_vmThread < 0 ||
+        gDvm.offJavaLangThread_group < 0 ||
+        gDvm.offJavaLangThread_daemon < 0 ||
+        gDvm.offJavaLangThread_name < 0 ||
+        gDvm.offJavaLangThread_priority < 0)
+    {
+        LOGE("Unable to find all fields in java.lang.Thread\n");
+        return false;
+    }
+
+    gDvm.offJavaLangVMThread_thread =
+        dvmFindFieldOffset(gDvm.classJavaLangVMThread,
+            "thread", "Ljava/lang/Thread;");
+    gDvm.offJavaLangVMThread_vmData =
+        dvmFindFieldOffset(gDvm.classJavaLangVMThread, "vmData", "I");
+    if (gDvm.offJavaLangVMThread_thread < 0 ||
+        gDvm.offJavaLangVMThread_vmData < 0)
+    {
+        LOGE("Unable to find all fields in java.lang.VMThread\n");
+        return false;
+    }
+
+    /*
+     * Cache the vtable offset for "run()".
+     *
+     * We don't want to keep the Method* because then we won't find see
+     * methods defined in subclasses.
+     */
+    Method* meth;
+    meth = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangThread, "run", "()V");
+    if (meth == NULL) {
+        LOGE("Unable to find run() in java.lang.Thread\n");
+        return false;
+    }
+    gDvm.voffJavaLangThread_run = meth->methodIndex;
+
+    /*
+     * Cache vtable offsets for ThreadGroup methods.
+     */
+    meth = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangThreadGroup,
+        "removeThread", "(Ljava/lang/Thread;)V");
+    if (meth == NULL) {
+        LOGE("Unable to find removeThread(Thread) in java.lang.ThreadGroup\n");
+        return false;
+    }
+    gDvm.voffJavaLangThreadGroup_removeThread = meth->methodIndex;
+
+    return true;
+}
+
+/*
+ * All threads should be stopped by now.  Clean up some thread globals.
+ */
+void dvmThreadShutdown(void)
+{
+    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(void)
+{
+    /*
+     * 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(void)
+{
+    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 */
+        //LOGW("NULL self in dvmLockThreadList\n");
+        oldStatus = -1;         // shut up gcc
+    }
+
+    dvmLockMutex(&gDvm.threadListLock);
+
+    if (self != NULL)
+        self->status = oldStatus;
+}
+
+/*
+ * Release the thread list global lock.
+ */
+void dvmUnlockThreadList(void)
+{
+    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";
+#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.
+                 */
+                LOGI("threadid=%d ODD: want thread-suspend lock (%s:%s),"
+                     " it's held, no suspend pending\n",
+                    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)) {
+                LOGE("threadid=%d: couldn't get thread-suspend lock (%s:%s),"
+                     " bailing\n",
+                    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(void)
+{
+    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(void)
+{
+    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 */
+            LOGW("threadid=%d: non-daemon id=%d still running at shutdown?!\n",
+                threadId, target->threadId);
+        }
+
+        char* threadName = dvmGetThreadName(target);
+        LOGD("threadid=%d: suspending daemon id=%d name='%s'\n",
+            threadId, target->threadId, threadName);
+        free(threadName);
+
+        /* mark as suspended */
+        lockThreadSuspendCount();
+        dvmAddToThreadSuspendCount(&target->suspendCount, 1);
+        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)
+                        LOGD("threadid=%d not ready yet\n", target->threadId);
+                    allSuspended = false;
+                    /* keep going so we log each running daemon once */
+                }
+
+                target = target->next;
+            }
+
+            if (allSuspended) {
+                LOGD("threadid=%d: all daemons have suspended\n", threadId);
+                break;
+            } else {
+                if (!complained) {
+                    complained = true;
+                    LOGD("threadid=%d: waiting briefly for daemon suspension\n",
+                        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(void)
+{
+    Thread* thread;
+    Object* groupObj;
+    Object* threadObj;
+    Object* vmThreadObj;
+    StringObject* threadNameStr;
+    Method* init;
+    JValue unused;
+
+    LOGV("+++ finishing prep on main VM thread\n");
+
+    /* 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)) {
+        LOGE("'Class' class failed to initialize\n");
+        return false;
+    }
+    if (!dvmInitClass(gDvm.classJavaLangThreadGroup) ||
+        !dvmInitClass(gDvm.classJavaLangThread) ||
+        !dvmInitClass(gDvm.classJavaLangVMThread))
+    {
+        LOGE("thread classes failed to initialize\n");
+        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) {
+        LOGE("unable to allocate main thread object\n");
+        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)) {
+        LOGE("exception thrown while constructing main thread object\n");
+        return false;
+    }
+
+    /*
+     * Allocate and construct a VMThread.
+     */
+    vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+    if (vmThreadObj == NULL) {
+        LOGE("unable to allocate main vmthread object\n");
+        return false;
+    }
+    dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+
+    init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangVMThread, "<init>",
+            "(Ljava/lang/Thread;)V");
+    dvmCallMethod(thread, init, vmThreadObj, &unused, threadObj);
+    if (dvmCheckException(thread)) {
+        LOGE("exception thrown while constructing main vmthread object\n");
+        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.  This invokes a ClassLoader method,
+     * 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) {
+        LOGW("WARNING: system class loader is NULL (setting main ctxt)\n");
+        /* keep going */
+    }
+    int ctxtClassLoaderOffset = dvmFindFieldOffset(gDvm.classJavaLangThread,
+        "contextClassLoader", "Ljava/lang/ClassLoader;");
+    if (ctxtClassLoaderOffset < 0) {
+        LOGE("Unable to find contextClassLoader field in Thread\n");
+        return false;
+    }
+    dvmSetFieldObject(threadObj, ctxtClassLoaderOffset, systemLoader);
+
+    /*
+     * Finish our thread prep.
+     */
+
+    /* 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;
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (dvmSelfVerificationShadowSpaceAlloc(thread) == NULL)
+        return NULL;
+#endif
+
+    assert(interpStackSize >= kMinStackSize && interpStackSize <=kMaxStackSize);
+
+    thread->status = THREAD_INITIALIZING;
+    thread->suspendCount = 0;
+
+#ifdef WITH_ALLOC_LIMITS
+    thread->allocLimit = -1;
+#endif
+
+    /*
+     * 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 = 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;
+
+    /* give the thread code a chance to set things up */
+    dvmInitInterpStack(thread, interpStackSize);
+
+    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(void)
+{
+#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();
+
+    //LOGI("SYSTEM TID IS %d (pid is %d)\n", (int) thread->systemTid,
+    //    (int) getpid());
+    /*
+     * If we were called by dvmAttachCurrentThread, the self value is
+     * already correctly established as "thread".
+     */
+    setThreadSelf(thread);
+
+    LOGV("threadid=%d: interp stack at %p\n",
+        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).
+     */
+#ifdef USE_INDIRECT_REF
+    if (!dvmInitIndirectRefTable(&thread->jniLocalRefTable,
+            kJniLocalRefMin, kJniLocalRefMax, kIndirectKindLocal))
+        return false;
+#else
+    /*
+     * The JNI local ref table *must* be fixed-size because we keep pointers
+     * into the table in our stack frames.
+     */
+    if (!dvmInitReferenceTable(&thread->jniLocalRefTable,
+            kJniLocalRefMax, kJniLocalRefMax))
+        return false;
+#endif
+    if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
+            kInternalRefDefault, kInternalRefMax))
+        return false;
+
+    memset(&thread->jniMonitorRefTable, 0, sizeof(thread->jniMonitorRefTable));
+
+    pthread_cond_init(&thread->waitCond, NULL);
+    dvmInitMutex(&thread->waitMutex);
+
+    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\n", 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\n", 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)
+            LOGW("munmap(thread stack) failed\n");
+#endif
+    }
+
+#ifdef USE_INDIRECT_REF
+    dvmClearIndirectRefTable(&thread->jniLocalRefTable);
+#else
+    dvmClearReferenceTable(&thread->jniLocalRefTable);
+#endif
+    dvmClearReferenceTable(&thread->internalLocalRefTable);
+    if (&thread->jniMonitorRefTable.table != NULL)
+        dvmClearReferenceTable(&thread->jniMonitorRefTable);
+
+#if defined(WITH_SELF_VERIFICATION)
+    dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+    free(thread);
+}
+
+/*
+ * Like pthread_self(), but on a Thread*.
+ */
+Thread* dvmThreadSelf(void)
+{
+    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) {
+            LOGE("pthread_setspecific(%p) failed, err=%d\n", 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);
+
+    LOGV("threadid=%d: threadExitCheck(%p) count=%d\n",
+        self->threadId, arg, self->threadExitCheckCount);
+
+    if (self->status == THREAD_ZOMBIE) {
+        LOGW("threadid=%d: Weird -- shouldn't be in threadExitCheck\n",
+            self->threadId);
+        return;
+    }
+
+    if (self->threadExitCheckCount < kMaxCount) {
+        /*
+         * Spin a couple of times to let other destructors fire.
+         */
+        LOGD("threadid=%d: thread exiting, not yet detached (count=%d)\n",
+            self->threadId, self->threadExitCheckCount);
+        self->threadExitCheckCount++;
+        int cc = pthread_setspecific(gDvm.pthreadKeySelf, self);
+        if (cc != 0) {
+            LOGE("threadid=%d: unable to re-add thread to TLS\n",
+                self->threadId);
+            dvmAbort();
+        }
+    } else {
+        LOGE("threadid=%d: native thread exited without detaching\n",
+            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) {
+        LOGE("Ran out of thread IDs\n");
+        dvmAbort();     // TODO: make this a non-fatal error result
+    }
+
+    thread->threadId = num + 1;
+
+    assert(thread->threadId != 0);
+    assert(thread->threadId != DVM_LOCK_INITIAL_THIN_VALUE);
+}
+
+/*
+ * 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)
+{
+    assert(thread->threadId == kMainThreadId);      // main thread only
+
+    /* find the method on first use */
+    if (gDvm.methFakeNativeEntry == NULL) {
+        ClassObject* nativeStart;
+        Method* mainMeth;
+
+        nativeStart = dvmFindSystemClassNoInit(
+                "Ldalvik/system/NativeStart;");
+        if (nativeStart == NULL) {
+            LOGE("Unable to find dalvik.system.NativeStart class\n");
+            return false;
+        }
+
+        /*
+         * 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.
+         *
+         * So we save a pointer to the method in gDvm.methFakeNativeEntry
+         * and check it in FindClass.  The method is private so nobody else
+         * can call it.
+         */
+        //nativeStart->classLoader = dvmGetSystemClassLoader();
+
+        mainMeth = dvmFindDirectMethodByDescriptor(nativeStart,
+                    "main", "([Ljava/lang/String;)V");
+        if (mainMeth == NULL) {
+            LOGE("Unable to find 'main' in dalvik.system.NativeStart\n");
+            return false;
+        }
+
+        gDvm.methFakeNativeEntry = mainMeth;
+    }
+
+    if (!dvmPushJNIFrame(thread, gDvm.methFakeNativeEntry))
+        return false;
+
+    /*
+     * Null out the "String[] args" argument.
+     */
+    assert(gDvm.methFakeNativeEntry->registersSize == 1);
+    u4* framePtr = (u4*) thread->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)
+{
+    ClassObject* nativeStart;
+    Method* runMeth;
+
+    /*
+     * TODO: cache this result so we don't have to dig for it every time
+     * somebody attaches a thread to the VM.  Also consider changing this
+     * to a static method so we don't have a null "this" pointer in the
+     * "ins" on the stack.  (Does it really need to look like a Runnable?)
+     */
+    nativeStart = dvmFindSystemClassNoInit("Ldalvik/system/NativeStart;");
+    if (nativeStart == NULL) {
+        LOGE("Unable to find dalvik.system.NativeStart class\n");
+        return false;
+    }
+
+    runMeth = dvmFindVirtualMethodByDescriptor(nativeStart, "run", "()V");
+    if (runMeth == NULL) {
+        LOGE("Unable to find 'run' in dalvik.system.NativeStart\n");
+        return false;
+    }
+
+    if (!dvmPushJNIFrame(thread, runMeth))
+        return false;
+
+    /*
+     * Provide a NULL 'this' argument.  The method we've put at the top of
+     * the stack looks like a virtual call to run() in a Runnable class.
+     * (If we declared the method static, it wouldn't take any arguments
+     * and we wouldn't have to do this.)
+     */
+    assert(runMeth->registersSize == 1);
+    u4* framePtr = (u4*) thread->curFrame;
+    framePtr[0] = 0;
+
+    return true;
+}
+
+/*
+ * Helper function to set the name of the current thread
+ */
+static void setThreadName(const char *threadName)
+{
+    int hasAt = 0;
+    int hasDot = 0;
+    const char *s = threadName;
+    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) {
+        LOGW("Unable to set the name of current thread to '%s': %s\n",
+            buf, strerror(err));
+    }
+#elif defined(HAVE_PRCTL)
+    prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
+#else
+    LOGD("No way to set current thread's name (%s)\n", 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)
+{
+    pthread_attr_t threadAttr;
+    pthread_t threadHandle;
+    Thread* self;
+    Thread* newThread = NULL;
+    Object* vmThreadObj = NULL;
+    int stackSize;
+
+    assert(threadObj != NULL);
+
+    if(gDvm.zygote) {
+        // Allow the sampling profiler thread. We shut it down before forking.
+        StringObject* nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+                    gDvm.offJavaLangThread_name);
+        char* threadName = dvmCreateCstrFromString(nameStr);
+        bool profilerThread = strcmp(threadName, "SamplingProfiler") == 0;
+        free(threadName);
+        if (!profilerThread) {
+            dvmThrowException("Ljava/lang/IllegalStateException;",
+                "No new threads in -Xzygote mode");
+
+            goto fail;
+        }
+    }
+
+    self = dvmThreadSelf();
+    if (reqStackSize == 0)
+        stackSize = gDvm.stackSize;
+    else if (reqStackSize < kMinStackSize)
+        stackSize = kMinStackSize;
+    else if (reqStackSize > kMaxStackSize)
+        stackSize = kMaxStackSize;
+    else
+        stackSize = reqStackSize;
+
+    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.
+     */
+    vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+    if (vmThreadObj == NULL)
+        goto fail;
+
+    newThread = allocThread(stackSize);
+    if (newThread == NULL)
+        goto fail;
+    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();
+        dvmThrowException("Ljava/lang/IllegalThreadStateException;",
+            "thread has already been started");
+        goto fail;
+    }
+
+    /*
+     * 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);
+    int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart,
+            newThread);
+    oldStatus = dvmChangeStatus(self, oldStatus);
+
+    if (cc != 0) {
+        /*
+         * Failure generally indicates that we have exceeded system
+         * resource limits.  VirtualMachineError is probably too severe,
+         * so use OutOfMemoryError.
+         */
+        LOGE("Thread creation failed (err=%s)\n", strerror(errno));
+
+        dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+        dvmThrowException("Ljava/lang/OutOfMemoryError;",
+            "thread creation failed");
+        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\n", newThread->threadId);
+    newThread->next = gDvm.threadList->next;
+    if (newThread->next != NULL)
+        newThread->next->prev = newThread;
+    newThread->prev = gDvm.threadList;
+    gDvm.threadList->next = 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;
+
+    char *threadName = dvmGetThreadName(self);
+    setThreadName(threadName);
+    free(threadName);
+
+    /*
+     * Finish initializing the Thread struct.
+     */
+    dvmLockThreadList(self);
+    prepareThread(self);
+
+    LOG_THREAD("threadid=%d: created from interp\n", 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).
+     */
+    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;
+
+    LOGV("threadid=%d: calling run()\n", self->threadId);
+    assert(strcmp(run->name, "run") == 0);
+    dvmCallMethod(self, run, self->threadObj, &unused);
+    LOGV("threadid=%d: exiting\n", 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.
+ */
+static void threadExitUncaughtException(Thread* self, Object* group)
+{
+    Object* exception;
+    Object* handlerObj;
+    Method* uncaughtHandler = NULL;
+    InstField* threadHandler;
+
+    LOGW("threadid=%d: thread exiting with uncaught exception (group=%p)\n",
+        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);
+    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).
+     */
+    threadHandler = dvmFindInstanceField(gDvm.classJavaLangThread,
+            "uncaughtHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;");
+    if (threadHandler == NULL) {
+        LOGW("WARNING: no 'uncaughtHandler' field in java/lang/Thread\n");
+        goto bail;
+    }
+    handlerObj = dvmGetFieldObject(self->threadObj, threadHandler->byteOffset);
+    if (handlerObj == NULL)
+        handlerObj = group;
+
+    /*
+     * Find the "uncaughtHandler" field in this object.
+     */
+    uncaughtHandler = dvmFindVirtualMethodHierByDescriptor(handlerObj->clazz,
+            "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+
+    if (uncaughtHandler != NULL) {
+        //LOGI("+++ calling %s.uncaughtException\n",
+        //     handlerObj->clazz->descriptor);
+        JValue unused;
+        dvmCallMethod(self, uncaughtHandler, handlerObj, &unused,
+            self->threadObj, exception);
+    } else {
+        /* restore it and dump a stack trace */
+        LOGW("WARNING: no 'uncaughtException' method in class %s\n",
+            handlerObj->clazz->descriptor);
+        dvmSetException(self, exception);
+        dvmLogExceptionStackTrace();
+    }
+
+bail:
+#if defined(WITH_JIT)
+    /* Remove this thread's suspendCount from global suspendCount sum */
+    lockThreadSuspendCount();
+    dvmAddToThreadSuspendCount(&self->suspendCount, -self->suspendCount);
+    unlockThreadSuspendCount();
+#endif
+    dvmReleaseTrackedAlloc(exception, self);
+}
+
+
+/*
+ * 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;
+    pthread_attr_t threadAttr;
+    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_init(&threadAttr);
+    //pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+
+    if (pthread_create(pHandle, &threadAttr, internalThreadStart,
+            pArgs) != 0)
+    {
+        LOGE("internal thread creation failed\n");
+        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) {
+        LOGW("internal thread create failed (createStatus=%d)\n", 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 = pArgs->group;
+
+    setThreadName(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'\n",
+            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)\n", 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, getThreadPriorityFromSystem(), isDaemon);
+    if (dvmCheckException(self)) {
+        LOGE("exception thrown while constructing attached thread object\n");
+        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) {
+        LOGW("WOW: thread start hijack\n");
+        dvmThrowException("Ljava/lang/IllegalThreadStateException;",
+            "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\n",
+        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(void)
+{
+    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->curFrame);
+    if (curDepth != 0) {
+        bool topIsNative = false;
+
+        if (curDepth == 1) {
+            /* not expecting a lingering break frame; just look at curFrame */
+            assert(!dvmIsBreakFrame(self->curFrame));
+            StackSaveArea* ssa = SAVEAREA_FROM_FP(self->curFrame);
+            if (dvmIsNativeMethod(ssa->method))
+                topIsNative = true;
+        }
+
+        if (!topIsNative) {
+            LOGE("ERROR: detaching thread with interp frames (count=%d)\n",
+                curDepth);
+            dvmDumpThread(self, false);
+            dvmAbort();
+        }
+    }
+
+    group = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_group);
+    LOG_THREAD("threadid=%d: detach (group=%p)\n", 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.
+     */
+    android_atomic_release_store(THREAD_VMWAIT, &self->status);
+
+    /*
+     * 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) {
+        LOGI("threadid=%d: waiting for method trace to finish\n",
+            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) {
+            int cc;
+
+            LOGV("threadid=%d: last non-daemon thread\n", self->threadId);
+            //dvmDumpAllThreads(false);
+            // cond var guarded by threadListLock, which we already hold
+            cc = pthread_cond_signal(&gDvm.vmExitCond);
+            assert(cc == 0);
+        }
+    }
+
+    LOGV("threadid=%d: bye!\n", 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();
+    dvmAddToThreadSuspendCount(&thread->suspendCount, 1);
+    thread->dbgSuspendCount++;
+
+    LOG_THREAD("threadid=%d: suspend++, now=%d\n",
+        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) {
+        dvmAddToThreadSuspendCount(&thread->suspendCount, -1);
+        thread->dbgSuspendCount--;
+    } else {
+        LOG_THREAD("threadid=%d:  suspendCount already zero\n",
+            thread->threadId);
+    }
+
+    LOG_THREAD("threadid=%d: suspend--, now=%d\n",
+        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();
+    dvmAddToThreadSuspendCount(&self->suspendCount, 1);
+    self->dbgSuspendCount++;
+
+    /*
+     * Suspend ourselves.
+     */
+    assert(self->suspendCount > 0);
+    self->status = THREAD_SUSPENDED;
+    LOG_THREAD("threadid=%d: self-suspending (dbg)\n", 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) {
+        //LOGI("threadid=%d: clearing wait-for-event (my handle=%08x)\n",
+        //    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).
+             */
+            LOGD("threadid=%d: still suspended after undo (sc=%d dc=%d)\n",
+                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\n",
+        self->threadId, self->status);
+
+    unlockThreadSuspendCount();
+}
+
+
+#ifdef HAVE_GLIBC
+# define NUM_FRAMES  20
+# 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.
+ */
+static void printBackTrace(void)
+{
+    void* array[NUM_FRAMES];
+    size_t size;
+    char** strings;
+    size_t i;
+
+    size = backtrace(array, NUM_FRAMES);
+    strings = backtrace_symbols(array, size);
+
+    LOGW("Obtained %zd stack frames.\n", size);
+
+    for (i = 0; i < size; i++)
+        LOGW("%s\n", strings[i]);
+
+    free(strings);
+}
+#else
+static void printBackTrace(void) {}
+#endif
+
+/*
+ * 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);
+    printBackTrace();
+
+    // 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) {
+        LOGW("Unable to get priority for threadid=%d sysTid=%d\n",
+            thread->threadId, thread->systemTid);
+        return 0;
+    }
+    if (get_sched_policy(thread->systemTid, pSavedThreadPolicy) != 0) {
+        LOGW("Unable to get policy for threadid=%d sysTid=%d\n",
+            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) {
+            LOGW("Couldn't set fg policy on tid %d\n", thread->systemTid);
+        } else {
+            changeFlags |= kChangedPolicy;
+            LOGD("Temporarily moving tid %d to fg (was %d)\n",
+                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) {
+            LOGW("Couldn't raise priority on tid %d to %d\n",
+                thread->systemTid, kHigher);
+        } else {
+            changeFlags |= kChangedPriority;
+            LOGD("Temporarily raised priority on tid %d (%d -> %d)\n",
+                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) {
+            LOGW("NOTE: couldn't reset tid %d to (%d)\n",
+                thread->systemTid, savedThreadPolicy);
+        } else {
+            LOGD("Restored policy of %d to %d\n",
+                thread->systemTid, savedThreadPolicy);
+        }
+    }
+
+    if ((changeFlags & kChangedPriority) != 0) {
+        if (setpriority(PRIO_PROCESS, thread->systemTid, savedThreadPrio) != 0)
+        {
+            LOGW("NOTE: couldn't reset priority on thread %d to %d\n",
+                thread->systemTid, savedThreadPrio);
+        } else {
+            LOGD("Restored priority on %d to %d\n",
+                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) {
+            LOGD("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) {
+                LOGW("threadid=%d: spin on suspend #%d threadid=%d (pcf=%d)\n",
+                    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) {
+                LOGE("Fatal spin-on-suspend, dumping threads\n");
+                dvmDumpAllThreads(false);
+
+                /* log this after -- long traces will scroll off log */
+                LOGE("threadid=%d: stuck on threadid=%d, giving up\n",
+                    self->threadId, thread->threadId);
+
+                /* try to get a debuggerd dump from the spinning thread */
+                dvmNukeThread(thread);
+                /* abort the VM */
+                dvmAbort();
+            }
+        }
+    }
+
+    if (complained) {
+        LOGW("threadid=%d: spin on suspend resolved in %lld msec\n",
+            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\n", 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;
+
+        dvmAddToThreadSuspendCount(&thread->suspendCount, 1);
+        if (why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT)
+            thread->dbgSuspendCount++;
+    }
+    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\n",
+            self->threadId,
+            thread->threadId, thread->status, thread->suspendCount,
+            thread->dbgSuspendCount);
+    }
+
+    dvmUnlockThreadList();
+    unlockThreadSuspend();
+
+    LOG_THREAD("threadid=%d: SuspendAll complete\n", 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;
+    int cc;
+
+    lockThreadSuspend("res-all", why);  /* one suspend/resume at a time */
+    LOG_THREAD("threadid=%d: ResumeAll starting\n", 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) {
+            dvmAddToThreadSuspendCount(&thread->suspendCount, -1);
+            if (why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT)
+                thread->dbgSuspendCount--;
+        } else {
+            LOG_THREAD("threadid=%d:  suspendCount already zero\n",
+                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\n", 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();
+    cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+    assert(cc == 0);
+    unlockThreadSuspendCount();
+
+    LOG_THREAD("threadid=%d: ResumeAll complete\n", self->threadId);
+}
+
+/*
+ * Undo any debugger suspensions.  This is called when the debugger
+ * disconnects.
+ */
+void dvmUndoDebuggerSuspensions(void)
+{
+    Thread* self = dvmThreadSelf();
+    Thread* thread;
+    int cc;
+
+    lockThreadSuspend("undo", SUSPEND_FOR_DEBUG);
+    LOG_THREAD("threadid=%d: UndoDebuggerSusp starting\n", 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);
+        dvmAddToThreadSuspendCount(&thread->suspendCount,
+                                   -thread->dbgSuspendCount);
+        thread->dbgSuspendCount = 0;
+    }
+    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();
+    cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+    assert(cc == 0);
+    unlockThreadSuspendCount();
+
+    unlockThreadSuspend();
+
+    LOG_THREAD("threadid=%d: UndoDebuggerSusp complete\n", 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\n",
+        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\n",
+        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\n", self->threadId);
+        ThreadStatus oldStatus = self->status;      /* should be RUNNING */
+        self->status = THREAD_SUSPENDED;
+
+        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);
+        }
+        assert(self->suspendCount == 0 && self->dbgSuspendCount == 0);
+        self->status = oldStatus;
+        LOG_THREAD("threadid=%d: self-reviving, status=%d\n",
+            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)\n",
+        self->threadId, self->status, newStatus);
+
+    oldStatus = self->status;
+
+    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.
+         */
+        assert(oldStatus != THREAD_RUNNING);
+        android_atomic_acquire_store(newStatus, &self->status);
+        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);
+        android_atomic_release_store(newStatus, &self->status);
+    }
+
+    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) {
+        LOGE("java.lang.ThreadGroup does not have an '%s' field\n", fieldName);
+        dvmThrowException("Ljava/lang/IncompatibleClassChangeError;", NULL);
+        return NULL;
+    }
+    groupObj = dvmGetStaticFieldObject(groupField);
+    if (groupObj == NULL) {
+        LOGE("java.lang.ThreadGroup.%s not initialized\n", fieldName);
+        dvmThrowException("Ljava/lang/InternalError;", NULL);
+        return NULL;
+    }
+
+    return groupObj;
+}
+Object* dvmGetSystemThreadGroup(void)
+{
+    return getStaticThreadGroup("mSystem");
+}
+Object* dvmGetMainThreadGroup(void)
+{
+    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) {
+            LOGW("WARNING: vmThreadObj=%p has thread=%p, not in thread list\n",
+                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;
+}
+
+
+/*
+ * 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) */
+};
+
+/*
+ * Change 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 dvmChangeThreadPriority(Thread* thread, int newPriority)
+{
+    pid_t pid = thread->systemTid;
+    int newNice;
+
+    if (newPriority < 1 || newPriority > 10) {
+        LOGW("bad priority %d\n", newPriority);
+        newPriority = 5;
+    }
+    newNice = kNiceValues[newPriority-1];
+
+    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) {
+        char* str = dvmGetThreadName(thread);
+        LOGI("setPriority(%d) '%s' to prio=%d(n=%d) failed: %s\n",
+            pid, str, newPriority, newNice, strerror(errno));
+        free(str);
+    } else {
+        LOGV("setPriority(%d) to prio=%d(n=%d)\n",
+            pid, newPriority, newNice);
+    }
+}
+
+/*
+ * Get 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).
+ */
+static int getThreadPriorityFromSystem(void)
+{
+    int i, sysprio, jprio;
+
+    errno = 0;
+    sysprio = getpriority(PRIO_PROCESS, 0);
+    if (sysprio == -1 && errno != 0) {
+        LOGW("getpriority() failed: %s\n", strerror(errno));
+        return THREAD_NORM_PRIORITY;
+    }
+
+    jprio = THREAD_MIN_PRIORITY;
+    for (i = 0; i < NELEM(kNiceValues); i++) {
+        if (sysprio >= kNiceValues[i])
+            break;
+        jprio++;
+    }
+    if (jprio > THREAD_MAX_PRIORITY)
+        jprio = THREAD_MAX_PRIORITY;
+
+    return jprio;
+}
+
+
+/*
+ * 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:
+    LOGE("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";
+    }
+}
+
+/*
+ * 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;
+    char schedulerGroupBuf[32];
+    bool isDaemon;
+    int priority;               // java.lang.Thread priority
+    int policy;                 // pthread policy
+    struct sched_param sp;      // pthread scheduling parameters
+    char schedstatBuf[64];      // contents of /proc/[pid]/task/[tid]/schedstat
+    int schedstatFd;
+
+    /*
+     * 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) {
+        LOGI("Can't dump thread %d: threadObj not set\n", 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);
+
+    if (pthread_getschedparam(pthread_self(), &policy, &sp) != 0) {
+        LOGW("Warning: pthread_getschedparam failed\n");
+        policy = -1;
+        sp.sched_priority = -1;
+    }
+    if (getSchedulerGroup(thread->systemTid, schedulerGroupBuf,
+                sizeof(schedulerGroupBuf)) == 0 &&
+            schedulerGroupBuf[0] == '\0') {
+        strcpy(schedulerGroupBuf, "default");
+    }
+
+    /* a null value for group is not expected, but deal with it anyway */
+    groupObj = (Object*) dvmGetFieldObject(threadObj,
+                gDvm.offJavaLangThread_group);
+    if (groupObj != NULL) {
+        int offset = dvmFindFieldOffset(gDvm.classJavaLangThreadGroup,
+            "name", "Ljava/lang/String;");
+        if (offset < 0) {
+            LOGW("Unable to find 'name' field in ThreadGroup\n");
+        } else {
+            nameStr = (StringObject*) dvmGetFieldObject(groupObj, offset);
+            groupName = dvmCreateCstrFromString(nameStr);
+        }
+    }
+    if (groupName == NULL)
+        groupName = strdup("(null; initializing?)");
+
+    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),
+        policy, sp.sched_priority, schedulerGroupBuf, (int)thread->handle);
+
+    snprintf(schedstatBuf, sizeof(schedstatBuf), "/proc/%d/task/%d/schedstat",
+             getpid(), thread->systemTid);
+    schedstatFd = open(schedstatBuf, O_RDONLY);
+    if (schedstatFd >= 0) {
+        int bytes;
+        bytes = read(schedstatFd, schedstatBuf, sizeof(schedstatBuf) - 1);
+        close(schedstatFd);
+        if (bytes > 1) {
+            schedstatBuf[bytes-1] = 0;  // trailing newline
+            dvmPrintDebugMessage(target, "  | schedstat=( %s )\n", schedstatBuf);
+        }
+    }
+
+#ifdef WITH_MONITOR_TRACKING
+    if (!isRunning) {
+        LockedObjectData* lod = thread->pLockedObjects;
+        if (lod != NULL)
+            dvmPrintDebugMessage(target, "  | monitors held:\n");
+        else
+            dvmPrintDebugMessage(target, "  | monitors held: <none>\n");
+        while (lod != NULL) {
+            Object* obj = lod->obj;
+            if (obj->clazz == gDvm.classJavaLangClass) {
+                ClassObject* clazz = (ClassObject*) obj;
+                dvmPrintDebugMessage(target, "  >  %p[%d] (%s object for class %s)\n",
+                    obj, lod->recursionCount, obj->clazz->descriptor,
+                    clazz->descriptor);
+            } else {
+                dvmPrintDebugMessage(target, "  >  %p[%d] (%s)\n",
+                    obj, lod->recursionCount, obj->clazz->descriptor);
+            }
+            lod = lod->next;
+        }
+    }
+#endif
+
+    if (isRunning)
+        dvmDumpRunningThreadStack(target, thread);
+    else
+        dvmDumpThreadStack(target, thread);
+
+    dvmReleaseTrackedAlloc(threadObj, NULL);
+    free(threadName);
+    free(groupName);
+}
+
+/*
+ * 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.
+ *
+ * Returns a newly-allocated string, or NULL if the Thread doesn't have a name.
+ */
+char* dvmGetThreadName(Thread* thread)
+{
+    StringObject* nameObj;
+
+    if (thread->threadObj == NULL) {
+        LOGW("threadObj is NULL, name not available\n");
+        return strdup("-unknown-");
+    }
+
+    nameObj = (StringObject*)
+        dvmGetFieldObject(thread->threadObj, gDvm.offJavaLangThread_name);
+    return dvmCreateCstrFromString(nameObj);
+}
+
+/*
+ * 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 hwl=%x hwll=%x)\n",
+        gDvm.threadListLock.value,
+        gDvm._threadSuspendLock.value,
+        gDvm.threadSuspendCountLock.value,
+        gDvm.gcHeapLock.value,
+        gDvm.heapWorkerLock.value,
+        gDvm.heapWorkerListLock.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;
+    }
+
+    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.)
+     */
+    LOGD("threadid=%d: sending two SIGSTKFLTs to threadid=%d (tid=%d) to"
+         " cause debuggerd dump\n",
+        dvmThreadSelf()->threadId, thread->threadId, thread->systemTid);
+    killResult = pthread_kill(thread->handle, SIGSTKFLT);
+    if (killResult != 0) {
+        LOGD("NOTE: pthread_kill #1 failed: %s\n", strerror(killResult));
+    }
+    usleep(2 * 1000 * 1000);    // TODO: timed-wait until debuggerd attaches
+    killResult = pthread_kill(thread->handle, SIGSTKFLT);
+    if (killResult != 0) {
+        LOGD("NOTE: pthread_kill #2 failed: %s\n", strerror(killResult));
+    }
+    LOGD("Sent, pausing to let debuggerd run\n");
+    usleep(8 * 1000 * 1000);    // TODO: timed-wait until debuggerd finishes
+
+    /* ignore SIGSEGV so the eventual dmvAbort() doesn't notify debuggerd */
+    signal(SIGSEGV, SIG_IGN);
+    LOGD("Continuing\n");
+}
+
+#ifdef WITH_MONITOR_TRACKING
+/*
+ * Count up the #of locked objects in the current thread.
+ */
+static int getThreadObjectCount(const Thread* self)
+{
+    LockedObjectData* lod;
+    int count = 0;
+
+    lod = self->pLockedObjects;
+    while (lod != NULL) {
+        count++;
+        lod = lod->next;
+    }
+    return count;
+}
+
+/*
+ * Add the object to the thread's locked object list if it doesn't already
+ * exist.  The most recently added object is the most likely to be released
+ * next, so we insert at the head of the list.
+ *
+ * If it already exists, we increase the recursive lock count.
+ *
+ * The object's lock may be thin or fat.
+ */
+void dvmAddToMonitorList(Thread* self, Object* obj, bool withTrace)
+{
+    LockedObjectData* newLod;
+    LockedObjectData* lod;
+    int* trace;
+    int depth;
+
+    lod = self->pLockedObjects;
+    while (lod != NULL) {
+        if (lod->obj == obj) {
+            lod->recursionCount++;
+            LOGV("+++ +recursive lock %p -> %d\n", obj, lod->recursionCount);
+            return;
+        }
+        lod = lod->next;
+    }
+
+    newLod = (LockedObjectData*) calloc(1, sizeof(LockedObjectData));
+    if (newLod == NULL) {
+        LOGE("malloc failed on %d bytes\n", sizeof(LockedObjectData));
+        return;
+    }
+    newLod->obj = obj;
+    newLod->recursionCount = 0;
+
+    if (withTrace) {
+        trace = dvmFillInStackTraceRaw(self, &depth);
+        newLod->rawStackTrace = trace;
+        newLod->stackDepth = depth;
+    }
+
+    newLod->next = self->pLockedObjects;
+    self->pLockedObjects = newLod;
+
+    LOGV("+++ threadid=%d: added %p, now %d\n",
+        self->threadId, newLod, getThreadObjectCount(self));
+}
+
+/*
+ * Remove the object from the thread's locked object list.  If the entry
+ * has a nonzero recursion count, we just decrement the count instead.
+ */
+void dvmRemoveFromMonitorList(Thread* self, Object* obj)
+{
+    LockedObjectData* lod;
+    LockedObjectData* prevLod;
+
+    lod = self->pLockedObjects;
+    prevLod = NULL;
+    while (lod != NULL) {
+        if (lod->obj == obj) {
+            if (lod->recursionCount > 0) {
+                lod->recursionCount--;
+                LOGV("+++ -recursive lock %p -> %d\n",
+                    obj, lod->recursionCount);
+                return;
+            } else {
+                break;
+            }
+        }
+        prevLod = lod;
+        lod = lod->next;
+    }
+
+    if (lod == NULL) {
+        LOGW("BUG: object %p not found in thread's lock list\n", obj);
+        return;
+    }
+    if (prevLod == NULL) {
+        /* first item in list */
+        assert(self->pLockedObjects == lod);
+        self->pLockedObjects = lod->next;
+    } else {
+        /* middle/end of list */
+        prevLod->next = lod->next;
+    }
+
+    LOGV("+++ threadid=%d: removed %p, now %d\n",
+        self->threadId, lod, getThreadObjectCount(self));
+    free(lod->rawStackTrace);
+    free(lod);
+}
+
+/*
+ * If the specified object is already in the thread's locked object list,
+ * return the LockedObjectData struct.  Otherwise return NULL.
+ */
+LockedObjectData* dvmFindInMonitorList(const Thread* self, const Object* obj)
+{
+    LockedObjectData* lod;
+
+    lod = self->pLockedObjects;
+    while (lod != NULL) {
+        if (lod->obj == obj)
+            return lod;
+        lod = lod->next;
+    }
+    return NULL;
+}
+#endif /*WITH_MONITOR_TRACKING*/
+
+
+/*
+ * GC helper functions
+ */
+
+/*
+ * Add the contents of the registers from the interpreted call stack.
+ */
+static void gcScanInterpStackReferences(Thread *thread)
+{
+    const u4 *framePtr;
+#if WITH_EXTRA_GC_CHECKS > 1
+    bool first = true;
+#endif
+
+    framePtr = (const u4 *)thread->curFrame;
+    while (framePtr != NULL) {
+        const StackSaveArea *saveArea;
+        const Method *method;
+
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+        method = saveArea->method;
+        if (method != NULL) {
+#ifdef COUNT_PRECISE_METHODS
+            /* the GC is running, so no lock required */
+            if (dvmPointerSetAddEntry(gDvm.preciseMethods, method))
+                LOGI("PGC: added %s.%s %p\n",
+                    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) {
+                    LOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p\n",
+                        saveArea->xtra.currentPc, thread->currentPc2,
+                        method->clazz->descriptor, method->name, method->insns);
+                    if (saveArea->xtra.currentPc != NULL)
+                        LOGE("  pc inst = 0x%04x\n", *saveArea->xtra.currentPc);
+                    if (thread->currentPc2 != NULL)
+                        LOGE("  pc2 inst = 0x%04x\n", *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;
+            int i;
+
+            Method* nonConstMethod = (Method*) method;  // quiet gcc
+            pMap = dvmGetExpandedRegisterMap(nonConstMethod);
+            if (pMap != NULL) {
+                /* found map, get registers for this address */
+                int addr = saveArea->xtra.currentPc - method->insns;
+                regVector = dvmRegisterMapGetLine(pMap, addr);
+                if (regVector == NULL) {
+                    LOGW("PGC: map but no entry for %s.%s addr=0x%04x\n",
+                        method->clazz->descriptor, method->name, addr);
+                } else {
+                    LOGV("PGC: found map for %s.%s 0x%04x (t=%d)\n",
+                        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) {
+                    LOGVV("PGC: no map for %s.%s\n",
+                        method->clazz->descriptor, method->name);
+                }
+                regVector = NULL;
+            }
+
+            if (regVector == NULL) {
+                /* conservative scan */
+                for (i = method->registersSize - 1; i >= 0; i--) {
+                    u4 rval = *framePtr++;
+                    if (rval != 0 && (rval & 0x3) == 0) {
+                        dvmMarkIfObject((Object *)rval);
+                    }
+                }
+            } 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 (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;
+                        LOGVV("loaded bits: 0x%02x\n", bits & 0xff);
+                    }
+
+                    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 */
+                            LOGE("PGC: invalid ref in reg %d: 0x%08x\n",
+                                method->registersSize-1 - i, rval);
+                            LOGE("PGC: %s.%s addr 0x%04x\n",
+                                method->clazz->descriptor, method->name,
+                                saveArea->xtra.currentPc - method->insns);
+                        } else
+#endif
+                        {
+                            dvmMarkObjectNonNull((Object *)rval);
+                        }
+                    } 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 */
+                            LOGD("PGC: ignoring valid ref in reg %d: 0x%08x\n",
+                                method->registersSize-1 - i, rval);
+                        }
+#endif
+                    }
+                }
+                dvmReleaseRegisterMapLine(pMap, regVector);
+            }
+        }
+
+#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 gcScanReferenceTable(ReferenceTable *refTable)
+{
+    Object **op;
+
+    //TODO: these asserts are overkill; turn them off when things stablize.
+    assert(refTable != NULL);
+    assert(refTable->table != NULL);
+    assert(refTable->nextEntry != NULL);
+    assert((uintptr_t)refTable->nextEntry >= (uintptr_t)refTable->table);
+    assert(refTable->nextEntry - refTable->table <= refTable->maxEntries);
+
+    op = refTable->table;
+    while ((uintptr_t)op < (uintptr_t)refTable->nextEntry) {
+        dvmMarkObjectNonNull(*(op++));
+    }
+}
+
+#ifdef USE_INDIRECT_REF
+static void gcScanIndirectRefTable(IndirectRefTable* pRefTable)
+{
+    Object** op = pRefTable->table;
+    int numEntries = dvmIndirectRefTableEntries(pRefTable);
+    int i;
+
+    for (i = 0; i < numEntries; i++) {
+        Object* obj = *op;
+        if (obj != NULL)
+            dvmMarkObjectNonNull(obj);
+        op++;
+    }
+}
+#endif
+
+/*
+ * Scan a Thread and mark any objects it references.
+ */
+static void gcScanThread(Thread *thread)
+{
+    assert(thread != NULL);
+
+    /*
+     * The target thread must be suspended or in a state where it can't do
+     * any harm (e.g. in Object.wait()).  The only exception is the current
+     * thread, which will still be active and in the "running" state.
+     *
+     * It's possible to encounter a false-positive here because a thread
+     * transitioning to running from (say) vmwait or native will briefly
+     * set their status to running before switching to suspended.  This
+     * is highly unlikely, but does mean that we don't want to abort if
+     * the situation arises.
+     */
+    if (thread->status == THREAD_RUNNING && thread != dvmThreadSelf()) {
+        Thread* self = dvmThreadSelf();
+        LOGW("threadid=%d: Warning: GC scanning a running thread (%d)\n",
+            self->threadId, thread->threadId);
+        dvmDumpThread(thread, true);
+        LOGW("Found by:\n");
+        dvmDumpThread(self, false);
+
+        /* continue anyway */
+    }
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_THREAD_OBJECT, thread->threadId);
+
+    dvmMarkObject(thread->threadObj);   // could be NULL, when constructing
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_NATIVE_STACK, thread->threadId);
+
+    dvmMarkObject(thread->exception);   // usually NULL
+    gcScanReferenceTable(&thread->internalLocalRefTable);
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_LOCAL, thread->threadId);
+
+#ifdef USE_INDIRECT_REF
+    gcScanIndirectRefTable(&thread->jniLocalRefTable);
+#else
+    gcScanReferenceTable(&thread->jniLocalRefTable);
+#endif
+
+    if (thread->jniMonitorRefTable.table != NULL) {
+        HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_MONITOR, thread->threadId);
+
+        gcScanReferenceTable(&thread->jniMonitorRefTable);
+    }
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JAVA_FRAME, thread->threadId);
+
+    gcScanInterpStackReferences(thread);
+
+    HPROF_CLEAR_GC_SCAN_STATE();
+}
+
+static void gcScanAllThreads()
+{
+    Thread *thread;
+
+    /* Lock the thread list so we can safely use the
+     * next/prev pointers.
+     */
+    dvmLockThreadList(dvmThreadSelf());
+
+    for (thread = gDvm.threadList; thread != NULL;
+            thread = thread->next)
+    {
+        /* We need to scan our own stack, so don't special-case
+         * the current thread.
+         */
+        gcScanThread(thread);
+    }
+
+    dvmUnlockThreadList();
+}
+
+void dvmGcScanRootThreadGroups()
+{
+    /* We scan the VM's list of threads instead of going
+     * through the actual ThreadGroups, but it should be
+     * equivalent.
+     *
+     * This assumes that the ThreadGroup class object is in
+     * the root set, which should always be true;  it's
+     * loaded by the built-in class loader, which is part
+     * of the root set.
+     */
+    gcScanAllThreads();
+}
diff --git a/vm/Thread.h b/vm/Thread.h
new file mode 100644
index 0000000..e336dda
--- /dev/null
+++ b/vm/Thread.h
@@ -0,0 +1,563 @@
+/*
+ * 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
+#define _DALVIK_THREAD
+
+#include "jni.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
+
+#ifdef WITH_MONITOR_TRACKING
+struct LockedObjectData;
+#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).
+ */
+typedef 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 */
+} ThreadStatus;
+
+/* thread priorities, from java.lang.Thread */
+enum {
+    THREAD_MIN_PRIORITY     = 1,
+    THREAD_NORM_PRIORITY    = 5,
+    THREAD_MAX_PRIORITY     = 10,
+};
+
+
+/* initialization */
+bool dvmThreadStartup(void);
+bool dvmThreadObjStartup(void);
+void dvmThreadShutdown(void);
+void dvmSlayDaemons(void);
+
+
+#define kJniLocalRefMin         32
+#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   (12*1024)   /* three 4K pages */
+#define kMaxStackSize       (256*1024 + STACK_OVERFLOW_RESERVE)
+
+/*
+ * Our per-thread data.
+ *
+ * These are allocated on the system heap.
+ */
+typedef struct Thread {
+    /* small unique integer; useful for "thin" locks and debug messages */
+    u4          threadId;
+
+    /*
+     * 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;
+
+    /*
+     * This is the number of times the thread has been suspended.  When the
+     * count drops to zero, the thread resumes.
+     *
+     * "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.
+     *
+     * (We could store both of these in the same 32-bit, using 16-bit
+     * halves, to make atomic ops possible.  In practice, you only need
+     * to read suspendCount, and we need to hold a mutex when making
+     * changes, so there's no need to merge them.  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;
+
+    /* thread handle, as reported by pthread_self() */
+    pthread_t   handle;
+
+    /* thread ID, only useful under Linux */
+    pid_t       systemTid;
+
+    /* start (high addr) of interp stack (subtract size to get malloc addr) */
+    u1*         interpStackStart;
+
+    /* current limit of stack; flexes for StackOverflowError */
+    const u1*   interpStackEnd;
+
+    /* interpreter stack size; our stacks are fixed-length */
+    int         interpStackSize;
+    bool        stackOverflowed;
+
+    /* FP of bottom-most (currently executing) stack frame on interp stack */
+    void*       curFrame;
+
+    /* current exception, or NULL if nothing pending */
+    Object*     exception;
+
+    /* 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;
+
+#if defined(WITH_JIT)
+    /*
+     * 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;
+#if defined(WITH_SELF_VERIFICATION)
+    /* Buffer for register state during self verification */
+    struct ShadowSpace* shadowSpace;
+#endif
+#endif
+
+    /* JNI local reference tracking */
+#ifdef USE_INDIRECT_REF
+    IndirectRefTable jniLocalRefTable;
+#else
+    ReferenceTable  jniLocalRefTable;
+#endif
+
+    /* 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;
+
+#ifdef WITH_MONITOR_TRACKING
+    /* objects locked by this thread; most recent is at head of list */
+    struct LockedObjectData* pLockedObjects;
+#endif
+
+#ifdef WITH_ALLOC_LIMITS
+    /* allocation limit, for Debug.setAllocationLimit() regression testing */
+    int         allocLimit;
+#endif
+
+    /* base time for per-thread CPU timing (used by method profiling) */
+    bool        cpuClockBaseSet;
+    u8          cpuClockBase;
+
+    /* 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
+} Thread;
+
+/* start point for an internal thread; mimics pthread args */
+typedef void* (*InternalThreadStart)(void* arg);
+
+/* args for internal thread creation */
+typedef struct InternalStartArgs {
+    /* inputs */
+    InternalThreadStart func;
+    void*       funcArg;
+    char*       name;
+    Object*     group;
+    bool        isDaemon;
+    /* result */
+    volatile Thread** pThread;
+    volatile int*     pCreateStatus;
+} InternalStartArgs;
+
+/* 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);
+/* release the thread list global lock */
+void dvmUnlockThreadList(void);
+
+/*
+ * Thread suspend/resume, used by the GC and debugger.
+ */
+typedef 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,
+#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
+} SuspendCause;
+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.
+ */
+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->suspendCount != 0);
+}
+
+/*
+ * 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 safety, hold the thread list lock.)
+ */
+char* 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);
+
+#ifdef WITH_MONITOR_TRACKING
+/*
+ * Track locks held by the current thread, along with the stack trace at
+ * the point the lock was acquired.
+ *
+ * At any given time the number of locks held across the VM should be
+ * fairly small, so there's no reason not to generate and store the entire
+ * stack trace.
+ */
+typedef struct LockedObjectData {
+    /* the locked object */
+    struct Object*  obj;
+
+    /* number of times it has been locked recursively (zero-based ref count) */
+    int             recursionCount;
+
+    /* stack trace at point of initial acquire */
+    u4              stackDepth;
+    int*            rawStackTrace;
+
+    struct LockedObjectData* next;
+} LockedObjectData;
+
+/*
+ * Add/remove/find objects from the thread's monitor list.
+ */
+void dvmAddToMonitorList(Thread* self, Object* obj, bool withTrace);
+void dvmRemoveFromMonitorList(Thread* self, Object* obj);
+LockedObjectData* dvmFindInMonitorList(const Thread* self, const Object* obj);
+#endif
+
+#endif /*_DALVIK_THREAD*/
diff --git a/vm/UtfString.c b/vm/UtfString.c
new file mode 100644
index 0000000..f560dac
--- /dev/null
+++ b/vm/UtfString.c
@@ -0,0 +1,523 @@
+/*
+ * 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>
+
+/*
+ * Initialize string globals.
+ *
+ * This isn't part of the VM init sequence because it's hard to get the
+ * timing right -- we need it to happen after java/lang/String has been
+ * loaded, but before anybody wants to use a string.  It's easiest to
+ * just initialize it on first use.
+ *
+ * In some unusual circumstances (e.g. trying to throw an exception because
+ * String implements java/lang/CharSequence, but CharSequence doesn't exist)
+ * we can try to create an exception string internally before anything has
+ * really tried to use String.  In that case we basically self-destruct.
+ *
+ * We're expecting to be essentially single-threaded at this point.
+ * We employ atomics to ensure everything is observed correctly, and also
+ * to guarantee that we do detect a problem if our assumption is wrong.
+ */
+static bool stringStartup()
+{
+    if (gDvm.javaLangStringReady < 0) {
+        LOGE("ERROR: reentrant string initialization\n");
+        assert(false);
+        return false;
+    }
+
+    if (android_atomic_acquire_cas(0, -1, &gDvm.javaLangStringReady) != 0) {
+        LOGE("ERROR: initial string-ready state not 0 (%d)\n",
+            gDvm.javaLangStringReady);
+        return false;
+    }
+
+    if (gDvm.classJavaLangString == NULL)
+        gDvm.classJavaLangString =
+            dvmFindSystemClassNoInit("Ljava/lang/String;");
+
+    gDvm.offJavaLangString_value =
+        dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C");
+    gDvm.offJavaLangString_count =
+        dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I");
+    gDvm.offJavaLangString_offset =
+        dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I");
+    gDvm.offJavaLangString_hashCode =
+        dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I");
+
+    if (gDvm.offJavaLangString_value < 0 ||
+        gDvm.offJavaLangString_count < 0 ||
+        gDvm.offJavaLangString_offset < 0 ||
+        gDvm.offJavaLangString_hashCode < 0)
+    {
+        LOGE("VM-required field missing from java/lang/String\n");
+        return false;
+    }
+
+    bool badValue = false;
+    if (gDvm.offJavaLangString_value != STRING_FIELDOFF_VALUE) {
+        LOGE("InlineNative: String.value offset = %d, expected %d\n",
+            gDvm.offJavaLangString_value, STRING_FIELDOFF_VALUE);
+        badValue = true;
+    }
+    if (gDvm.offJavaLangString_count != STRING_FIELDOFF_COUNT) {
+        LOGE("InlineNative: String.count offset = %d, expected %d\n",
+            gDvm.offJavaLangString_count, STRING_FIELDOFF_COUNT);
+        badValue = true;
+    }
+    if (gDvm.offJavaLangString_offset != STRING_FIELDOFF_OFFSET) {
+        LOGE("InlineNative: String.offset offset = %d, expected %d\n",
+            gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET);
+        badValue = true;
+    }
+    if (gDvm.offJavaLangString_hashCode != STRING_FIELDOFF_HASHCODE) {
+        LOGE("InlineNative: String.hashCode offset = %d, expected %d\n",
+            gDvm.offJavaLangString_hashCode, STRING_FIELDOFF_HASHCODE);
+        badValue = true;
+    }
+    if (badValue)
+        return false;
+
+    android_atomic_release_store(1, &gDvm.javaLangStringReady);
+
+    return true;
+}
+
+/*
+ * Discard heap-allocated storage.
+ */
+void dvmStringShutdown()
+{
+    // currently unused
+}
+
+/*
+ * 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.)
+ */
+int dvmUtf8Len(const char* utf8Str)
+{
+    int ic, len = 0;
+
+    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 dvmComputeUtf16Hash(const u2* utf16Str, int len)
+{
+    u4 hash = 0;
+
+    while (len--)
+        hash = hash * 31 + *utf16Str++;
+
+    return hash;
+}
+u4 dvmComputeStringHash(const StringObject* strObj) {
+    ArrayObject* chars = (ArrayObject*) dvmGetFieldObject((Object*) strObj,
+                                STRING_FIELDOFF_VALUE);
+    int offset, len;
+
+    len = dvmGetFieldInt((Object*) strObj, STRING_FIELDOFF_COUNT);
+    offset = dvmGetFieldInt((Object*) strObj, STRING_FIELDOFF_OFFSET);
+
+    return dvmComputeUtf16Hash((u2*) chars->contents + offset, len);
+}
+
+/*
+ * Create a new java/lang/String object, using the string data in "utf8Str".
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstr(const char* utf8Str)
+{
+    assert(utf8Str != NULL);
+    return dvmCreateStringFromCstrAndLength(utf8Str, dvmUtf8Len(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)
+{
+    StringObject* newObj;
+    ArrayObject* chars;
+    u4 hashCode = 0;
+
+    //LOGV("Creating String from '%s'\n", utf8Str);
+    assert(utf8Str != NULL);
+
+    if (gDvm.javaLangStringReady <= 0) {
+        if (!stringStartup())
+            return NULL;
+    }
+
+    /* init before alloc */
+    if (!dvmIsClassInitialized(gDvm.classJavaLangString) &&
+        !dvmInitClass(gDvm.classJavaLangString))
+    {
+        return NULL;
+    }
+
+    newObj = (StringObject*) dvmAllocObject(gDvm.classJavaLangString,
+                ALLOC_DEFAULT);
+    if (newObj == NULL)
+        return NULL;
+
+    chars = dvmAllocPrimitiveArray('C', utf16Length, ALLOC_DEFAULT);
+    if (chars == NULL) {
+        dvmReleaseTrackedAlloc((Object*) newObj, NULL);
+        return NULL;
+    }
+    dvmConvertUtf8ToUtf16((u2*)chars->contents, utf8Str);
+    hashCode = dvmComputeUtf16Hash((u2*) chars->contents, utf16Length);
+
+    dvmSetFieldObject((Object*)newObj, STRING_FIELDOFF_VALUE,
+        (Object*)chars);
+    dvmReleaseTrackedAlloc((Object*) chars, NULL);
+    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_COUNT, utf16Length);
+    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
+    /* leave offset set to zero */
+
+    /* debugging stuff */
+    //dvmDumpObject((Object*)newObj);
+    //printHexDumpEx(ANDROID_LOG_DEBUG, chars->contents, utf16Length * 2,
+    //    kHexDumpMem);
+
+    /* caller may need to dvmReleaseTrackedAlloc(newObj) */
+    return newObj;
+}
+
+/*
+ * Create a new java/lang/String object, using the Unicode data.
+ */
+StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len)
+{
+    StringObject* newObj;
+    ArrayObject* chars;
+    u4 hashCode = 0;
+
+    /* we allow a null pointer if the length is zero */
+    assert(len == 0 || unichars != NULL);
+
+    if (gDvm.javaLangStringReady <= 0) {
+        if (!stringStartup())
+            return NULL;
+    }
+
+    /* init before alloc */
+    if (!dvmIsClassInitialized(gDvm.classJavaLangString) &&
+        !dvmInitClass(gDvm.classJavaLangString))
+    {
+        return NULL;
+    }
+
+    newObj = (StringObject*) dvmAllocObject(gDvm.classJavaLangString,
+        ALLOC_DEFAULT);
+    if (newObj == NULL)
+        return NULL;
+
+    chars = dvmAllocPrimitiveArray('C', len, ALLOC_DEFAULT);
+    if (chars == NULL) {
+        dvmReleaseTrackedAlloc((Object*) newObj, NULL);
+        return NULL;
+    }
+    if (len > 0)
+        memcpy(chars->contents, unichars, len * sizeof(u2));
+    hashCode = dvmComputeUtf16Hash((u2*) chars->contents, len);
+
+    dvmSetFieldObject((Object*)newObj, STRING_FIELDOFF_VALUE,
+        (Object*)chars);
+    dvmReleaseTrackedAlloc((Object*) chars, NULL);
+    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_COUNT, len);
+    dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
+    /* leave offset set to zero */
+
+    /* debugging stuff */
+    //dvmDumpObject((Object*)newObj);
+    //printHexDumpEx(ANDROID_LOG_DEBUG, chars->contents, len*2, kHexDumpMem);
+
+    /* caller must dvmReleaseTrackedAlloc(newObj) */
+    return newObj;
+}
+
+/*
+ * Create a new C string from a java/lang/String object.
+ *
+ * Returns NULL if the object is NULL.
+ */
+char* dvmCreateCstrFromString(StringObject* jstr)
+{
+    char* newStr;
+    ArrayObject* chars;
+    int len, byteLen, offset;
+    const u2* data;
+
+    assert(gDvm.javaLangStringReady > 0);
+
+    if (jstr == NULL)
+        return NULL;
+
+    len = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
+    offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
+    chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+                                STRING_FIELDOFF_VALUE);
+    data = (const u2*) chars->contents + offset;
+    assert(offset + len <= (int) chars->length);
+
+    byteLen = utf16_utf8ByteLen(data, len);
+    newStr = (char*) malloc(byteLen+1);
+    if (newStr == NULL)
+        return NULL;
+    convertUtf16ToUtf8(newStr, data, len);
+
+    return newStr;
+}
+
+/*
+ * Create a UTF-8 C string from a region of a java/lang/String.  (Used by
+ * the JNI GetStringUTFRegion call.)
+ */
+void dvmCreateCstrFromStringRegion(StringObject* jstr, int start, int len,
+    char* buf)
+{
+    const u2* data;
+
+    data = dvmStringChars(jstr) + start;
+    convertUtf16ToUtf8(buf, data, len);
+}
+
+/*
+ * Compute the length, in modified UTF-8, of a java/lang/String object.
+ *
+ * Does not include the terminating null byte.
+ */
+int dvmStringUtf8ByteLen(StringObject* jstr)
+{
+    ArrayObject* chars;
+    int len, offset;
+    const u2* data;
+
+    assert(gDvm.javaLangStringReady > 0);
+
+    if (jstr == NULL)
+        return 0;       // should we throw something?  assert?
+
+    len = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
+    offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
+    chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+                                STRING_FIELDOFF_VALUE);
+    data = (const u2*) chars->contents + offset;
+    assert(offset + len <= (int) chars->length);
+
+    return utf16_utf8ByteLen(data, len);
+}
+
+/*
+ * Get the string's length.
+ */
+int dvmStringLen(StringObject* jstr)
+{
+    return dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
+}
+
+/*
+ * Get the char[] object from the String.
+ */
+ArrayObject* dvmStringCharArray(StringObject* jstr)
+{
+    return (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+                                STRING_FIELDOFF_VALUE);
+}
+
+/*
+ * Get the string's data.
+ */
+const u2* dvmStringChars(StringObject* jstr)
+{
+    ArrayObject* chars;
+    int offset;
+
+    offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
+    chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+                                STRING_FIELDOFF_VALUE);
+    return (const u2*) 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;
+    ArrayObject* chars1;
+    ArrayObject* chars2;
+    int len1, len2, offset1, offset2;
+
+    assert(gDvm.javaLangStringReady > 0);
+
+    /* get offset and length into char array; all values are in 16-bit units */
+    len1 = dvmGetFieldInt((Object*) strObj1, STRING_FIELDOFF_COUNT);
+    offset1 = dvmGetFieldInt((Object*) strObj1, STRING_FIELDOFF_OFFSET);
+    len2 = dvmGetFieldInt((Object*) strObj2, STRING_FIELDOFF_COUNT);
+    offset2 = dvmGetFieldInt((Object*) strObj2, STRING_FIELDOFF_OFFSET);
+    if (len1 != len2)
+        return len1 - len2;
+
+    chars1 = (ArrayObject*) dvmGetFieldObject((Object*) strObj1,
+                                STRING_FIELDOFF_VALUE);
+    chars2 = (ArrayObject*) dvmGetFieldObject((Object*) 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*) chars1->contents + offset1,
+                  (const u2*) chars2->contents + offset2,
+                  len1 * sizeof(u2));
+}
diff --git a/vm/UtfString.h b/vm/UtfString.h
new file mode 100644
index 0000000..b291f5a
--- /dev/null
+++ b/vm/UtfString.h
@@ -0,0 +1,138 @@
+/*
+ * 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
+#define _DALVIK_STRING
+
+/*
+ * (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.
+ */
+u4 dvmComputeStringHash(const StringObject* strObj);
+
+/*
+ * 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, 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.
+ */
+int 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(StringObject* jstr);
+
+/*
+ * Create a UTF-8 C string from a region of a java/lang/String.  (Used by
+ * the JNI GetStringUTFRegion call.)
+ */
+void dvmCreateCstrFromStringRegion(StringObject* jstr, int start, int len,
+    char* buf);
+
+/*
+ * Compute the length in bytes of the modified UTF-8 representation of a
+ * string.
+ */
+int dvmStringUtf8ByteLen(StringObject* jstr);
+
+/*
+ * Get the length in Unicode characters of a string.
+ */
+int dvmStringLen(StringObject* jstr);
+
+/*
+ * Get the char[] object from the String.
+ */
+ArrayObject* dvmStringCharArray(StringObject* jstr);
+
+/*
+ * Get a pointer to the Unicode data.
+ */
+const u2* dvmStringChars(StringObject* jstr);
+
+/*
+ * Compare two string objects.  (This is a dvmHashTableLookup() callback.)
+ */
+int dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2);
+
+#endif /*_DALVIK_STRING*/
diff --git a/vm/alloc/Alloc.c b/vm/alloc/Alloc.c
new file mode 100644
index 0000000..7b56a35
--- /dev/null
+++ b/vm/alloc/Alloc.c
@@ -0,0 +1,350 @@
+/*
+ * 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 "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+
+#if WITH_HPROF && WITH_HPROF_STACK
+#include "hprof/Hprof.h"
+#endif
+
+
+/*
+ * 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(void)
+{
+    dvmInitMutex(&gDvm.gcHeapLock);
+
+    return dvmHeapStartup();
+}
+
+/*
+ * Post-zygote heap initialization, including starting
+ * the HeapWorker thread.
+ */
+bool dvmGcStartupAfterZygote(void)
+{
+    if (!dvmHeapWorkerStartup()) {
+        return false;
+    }
+    return dvmHeapStartupAfterZygote();
+}
+
+/*
+ * Shutdown the threads internal to the garbage collector.
+ */
+void dvmGcThreadShutdown(void)
+{
+    dvmHeapWorkerShutdown();
+    dvmHeapThreadShutdown();
+}
+
+/*
+ * Shut the GC down.
+ */
+void dvmGcShutdown(void)
+{
+    //TODO: grab and destroy the lock
+    dvmHeapShutdown();
+}
+
+/*
+ * Do any last-minute preparation before we call fork() for the first time.
+ */
+bool dvmGcPreZygoteFork(void)
+{
+    return dvmHeapSourceStartupBeforeFork();
+}
+
+/*
+ * 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) {
+        LOGE("Unable to find %s\n", descriptor);
+        return NULL;
+    }
+
+    init = dvmFindDirectMethodByDescriptor(clazz, "<init>",
+            "(Ljava/lang/String;)V");
+    if (init == NULL) {
+        LOGE("Unable to find String-arg constructor for %s\n", 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) {
+            LOGW("Could not allocate message string \"%s\"\n", 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(void)
+{
+    /*
+     * 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)
+    {
+        LOGW("Unable to create stock exceptions\n");
+        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(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz));
+
+    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) {
+        flags |= ALLOC_FINALIZABLE;
+    }
+
+    /* allocate on GC heap; memory is zeroed out */
+    newObj = dvmMalloc(clazz->objectSize, flags);
+    if (newObj != NULL) {
+        DVM_OBJECT_INIT(newObj, clazz);
+#if WITH_HPROF && WITH_HPROF_STACK
+        hprofFillInStackTrace(newObj);
+#endif
+        dvmTrackAllocation(clazz, clazz->objectSize);
+    }
+
+    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)
+{
+    Object* copy;
+    int size;
+    int flags;
+
+    assert(dvmIsValidObject(obj));
+
+    /* 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(obj->clazz != gDvm.classJavaLangClass);
+
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE))
+        flags = ALLOC_DEFAULT | ALLOC_FINALIZABLE;
+    else
+        flags = ALLOC_DEFAULT;
+
+    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        size = dvmArrayObjectSize((ArrayObject *)obj);
+    } else {
+        size = obj->clazz->objectSize;
+    }
+
+    copy = dvmMalloc(size, flags);
+    if (copy == NULL)
+        return NULL;
+#if WITH_HPROF && WITH_HPROF_STACK
+    hprofFillInStackTrace(copy);
+    dvmTrackAllocation(obj->clazz, size);
+#endif
+
+    memcpy(copy, obj, size);
+    DVM_LOCK_INIT(&copy->lock);
+    dvmWriteBarrierObject(copy);
+
+    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.
+ *
+ * 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(self != NULL);
+    if (!dvmAddToReferenceTable(&self->internalLocalRefTable, obj)) {
+        LOGE("threadid=%d: unable to add %p to internal ref table\n",
+            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))
+    {
+        LOGE("threadid=%d: failed to remove %p from internal ref table\n",
+            self->threadId, obj);
+        dvmAbort();
+    }
+}
+
+
+/*
+ * Explicitly initiate garbage collection.
+ */
+void dvmCollectGarbage(bool collectSoftReferences)
+{
+    dvmLockHeap();
+    while (gDvm.gcHeap->gcRunning) {
+        dvmWaitForConcurrentGcToComplete();
+    }
+    dvmCollectGarbageInternal(collectSoftReferences, GC_EXPLICIT);
+    dvmUnlockHeap();
+}
+
+typedef struct {
+    const ClassObject *clazz;
+    size_t count;
+} CountContext;
+
+static void countInstancesOfClassCallback(void *ptr, void *arg)
+{
+    CountContext *ctx = arg;
+    const Object *obj = ptr;
+
+    assert(ctx != NULL);
+    if (obj->clazz == ctx->clazz) {
+        ctx->count += 1;
+    }
+}
+
+size_t dvmCountInstancesOfClass(const ClassObject *clazz)
+{
+    CountContext ctx = { clazz, 0 };
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    dvmLockHeap();
+    dvmHeapBitmapWalk(bitmap, countInstancesOfClassCallback, &ctx);
+    dvmUnlockHeap();
+    return ctx.count;
+}
+
+static void countAssignableInstancesOfClassCallback(void *ptr, void *arg)
+{
+    CountContext *ctx = arg;
+    const Object *obj = ptr;
+
+    assert(ctx != NULL);
+    if (dvmInstanceof(obj->clazz, ctx->clazz)) {
+        ctx->count += 1;
+    }
+}
+
+size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz)
+{
+    CountContext ctx = { clazz, 0 };
+    HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    dvmLockHeap();
+    dvmHeapBitmapWalk(bitmap, countAssignableInstancesOfClassCallback, &ctx);
+    dvmUnlockHeap();
+    return ctx.count;
+}
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
new file mode 100644
index 0000000..fd9c633
--- /dev/null
+++ b/vm/alloc/Alloc.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.
+ */
+/*
+ * Garbage-collecting allocator.
+ */
+#ifndef _DALVIK_ALLOC_ALLOC
+#define _DALVIK_ALLOC_ALLOC
+
+#include <stddef.h>
+
+/*
+ * Initialization.
+ */
+bool dvmGcStartup(void);
+bool dvmCreateStockExceptions(void);
+bool dvmGcStartupAfterZygote(void);
+void dvmGcShutdown(void);
+void dvmGcThreadShutdown(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.
+ */
+Object* dvmAllocObject(ClassObject* clazz, int flags);
+
+/* flags for dvmMalloc */
+enum {
+    ALLOC_DEFAULT       = 0x00,
+    ALLOC_DONT_TRACK    = 0x01,     /* don't add to internal tracking list */
+    ALLOC_FINALIZABLE   = 0x02,     /* call finalize() before freeing */
+};
+
+/*
+ * Call when a request is so far off that we can't call dvmMalloc().  Throws
+ * an exception with the specified message.
+ */
+void dvmThrowBadAllocException(const char* msg);
+
+/*
+ * 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.
+ */
+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.
+ */
+void dvmReleaseTrackedAlloc(Object* obj, Thread* self);
+
+/*
+ * Returns true iff <obj> points to a valid allocated object.
+ */
+bool dvmIsValidObject(const Object* obj);
+
+/*
+ * Create a copy of an object.
+ *
+ * The new object will be added to the "tracked alloc" table.
+ */
+Object* dvmCloneObject(Object* obj);
+
+/*
+ * Validate the object pointer.  Returns "false" and throws an exception if
+ * "obj" is null or invalid.
+ *
+ * This may be used in performance critical areas as a null-pointer check;
+ * anything else here should be for debug builds only.  In particular, for
+ * "release" builds we want to skip the call to dvmIsValidObject() -- the
+ * classfile validation will screen out code that puts invalid data into
+ * object reference registers.
+ */
+INLINE int dvmValidateObject(Object* obj)
+{
+    if (obj == NULL) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        dvmAbort();
+        dvmThrowException("Ljava/lang/InternalError;",
+            "VM detected invalid object ptr");
+        return false;
+    }
+#endif
+#ifndef NDEBUG
+    /* check for heap corruption */
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        dvmAbort();
+        dvmThrowException("Ljava/lang/InternalError;",
+            "VM detected invalid object class ptr");
+        return false;
+    }
+#endif
+    return true;
+}
+
+/*
+ * 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);
+
+/*
+ * If set is true, sets the new minimum heap size to size; always
+ * returns the current (or previous) size.  If size is zero,
+ * removes the current minimum constraint (if present).
+ */
+size_t dvmMinimumHeapSize(size_t size, bool set);
+
+/*
+ * Updates the internal count of externally-allocated memory.  If there's
+ * enough room for that memory, returns true.  If not, returns false and
+ * does not update the count.
+ *
+ * May cause a GC as a side-effect.
+ */
+bool dvmTrackExternalAllocation(size_t n);
+
+/*
+ * Reduces the internal count of externally-allocated memory.
+ */
+void dvmTrackExternalFree(size_t n);
+
+/*
+ * Returns the number of externally-allocated bytes being tracked by
+ * dvmTrackExternalAllocation/Free().
+ */
+size_t dvmGetExternalBytesAllocated(void);
+
+/*
+ * 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);
+
+#endif /*_DALVIK_ALLOC_ALLOC*/
diff --git a/vm/alloc/CardTable.c b/vm/alloc/CardTable.c
new file mode 100644
index 0000000..9e3677b
--- /dev/null
+++ b/vm/alloc/CardTable.c
@@ -0,0 +1,271 @@
+/*
+ * 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/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(void)
+{
+    size_t length;
+    void *allocBase;
+    u1 *biasedBase;
+    GcHeap *gcHeap = gDvm.gcHeap;
+    void *heapBase = dvmHeapSourceGetBase();
+    assert(gcHeap != NULL);
+    assert(heapBase != NULL);
+
+    /* Set up the card table */
+    length = gDvm.heapSizeMax / 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 = allocBase;
+    gcHeap->cardTableLength = length;
+    /* All zeros is the correct initial value; all clean. */
+    assert(GC_CARD_CLEAN == 0);
+
+    biasedBase = (u1 *)((uintptr_t)allocBase -
+                        ((uintptr_t)heapBase >> GC_CARD_SHIFT));
+    if (((uintptr_t)biasedBase & 0xff) != GC_CARD_DIRTY) {
+        int offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff);
+        biasedBase += offset + (offset < 0 ? 0x100 : 0);
+    }
+    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(void)
+{
+    assert(gDvm.gcHeap->cardTableBase != NULL);
+    memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, gDvm.gcHeap->cardTableLength);
+}
+
+/*
+ * Returns true iff the address is within the bounds of the card table.
+ */
+bool dvmIsValidCard(const u1 *cardAddr)
+{
+    GcHeap *h = gDvm.gcHeap;
+    return cardAddr >= h->cardTableBase &&
+        cardAddr < &h->cardTableBase[h->cardTableLength];
+}
+
+/*
+ * Returns the address of the relevent 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.
+ */
+typedef struct {
+    HeapBitmap *markBits;
+    size_t whiteRefs;
+} WhiteReferenceCounter;
+
+/*
+ * 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 = arg;
+    if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
+        return;
+    }
+    ctx->whiteRefs += 1;
+}
+
+/*
+ * 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)
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    const Object **ptr;
+
+    for (ptr = ctx->stack.top; ptr != ctx->stack.base; ++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(void *ptr, void *arg)
+{
+    Object *obj = ptr;
+    WhiteReferenceCounter ctx = { 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 {
+        LOGE("Verify failed, object %p is gray and on an unmarked card", obj);
+        dvmDumpObject(obj);
+        dvmAbort();
+    }
+}
+
+/*
+ * Verifies that gray objects are on a dirty card.
+ */
+void dvmVerifyCardTable(void)
+{
+    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..1a6db11
--- /dev/null
+++ b/vm/alloc/CardTable.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+#define _DALVIK_ALLOC_CARDTABLE
+
+#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(void);
+
+/*
+ * 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);
+
+/*
+ * 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*/
diff --git a/vm/alloc/Copying.c b/vm/alloc/Copying.c
new file mode 100644
index 0000000..940d9f5
--- /dev/null
+++ b/vm/alloc/Copying.c
@@ -0,0 +1,2360 @@
+/*
+ * 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"
+#include "alloc/clz.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 LOGI
+#define LOG_PIN LOGI
+#define LOG_PROM LOGI
+#define LOG_REF LOGI
+#define LOG_SCAV LOGI
+#define LOG_TRAN LOGI
+#define LOG_VER LOGI
+#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(void);
+
+/*
+ * 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)
+{
+    size_t i;
+
+    for (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)
+{
+    void *addr;
+    int flags, prot;
+
+    flags = MAP_PRIVATE | MAP_ANONYMOUS;
+    prot = PROT_READ | PROT_WRITE;
+    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)
+{
+    int res;
+
+    assert(addr != NULL);
+    assert((uintptr_t)addr % SYSTEM_PAGE_SIZE == 0);
+    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)
+{
+    void *addr;
+    size_t allocBlocks, totalBlocks;
+    size_t i, j;
+
+    allocBlocks = heapSource->allocBlocks;
+    totalBlocks = heapSource->totalBlocks;
+    /* Check underflow. */
+    assert(blocks != 0);
+    /* Check overflow. */
+    if (allocBlocks + blocks > totalBlocks / 2) {
+        return NULL;
+    }
+    /* Scan block map. */
+    for (i = 0; i < totalBlocks; ++i) {
+        /* Check fit. */
+        for (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 (j = 1; j < blocks; ++j) {
+            heapSource->blockSpace[i+j] = BLOCK_CONTINUED;
+        }
+        heapSource->allocBlocks += blocks;
+        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. */
+    LOGE("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)
+{
+    u1 *addr;
+    size_t i;
+
+    assert(heapSource != NULL);
+    assert(block < heapSource->totalBlocks);
+    addr = heapSource->blockBase + block*BLOCK_SIZE;
+    memset(addr, 0xCC, BLOCK_SIZE);
+    for (i = 0; i < BLOCK_SIZE; i += 8) {
+        dvmHeapBitmapClearObjectBit(&heapSource->allocBits, addr + i);
+    }
+}
+
+static void clearFromSpace(HeapSource *heapSource)
+{
+    size_t i, count;
+
+    assert(heapSource != NULL);
+    i = 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 = malloc(sizeof(*heapSource));
+    assert(heapSource != NULL);
+    memset(heapSource, 0, sizeof(*heapSource));
+
+    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 = malloc(heapSource->totalBlocks*size);
+        assert(heapSource->blockSpace != NULL);
+        memset(heapSource->blockSpace, 0, heapSource->totalBlocks*size);
+    }
+
+    dvmHeapBitmapInit(&heapSource->allocBits,
+                      heapSource->blockBase,
+                      heapSource->maximumSize,
+                      "blockBase");
+
+    /* Initialize allocation pointers. */
+    heapSource->allocPtr = allocateBlocks(heapSource, 1);
+    heapSource->allocLimit = heapSource->allocPtr + BLOCK_SIZE;
+
+    gcHeap = malloc(sizeof(*gcHeap));
+    assert(gcHeap != NULL);
+    memset(gcHeap, 0, sizeof(*gcHeap));
+    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(void)
+{
+    return true;
+}
+
+bool dvmHeapSourceStartupBeforeFork(void)
+{
+    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(enum HeapSourceValueSpec spec,
+                             size_t perHeapStats[],
+                             size_t arrayLen)
+{
+    HeapSource *heapSource;
+    size_t value;
+
+    heapSource = gDvm.gcHeap->heapSource;
+    switch (spec) {
+    case HS_EXTERNAL_BYTES_ALLOCATED:
+        value = 0;
+        break;
+    case HS_EXTERNAL_LIMIT:
+        value = 0;
+        break;
+    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(void)
+{
+    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, enum HeapSourcePtrFlag flag)
+{
+    assert(!"implemented");
+    return false;
+}
+
+size_t dvmHeapSourceChunkSize(const void *ptr)
+{
+    assert(!"implemented");
+    return 0;
+}
+
+size_t dvmHeapSourceFootprint(void)
+{
+    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(void)
+{
+    return gDvm.gcHeap->heapSource->currentSize;
+}
+
+float dvmGetTargetHeapUtilization(void)
+{
+    return 0.5f;
+}
+
+void dvmSetTargetHeapUtilization(float newTarget)
+{
+    assert(newTarget > 0.0f && newTarget < 1.0f);
+}
+
+size_t dvmMinimumHeapSize(size_t size, bool set)
+{
+    return gDvm.gcHeap->heapSource->minimumSize;
+}
+
+/*
+ * 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(void)
+{
+    /* do nothing */
+}
+
+void dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen)
+{
+    /* do nothing */
+}
+
+void dvmHeapSourceWalk(void (*callback)(const void *chunkptr, size_t chunklen,
+                                        const void *userptr, size_t userlen,
+                                        void *arg),
+                       void *arg)
+{
+    assert(!"implemented");
+}
+
+size_t dvmHeapSourceGetNumHeaps(void)
+{
+    return 1;
+}
+
+bool dvmTrackExternalAllocation(size_t n)
+{
+    /* do nothing */
+    return true;
+}
+
+void dvmTrackExternalFree(size_t n)
+{
+    /* do nothing */
+}
+
+size_t dvmGetExternalBytesAllocated(void)
+{
+    assert(!"implemented");
+    return 0;
+}
+
+void dvmHeapSourceFlip(void)
+{
+    HeapSource *heapSource;
+    size_t i;
+
+    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 (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;
+
+    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 i, sum;
+
+    sum = 0;
+    for (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)
+{
+    int i;
+
+    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 (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 (i = 0; i < obj->interfaceCount; ++i) {
+        scavengeReference((Object **) &obj->interfaces[i]);
+    }
+}
+
+/*
+ * Array object scavenging.
+ */
+static size_t scavengeArrayObject(ArrayObject *array)
+{
+    size_t i, length;
+
+    LOG_SCAV("scavengeArrayObject(array=%p)", array);
+    /* Scavenge the class object. */
+    assert(toSpaceContains(array));
+    assert(array != NULL);
+    assert(array->obj.clazz != NULL);
+    scavengeReference((Object **) array);
+    length = dvmArrayObjectSize(array);
+    /* Scavenge the array contents. */
+    if (IS_CLASS_FLAG_SET(array->obj.clazz, CLASS_ISOBJECTARRAY)) {
+        Object **contents = (Object **)array->contents;
+        for (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)) {
+        LOGE("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(void)
+{
+    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\n");
+        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\n",
+                            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.\n", 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\n");
+        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)
+{
+    ClassObject *clazz;
+    int i;
+
+    // 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. */
+    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 (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 (clazz == gDvm.classJavaLangClass) {
+        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)
+{
+    HashEntry *entry;
+    void *obj;
+    int i;
+
+    LOG_PIN(">>> pinHashTableEntries(table=%p)", table);
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (i = 0; i < table->tableSize; ++i) {
+        entry = &table->pEntries[i];
+        obj = entry->data;
+        if (obj == NULL || obj == HASH_TOMBSTONE) {
+            continue;
+        }
+        pinObject(entry->data);
+    }
+    dvmHashTableUnlock(table);
+    LOG_PIN("<<< pinHashTableEntries(table=%p)", table);
+}
+
+static void pinPrimitiveClasses(void)
+{
+    size_t length;
+    size_t i;
+
+    length = ARRAYSIZE(gDvm.primitiveClass);
+    for (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(void)
+{
+    HashTable *table;
+    HashEntry *entry;
+    Object *obj;
+    int i;
+
+    table = gDvm.internedStrings;
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (i = 0; i < table->tableSize; ++i) {
+        entry = &table->pEntries[i];
+        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(void)
+{
+    HashTable *table;
+    HashEntry *entry;
+    Object *obj;
+    int i;
+
+    table = gDvm.internedStrings;
+    if (table == NULL) {
+        return;
+    }
+    dvmHashTableLock(table);
+    for (i = 0; i < table->tableSize; ++i) {
+        entry = &table->pEntries[i];
+        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)
+{
+    Object **entry;
+
+    assert(table != NULL);
+    assert(table->table != NULL);
+    assert(table->nextEntry != NULL);
+    for (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->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\n",
+                             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) {
+                    LOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p\n",
+                        saveArea->xtra.currentPc, thread->currentPc2,
+                        method->clazz->descriptor, method->name, method->insns);
+                    if (saveArea->xtra.currentPc != NULL)
+                        LOGE("  pc inst = 0x%04x\n", *saveArea->xtra.currentPc);
+                    if (thread->currentPc2 != NULL)
+                        LOGE("  pc2 inst = 0x%04x\n", *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;
+            int i;
+
+            Method* nonConstMethod = (Method*) method;  // quiet gcc
+            pMap = dvmGetExpandedRegisterMap(nonConstMethod);
+
+            //LOG_SCAV("PGC: %s.%s\n", 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\n",
+                                 method->clazz->descriptor, method->name, addr);
+                } else {
+                    LOG_SCAV("PGC: found map for %s.%s 0x%04x (t=%d)\n",
+                                 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\n", 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 (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 */
+                            LOGE("PGC: invalid ref in reg %d: 0x%08x\n",
+                                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 */
+                            LOGD("PGC: ignoring valid ref in reg %d: 0x%08x\n",
+                                 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(void)
+{
+    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;
+    int i;
+
+    saveArea = NULL;
+    framePtr = (const u4 *)thread->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\n",
+                    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 (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\n", obj);
+                    }
+                    break;
+                }
+            }
+        } else if (method != NULL && !dvmIsNativeMethod(method)) {
+            const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
+            const u1* regVector = NULL;
+
+            LOGI("conservative : %s.%s\n", 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 (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(void)
+{
+    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(void)
+{
+    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\n", block);
+        scavengeBlock(heapSource, block);
+        heapSource->queueHead = heapSource->blockQueue[block];
+        LOG_SCAV("New queue head is %zu\n", 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(void)
+{
+    HeapSource *heapSource;
+    size_t i;
+    size_t c0, c1, c2, c7;
+
+    c0 = c1 = c2 = c7 = 0;
+    heapSource = gDvm.gcHeap->heapSource;
+    for (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 (i = 0; i < heapSource->totalBlocks; ++i) {
+        if (heapSource->blockSpace[i] != BLOCK_TO_SPACE) {
+            continue;
+        }
+        verifyBlock(heapSource, i);
+    }
+}
+
+static void scavengeGlobals(void)
+{
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangClass);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangClassArray);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangError);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangObject);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangObjectArray);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangRuntimeException);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangString);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangThread);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangVMThread);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangThreadGroup);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangThrowable);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangStackTraceElement);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangStackTraceElementArray);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangAnnotationAnnotationArray);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangAnnotationAnnotationArrayArray);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectAccessibleObject);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectConstructor);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectConstructorArray);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectField);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectFieldArray);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectMethod);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectMethodArray);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectProxy);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangExceptionInInitializerError);
+    scavengeReference((Object **)(void *)&gDvm.classJavaLangRefReference);
+    scavengeReference((Object **)(void *)&gDvm.classJavaNioReadWriteDirectByteBuffer);
+    scavengeReference((Object **)(void *)&gDvm.classJavaSecurityAccessController);
+    scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory);
+    scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember);
+    scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray);
+    scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyNioInternalDirectBuffer);
+    scavengeReference((Object **)(void *)&gDvm.classArrayBoolean);
+    scavengeReference((Object **)(void *)&gDvm.classArrayChar);
+    scavengeReference((Object **)(void *)&gDvm.classArrayFloat);
+    scavengeReference((Object **)(void *)&gDvm.classArrayDouble);
+    scavengeReference((Object **)(void *)&gDvm.classArrayByte);
+    scavengeReference((Object **)(void *)&gDvm.classArrayShort);
+    scavengeReference((Object **)(void *)&gDvm.classArrayInt);
+    scavengeReference((Object **)(void *)&gDvm.classArrayLong);
+}
+
+void describeHeap(void)
+{
+    HeapSource *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(void)  /* 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(void)
+{
+    /* do nothing */
+}
+
+void dvmHeapMarkRootSet(void)
+{
+    /* do nothing */
+}
+
+void dvmHeapScanMarkedObjects(void)
+{
+    dvmScavengeRoots();
+}
+
+void dvmHeapScheduleFinalizations(void)
+{
+    /* do nothing */
+}
+
+void dvmHeapSweepUnmarkedObjects(GcMode mode, int *numFreed, size_t *sizeFreed)
+{
+    *numFreed = 0;
+    *sizeFreed = 0;
+    /* do nothing */
+}
+
+void dvmMarkObjectNonNull(const Object *obj)
+{
+    assert(!"implemented");
+}
+
+void dvmMarkDirtyObjects(void)
+{
+    assert(!"implemented");
+}
+
+void dvmHeapSourceThreadShutdown(void)
+{
+    /* do nothing */
+}
diff --git a/vm/alloc/DdmHeap.c b/vm/alloc/DdmHeap.c
new file mode 100644
index 0000000..4cb5cae
--- /dev/null
+++ b/vm/alloc/DdmHeap.c
@@ -0,0 +1,497 @@
+/*
+ * 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/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()) {
+            LOGW("%s(): can't lock heap to clear when\n", __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, gDvm.heapSizeMax); 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 {
+            LOGI("%s(): can't lock heap to set when\n", __func__);
+            return false;
+        }
+        break;
+    default:
+        LOGI("%s(): bad when value 0x%08x\n", __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)))
+
+typedef struct HeapChunkContext {
+    u1 *buf;
+    u1 *p;
+    u1 *pieceLenField;
+    size_t bufLen;
+    size_t totalAllocationUnits;
+    int type;
+    bool merge;
+    bool needHeader;
+} HeapChunkContext;
+
+#define ALLOCATION_UNIT_SIZE 8
+
+static void
+flush_hpsg_chunk(HeapChunkContext *ctx)
+{
+    /* 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
+heap_chunk_callback(const void *chunkptr, size_t chunklen,
+                    const void *userptr, size_t userlen, void *arg)
+{
+    HeapChunkContext *ctx = (HeapChunkContext *)arg;
+    u1 state;
+
+    UNUSED_PARAMETER(userlen);
+
+    assert((chunklen & (ALLOCATION_UNIT_SIZE-1)) == 0);
+
+    /* 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.
+     */
+    {
+        size_t needed = (((chunklen/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
+        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) {
+            LOGW("chunk is too big to transmit (chunklen=%zd, %zd bytes)\n",
+                chunklen, needed);
+            return;
+        }
+    }
+
+//TODO: notice when there's a gap and start a new heap, or at least a new range.
+    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)chunkptr); 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;
+    }
+
+    /* Determine the type of this chunk.
+     */
+    if (userptr == NULL) {
+        /* It's a free chunk.
+         */
+        state = HPSG_STATE(SOLIDITY_FREE, 0);
+    } else {
+        const Object *obj = userptr;
+        /* 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");
+
+        /* 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 (clazz == gDvm.classJavaLangClass) {
+                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);
+        }
+    }
+
+    /* Write out the chunk description.
+     */
+    chunklen /= ALLOCATION_UNIT_SIZE;   // convert to allocation units
+    ctx->totalAllocationUnits += chunklen;
+    while (chunklen > 256) {
+        *ctx->p++ = state | HPSG_PARTIAL;
+        *ctx->p++ = 255;     // length - 1
+        chunklen -= 256;
+    }
+    *ctx->p++ = state;
+    *ctx->p++ = chunklen - 1;
+}
+
+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)
+
+void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*),void*);
+
+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) {
+        dlmalloc_walk_heap(heap_chunk_callback, (void *)&ctx);
+    } 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()) {
+        LOGW("Can't lock heap for DDM HPSx dump\n");
+        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)
+{
+    LOGI("dvmDdmHandleHpsgChunk(when %d, what %d, heap %d)\n", when, what,
+         native);
+    switch (when) {
+    case HPSG_WHEN_NEVER:
+    case HPSG_WHEN_EVERY_GC:
+        break;
+    default:
+        LOGI("%s(): bad when value 0x%08x\n", __func__, when);
+        return false;
+    }
+
+    switch (what) {
+    case HPSG_WHAT_MERGED_OBJECTS:
+    case HPSG_WHAT_DISTINCT_OBJECTS:
+        break;
+    default:
+        LOGI("%s(): bad what value 0x%08x\n", __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 {
+        LOGI("%s(): can't lock heap to set when/what\n", __func__);
+        return false;
+    }
+
+    return true;
+}
diff --git a/vm/alloc/DdmHeap.h b/vm/alloc/DdmHeap.h
new file mode 100644
index 0000000..c3e11dc
--- /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
+#define _DALVIK_ALLOC_DDMHEAP
+
+/*
+ * 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
diff --git a/vm/alloc/Float12.h b/vm/alloc/Float12.h
new file mode 100644
index 0000000..324cc51
--- /dev/null
+++ b/vm/alloc/Float12.h
@@ -0,0 +1,130 @@
+/*
+ * 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_FLOAT12_H
+#define _DALVIK_FLOAT12_H
+
+/* Encodes a 32-bit number in 12 bits with +/-1.5% error,
+ * though the majority (80%) are within +/-0.25%.
+ *
+ * The encoding looks like:
+ *
+ *     EEEMMMMM MMMMMMMM MMMMMMMM
+ *     76543210 76543210 76543210
+ *
+ * where EEE is a base-16 exponent and MMMM is the mantissa.
+ * The output value is (MMMM * 16^EEE), or (MMMM << (EEE * 4)).
+ *
+ * TODO: do this in a less brain-dead way.  I'm sure we can do
+ *       it without all of these loops.
+ */
+inline unsigned short intToFloat12(unsigned int val)
+{
+    int oval = val;
+    int shift = 0;
+
+    /* Shift off the precision we don't care about.
+     * Don't round here; it biases the values too high
+     * (such that the encoded value is always greater
+     * than the actual value)
+     */
+    unsigned int pval = val;
+    while (val > 0x1ff) {
+        pval = val;
+        val >>= 1;
+        shift++;
+    }
+    if (shift > 0 && (pval & 1)) {
+        /* Round based on the last bit we shifted off.
+         */
+        val++;
+        if (val > 0x1ff) {
+            val = (val + 1) >> 1;
+            shift++;
+        }
+    }
+
+    /* Shift off enough bits to create a valid exponent.
+     * Since we care about the bits we're losing, be sure
+     * to round them.
+     */
+    while (shift % 4 != 0) {
+        val = (val + 1) >> 1;
+        shift++;
+    }
+
+    /* In the end, only round by the most-significant lost bit.
+     * This centers the values around the closest match.
+     * All of the rounding we did above guarantees that this
+     * round won't overflow past 0x1ff.
+     */
+    if (shift > 0) {
+        val = ((oval >> (shift - 1)) + 1) >> 1;
+    }
+
+    val |= (shift / 4) << 9;
+    return val;
+}
+
+inline unsigned int float12ToInt(unsigned short f12)
+{
+    return (f12 & 0x1ff) << ((f12 >> 9) * 4);
+}
+
+#if 0   // testing
+
+#include <stdio.h>
+int main(int argc, char *argv[])
+{
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s <min> <max>\n", argv[0]);
+        return 1;
+    }
+
+    unsigned int min = atoi(argv[1]);
+    unsigned int max = atoi(argv[2]);
+    if (min > max) {
+        int t = min;
+        max = min;
+        min = t;
+    } else if (min == max) {
+        max++;
+    }
+
+    while (min < max) {
+        unsigned int out;
+        unsigned short sf;
+
+        sf = intToFloat12(min);
+        out = float12ToInt(sf);
+//        printf("%d 0x%03x / 0x%03x %d (%d)\n", min, min, sf, out, (int)min - (int)out);
+        printf("%6.6f %d %d\n", ((float)(int)(min - out)) / (float)(int)min, min, out);
+        if (min <= 8192) {
+            min++;
+        } else if (min < 10000) {
+            min += 10;
+        } else if (min < 100000) {
+            min += 1000;
+        } else {
+            min += 10000;
+        }
+    }
+    return 0;
+}
+
+#endif  // testing
+
+#endif  // _DALVIK_FLOAT12_H
diff --git a/vm/alloc/GC.h b/vm/alloc/GC.h
new file mode 100644
index 0000000..62e9aa6
--- /dev/null
+++ b/vm/alloc/GC.h
@@ -0,0 +1,155 @@
+/*
+ * 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 collector
+ */
+#ifndef _DALVIK_ALLOC_GC
+#define _DALVIK_ALLOC_GC
+
+/*
+ * Initiate garbage collection.
+ *
+ * This usually happens automatically, but can also be caused by Runtime.gc().
+ */
+void dvmCollectGarbage(bool collectSoftRefs);
+
+/****
+ **** NOTE: The functions after this point will (should) only be called
+ ****       during GC.
+ ****/
+
+/*
+ * Functions that mark an object.
+ *
+ * Currently implemented in Heap.c.
+ */
+
+/*
+ * Mark an object and schedule it to be scanned for
+ * references to other objects.
+ *
+ * @param obj must be a valid object
+ */
+void dvmMarkObjectNonNull(const Object *obj);
+
+/*
+ * Mark an object and schedule it to be scanned for
+ * references to other objects.
+ *
+ * @param obj must be a valid object or NULL
+ */
+#define dvmMarkObject(obj) \
+    do { \
+        Object *DMO_obj_ = (Object *)(obj); \
+        if (DMO_obj_ != NULL) { \
+            dvmMarkObjectNonNull(DMO_obj_); \
+        } \
+    } while (false)
+
+/*
+ * If obj points to a valid object, mark it and
+ * schedule it to be scanned for references to other
+ * objects.
+ *
+ * @param obj any pointer that may be an Object, or NULL
+TODO: check for alignment, too (would require knowledge of heap chunks)
+ */
+#define dvmMarkIfObject(obj) \
+    do { \
+        Object *DMIO_obj_ = (Object *)(obj); \
+        if (DMIO_obj_ != NULL && dvmIsValidObject(DMIO_obj_)) { \
+            dvmMarkObjectNonNull(DMIO_obj_); \
+        } \
+    } while (false)
+
+/*
+ * Functions that handle scanning various objects for references.
+ */
+
+/*
+ * Mark all class objects loaded by the root class loader;
+ * most of these are the java.* classes.
+ *
+ * Currently implemented in Class.c.
+ */
+void dvmGcScanRootClassLoader(void);
+
+/*
+ * Mark all root ThreadGroup objects, guaranteeing that
+ * all live Thread objects will eventually be scanned.
+ *
+ * NOTE: this is a misnomer, because the current implementation
+ * actually only scans the internal list of VM threads, which
+ * will mark all VM-reachable Thread objects.  Someone else
+ * must scan the root class loader, which will mark java/lang/ThreadGroup.
+ * The ThreadGroup class object has static members pointing to
+ * the root ThreadGroups, and these will be marked as a side-effect
+ * of marking the class object.
+ *
+ * Currently implemented in Thread.c.
+ */
+void dvmGcScanRootThreadGroups(void);
+
+/*
+ * Mark all interned string objects.
+ *
+ * Currently implemented in Intern.c.
+ */
+void dvmGcScanInternedStrings(void);
+
+/*
+ * Remove any unmarked interned string objects from the table.
+ *
+ * Currently implemented in Intern.c.
+ */
+void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *));
+
+/*
+ * Mark all primitive class objects.
+ *
+ * Currently implemented in Array.c.
+ */
+void dvmGcScanPrimitiveClasses(void);
+
+/*
+ * Mark all JNI global references.
+ *
+ * Currently implemented in JNI.c.
+ */
+void dvmGcMarkJniGlobalRefs(void);
+
+/*
+ * Mark all debugger references.
+ *
+ * Currently implemented in Debugger.c.
+ */
+void dvmGcMarkDebuggerRefs(void);
+
+/*
+ * Optional heap profiling.
+ */
+#if WITH_HPROF && !defined(_DALVIK_HPROF_HPROF)
+#include "hprof/Hprof.h"
+#define HPROF_SET_GC_SCAN_STATE(tag_, thread_) \
+    dvmHeapSetHprofGcScanState((tag_), (thread_))
+#define HPROF_CLEAR_GC_SCAN_STATE() \
+    dvmHeapSetHprofGcScanState(0, 0)
+#else
+#define HPROF_SET_GC_SCAN_STATE(tag_, thread_)  do {} while (false)
+#define HPROF_CLEAR_GC_SCAN_STATE()  do {} while (false)
+#endif
+
+#endif  // _DALVIK_ALLOC_GC
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
new file mode 100644
index 0000000..843ee38
--- /dev/null
+++ b/vm/alloc/Heap.c
@@ -0,0 +1,1007 @@
+/*
+ * 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 "alloc/HeapBitmap.h"
+#include "alloc/Verify.h"
+#include "alloc/HeapTable.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/DdmHeap.h"
+#include "alloc/HeapSource.h"
+#include "alloc/MarkSweep.h"
+#include "alloc/Visit.h"
+
+#include "utils/threads.h"      // need Android thread priorities
+#define kInvalidPriority        10000
+
+#include <cutils/sched_policy.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <errno.h>
+
+static const char* GcReasonStr[] = {
+    [GC_FOR_MALLOC] = "GC_FOR_MALLOC",
+    [GC_CONCURRENT] = "GC_CONCURRENT",
+    [GC_EXPLICIT] = "GC_EXPLICIT",
+    [GC_EXTERNAL_ALLOC] = "GC_EXTERNAL_ALLOC",
+    [GC_HPROF_DUMP_HEAP] = "GC_HPROF_DUMP_HEAP"
+};
+
+/*
+ * Initialize the GC heap.
+ *
+ * Returns true if successful, false otherwise.
+ */
+bool dvmHeapStartup()
+{
+    GcHeap *gcHeap;
+
+#if defined(WITH_ALLOC_LIMITS)
+    gDvm.checkAllocLimits = false;
+    gDvm.allocationLimit = -1;
+#endif
+
+    gcHeap = dvmHeapSourceStartup(gDvm.heapSizeStart, gDvm.heapSizeMax);
+    if (gcHeap == NULL) {
+        return false;
+    }
+    gcHeap->heapWorkerCurrentObject = NULL;
+    gcHeap->heapWorkerCurrentMethod = NULL;
+    gcHeap->heapWorkerInterpStartTime = 0LL;
+    gcHeap->ddmHpifWhen = 0;
+    gcHeap->ddmHpsgWhen = 0;
+    gcHeap->ddmHpsgWhat = 0;
+    gcHeap->ddmNhsgWhen = 0;
+    gcHeap->ddmNhsgWhat = 0;
+#if WITH_HPROF
+    gcHeap->hprofDumpOnGc = false;
+    gcHeap->hprofContext = NULL;
+#endif
+    gDvm.gcHeap = gcHeap;
+
+    /* Set up the lists and lock we'll use for finalizable
+     * and reference objects.
+     */
+    dvmInitMutex(&gDvm.heapWorkerListLock);
+    gcHeap->finalizableRefs = NULL;
+    gcHeap->pendingFinalizationRefs = NULL;
+    gcHeap->referenceOperations = NULL;
+
+    if (!dvmCardTableStartup()) {
+        LOGE_HEAP("card table startup failed.");
+        return false;
+    }
+
+    /* Initialize the HeapWorker locks and other state
+     * that the GC uses.
+     */
+    dvmInitializeHeapWorkerState();
+
+    return true;
+}
+
+bool dvmHeapStartupAfterZygote(void)
+{
+    return dvmHeapSourceStartupAfterZygote();
+}
+
+void dvmHeapShutdown()
+{
+//TODO: make sure we're locked
+    if (gDvm.gcHeap != NULL) {
+        dvmCardTableShutdown();
+         /* Tables are allocated on the native heap; they need to be
+         * cleaned up explicitly.  The process may stick around, so we
+         * don't want to leak any native memory.
+         */
+        dvmHeapFreeLargeTable(gDvm.gcHeap->finalizableRefs);
+        gDvm.gcHeap->finalizableRefs = NULL;
+
+        dvmHeapFreeLargeTable(gDvm.gcHeap->pendingFinalizationRefs);
+        gDvm.gcHeap->pendingFinalizationRefs = NULL;
+
+        dvmHeapFreeLargeTable(gDvm.gcHeap->referenceOperations);
+        gDvm.gcHeap->referenceOperations = NULL;
+
+        /* 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(void)
+{
+    dvmHeapSourceThreadShutdown();
+}
+
+/*
+ * We've been asked to allocate something we can't, e.g. an array so
+ * large that (length * elementWidth) is larger than 2^31.
+ *
+ * _The Java Programming Language_, 4th edition, says, "you can be sure
+ * that all SoftReferences to softly reachable objects will be cleared
+ * before an OutOfMemoryError is thrown."
+ *
+ * It's unclear whether that holds for all situations where an OOM can
+ * be thrown, or just in the context of an allocation that fails due
+ * to lack of heap space.  For simplicity we just throw the exception.
+ *
+ * (OOM due to actually running out of space is handled elsewhere.)
+ */
+void dvmThrowBadAllocException(const char* msg)
+{
+    dvmThrowException("Ljava/lang/OutOfMemoryError;", msg);
+}
+
+/*
+ * 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);
+}
+
+/* Pop an object from the list of pending finalizations and
+ * reference clears/enqueues, and return the object.
+ * The caller must call dvmReleaseTrackedAlloc()
+ * on the object when finished.
+ *
+ * Typically only called by the heap worker thread.
+ */
+Object *dvmGetNextHeapWorkerObject(HeapWorkerOperation *op)
+{
+    Object *obj;
+    GcHeap *gcHeap = gDvm.gcHeap;
+
+    assert(op != NULL);
+
+    dvmLockMutex(&gDvm.heapWorkerListLock);
+
+    obj = dvmHeapGetNextObjectFromLargeTable(&gcHeap->referenceOperations);
+    if (obj != NULL) {
+        *op = WORKER_ENQUEUE;
+    } else {
+        obj = dvmHeapGetNextObjectFromLargeTable(
+                &gcHeap->pendingFinalizationRefs);
+        if (obj != NULL) {
+            *op = WORKER_FINALIZE;
+        }
+    }
+
+    if (obj != NULL) {
+        /* Don't let the GC collect the object until the
+         * worker thread is done with it.
+         */
+        dvmAddTrackedAlloc(obj, NULL);
+    }
+
+    dvmUnlockMutex(&gDvm.heapWorkerListLock);
+
+    return obj;
+}
+
+/* Do a full garbage collection, which may grow the
+ * heap as a side-effect if the live set is large.
+ */
+static void gcForMalloc(bool collectSoftReferences)
+{
+    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.
+     */
+    LOGD_HEAP("dvmMalloc initiating GC%s\n",
+            collectSoftReferences ? "(collect SoftReferences)" : "");
+    dvmCollectGarbageInternal(collectSoftReferences, GC_FOR_MALLOC);
+}
+
+/* Try as hard as possible to allocate some memory.
+ */
+static void *tryMalloc(size_t size)
+{
+    void *ptr;
+
+    /* Don't try too hard if there's no way the allocation is
+     * going to succeed.  We have to collect SoftReferences before
+     * throwing an OOME, though.
+     */
+    if (size >= gDvm.heapSizeMax) {
+        LOGW_HEAP("dvmMalloc(%zu/0x%08zx): "
+                "someone's allocating a huge buffer\n", size, size);
+        ptr = NULL;
+        goto collect_soft_refs;
+    }
+
+//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();
+        ptr = dvmHeapSourceAlloc(size);
+        if (ptr != NULL) {
+            return ptr;
+        }
+    }
+    /*
+     * Another failure.  Our thread was starved or there may be too
+     * many live objects.  Try a foreground GC.  This will have no
+     * effect if the concurrent GC is already 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\n",
+                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
+collect_soft_refs:
+    LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation\n",
+            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.\n", 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.
+             */
+            dvmThrowException("Ljava/lang/OutOfMemoryError;", 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)
+{
+    GcHeap *gcHeap = gDvm.gcHeap;
+    void *ptr;
+
+#if defined(WITH_ALLOC_LIMITS)
+    /*
+     * See if they've exceeded the allocation limit for this thread.
+     *
+     * A limit value of -1 means "no limit".
+     *
+     * This is enabled at compile time because it requires us to do a
+     * TLS lookup for the Thread pointer.  This has enough of a performance
+     * impact that we don't want to do it if we don't have to.  (Now that
+     * we're using gDvm.checkAllocLimits we may want to reconsider this,
+     * but it's probably still best to just compile the check out of
+     * production code -- one less thing to hit on every allocation.)
+     */
+    if (gDvm.checkAllocLimits) {
+        Thread* self = dvmThreadSelf();
+        if (self != NULL) {
+            int count = self->allocLimit;
+            if (count > 0) {
+                self->allocLimit--;
+            } else if (count == 0) {
+                /* fail! */
+                assert(!gDvm.initializing);
+                self->allocLimit = -1;
+                dvmThrowException("Ldalvik/system/AllocationLimitError;",
+                    "thread allocation limit exceeded");
+                return NULL;
+            }
+        }
+    }
+
+    if (gDvm.allocationLimit >= 0) {
+        assert(!gDvm.initializing);
+        gDvm.allocationLimit = -1;
+        dvmThrowException("Ldalvik/system/AllocationLimitError;",
+            "global allocation limit exceeded");
+        return NULL;
+    }
+#endif
+
+    dvmLockHeap();
+
+    /* Try as hard as possible to allocate some memory.
+     */
+    ptr = tryMalloc(size);
+    if (ptr != NULL) {
+        /* We've got the memory.
+         */
+        if ((flags & ALLOC_FINALIZABLE) != 0) {
+            /* This object is an instance of a class that
+             * overrides finalize().  Add it to the finalizable list.
+             */
+            if (!dvmHeapAddRefToLargeTable(&gcHeap->finalizableRefs,
+                                    (Object *)ptr))
+            {
+                LOGE_HEAP("dvmMalloc(): no room for any more "
+                        "finalizable objects\n");
+                dvmAbort();
+            }
+        }
+
+        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(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(void)
+{
+    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(bool clearSoftRefs, GcReason reason)
+{
+    GcHeap *gcHeap = gDvm.gcHeap;
+    u4 rootSuspend, rootSuspendTime, rootStart, rootEnd;
+    u4 dirtySuspend, dirtyStart, dirtyEnd;
+    u4 totalTime;
+    size_t numObjectsFreed, numBytesFreed;
+    size_t currAllocated, currFootprint;
+    size_t extAllocated, extLimit;
+    size_t percentFree;
+    GcMode gcMode;
+    int oldThreadPriority = kInvalidPriority;
+
+    /* The heap lock must be held.
+     */
+
+    if (gcHeap->gcRunning) {
+        LOGW_HEAP("Attempted recursive GC\n");
+        return;
+    }
+
+    gcMode = (reason == GC_FOR_MALLOC) ? GC_PARTIAL : GC_FULL;
+    gcHeap->gcRunning = true;
+
+    rootSuspend = dvmGetRelativeTimeMsec();
+    dvmSuspendAllThreads(SUSPEND_FOR_GC);
+    rootStart = dvmGetRelativeTimeMsec();
+    rootSuspendTime = rootStart - rootSuspend;
+
+    /*
+     * If we are not marking concurrently raise the priority of the
+     * thread performing the garbage collection.
+     */
+    if (reason != GC_CONCURRENT) {
+        /* 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 priorityResult = getpriority(PRIO_PROCESS, 0);
+        if (errno != 0) {
+            LOGI_HEAP("getpriority(self) failed: %s\n", strerror(errno));
+        } else if (priorityResult > ANDROID_PRIORITY_NORMAL) {
+            /* Current value is numerically greater than "normal", which
+             * in backward UNIX terms means lower priority.
+             */
+
+            if (priorityResult >= ANDROID_PRIORITY_BACKGROUND) {
+                set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND);
+            }
+
+            if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL) != 0) {
+                LOGI_HEAP("Unable to elevate priority from %d to %d\n",
+                          priorityResult, ANDROID_PRIORITY_NORMAL);
+            } else {
+                /* priority elevated; save value so we can restore it later */
+                LOGD_HEAP("Elevating priority from %d to %d\n",
+                          priorityResult, ANDROID_PRIORITY_NORMAL);
+                oldThreadPriority = priorityResult;
+            }
+        }
+    }
+
+    /* Wait for the HeapWorker thread to block.
+     * (It may also already be suspended in interp code,
+     * in which case it's not holding heapWorkerLock.)
+     */
+    dvmLockMutex(&gDvm.heapWorkerLock);
+
+    /* Make sure that the HeapWorker thread hasn't become
+     * wedged inside interp code.  If it has, this call will
+     * print a message and abort the VM.
+     */
+    dvmAssertHeapWorkerThreadRunning();
+
+    /* Lock the pendingFinalizationRefs list.
+     *
+     * Acquire the lock after suspending so the finalizer
+     * thread can't block in the RUNNING state while
+     * we try to suspend.
+     */
+    dvmLockMutex(&gDvm.heapWorkerListLock);
+
+    if (gDvm.preVerify) {
+        LOGV_HEAP("Verifying roots and heap before GC");
+        verifyRootsAndHeap();
+    }
+
+    dvmMethodTraceGCBegin();
+
+#if WITH_HPROF
+
+/* Set DUMP_HEAP_ON_DDMS_UPDATE to 1 to enable heap dumps
+ * whenever DDMS requests a heap update (HPIF chunk).
+ * The output files will appear in /data/misc, which must
+ * already exist.
+ * You must define "WITH_HPROF := true" in your buildspec.mk
+ * and recompile libdvm for this to work.
+ *
+ * To enable stack traces for each allocation, define
+ * "WITH_HPROF_STACK := true" in buildspec.mk.  This option slows down
+ * allocations and also requires 8 additional bytes per object on the
+ * GC heap.
+ */
+#define DUMP_HEAP_ON_DDMS_UPDATE 0
+#if DUMP_HEAP_ON_DDMS_UPDATE
+    gcHeap->hprofDumpOnGc |= (gcHeap->ddmHpifWhen != 0);
+#endif
+
+    if (gcHeap->hprofDumpOnGc) {
+        char nameBuf[128];
+
+        gcHeap->hprofResult = -1;
+
+        if (gcHeap->hprofFileName == NULL) {
+            /* no filename was provided; invent one */
+            sprintf(nameBuf, "/data/misc/heap-dump-tm%d-pid%d.hprof",
+                (int) time(NULL), (int) getpid());
+            gcHeap->hprofFileName = nameBuf;
+        }
+        gcHeap->hprofContext = hprofStartup(gcHeap->hprofFileName,
+                gcHeap->hprofFd, gcHeap->hprofDirectToDdms);
+        if (gcHeap->hprofContext != NULL) {
+            hprofStartHeapDump(gcHeap->hprofContext);
+        }
+        gcHeap->hprofDumpOnGc = false;
+        gcHeap->hprofFileName = NULL;
+    }
+#endif
+
+    /* Set up the marking context.
+     */
+    if (!dvmHeapBeginMarkStep(gcMode)) {
+        LOGE_HEAP("dvmHeapBeginMarkStep failed; aborting\n");
+        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.
+     */
+    gcHeap->softReferences = NULL;
+    gcHeap->weakReferences = NULL;
+    gcHeap->phantomReferences = NULL;
+
+    if (reason == GC_CONCURRENT) {
+        /*
+         * Resume threads while tracing from the roots.  We unlock the
+         * heap to allow mutator threads to allocate from free space.
+         */
+        rootEnd = dvmGetRelativeTimeMsec();
+        dvmClearCardTable();
+        dvmUnlockHeap();
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+    }
+
+    /* 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 (reason == GC_CONCURRENT) {
+        /*
+         * Re-acquire the heap lock and perform the final thread
+         * suspension.
+         */
+        dvmLockHeap();
+        dirtySuspend = dvmGetRelativeTimeMsec();
+        dvmSuspendAllThreads(SUSPEND_FOR_GC);
+        dirtyStart = dvmGetRelativeTimeMsec();
+        /*
+         * 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.
+     */
+    LOGD_HEAP("Handling soft references...");
+    if (!clearSoftRefs) {
+        dvmHandleSoftRefs(&gcHeap->softReferences);
+    }
+    dvmClearWhiteRefs(&gcHeap->softReferences);
+
+    LOGD_HEAP("Handling weak references...");
+    dvmClearWhiteRefs(&gcHeap->weakReferences);
+
+    /* Once all weak-reachable objects have been taken
+     * care of, any remaining unmarked objects can be finalized.
+     */
+    LOGD_HEAP("Finding finalizations...");
+    dvmHeapScheduleFinalizations();
+
+    LOGD_HEAP("Handling f-reachable soft references...");
+    dvmClearWhiteRefs(&gcHeap->softReferences);
+
+    LOGD_HEAP("Handling f-reachable weak references...");
+    dvmClearWhiteRefs(&gcHeap->weakReferences);
+
+    /* Any remaining objects that are not pending finalization
+     * could be phantom-reachable.  This will mark any phantom-reachable
+     * objects, as well as enqueue their references.
+     */
+    LOGD_HEAP("Handling phantom references...");
+    dvmClearWhiteRefs(&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 (reason == GC_CONCURRENT) {
+        dirtyEnd = dvmGetRelativeTimeMsec();
+        dvmUnlockHeap();
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+    }
+    dvmHeapSweepUnmarkedObjects(gcMode, reason == GC_CONCURRENT,
+                                &numObjectsFreed, &numBytesFreed);
+    LOGD_HEAP("Cleaning up...");
+    dvmHeapFinishMarkStep();
+    if (reason == GC_CONCURRENT) {
+        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.
+     */
+    if (reason != GC_EXTERNAL_ALLOC) {
+        dvmHeapSourceGrowForUtilization();
+    }
+
+    currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
+    currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+
+#if WITH_HPROF
+    if (gcHeap->hprofContext != NULL) {
+        hprofFinishHeapDump(gcHeap->hprofContext);
+//TODO: write a HEAP_SUMMARY record
+        if (hprofShutdown(gcHeap->hprofContext))
+            gcHeap->hprofResult = 0;    /* indicate success */
+        gcHeap->hprofContext = NULL;
+    }
+#endif
+
+    /* Now that we've freed up the GC heap, return any large
+     * free chunks back to the system.  They'll get paged back
+     * in the next time they're used.  Don't do it immediately,
+     * though;  if the process is still allocating a bunch of
+     * memory, we'll be taking a ton of page faults that we don't
+     * necessarily need to.
+     *
+     * Cancel any old scheduled trims, and schedule a new one.
+     */
+    dvmScheduleHeapSourceTrim(5);  // in seconds
+
+    dvmMethodTraceGCEnd();
+    LOGV_HEAP("GC finished");
+
+    gcHeap->gcRunning = false;
+
+    LOGV_HEAP("Resuming threads");
+    dvmUnlockMutex(&gDvm.heapWorkerListLock);
+    dvmUnlockMutex(&gDvm.heapWorkerLock);
+
+    if (reason == GC_CONCURRENT) {
+        /*
+         * Wake-up any threads that blocked after a failed allocation
+         * request.
+         */
+        dvmBroadcastCond(&gDvm.gcHeapCond);
+    }
+
+    if (reason != GC_CONCURRENT) {
+        dirtyEnd = dvmGetRelativeTimeMsec();
+        dvmResumeAllThreads(SUSPEND_FOR_GC);
+        if (oldThreadPriority != kInvalidPriority) {
+            if (setpriority(PRIO_PROCESS, 0, oldThreadPriority) != 0) {
+                LOGW_HEAP("Unable to reset priority to %d: %s\n",
+                          oldThreadPriority, strerror(errno));
+            } else {
+                LOGD_HEAP("Reset priority to %d\n", oldThreadPriority);
+            }
+
+            if (oldThreadPriority >= ANDROID_PRIORITY_BACKGROUND) {
+                set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND);
+            }
+        }
+    }
+
+    extAllocated = dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0);
+    extLimit = dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0);
+    percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
+    if (reason != GC_CONCURRENT) {
+        u4 markSweepTime = dirtyEnd - rootStart;
+        bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
+        totalTime = rootSuspendTime + markSweepTime;
+        LOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, external %zdK/%zdK, "
+             "paused %ums",
+             GcReasonStr[reason],
+             isSmall ? "<" : "",
+             numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
+             percentFree,
+             currAllocated / 1024, currFootprint / 1024,
+             extAllocated / 1024, extLimit / 1024,
+             markSweepTime);
+    } else {
+        u4 rootTime = rootEnd - rootStart;
+        u4 dirtySuspendTime = dirtyStart - dirtySuspend;
+        u4 dirtyTime = dirtyEnd - dirtyStart;
+        bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
+        totalTime = rootSuspendTime + rootTime + dirtySuspendTime + dirtyTime;
+        LOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, external %zdK/%zdK, "
+             "paused %ums+%ums",
+             GcReasonStr[reason],
+             isSmall ? "<" : "",
+             numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
+             percentFree,
+             currAllocated / 1024, currFootprint / 1024,
+             extAllocated / 1024, extLimit / 1024,
+             rootTime, dirtyTime);
+    }
+    dvmLogGcStats(numObjectsFreed, numBytesFreed, totalTime);
+    if (gcHeap->ddmHpifWhen != 0) {
+        LOGD_HEAP("Sending VM heap info to DDM\n");
+        dvmDdmSendHeapInfo(gcHeap->ddmHpifWhen, false);
+    }
+    if (gcHeap->ddmHpsgWhen != 0) {
+        LOGD_HEAP("Dumping VM heap to DDM\n");
+        dvmDdmSendHeapSegments(false, false);
+    }
+    if (gcHeap->ddmNhsgWhen != 0) {
+        LOGD_HEAP("Dumping native heap to DDM\n");
+        dvmDdmSendHeapSegments(false, true);
+    }
+}
+
+void dvmWaitForConcurrentGcToComplete(void)
+{
+    Thread *self = dvmThreadSelf();
+    ThreadStatus oldStatus;
+    assert(self != NULL);
+    oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+    dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
+    dvmChangeStatus(self, oldStatus);
+}
+
+#if WITH_HPROF
+/*
+ * Perform garbage collection, 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 "fileName" is NULL, a suitable name will be generated automatically.
+ * (TODO: remove this when the SIGUSR1 feature goes away)
+ *
+ * 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)
+{
+    int result;
+
+    dvmLockMutex(&gDvm.gcHeapLock);
+
+    gDvm.gcHeap->hprofDumpOnGc = true;
+    gDvm.gcHeap->hprofFileName = fileName;
+    gDvm.gcHeap->hprofFd = fd;
+    gDvm.gcHeap->hprofDirectToDdms = directToDdms;
+    dvmCollectGarbageInternal(false, GC_HPROF_DUMP_HEAP);
+    result = gDvm.gcHeap->hprofResult;
+
+    dvmUnlockMutex(&gDvm.gcHeapLock);
+
+    return result;
+}
+
+void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber)
+{
+    if (gDvm.gcHeap->hprofContext != NULL) {
+        hprofSetGcScanState(gDvm.gcHeap->hprofContext, state,
+                threadSerialNumber);
+    }
+}
+#endif
diff --git a/vm/alloc/Heap.h b/vm/alloc/Heap.h
new file mode 100644
index 0000000..ea0510f
--- /dev/null
+++ b/vm/alloc/Heap.h
@@ -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.
+ */
+/*
+ * Internal heap functions
+ */
+#ifndef _DALVIK_ALLOC_HEAP
+#define _DALVIK_ALLOC_HEAP
+
+/*
+ * 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
+
+typedef enum {
+    /* GC all heaps. */
+    GC_FULL,
+    /* GC just the first heap. */
+    GC_PARTIAL
+} GcMode;
+
+typedef enum {
+    /* Not enough space for an "ordinary" Object to be allocated. */
+    GC_FOR_MALLOC,
+    /* Automatic GC triggered by exceeding a heap occupancy threshold. */
+    GC_CONCURRENT,
+    /* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */
+    GC_EXPLICIT,
+    /* GC to try to reduce heap footprint to allow more non-GC'ed memory. */
+    GC_EXTERNAL_ALLOC,
+    /* GC to dump heap contents to a file, only used under WITH_HPROF */
+    GC_HPROF_DUMP_HEAP
+} GcReason;
+
+/*
+ * Run the garbage collector without doing any locking.
+ */
+void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason);
+
+/*
+ * Blocks the until the GC thread signals the completion of a
+ * concurrent GC.
+ */
+void dvmWaitForConcurrentGcToComplete(void);
+
+#endif  // _DALVIK_ALLOC_HEAP
diff --git a/vm/alloc/HeapBitmap.c b/vm/alloc/HeapBitmap.c
new file mode 100644
index 0000000..08d5976
--- /dev/null
+++ b/vm/alloc/HeapBitmap.c
@@ -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.
+ */
+
+#include "Dalvik.h"
+#include "HeapBitmap.h"
+#include "clz.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) {
+        LOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name);
+        return false;
+    }
+    hb->bits = 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;
+    }
+}
+
+/*
+ * 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 permitted to increase the bitmap's max; the walk
+ * will use the updated max as a terminating condition,
+ */
+void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+                            BitmapSweepCallback *callback, void *callbackArg)
+{
+    static const size_t kPointerBufSize = 128;
+    void *pointerBuf[kPointerBufSize];
+    void **pb = pointerBuf;
+    size_t index;
+    size_t i;
+
+#define FLUSH_POINTERBUF() \
+    do { \
+        (*callback)(pb - pointerBuf, (void **)pointerBuf, \
+                    callbackArg); \
+        pb = pointerBuf; \
+    } while (false)
+
+#define DECODE_BITS(hb_, bits_, update_index_) \
+    do { \
+        if (UNLIKELY(bits_ != 0)) { \
+            static const unsigned long kHighBit = \
+                    (unsigned long)1 << (HB_BITS_PER_WORD - 1); \
+            const uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + hb_->base; \
+/*TODO: hold onto ptrBase so we can shrink max later if possible */ \
+/*TODO: see if this is likely or unlikely */ \
+            while (bits_ != 0) { \
+                const int rshift = CLZ(bits_); \
+                bits_ &= ~(kHighBit >> rshift); \
+                *pb++ = (void *)(ptrBase + rshift * HB_OBJECT_ALIGNMENT); \
+            } \
+            /* Make sure that there are always enough slots available */ \
+            /* for an entire word of 1s. */ \
+            if (kPointerBufSize - (pb - pointerBuf) < HB_BITS_PER_WORD) { \
+                FLUSH_POINTERBUF(); \
+                if (update_index_) { \
+                    /* The callback may have caused hb_->max to grow. */ \
+                    index = HB_OFFSET_TO_INDEX(hb_->max - hb_->base); \
+                } \
+            } \
+        } \
+    } while (false)
+
+    assert(liveHb != NULL);
+    assert(liveHb->bits != NULL);
+    assert(markHb != NULL);
+    assert(markHb->bits != NULL);
+    assert(callback != NULL);
+
+    if (liveHb->base != markHb->base) {
+        LOGW("dvmHeapBitmapSweepWalk: bitmaps cover different heaps (%zd != %zd)",
+             liveHb->base, markHb->base);
+        return;
+    }
+    if (liveHb->bitsLen != markHb->bitsLen) {
+        LOGW("dvmHeapBitmapSweepWalk: size of bitmaps differ (%zd != %zd)",
+             liveHb->bitsLen, markHb->bitsLen);
+        return;
+    }
+    if (liveHb->max < liveHb->base && markHb->max < markHb->base) {
+        /* Easy case; both are obviously empty.
+         */
+        return;
+    }
+
+    /* First, walk along the section of the bitmaps that may be the same.
+     */
+    if (liveHb->max >= liveHb->base && markHb->max >= markHb->base) {
+        unsigned long *live, *mark;
+        uintptr_t offset;
+
+        offset = ((liveHb->max < markHb->max) ? liveHb->max : markHb->max) - liveHb->base;
+//TODO: keep track of which (and whether) one is longer for later
+        index = HB_OFFSET_TO_INDEX(offset);
+
+        live = liveHb->bits;
+        mark = markHb->bits;
+        for (i = 0; i <= index; i++) {
+//TODO: unroll this. pile up a few in locals?
+            unsigned long garbage = live[i] & ~mark[i];
+            DECODE_BITS(liveHb, garbage, false);
+//BUG: if the callback was called, either max could have changed.
+        }
+        /* The next index to look at.
+         */
+        index++;
+    } else {
+        /* One of the bitmaps is empty.
+         */
+        index = 0;
+    }
+
+    /* If one bitmap's max is larger, walk through the rest of the
+     * set bits.
+     */
+const HeapBitmap *longHb;
+unsigned long *p;
+//TODO: may be the same size, in which case this is wasted work
+    longHb = (liveHb->max > markHb->max) ? liveHb : markHb;
+    i = index;
+    index = HB_OFFSET_TO_INDEX(longHb->max - longHb->base);
+    p = longHb->bits + i;
+    for (/* i = i */; i <= index; i++) {
+//TODO: unroll this
+        unsigned long bits = *p++;
+        DECODE_BITS(longHb, bits, true);
+    }
+
+    if (pb > pointerBuf) {
+        FLUSH_POINTERBUF();
+    }
+#undef FLUSH_POINTERBUF
+#undef DECODE_BITS
+}
diff --git a/vm/alloc/HeapBitmap.h b/vm/alloc/HeapBitmap.h
new file mode 100644
index 0000000..bb09020
--- /dev/null
+++ b/vm/alloc/HeapBitmap.h
@@ -0,0 +1,307 @@
+/*
+ * 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
+#define _DALVIK_HEAP_BITMAP
+
+#include <limits.h>
+#include <stdint.h>
+#include "clz.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)))
+
+/* Return the maximum offset (exclusive) that <hb> can represent.
+ */
+#define HB_MAX_OFFSET(hb_) \
+    HB_INDEX_TO_OFFSET((hb_)->bitsLen / sizeof(*(hb_)->bits))
+
+#define HB_INLINE_PROTO(p) \
+    static inline p __attribute__((always_inline)); \
+    static inline p
+
+typedef struct {
+    /* 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;
+} HeapBitmap;
+
+typedef void BitmapCallback(void *addr, void *arg);
+typedef void BitmapScanCallback(void *addr, 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);
+
+/*
+ * Visits set bits in address order.  The callback is not permitted to
+ * change the bitmap bits or max during the traversal.
+ */
+HB_INLINE_PROTO(
+    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);
+    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;
+            while (word != 0) {
+                const int shift = CLZ(word);
+                void *addr = (void *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+                (*callback)(addr, 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.
+ */
+HB_INLINE_PROTO(
+    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);
+                void *addr = (void *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+                (*callback)(addr, 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 permitted to increase the bitmap's max; the walk
+ * will use the updated max as a terminating condition.
+ */
+void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+                            BitmapSweepCallback *callback, void *callbackArg);
+
+/*
+ * 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.
+ */
+HB_INLINE_PROTO(
+    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;
+}
+
+/*
+ * Internal function; do not call directly.
+ */
+HB_INLINE_PROTO(
+    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.
+ */
+HB_INLINE_PROTO(
+    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.
+ */
+HB_INLINE_PROTO(
+    void
+    dvmHeapBitmapSetObjectBit(HeapBitmap *hb, const void *obj)
+)
+{
+    (void)_heapBitmapModifyObjectBit(hb, obj, true, false);
+}
+
+/*
+ * Clears the bit corresponding to <obj>.  Does no range checking.
+ */
+HB_INLINE_PROTO(
+    void
+    dvmHeapBitmapClearObjectBit(HeapBitmap *hb, const void *obj)
+)
+{
+    (void)_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.
+ */
+HB_INLINE_PROTO(
+    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;
+    }
+}
+
+#undef HB_INLINE_PROTO
+
+#endif  // _DALVIK_HEAP_BITMAP
diff --git a/vm/alloc/HeapDebug.c b/vm/alloc/HeapDebug.c
new file mode 100644
index 0000000..cb8fb48
--- /dev/null
+++ b/vm/alloc/HeapDebug.c
@@ -0,0 +1,401 @@
+/*
+ * 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 <fcntl.h>
+#include <stdlib.h>
+
+#include "Dalvik.h"
+#include "HeapInternal.h"
+#include "HeapSource.h"
+#include "Float12.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);
+    default:
+        return -1;
+    }
+}
+
+/* Looks up the cmdline for the process and tries to find
+ * the most descriptive five characters, then inserts the
+ * short name into the provided event value.
+ */
+#define PROC_NAME_LEN 5
+static void insertProcessName(long long *ep)
+{
+    static bool foundRealName = false;
+    static char name[PROC_NAME_LEN] = { 'X', 'X', 'X', 'X', 'X' };
+    long long event = *ep;
+
+    if (!foundRealName) {
+        int fd = open("/proc/self/cmdline", O_RDONLY);
+        if (fd > 0) {
+            char buf[128];
+            ssize_t n = read(fd, buf, sizeof(buf) - 1);
+            close(fd);
+            if (n > 0) {
+                memset(name, 0, sizeof(name));
+                if (n <= PROC_NAME_LEN) {
+                    // The whole name fits.
+                    memcpy(name, buf, n);
+                } else {
+                    /* We need to truncate.  The name will look something
+                     * like "com.android.home".  Favor the characters
+                     * immediately following the last dot.
+                     */
+                    buf[n] = '\0';
+                    char *dot = strrchr(buf, '.');
+                    if (dot == NULL) {
+                        /* Or, look for a slash, in case it's something like
+                         * "/system/bin/runtime".
+                         */
+                        dot = strrchr(buf, '/');
+                    }
+                    if (dot != NULL) {
+                        dot++;  // Skip the dot
+                        size_t dotlen = strlen(dot);
+                        if (dotlen < PROC_NAME_LEN) {
+                            /* Use all available characters.  We know that
+                             * n > PROC_NAME_LEN from the check above.
+                             */
+                            dot -= PROC_NAME_LEN - dotlen;
+                        }
+                        strncpy(name, dot, PROC_NAME_LEN);
+                    } else {
+                        // No dot; just use the leading characters.
+                        memcpy(name, buf, PROC_NAME_LEN);
+                    }
+                }
+                if (strcmp(buf, "zygote") != 0) {
+                    /* If the process is no longer called "zygote",
+                     * cache this name.
+                     */
+                    foundRealName = true;
+                }
+            }
+        }
+    }
+
+    event &= ~(0xffffffffffLL << 24);
+    event |= (long long)name[0] << 56;
+    event |= (long long)name[1] << 48;
+    event |= (long long)name[2] << 40;
+    event |= (long long)name[3] << 32;
+    event |= (long long)name[4] << 24;
+
+    *ep = event;
+}
+
+// See device/data/etc/event-log-tags
+#define EVENT_LOG_TAG_dvm_gc_info 20001
+#define EVENT_LOG_TAG_dvm_gc_madvise_info 20002
+
+void dvmLogGcStats(size_t numFreed, size_t sizeFreed, size_t gcTimeMs)
+{
+    size_t perHeapActualSize[HEAP_SOURCE_MAX_HEAP_COUNT],
+           perHeapAllowedSize[HEAP_SOURCE_MAX_HEAP_COUNT],
+           perHeapNumAllocated[HEAP_SOURCE_MAX_HEAP_COUNT],
+           perHeapSizeAllocated[HEAP_SOURCE_MAX_HEAP_COUNT];
+    unsigned char eventBuf[1 + (1 + sizeof(long long)) * 4];
+    size_t actualSize, allowedSize, numAllocated, sizeAllocated;
+    size_t softLimit = dvmHeapSourceGetIdealFootprint();
+    size_t nHeaps = dvmHeapSourceGetNumHeaps();
+
+    /* Enough to quiet down gcc for unitialized variable check */
+    perHeapActualSize[0] = perHeapAllowedSize[0] = perHeapNumAllocated[0] =
+                           perHeapSizeAllocated[0] = 0;
+    actualSize = dvmHeapSourceGetValue(HS_FOOTPRINT, perHeapActualSize,
+                                       HEAP_SOURCE_MAX_HEAP_COUNT);
+    allowedSize = dvmHeapSourceGetValue(HS_ALLOWED_FOOTPRINT,
+                      perHeapAllowedSize, HEAP_SOURCE_MAX_HEAP_COUNT);
+    numAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED,
+                      perHeapNumAllocated, HEAP_SOURCE_MAX_HEAP_COUNT);
+    sizeAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED,
+                      perHeapSizeAllocated, HEAP_SOURCE_MAX_HEAP_COUNT);
+
+    /*
+     * Construct the the first 64-bit value to write to the log.
+     * Global information:
+     *
+     * [63   ] Must be zero
+     * [62-24] ASCII process identifier
+     * [23-12] GC time in ms
+     * [11- 0] Bytes freed
+     *
+     */
+    long long event0;
+    event0 = 0LL << 63 |
+            (long long)intToFloat12(gcTimeMs) << 12 |
+            (long long)intToFloat12(sizeFreed);
+    insertProcessName(&event0);
+
+    /*
+     * Aggregated heap stats:
+     *
+     * [63-62] 10
+     * [61-60] Reserved; must be zero
+     * [59-48] Objects freed
+     * [47-36] Actual size (current footprint)
+     * [35-24] Allowed size (current hard max)
+     * [23-12] Objects allocated
+     * [11- 0] Bytes allocated
+     */
+    long long event1;
+    event1 = 2LL << 62 |
+            (long long)intToFloat12(numFreed) << 48 |
+            (long long)intToFloat12(actualSize) << 36 |
+            (long long)intToFloat12(allowedSize) << 24 |
+            (long long)intToFloat12(numAllocated) << 12 |
+            (long long)intToFloat12(sizeAllocated);
+
+    /*
+     * Report the current state of the zygote heap(s).
+     *
+     * The active heap is always heap[0].  We can be in one of three states
+     * at present:
+     *
+     *  (1) Still in the zygote.  Zygote using heap[0].
+     *  (2) In the zygote, when the first child is started.  We created a
+     *      new heap just before the first fork() call, so the original
+     *      "zygote heap" is now heap[1], and we have a small heap[0] for
+     *      anything we do from here on.
+     *  (3) In an app process.  The app gets a new heap[0], and can also
+     *      see the two zygote heaps [1] and [2] (probably unwise to
+     *      assume any specific ordering).
+     *
+     * So if nHeaps == 1, we want the stats from heap[0]; else we want
+     * the sum of the values from heap[1] to heap[nHeaps-1].
+     *
+     *
+     * Zygote heap stats (except for the soft limit, which belongs to the
+     * active heap):
+     *
+     * [63-62] 11
+     * [61-60] Reserved; must be zero
+     * [59-48] Soft Limit (for the active heap)
+     * [47-36] Actual size (current footprint)
+     * [35-24] Allowed size (current hard max)
+     * [23-12] Objects allocated
+     * [11- 0] Bytes allocated
+     */
+    long long event2;
+    size_t zActualSize, zAllowedSize, zNumAllocated, zSizeAllocated;
+    int firstHeap = (nHeaps == 1) ? 0 : 1;
+    size_t hh;
+
+    zActualSize = zAllowedSize = zNumAllocated = zSizeAllocated = 0;
+    for (hh = firstHeap; hh < nHeaps; hh++) {
+        zActualSize += perHeapActualSize[hh];
+        zAllowedSize += perHeapAllowedSize[hh];
+        zNumAllocated += perHeapNumAllocated[hh];
+        zSizeAllocated += perHeapSizeAllocated[hh];
+    }
+    event2 = 3LL << 62 |
+            (long long)intToFloat12(softLimit) << 48 |
+            (long long)intToFloat12(zActualSize) << 36 |
+            (long long)intToFloat12(zAllowedSize) << 24 |
+            (long long)intToFloat12(zNumAllocated) << 12 |
+            (long long)intToFloat12(zSizeAllocated);
+
+    /*
+     * Report the current external allocation stats and the native heap
+     * summary.
+     *
+     * [63-48] Reserved; must be zero (TODO: put new data in these slots)
+     * [47-36] dlmalloc_footprint
+     * [35-24] mallinfo: total allocated space
+     * [23-12] External byte limit
+     * [11- 0] External bytes allocated
+     */
+    long long event3;
+    size_t externalLimit, externalBytesAllocated;
+    size_t uordblks, footprint;
+
+#if 0
+    /*
+     * This adds 2-5msec to the GC cost on a DVT, or about 2-3% of the cost
+     * of a GC, so it's not horribly expensive but it's not free either.
+     */
+    extern size_t dlmalloc_footprint(void);
+    struct mallinfo mi;
+    //u8 start, end;
+
+    //start = dvmGetRelativeTimeNsec();
+    mi = mallinfo();
+    uordblks = mi.uordblks;
+    footprint = dlmalloc_footprint();
+    //end = dvmGetRelativeTimeNsec();
+    //LOGD("mallinfo+footprint took %dusec; used=%zd footprint=%zd\n",
+    //    (int)((end - start) / 1000), mi.uordblks, footprint);
+#else
+    uordblks = footprint = 0;
+#endif
+
+    externalLimit =
+            dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0);
+    externalBytesAllocated =
+            dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0);
+    event3 =
+            (long long)intToFloat12(footprint) << 36 |
+            (long long)intToFloat12(uordblks) << 24 |
+            (long long)intToFloat12(externalLimit) << 12 |
+            (long long)intToFloat12(externalBytesAllocated);
+
+    /* Build the event data.
+     * [ 0: 0] item count (4)
+     * [ 1: 1] EVENT_TYPE_LONG
+     * [ 2: 9] event0
+     * [10:10] EVENT_TYPE_LONG
+     * [11:18] event1
+     * [19:19] EVENT_TYPE_LONG
+     * [20:27] event2
+     * [28:28] EVENT_TYPE_LONG
+     * [29:36] event2
+     */
+    unsigned char *c = eventBuf;
+    *c++ = 4;
+    *c++ = EVENT_TYPE_LONG;
+    memcpy(c, &event0, sizeof(event0));
+    c += sizeof(event0);
+    *c++ = EVENT_TYPE_LONG;
+    memcpy(c, &event1, sizeof(event1));
+    c += sizeof(event1);
+    *c++ = EVENT_TYPE_LONG;
+    memcpy(c, &event2, sizeof(event2));
+    c += sizeof(event2);
+    *c++ = EVENT_TYPE_LONG;
+    memcpy(c, &event3, sizeof(event3));
+
+    (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_info, EVENT_TYPE_LIST,
+            eventBuf, sizeof(eventBuf));
+}
+
+void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen)
+{
+    unsigned char eventBuf[1 + (1 + sizeof(int)) * 2];
+    size_t total, zyg;
+    size_t firstHeap, i;
+    size_t nHeaps = dvmHeapSourceGetNumHeaps();
+
+    assert(arrayLen >= nHeaps);
+
+    firstHeap = nHeaps > 1 ? 1 : 0;
+    total = 0;
+    zyg = 0;
+    for (i = 0; i < nHeaps; i++) {
+        total += madvisedSizes[i];
+        if (i >= firstHeap) {
+            zyg += madvisedSizes[i];
+        }
+    }
+
+    /* Build the event data.
+     * [ 0: 0] item count (2)
+     * [ 1: 1] EVENT_TYPE_INT
+     * [ 2: 5] total madvise byte count
+     * [ 6: 6] EVENT_TYPE_INT
+     * [ 7:10] zygote heap madvise byte count
+     */
+    unsigned char *c = eventBuf;
+    *c++ = 2;
+    *c++ = EVENT_TYPE_INT;
+    memcpy(c, &total, sizeof(total));
+    c += sizeof(total);
+    *c++ = EVENT_TYPE_INT;
+    memcpy(c, &zyg, sizeof(zyg));
+    c += sizeof(zyg);
+
+    (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_madvise_info,
+            EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf));
+}
+
+#if 0
+#include <errno.h>
+#include <stdio.h>
+
+typedef struct HeapDumpContext {
+    FILE *fp;
+    void *chunkStart;
+    size_t chunkLen;
+    bool chunkFree;
+} HeapDumpContext;
+
+static void
+dump_context(const HeapDumpContext *ctx)
+{
+    fprintf(ctx->fp, "0x%08x %12.12zd %s\n", (uintptr_t)ctx->chunkStart,
+            ctx->chunkLen, ctx->chunkFree ? "FREE" : "USED");
+}
+
+static void
+heap_chunk_callback(const void *chunkptr, size_t chunklen,
+                    const void *userptr, size_t userlen, void *arg)
+{
+    HeapDumpContext *ctx = (HeapDumpContext *)arg;
+    bool chunkFree = (userptr == NULL);
+
+    if (chunkFree != ctx->chunkFree ||
+            ((char *)ctx->chunkStart + ctx->chunkLen) != chunkptr)
+    {
+        /* The new chunk is of a different type or isn't
+         * contiguous with the current chunk.  Dump the
+         * old one and start a new one.
+         */
+        if (ctx->chunkStart != NULL) {
+            /* It's not the first chunk. */
+            dump_context(ctx);
+        }
+        ctx->chunkStart = (void *)chunkptr;
+        ctx->chunkLen = chunklen;
+        ctx->chunkFree = chunkFree;
+    } else {
+        /* Extend the current chunk.
+         */
+        ctx->chunkLen += chunklen;
+    }
+}
+
+/* Dumps free and used ranges, as text, to the named file.
+ */
+void dvmDumpHeapToFile(const char *fileName)
+{
+    HeapDumpContext ctx;
+    FILE *fp;
+
+    fp = fopen(fileName, "w+");
+    if (fp == NULL) {
+        LOGE("Can't open %s for writing: %s\n", fileName, strerror(errno));
+        return;
+    }
+    LOGW("Dumping heap to %s...\n", fileName);
+
+    fprintf(fp, "==== Dalvik heap dump ====\n");
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.fp = fp;
+    dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx);
+    dump_context(&ctx);
+    fprintf(fp, "==== end heap dump ====\n");
+
+    LOGW("Dumped heap to %s.\n", fileName);
+
+    fclose(fp);
+}
+#endif
diff --git a/vm/alloc/HeapDebug.h b/vm/alloc/HeapDebug.h
new file mode 100644
index 0000000..19f4b45
--- /dev/null
+++ b/vm/alloc/HeapDebug.h
@@ -0,0 +1,31 @@
+/*
+ * 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
+#define _DALVIK_HEAPDEBUG
+
+typedef enum HeapDebugInfoType {
+    kVirtualHeapSize = 0,
+    kNativeHeapSize = 1,
+    kVirtualHeapAllocated = 2,
+    kNativeHeapAllocated = 3,
+} HeapDebugInfoType;
+
+/* Return the specified value.
+ * Returns -1 if the type is unknown.
+ */
+int dvmGetHeapDebugInfo(HeapDebugInfoType info);
+
+#endif  // _DALVIK_HEAPDEBUG
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
new file mode 100644
index 0000000..0298f84
--- /dev/null
+++ b/vm/alloc/HeapInternal.h
@@ -0,0 +1,162 @@
+/*
+ * 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
+#define _DALVIK_ALLOC_HEAP_INTERNAL
+
+#include <time.h>  // for struct timespec
+
+#include "HeapTable.h"
+#include "MarkSweep.h"
+
+struct GcHeap {
+    HeapSource      *heapSource;
+
+    /* List of heap objects that will require finalization when
+     * collected.  I.e., instance objects
+     *
+     *     a) whose class definitions override java.lang.Object.finalize()
+     *
+     * *** AND ***
+     *
+     *     b) that have never been finalized.
+     *
+     * Note that this does not exclude non-garbage objects;  this
+     * is not the list of pending finalizations, but of objects that
+     * potentially have finalization in their futures.
+     */
+    LargeHeapRefTable  *finalizableRefs;
+
+    /* The list of objects that need to have finalize() called
+     * on themselves.  These references are part of the root set.
+     *
+     * This table is protected by gDvm.heapWorkerListLock, which must
+     * be acquired after the heap lock.
+     */
+    LargeHeapRefTable  *pendingFinalizationRefs;
+
+    /* Linked lists of subclass instances of java/lang/ref/Reference
+     * that we find while recursing.  The "next" pointers are hidden
+     * in the objects' <code>int Reference.vmData</code> fields.
+     * These lists are cleared and rebuilt each time the GC runs.
+     */
+    Object         *softReferences;
+    Object         *weakReferences;
+    Object         *phantomReferences;
+
+    /* The list of Reference objects that need to be cleared and/or
+     * enqueued.  The bottom two bits of the object pointers indicate
+     * whether they should be cleared and/or enqueued.
+     *
+     * This table is protected by gDvm.heapWorkerListLock, which must
+     * be acquired after the heap lock.
+     */
+    LargeHeapRefTable  *referenceOperations;
+
+    /* If non-null, the method that the HeapWorker is currently
+     * executing.
+     */
+    Object *heapWorkerCurrentObject;
+    Method *heapWorkerCurrentMethod;
+
+    /* If heapWorkerCurrentObject is non-null, this gives the time when
+     * HeapWorker started executing that method.  The time value must come
+     * from dvmGetRelativeTimeUsec().
+     *
+     * The "Cpu" entry tracks the per-thread CPU timer (when available).
+     */
+    u8 heapWorkerInterpStartTime;
+    u8 heapWorkerInterpCpuStartTime;
+
+    /* If any fields are non-zero, indicates the next (absolute) time that
+     * the HeapWorker thread should call dvmHeapSourceTrim().
+     */
+    struct timespec heapWorkerNextTrim;
+
+    /* The current state of the mark step.
+     * Only valid during a GC.
+     */
+    GcMarkContext   markContext;
+
+    /* GC's card table */
+    u1*             cardTableBase;
+    size_t          cardTableLength;
+
+    /* 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;
+
+#if WITH_HPROF
+    bool            hprofDumpOnGc;
+    const char*     hprofFileName;
+    int             hprofFd;
+    hprof_context_t *hprofContext;
+    int             hprofResult;
+    bool            hprofDirectToDdms;
+#endif
+};
+
+bool dvmLockHeap(void);
+void dvmUnlockHeap(void);
+void dvmLogGcStats(size_t numFreed, size_t sizeFreed, size_t gcTimeMs);
+void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen);
+
+/*
+ * 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(...)    LOG(LOG_VERBOSE, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGD_HEAP(...)    LOG(LOG_DEBUG, HEAP_LOG_TAG, __VA_ARGS__)
+#endif
+#define LOGI_HEAP(...)    LOG(LOG_INFO, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGW_HEAP(...)    LOG(LOG_WARN, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGE_HEAP(...)    LOG(LOG_ERROR, HEAP_LOG_TAG, __VA_ARGS__)
+
+#define QUIET_ZYGOTE_GC 1
+#if QUIET_ZYGOTE_GC
+#undef LOGI_HEAP
+#define LOGI_HEAP(...) \
+    do { \
+        if (!gDvm.zygote) { \
+            LOG(LOG_INFO, HEAP_LOG_TAG, __VA_ARGS__); \
+        } \
+    } while (false)
+#endif
+
+#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
diff --git a/vm/alloc/HeapSource.c b/vm/alloc/HeapSource.c
new file mode 100644
index 0000000..a18def5
--- /dev/null
+++ b/vm/alloc/HeapSource.c
@@ -0,0 +1,1891 @@
+/*
+ * 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 <cutils/mspace.h>
+#include <stdint.h>     // for SIZE_MAX
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/HeapBitmap.h"
+
+// TODO: find a real header file for these.
+extern int dlmalloc_trim(size_t);
+extern void dlmalloc_walk_free_pages(void(*)(void*, void*, void*), void*);
+
+static void snapIdealFootprint(void);
+static void setIdealFootprint(size_t max);
+
+#define ALIGN_UP_TO_PAGE_SIZE(p) \
+    (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
+#define ALIGN_DOWN_TO_PAGE_SIZE(p) \
+    ((size_t)(p) & ~(SYSTEM_PAGE_SIZE - 1))
+
+#define HEAP_UTILIZATION_MAX        1024
+#define DEFAULT_HEAP_UTILIZATION    512     // Range 1..HEAP_UTILIZATION_MAX
+#define HEAP_IDEAL_FREE             (2 * 1024 * 1024)
+#define HEAP_MIN_FREE               (HEAP_IDEAL_FREE / 4)
+
+/* 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)
+
+#define DEBUG_HEAP_SOURCE 0
+#if DEBUG_HEAP_SOURCE
+#define HSTRACE(...)  LOG(LOG_INFO, LOG_TAG "-hs", __VA_ARGS__)
+#else
+#define HSTRACE(...)  /**/
+#endif
+
+/*
+=======================================================
+=======================================================
+=======================================================
+
+How will this be used?
+allocating/freeing: Heap.c just wants to say "alloc(n)" and get a ptr
+    - if allocating in large doesn't work, try allocating from small
+Heap.c will use HeapSource.h; HeapSource.c will do the right thing
+    between small and large
+    - some operations should be abstracted; put in a structure
+
+How do we manage the size trade-offs?
+- keep mspace max footprint clamped to actual footprint
+- if small-alloc returns null, adjust large vs. small ratio
+    - give small all available slack and retry
+    - success or fail, snap back to actual footprint and give rest to large
+
+managed as "small actual" + "large actual" + "delta to allowed total footprint"
+- when allocating from one source or the other, give the delta to the
+    active source, but snap back afterwards
+- that may not work so great for a gc heap, because small will always consume.
+    - but we need to use the memory, and the current max is the amount we
+      need to fill before a GC.
+
+Find a way to permanently steal pages from the middle of the heap
+    - segment tricks?
+
+Allocate String and char[] in a separate heap?
+
+Maybe avoid growing small heap, even if there's slack?  Look at
+live ratio of small heap after a gc; scale it based on that.
+
+=======================================================
+=======================================================
+=======================================================
+*/
+
+typedef struct {
+    /* The mspace to allocate from.
+     */
+    mspace msp;
+
+    /* The largest size that this heap is allowed to grow to.
+     */
+    size_t absoluteMaxSize;
+
+    /* 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;
+} Heap;
+
+struct HeapSource {
+    /* Target ideal heap utilization ratio; range 1..HEAP_UTILIZATION_MAX
+     */
+    size_t targetUtilization;
+
+    /* Requested minimum heap size, or zero if there is no minimum.
+     */
+    size_t minimumSize;
+
+    /* The starting heap size.
+     */
+    size_t startSize;
+
+    /* The largest that the heap source as a whole is allowed to grow.
+     */
+    size_t absoluteMaxSize;
+
+    /* 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;
+
+    /* 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;
+
+    /* External allocation count.
+     */
+    size_t externalBytesAllocated;
+
+    /* The maximum number of external bytes that may be allocated.
+     */
+    size_t externalLimit;
+
+    /* 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;
+
+    /*
+     * State for the GC daemon.
+     */
+    bool hasGcThread;
+    pthread_t gcThread;
+    bool gcThreadShutdown;
+    pthread_mutex_t gcThreadMutex;
+    pthread_cond_t gcThreadCond;
+};
+
+#define hs2heap(hs_) (&((hs_)->heaps[0]))
+
+/*
+ * Returns true iff a soft limit is in effect for the active heap.
+ */
+static inline bool
+softLimited(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 (softLimited(hs)) {
+        return hs->softLimit;
+    } else {
+        return mspace_max_allowed_footprint(hs2heap(hs)->msp);
+    }
+}
+
+/*
+ * Returns the current footprint of all heaps.  If includeActive
+ * is false, don't count the heap at index 0.
+ */
+static inline 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 inline Heap *
+ptr2heap(const HeapSource *hs, const void *ptr)
+{
+    const size_t numHeaps = hs->numHeaps;
+    size_t i;
+
+//TODO: unroll this to HEAP_SOURCE_MAX_HEAP_COUNT
+    if (ptr != NULL) {
+        for (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, bool isObj)
+{
+    HeapSource *hs;
+
+    assert(heap->bytesAllocated < mspace_footprint(heap->msp));
+
+    heap->bytesAllocated += mspace_usable_size(heap->msp, ptr) +
+            HEAP_SOURCE_CHUNK_OVERHEAD;
+    if (isObj) {
+        heap->objectsAllocated++;
+        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)
+{
+    HeapSource *hs;
+    size_t delta;
+
+    delta = mspace_usable_size(heap->msp, ptr) + HEAP_SOURCE_CHUNK_OVERHEAD;
+    assert(delta > 0);
+    if (delta < heap->bytesAllocated) {
+        heap->bytesAllocated -= delta;
+    } else {
+        heap->bytesAllocated = 0;
+    }
+    hs = gDvm.gcHeap->heapSource;
+    dvmHeapBitmapClearObjectBit(&hs->liveBits, ptr);
+    if (heap->objectsAllocated > 0) {
+        heap->objectsAllocated--;
+    }
+    *numBytes += delta;
+}
+
+static HeapSource *gHs = NULL;
+
+static mspace
+createMspace(void *base, size_t startSize, size_t absoluteMaxSize)
+{
+    mspace msp;
+
+    /* Create an unlocked dlmalloc mspace to use as
+     * a small-object heap source.
+     *
+     * We start off reserving heapSizeStart/2 bytes but
+     * letting the heap grow to heapSizeStart.  This saves
+     * memory in the case where a process uses even less
+     * than the starting size.
+     */
+    LOGV_HEAP("Creating VM heap of size %u\n", startSize);
+    errno = 0;
+    msp = create_contiguous_mspace_with_base(startSize/2,
+            absoluteMaxSize, /*locked=*/false, base);
+    if (msp != NULL) {
+        /* Don't let the heap grow past the starting size without
+         * our intervention.
+         */
+        mspace_set_max_allowed_footprint(msp, startSize);
+    } else {
+        /* There's no guarantee that errno has meaning when the call
+         * fails, but it often does.
+         */
+        LOGE_HEAP("Can't create VM heap of size (%u,%u): %s\n",
+            startSize/2, absoluteMaxSize, strerror(errno));
+    }
+
+    return msp;
+}
+
+static bool
+addNewHeap(HeapSource *hs, mspace msp, size_t mspAbsoluteMaxSize)
+{
+    Heap heap;
+
+    if (hs->numHeaps >= HEAP_SOURCE_MAX_HEAP_COUNT) {
+        LOGE("Attempt to create too many heaps (%zd >= %zd)\n",
+                hs->numHeaps, HEAP_SOURCE_MAX_HEAP_COUNT);
+        dvmAbort();
+        return false;
+    }
+
+    memset(&heap, 0, sizeof(heap));
+
+    if (msp != NULL) {
+        heap.msp = msp;
+        heap.absoluteMaxSize = mspAbsoluteMaxSize;
+        heap.concurrentStartBytes = SIZE_MAX;
+        heap.base = hs->heapBase;
+        heap.limit = hs->heapBase + heap.absoluteMaxSize;
+    } else {
+        void *sbrk0 = contiguous_mspace_sbrk0(hs->heaps[0].msp);
+        char *base = (char *)ALIGN_UP_TO_PAGE_SIZE(sbrk0);
+        size_t overhead = base - hs->heaps[0].base;
+
+        assert(((size_t)hs->heaps[0].base & (SYSTEM_PAGE_SIZE - 1)) == 0);
+        if (overhead + HEAP_MIN_FREE >= hs->absoluteMaxSize) {
+            LOGE_HEAP("No room to create any more heaps "
+                    "(%zd overhead, %zd max)\n",
+                    overhead, hs->absoluteMaxSize);
+            return false;
+        }
+        hs->heaps[0].absoluteMaxSize = overhead;
+        hs->heaps[0].limit = base;
+        heap.absoluteMaxSize = hs->absoluteMaxSize - overhead;
+        heap.msp = createMspace(base, HEAP_MIN_FREE, heap.absoluteMaxSize);
+        heap.concurrentStartBytes = HEAP_MIN_FREE - CONCURRENT_START;
+        heap.base = base;
+        heap.limit = heap.base + heap.absoluteMaxSize;
+        if (heap.msp == NULL) {
+            return false;
+        }
+    }
+
+    /* Don't let the soon-to-be-old heap grow any further.
+     */
+    if (hs->numHeaps > 0) {
+        mspace msp = hs->heaps[0].msp;
+        mspace_set_max_allowed_footprint(msp, mspace_footprint(msp));
+    }
+
+    /* 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.
+ */
+static void *gcDaemonThread(void* arg)
+{
+    dvmChangeStatus(NULL, THREAD_VMWAIT);
+    dvmLockMutex(&gHs->gcThreadMutex);
+    while (gHs->gcThreadShutdown != true) {
+        dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);
+        dvmLockHeap();
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+        dvmCollectGarbageInternal(false, GC_CONCURRENT);
+        dvmChangeStatus(NULL, THREAD_VMWAIT);
+        dvmUnlockHeap();
+    }
+    dvmChangeStatus(NULL, THREAD_RUNNING);
+    return NULL;
+}
+
+static bool gcDaemonStartup(void)
+{
+    dvmInitMutex(&gHs->gcThreadMutex);
+    pthread_cond_init(&gHs->gcThreadCond, NULL);
+    gHs->gcThreadShutdown = false;
+    gHs->hasGcThread = dvmCreateInternalThread(&gHs->gcThread, "GC",
+                                               gcDaemonThread, NULL);
+    return gHs->hasGcThread;
+}
+
+static void gcDaemonShutdown(void)
+{
+    if (gHs->hasGcThread) {
+        dvmLockMutex(&gHs->gcThreadMutex);
+        gHs->gcThreadShutdown = true;
+        dvmSignalCond(&gHs->gcThreadCond);
+        dvmUnlockMutex(&gHs->gcThreadMutex);
+        pthread_join(gHs->gcThread, NULL);
+    }
+}
+
+/*
+ * 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 absoluteMaxSize)
+{
+    GcHeap *gcHeap;
+    HeapSource *hs;
+    mspace msp;
+    size_t length;
+    void *base;
+
+    assert(gHs == NULL);
+
+    if (startSize > absoluteMaxSize) {
+        LOGE("Bad heap parameters (start=%d, max=%d)\n",
+           startSize, absoluteMaxSize);
+        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(absoluteMaxSize);
+    base = dvmAllocRegion(length, PROT_NONE, "dalvik-heap");
+    if (base == NULL) {
+        return NULL;
+    }
+
+    /* Create an unlocked dlmalloc mspace to use as
+     * the small object heap source.
+     */
+    msp = createMspace(base, startSize, absoluteMaxSize);
+    if (msp == NULL) {
+        goto fail;
+    }
+
+    /* Allocate a descriptor from the heap we just created.
+     */
+    gcHeap = mspace_malloc(msp, sizeof(*gcHeap));
+    if (gcHeap == NULL) {
+        LOGE_HEAP("Can't allocate heap descriptor\n");
+        goto fail;
+    }
+    memset(gcHeap, 0, sizeof(*gcHeap));
+
+    hs = mspace_malloc(msp, sizeof(*hs));
+    if (hs == NULL) {
+        LOGE_HEAP("Can't allocate heap source\n");
+        goto fail;
+    }
+    memset(hs, 0, sizeof(*hs));
+
+    hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
+    hs->minimumSize = 0;
+    hs->startSize = startSize;
+    hs->absoluteMaxSize = absoluteMaxSize;
+    hs->idealSize = startSize;
+    hs->softLimit = SIZE_MAX;    // no soft limit at first
+    hs->numHeaps = 0;
+    hs->sawZygote = gDvm.zygote;
+    hs->hasGcThread = false;
+    hs->heapBase = base;
+    hs->heapLength = length;
+    if (!addNewHeap(hs, msp, absoluteMaxSize)) {
+        LOGE_HEAP("Can't add initial heap\n");
+        goto fail;
+    }
+    if (!dvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1")) {
+        LOGE_HEAP("Can't create liveBits\n");
+        goto fail;
+    }
+    if (!dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2")) {
+        LOGE_HEAP("Can't create markBits\n");
+        dvmHeapBitmapDelete(&hs->liveBits);
+        goto fail;
+    }
+
+    gcHeap->markContext.bitmap = &hs->markBits;
+    gcHeap->heapSource = hs;
+
+    countAllocation(hs2heap(hs), gcHeap, false);
+    countAllocation(hs2heap(hs), hs, false);
+
+    gHs = hs;
+    return gcHeap;
+
+fail:
+    munmap(base, length);
+    return NULL;
+}
+
+bool dvmHeapSourceStartupAfterZygote(void)
+{
+    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) {
+        /* Create a new heap for post-fork zygote allocations.  We only
+         * try once, even if it fails.
+         */
+        LOGV("Splitting out new zygote heap\n");
+        gDvm.newZygoteHeapAllocated = true;
+        return addNewHeap(hs, NULL, 0);
+    }
+    return true;
+}
+
+void dvmHeapSourceThreadShutdown(void)
+{
+    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)
+{
+    if (*gcHeap != NULL && (*gcHeap)->heapSource != NULL) {
+        HeapSource *hs;
+
+        hs = (*gcHeap)->heapSource;
+
+        assert((char *)*gcHeap >= hs->heapBase);
+        assert((char *)*gcHeap < hs->heapBase + hs->heapLength);
+
+        dvmHeapBitmapDelete(&hs->liveBits);
+        dvmHeapBitmapDelete(&hs->markBits);
+
+        munmap(hs->heapBase, hs->heapLength);
+        gHs = NULL;
+        *gcHeap = NULL;
+    }
+}
+
+/*
+ * Gets the begining of the allocation for the HeapSource.
+ */
+void *dvmHeapSourceGetBase(void)
+{
+    return gHs->heapBase;
+}
+
+/*
+ * Returns the requested value. If the per-heap stats are requested, fill
+ * them as well.
+ *
+ * Caller must hold the heap lock.
+ */
+size_t
+dvmHeapSourceGetValue(enum HeapSourceValueSpec spec, size_t perHeapStats[],
+                      size_t arrayLen)
+{
+    HeapSource *hs = gHs;
+    size_t value = 0;
+    size_t total = 0;
+    size_t i;
+
+    HS_BOILERPLATE();
+
+    switch (spec) {
+    case HS_EXTERNAL_BYTES_ALLOCATED:
+        return hs->externalBytesAllocated;
+    case HS_EXTERNAL_LIMIT:
+        return hs->externalLimit;
+    default:
+        // look at all heaps.
+        ;
+    }
+
+    assert(arrayLen >= hs->numHeaps || perHeapStats == NULL);
+    for (i = 0; i < hs->numHeaps; i++) {
+        Heap *const heap = &hs->heaps[i];
+
+        switch (spec) {
+        case HS_FOOTPRINT:
+            value = mspace_footprint(heap->msp);
+            break;
+        case HS_ALLOWED_FOOTPRINT:
+            value = mspace_max_allowed_footprint(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;
+}
+
+static void aliasBitmap(HeapBitmap *dst, HeapBitmap *src,
+                        uintptr_t base, uintptr_t max) {
+    size_t offset;
+
+    dst->base = base;
+    dst->max = max;
+    dst->bitsLen = HB_OFFSET_TO_BYTE_INDEX(max - base) + sizeof(dst->bits);
+    /* The exclusive limit from bitsLen is greater than the inclusive max. */
+    assert(base + HB_MAX_OFFSET(dst) > max);
+    /* The exclusive limit is at most one word of bits beyond max. */
+    assert((base + HB_MAX_OFFSET(dst)) - max <=
+           HB_OBJECT_ALIGNMENT * HB_BITS_PER_WORD);
+    dst->allocLen = dst->bitsLen;
+    offset = base - src->base;
+    assert(HB_OFFSET_TO_MASK(offset) == 1 << 31);
+    dst->bits = &src->bits[HB_OFFSET_TO_INDEX(offset)];
+}
+
+/*
+ * Initializes a vector of object and mark bits to the object and mark
+ * bits of each heap.  The bits are aliased to the heapsource
+ * object and mark bitmaps.  This routine is used by the sweep code
+ * which needs to free each object in the correct heap.
+ */
+void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[],
+                                   size_t numHeaps)
+{
+    HeapSource *hs = gHs;
+    uintptr_t base, max;
+    size_t i;
+
+    HS_BOILERPLATE();
+
+    assert(numHeaps == hs->numHeaps);
+    for (i = 0; i < hs->numHeaps; ++i) {
+        base = (uintptr_t)hs->heaps[i].base;
+        /* -1 because limit is exclusive but max is inclusive. */
+        max = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->markBits.max);
+        aliasBitmap(&liveBits[i], &hs->liveBits, base, max);
+        aliasBitmap(&markBits[i], &hs->markBits, base, max);
+    }
+}
+
+/*
+ * Get the bitmap representing all live objects.
+ */
+HeapBitmap *dvmHeapSourceGetLiveBits(void)
+{
+    HS_BOILERPLATE();
+
+    return &gHs->liveBits;
+}
+
+void dvmHeapSourceSwapBitmaps(void)
+{
+    HeapBitmap tmp;
+
+    tmp = gHs->liveBits;
+    gHs->liveBits = gHs->markBits;
+    gHs->markBits = tmp;
+}
+
+void dvmHeapSourceZeroMarkBitmap(void)
+{
+    HS_BOILERPLATE();
+
+    dvmHeapBitmapZero(&gHs->markBits);
+}
+
+void dvmMarkImmuneObjects(const char *immuneLimit)
+{
+    char *dst, *src;
+    size_t i, index, length;
+
+    /*
+     * 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 (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. */
+            index = HB_OFFSET_TO_INDEX(
+                (uintptr_t)gHs->heaps[i].base - gHs->liveBits.base);
+            /* Compute the starting offset in the live and mark bits. */
+            src = (char *)(gHs->liveBits.bits + index);
+            dst = (char *)(gHs->markBits.bits + index);
+            /* Compute the number of bytes of the live bitmap to copy. */
+            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)
+{
+    HeapSource *hs = gHs;
+    Heap *heap;
+    void *ptr;
+
+    HS_BOILERPLATE();
+    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\n",
+                  FRACTIONAL_MB(hs->softLimit), n);
+        return NULL;
+    }
+    ptr = mspace_calloc(heap->msp, 1, n);
+    if (ptr == NULL) {
+        return NULL;
+    }
+    countAllocation(heap, ptr, true);
+    /*
+     * 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)
+{
+    void *ptr;
+    size_t max;
+
+    /* Grow as much as possible, but don't let the real footprint
+     * plus external allocations go over the absolute max.
+     */
+    max = heap->absoluteMaxSize;
+    if (max > hs->externalBytesAllocated) {
+        max -= hs->externalBytesAllocated;
+
+        mspace_set_max_allowed_footprint(heap->msp, max);
+        ptr = dvmHeapSourceAlloc(n);
+
+        /* Shrink back down as small as possible.  Our caller may
+         * readjust max_allowed to a more appropriate value.
+         */
+        mspace_set_max_allowed_footprint(heap->msp,
+                mspace_footprint(heap->msp));
+    } else {
+        ptr = NULL;
+    }
+
+    return ptr;
+}
+
+/*
+ * Allocates <n> bytes of zeroed data, growing as much as possible
+ * if necessary.
+ */
+void *
+dvmHeapSourceAllocAndGrow(size_t n)
+{
+    HeapSource *hs = gHs;
+    Heap *heap;
+    void *ptr;
+    size_t oldIdealSize;
+
+    HS_BOILERPLATE();
+    heap = hs2heap(hs);
+
+    ptr = dvmHeapSourceAlloc(n);
+    if (ptr != NULL) {
+        return ptr;
+    }
+
+    oldIdealSize = hs->idealSize;
+    if (softLimited(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)
+{
+    Heap *heap;
+    size_t numBytes;
+
+    HS_BOILERPLATE();
+
+    if (numPtrs == 0) {
+        return 0;
+    }
+
+    assert(ptrs != NULL);
+    assert(*ptrs != NULL);
+    heap = ptr2heap(gHs, *ptrs);
+    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) {
+            // mspace_merge_objects takes two allocated objects, and
+            // if the second immediately follows the first, will merge
+            // them, returning a larger object occupying the same
+            // memory. This is a local operation, and doesn't require
+            // dlmalloc to manipulate any freelists. It's pretty
+            // inexpensive compared to free().
+
+            // ptrs is an array of objects all in memory order, and if
+            // client code has been allocating lots of short-lived
+            // objects, this is likely to contain runs of objects all
+            // now garbage, and thus highly amenable to this optimization.
+
+            // Unroll the 0th iteration around the loop below,
+            // countFree ptrs[0] and initializing merged.
+            assert(ptrs[0] != NULL);
+            assert(ptr2heap(gHs, ptrs[0]) == heap);
+            countFree(heap, ptrs[0], &numBytes);
+            void *merged = ptrs[0];
+
+            size_t i;
+            for (i = 1; i < numPtrs; i++) {
+                assert(merged != NULL);
+                assert(ptrs[i] != NULL);
+                assert((intptr_t)merged < (intptr_t)ptrs[i]);
+                assert(ptr2heap(gHs, ptrs[i]) == heap);
+                countFree(heap, ptrs[i], &numBytes);
+                // Try to merge. If it works, merged now includes the
+                // memory of ptrs[i]. If it doesn't, free merged, and
+                // see if ptrs[i] starts a new run of adjacent
+                // objects to merge.
+                if (mspace_merge_objects(msp, merged, ptrs[i]) == NULL) {
+                    mspace_free(msp, merged);
+                    merged = ptrs[i];
+                }
+            }
+            assert(merged != NULL);
+            mspace_free(msp, merged);
+        } else {
+            // This is not an 'active heap'. Only do the accounting.
+            size_t i;
+            for (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 (dvmHeapBitmapCoversAddress(&gHs->liveBits, ptr));
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Returns the value of the requested flag.
+ */
+bool
+dvmHeapSourceGetPtrFlag(const void *ptr, enum HeapSourcePtrFlag flag)
+{
+    if (ptr == NULL) {
+        return false;
+    }
+
+    if (flag == HS_CONTAINS) {
+        return dvmHeapSourceContains(ptr);
+    } else if (flag == HS_ALLOCATED_IN_ZYGOTE) {
+        HeapSource *hs = gHs;
+
+        HS_BOILERPLATE();
+
+        if (hs->sawZygote) {
+            Heap *heap;
+
+            heap = ptr2heap(hs, ptr);
+            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;
+    }
+
+    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)
+{
+    Heap *heap;
+
+    HS_BOILERPLATE();
+
+    heap = ptr2heap(gHs, ptr);
+    if (heap != NULL) {
+        return mspace_usable_size(heap->msp, 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);
+}
+
+/*
+ * Return the real bytes used by old heaps and external memory
+ * 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)
+{
+    HeapSource *hs = gHs;
+    size_t ret;
+
+    HS_BOILERPLATE();
+
+    ret = oldHeapOverhead(hs, false) + hs->externalBytesAllocated;
+    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_max_allowed_footprint(msp, currentHeapSize);
+        hs->softLimit = softLimit;
+    } else {
+        /* Let the heap grow to the requested max, and remove any
+         * soft limit, if set.
+         */
+        mspace_set_max_allowed_footprint(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)
+{
+    HeapSource *hs = gHs;
+#if DEBUG_HEAP_SOURCE
+    HeapSource oldHs = *hs;
+    mspace msp = hs->heaps[0].msp;
+    size_t oldAllowedFootprint =
+            mspace_max_allowed_footprint(msp);
+#endif
+
+    HS_BOILERPLATE();
+
+    if (max > hs->absoluteMaxSize) {
+        LOGI_HEAP("Clamp target GC heap from %zd.%03zdMB to %u.%03uMB\n",
+                FRACTIONAL_MB(max),
+                FRACTIONAL_MB(hs->absoluteMaxSize));
+        max = hs->absoluteMaxSize;
+    } else if (max < hs->minimumSize) {
+        max = hs->minimumSize;
+    }
+
+    /* Convert max into a size that applies to the active heap.
+     * Old heaps and external allocations 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;
+
+    HSTRACE("IDEAL %zd->%zd (%d), soft %zd->%zd (%d), allowed %zd->%zd (%d), "
+            "ext %zd\n",
+            oldHs.idealSize, hs->idealSize, hs->idealSize - oldHs.idealSize,
+            oldHs.softLimit, hs->softLimit, hs->softLimit - oldHs.softLimit,
+            oldAllowedFootprint, mspace_max_allowed_footprint(msp),
+            mspace_max_allowed_footprint(msp) - oldAllowedFootprint,
+            hs->externalBytesAllocated);
+
+}
+
+/*
+ * 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);
+    LOGV("Set heap target utilization to %zd/%d (%f)\n",
+            hs->targetUtilization, HEAP_UTILIZATION_MAX, newTarget);
+}
+
+/*
+ * If set is true, sets the new minimum heap size to size; always
+ * returns the current (or previous) size.  If size is negative,
+ * removes the current minimum constraint (if present).
+ */
+size_t
+dvmMinimumHeapSize(size_t size, bool set)
+{
+    HeapSource *hs = gHs;
+    size_t oldMinimumSize;
+
+    /* gHs caches an entry in gDvm.gcHeap;  we need to hold the
+     * heap lock if we're going to look at it.  We also need the
+     * lock for the call to setIdealFootprint().
+     */
+    dvmLockHeap();
+
+    HS_BOILERPLATE();
+
+    oldMinimumSize = hs->minimumSize;
+
+    if (set) {
+        /* Don't worry about external allocations right now.
+         * setIdealFootprint() will take them into account when
+         * minimumSize is used, and it's better to hold onto the
+         * intended minimumSize than to clamp it arbitrarily based
+         * on the current allocations.
+         */
+        if (size > hs->absoluteMaxSize) {
+            size = hs->absoluteMaxSize;
+        }
+        hs->minimumSize = size;
+        if (size > hs->idealSize) {
+            /* Force a snap to the minimum value, which we just set
+             * and which setIdealFootprint() will take into consideration.
+             */
+            setIdealFootprint(hs->idealSize);
+        }
+        /* Otherwise we'll just keep it in mind the next time
+         * setIdealFootprint() is called.
+         */
+    }
+
+    dvmUnlockHeap();
+
+    return oldMinimumSize;
+}
+
+/*
+ * Given the size of a live set, returns the ideal heap size given
+ * the current target utilization and MIN/MAX values.
+ *
+ * targetUtilization is in the range 1..HEAP_UTILIZATION_MAX.
+ */
+static size_t
+getUtilizationTarget(size_t liveSize, size_t targetUtilization)
+{
+    size_t targetSize;
+
+    /* Use the current target utilization ratio to determine the
+     * ideal heap size based on the size of the live set.
+     */
+    targetSize = (liveSize / 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 + HEAP_IDEAL_FREE) {
+        targetSize = liveSize + HEAP_IDEAL_FREE;
+    } else if (targetSize < liveSize + HEAP_MIN_FREE) {
+        targetSize = liveSize + HEAP_MIN_FREE;
+    }
+    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()
+{
+    HeapSource *hs = gHs;
+    Heap *heap;
+    size_t targetHeapSize;
+    size_t currentHeapUsed;
+    size_t oldIdealSize;
+    size_t newHeapMax;
+    size_t overhead;
+    size_t freeBytes;
+
+    HS_BOILERPLATE();
+    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.
+     */
+    currentHeapUsed = heap->bytesAllocated;
+#define LET_EXTERNAL_INFLUENCE_UTILIZATION 1
+#if LET_EXTERNAL_INFLUENCE_UTILIZATION
+    /* This is a hack to deal with the side-effects of moving
+     * bitmap data out of the Dalvik heap.  Since the amount
+     * of free space after a GC scales with the size of the
+     * live set, many apps expected the large free space that
+     * appeared along with megabytes' worth of bitmaps.  When
+     * the bitmaps were removed, the free size shrank significantly,
+     * and apps started GCing constantly.  This makes it so the
+     * post-GC free space is the same size it would have been
+     * if the bitmaps were still in the Dalvik heap.
+     */
+    currentHeapUsed += hs->externalBytesAllocated;
+#endif
+    targetHeapSize =
+            getUtilizationTarget(currentHeapUsed, hs->targetUtilization);
+#if LET_EXTERNAL_INFLUENCE_UTILIZATION
+    currentHeapUsed -= hs->externalBytesAllocated;
+    targetHeapSize -= hs->externalBytesAllocated;
+#endif
+
+    /* 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.
+     */
+    overhead = getSoftFootprint(false);
+    oldIdealSize = hs->idealSize;
+    setIdealFootprint(targetHeapSize + overhead);
+
+    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;
+    }
+    newHeapMax = mspace_max_allowed_footprint(heap->msp);
+    if (softLimited(hs)) {
+        LOGD_HEAP("GC old usage %zd.%zd%%; now "
+                "%zd.%03zdMB used / %zd.%03zdMB soft max "
+                "(%zd.%03zdMB over, "
+                "%zd.%03zdMB ext, "
+                "%zd.%03zdMB real max)\n",
+                FRACTIONAL_PCT(currentHeapUsed, oldIdealSize),
+                FRACTIONAL_MB(currentHeapUsed),
+                FRACTIONAL_MB(hs->softLimit),
+                FRACTIONAL_MB(overhead),
+                FRACTIONAL_MB(hs->externalBytesAllocated),
+                FRACTIONAL_MB(newHeapMax));
+    } else {
+        LOGD_HEAP("GC old usage %zd.%zd%%; now "
+                "%zd.%03zdMB used / %zd.%03zdMB real max "
+                "(%zd.%03zdMB over, "
+                "%zd.%03zdMB ext)\n",
+                FRACTIONAL_PCT(currentHeapUsed, oldIdealSize),
+                FRACTIONAL_MB(currentHeapUsed),
+                FRACTIONAL_MB(newHeapMax),
+                FRACTIONAL_MB(overhead),
+                FRACTIONAL_MB(hs->externalBytesAllocated));
+    }
+}
+
+/*
+ * Return free pages to the system.
+ * TODO: move this somewhere else, especially the native heap part.
+ */
+
+static void releasePagesInRange(void *start, void *end, void *nbytes)
+{
+    /* 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 (start < end) {
+        size_t length = (char *)end - (char *)start;
+        madvise(start, length, MADV_DONTNEED);
+        *(size_t *)nbytes += length;
+    }
+}
+
+/*
+ * Return unused memory to the system if possible.
+ */
+void
+dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen)
+{
+    HeapSource *hs = gHs;
+    size_t nativeBytes, heapBytes;
+    size_t i;
+
+    HS_BOILERPLATE();
+
+    assert(arrayLen >= hs->numHeaps);
+
+    heapBytes = 0;
+    for (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.
+         */
+        bytesTrimmed[i] = 0;
+        mspace_walk_free_pages(heap->msp, releasePagesInRange,
+                               &bytesTrimmed[i]);
+        heapBytes += bytesTrimmed[i];
+    }
+
+    /* Same for the native heap.
+     */
+    dlmalloc_trim(0);
+    nativeBytes = 0;
+    dlmalloc_walk_free_pages(releasePagesInRange, &nativeBytes);
+
+    LOGD_HEAP("madvised %zd (GC) + %zd (native) = %zd total bytes\n",
+            heapBytes, nativeBytes, heapBytes + nativeBytes);
+}
+
+/*
+ * Walks over the heap source and passes every allocated and
+ * free chunk to the callback.
+ */
+void
+dvmHeapSourceWalk(void(*callback)(const void *chunkptr, size_t chunklen,
+                                      const void *userptr, size_t userlen,
+                                      void *arg),
+                  void *arg)
+{
+    HeapSource *hs = gHs;
+    size_t i;
+
+    HS_BOILERPLATE();
+
+    /* Walk the heaps from oldest to newest.
+     */
+//TODO: do this in address order
+    for (i = hs->numHeaps; i > 0; --i) {
+        mspace_walk_heap(hs->heaps[i-1].msp, callback, arg);
+    }
+}
+
+/*
+ * 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()
+{
+    HeapSource *hs = gHs;
+
+    HS_BOILERPLATE();
+
+    return hs->numHeaps;
+}
+
+
+/*
+ * External allocation tracking
+ *
+ * In some situations, memory outside of the heap is tied to the
+ * lifetime of objects in the heap.  Since that memory is kept alive
+ * by heap objects, it should provide memory pressure that can influence
+ * GCs.
+ */
+
+/*
+ * Returns true if the requested number of bytes can be allocated from
+ * available storage.
+ */
+static bool externalBytesAvailable(const HeapSource *hs, size_t numBytes)
+{
+    const Heap *heap;
+    size_t currentHeapSize, newHeapSize;
+
+    /* Make sure that this allocation is even possible.
+     * Don't let the external size plus the actual heap size
+     * go over the absolute max.  This essentially treats
+     * external allocations as part of the active heap.
+     *
+     * Note that this will fail "mysteriously" if there's
+     * a small softLimit but a large heap footprint.
+     */
+    heap = hs2heap(hs);
+    currentHeapSize = mspace_max_allowed_footprint(heap->msp);
+    newHeapSize = currentHeapSize + hs->externalBytesAllocated + numBytes;
+    if (newHeapSize <= heap->absoluteMaxSize) {
+        return true;
+    }
+    HSTRACE("externalBytesAvailable(): "
+            "footprint %zu + extAlloc %zu + n %zu >= max %zu (space for %zu)\n",
+            currentHeapSize, hs->externalBytesAllocated, numBytes,
+            heap->absoluteMaxSize,
+            heap->absoluteMaxSize -
+                    (currentHeapSize + hs->externalBytesAllocated));
+    return false;
+}
+
+#define EXTERNAL_TARGET_UTILIZATION 820  // 80%
+
+/*
+ * Tries to update the internal count of externally-allocated memory.
+ * If there's enough room for that memory, returns true.  If not, returns
+ * false and does not update the count.
+ *
+ * The caller must ensure externalBytesAvailable(hs, n) == true.
+ */
+static bool
+externalAlloc(HeapSource *hs, size_t n, bool grow)
+{
+    assert(hs->externalLimit >= hs->externalBytesAllocated);
+
+    HSTRACE("externalAlloc(%zd%s)\n", n, grow ? ", grow" : "");
+    assert(externalBytesAvailable(hs, n));  // The caller must ensure this.
+
+    /* External allocations have their own "free space" that they
+     * can allocate from without causing a GC.
+     */
+    if (hs->externalBytesAllocated + n <= hs->externalLimit) {
+        hs->externalBytesAllocated += n;
+#if PROFILE_EXTERNAL_ALLOCATIONS
+        if (gDvm.allocProf.enabled) {
+            Thread* self = dvmThreadSelf();
+            gDvm.allocProf.externalAllocCount++;
+            gDvm.allocProf.externalAllocSize += n;
+            if (self != NULL) {
+                self->allocProf.externalAllocCount++;
+                self->allocProf.externalAllocSize += n;
+            }
+        }
+#endif
+        return true;
+    }
+    if (!grow) {
+        return false;
+    }
+
+    /* GROW */
+    hs->externalBytesAllocated += n;
+    hs->externalLimit = getUtilizationTarget(
+            hs->externalBytesAllocated, EXTERNAL_TARGET_UTILIZATION);
+    HSTRACE("EXTERNAL grow limit to %zd\n", hs->externalLimit);
+    return true;
+}
+
+static void
+gcForExternalAlloc(bool collectSoftReferences)
+{
+    if (gDvm.allocProf.enabled) {
+        Thread* self = dvmThreadSelf();
+        gDvm.allocProf.gcCount++;
+        if (self != NULL) {
+            self->allocProf.gcCount++;
+        }
+    }
+    dvmCollectGarbageInternal(collectSoftReferences, GC_EXTERNAL_ALLOC);
+}
+
+/*
+ * Returns true if there is enough unused storage to perform an
+ * external allocation of the specified size.  If there insufficient
+ * free storage we try to releasing memory from external allocations
+ * and trimming the heap.
+ */
+static bool externalAllocPossible(const HeapSource *hs, size_t n)
+{
+    size_t bytesTrimmed[HEAP_SOURCE_MAX_HEAP_COUNT];
+
+    /*
+     * If there is sufficient space return immediately.
+     */
+    if (externalBytesAvailable(hs, n)) {
+        return true;
+    }
+    /*
+     * There is insufficient space.  Wait for the garbage collector to
+     * become inactive before proceeding.
+     */
+    while (gDvm.gcHeap->gcRunning) {
+        dvmWaitForConcurrentGcToComplete();
+    }
+    /*
+     * The heap may have grown or become trimmed while we were
+     * waiting.
+     */
+    if (externalBytesAvailable(hs, n)) {
+        return true;
+    }
+    /*
+     * Try a garbage collection that clears soft references.  This may
+     * make trimming more effective.
+     */
+    gcForExternalAlloc(true);
+    if (externalBytesAvailable(hs, n)) {
+        return true;
+    }
+    /*
+     * Try trimming the mspace to reclaim unused pages.
+     */
+    dvmHeapSourceTrim(bytesTrimmed, NELEM(bytesTrimmed));
+    snapIdealFootprint();
+    if (externalBytesAvailable(hs, n)) {
+        return true;
+    }
+    /*
+     * Nothing worked, return an error.
+     */
+    return false;
+}
+
+/*
+ * Updates the internal count of externally-allocated memory.  If there's
+ * enough room for that memory, returns true.  If not, returns false and
+ * does not update the count.
+ *
+ * May cause a GC as a side-effect.
+ */
+bool
+dvmTrackExternalAllocation(size_t n)
+{
+    HeapSource *hs = gHs;
+    bool ret = false;
+
+    /* gHs caches an entry in gDvm.gcHeap;  we need to hold the
+     * heap lock if we're going to look at it.
+     */
+    dvmLockHeap();
+
+    HS_BOILERPLATE();
+    assert(hs->externalLimit >= hs->externalBytesAllocated);
+
+    /*
+     * The externalAlloc calls require the externalAllocPossible
+     * invariant to be established.
+     */
+    if (!externalAllocPossible(hs, n)) {
+        LOGE_HEAP("%zd-byte external allocation "
+                  "too large for this process.", n);
+        goto out;
+    }
+
+    /* Try "allocating" using the existing "free space".
+     */
+    HSTRACE("EXTERNAL alloc %zu (%zu < %zu)\n",
+            n, hs->externalBytesAllocated, hs->externalLimit);
+    if (externalAlloc(hs, n, false)) {
+        ret = true;
+        goto out;
+    }
+    /*
+     * Wait until garbage collector is quiescent before proceeding.
+     */
+    while (gDvm.gcHeap->gcRunning) {
+        dvmWaitForConcurrentGcToComplete();
+    }
+    /*
+     * Re-establish the invariant if it was lost while we were
+     * waiting.
+     */
+    if (!externalAllocPossible(hs, n)) {
+        LOGE_HEAP("%zd-byte external allocation "
+                  "too large for this process.", n);
+        goto out;
+    }
+    /* The "allocation" failed.  Free up some space by doing
+     * a full garbage collection.  This may grow the heap source
+     * if the live set is sufficiently large.
+     */
+    HSTRACE("EXTERNAL alloc %zd: GC 1\n", n);
+    gcForExternalAlloc(false);  // don't collect SoftReferences
+    if (externalAlloc(hs, n, false)) {
+        ret = true;
+        goto out;
+    }
+
+    /* Even that didn't work;  this is an exceptional state.
+     * Try harder, growing the heap source if necessary.
+     */
+    HSTRACE("EXTERNAL alloc %zd: frag\n", n);
+    ret = externalAlloc(hs, n, true);
+    if (ret) {
+        goto out;
+    }
+
+    /* We couldn't even grow enough to satisfy the request.
+     * Try one last GC, collecting SoftReferences this time.
+     */
+    HSTRACE("EXTERNAL alloc %zd: GC 2\n", n);
+    gcForExternalAlloc(true);  // collect SoftReferences
+    ret = externalAlloc(hs, n, true);
+    if (!ret) {
+        LOGE_HEAP("Out of external memory on a %zu-byte allocation.\n", n);
+    }
+
+#if PROFILE_EXTERNAL_ALLOCATIONS
+    if (gDvm.allocProf.enabled) {
+        Thread* self = dvmThreadSelf();
+        gDvm.allocProf.failedExternalAllocCount++;
+        gDvm.allocProf.failedExternalAllocSize += n;
+        if (self != NULL) {
+            self->allocProf.failedExternalAllocCount++;
+            self->allocProf.failedExternalAllocSize += n;
+        }
+    }
+#endif
+
+out:
+    dvmUnlockHeap();
+
+    return ret;
+}
+
+/*
+ * Reduces the internal count of externally-allocated memory.
+ */
+void
+dvmTrackExternalFree(size_t n)
+{
+    HeapSource *hs = gHs;
+    size_t newExternalLimit;
+    size_t oldExternalBytesAllocated;
+
+    HSTRACE("EXTERNAL free %zu (%zu < %zu)\n",
+            n, hs->externalBytesAllocated, hs->externalLimit);
+
+    /* gHs caches an entry in gDvm.gcHeap;  we need to hold the
+     * heap lock if we're going to look at it.
+     */
+    dvmLockHeap();
+
+    HS_BOILERPLATE();
+    assert(hs->externalLimit >= hs->externalBytesAllocated);
+
+    oldExternalBytesAllocated = hs->externalBytesAllocated;
+    if (n <= hs->externalBytesAllocated) {
+        hs->externalBytesAllocated -= n;
+    } else {
+        n = hs->externalBytesAllocated;
+        hs->externalBytesAllocated = 0;
+    }
+
+#if PROFILE_EXTERNAL_ALLOCATIONS
+    if (gDvm.allocProf.enabled) {
+        Thread* self = dvmThreadSelf();
+        gDvm.allocProf.externalFreeCount++;
+        gDvm.allocProf.externalFreeSize += n;
+        if (self != NULL) {
+            self->allocProf.externalFreeCount++;
+            self->allocProf.externalFreeSize += n;
+        }
+    }
+#endif
+
+    /* Shrink as quickly as we can.
+     */
+    newExternalLimit = getUtilizationTarget(
+            hs->externalBytesAllocated, EXTERNAL_TARGET_UTILIZATION);
+    if (newExternalLimit < oldExternalBytesAllocated) {
+        /* Make sure that the remaining free space is at least
+         * big enough to allocate something of the size that was
+         * just freed.  This makes it more likely that
+         *     externalFree(N); externalAlloc(N);
+         * will work without causing a GC.
+         */
+        HSTRACE("EXTERNAL free preserved %zu extra free bytes\n",
+                oldExternalBytesAllocated - newExternalLimit);
+        newExternalLimit = oldExternalBytesAllocated;
+    }
+    if (newExternalLimit < hs->externalLimit) {
+        hs->externalLimit = newExternalLimit;
+    }
+
+    dvmUnlockHeap();
+}
+
+/*
+ * Returns the number of externally-allocated bytes being tracked by
+ * dvmTrackExternalAllocation/Free().
+ */
+size_t
+dvmGetExternalBytesAllocated()
+{
+    const HeapSource *hs = gHs;
+    size_t ret;
+
+    /* gHs caches an entry in gDvm.gcHeap;  we need to hold the
+     * heap lock if we're going to look at it.  We also need the
+     * lock for the call to setIdealFootprint().
+     */
+    dvmLockHeap();
+    HS_BOILERPLATE();
+    ret = hs->externalBytesAllocated;
+    dvmUnlockHeap();
+
+    return ret;
+}
+
+void *dvmHeapSourceGetImmuneLimit(GcMode mode)
+{
+    if (mode == GC_PARTIAL) {
+        return hs2heap(gHs)->base;
+    } else {
+        return NULL;
+    }
+}
diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h
new file mode 100644
index 0000000..84b5794
--- /dev/null
+++ b/vm/alloc/HeapSource.h
@@ -0,0 +1,202 @@
+/*
+ * 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
+#define _DALVIK_HEAP_SOURCE
+
+#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))
+#define HEAP_SOURCE_WORST_CHUNK_OVERHEAD   (32 * sizeof (size_t))
+
+/* The largest number of separate heaps we can handle.
+ */
+#define HEAP_SOURCE_MAX_HEAP_COUNT 2
+
+/*
+ * Initializes the heap source; must be called before any other
+ * dvmHeapSource*() functions.
+ */
+GcHeap *dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize);
+
+/*
+ * 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);
+
+/*
+ * Initializes a vector of object and mark bits to the object and mark
+ * bits of each heap.
+ */
+void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[],
+                                   size_t numHeaps);
+
+/*
+ * Get the bitmap representing all live objects.
+ */
+HeapBitmap *dvmHeapSourceGetLiveBits(void);
+
+/*
+ * Gets the begining of the allocation for the HeapSource.
+ */
+void *dvmHeapSourceGetBase(void);
+
+/*
+ * Returns the requested value. If the per-heap stats are requested, fill
+ * them as well.
+ */
+enum HeapSourceValueSpec {
+    HS_FOOTPRINT,
+    HS_ALLOWED_FOOTPRINT,
+    HS_BYTES_ALLOCATED,
+    HS_OBJECTS_ALLOCATED,
+    HS_EXTERNAL_BYTES_ALLOCATED,
+    HS_EXTERNAL_LIMIT
+};
+size_t dvmHeapSourceGetValue(enum 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 value of the requested flag.
+ */
+enum HeapSourcePtrFlag {
+    HS_CONTAINS,    // identical to dvmHeapSourceContains()
+    HS_ALLOCATED_IN_ZYGOTE
+};
+bool dvmHeapSourceGetPtrFlag(const void *ptr, enum HeapSourcePtrFlag flag);
+
+/*
+ * 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);
+
+/*
+ * Return unused memory to the system if possible.  If <bytesTrimmed>
+ * is non-NULL, the number of bytes returned to the system is written to it.
+ */
+void dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen);
+
+/*
+ * Walks over the heap source and passes every allocated and
+ * free chunk to the callback.
+ */
+void dvmHeapSourceWalk(void(*callback)(const void *chunkptr, size_t chunklen,
+                                      const void *userptr, size_t userlen,
+                                      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(GcMode mode);
+
+#endif  // _DALVIK_HEAP_SOURCE
diff --git a/vm/alloc/HeapTable.c b/vm/alloc/HeapTable.c
new file mode 100644
index 0000000..e9f2729
--- /dev/null
+++ b/vm/alloc/HeapTable.c
@@ -0,0 +1,198 @@
+/*
+ * 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/HeapTable.h"
+#include "alloc/HeapInternal.h"
+
+#include <limits.h> // for INT_MAX
+
+static const int kLargeHeapRefTableNElems = 1024;
+static const int  kFinalizableRefDefault = 128;
+
+void dvmHeapHeapTableFree(void *ptr)
+{
+    free(ptr);
+}
+
+#define heapRefTableIsFull(refs) \
+    dvmIsReferenceTableFull(refs)
+
+bool dvmHeapInitHeapRefTable(HeapRefTable *refs)
+{
+    memset(refs, 0, sizeof(*refs));
+    return dvmInitReferenceTable(refs, kFinalizableRefDefault, INT_MAX);
+}
+
+/* Frees the array inside the HeapRefTable, not the HeapRefTable itself.
+ */
+void dvmHeapFreeHeapRefTable(HeapRefTable *refs)
+{
+    dvmClearReferenceTable(refs);
+}
+
+/*
+ * Large, non-contiguous reference tables
+ */
+
+bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref)
+{
+    LargeHeapRefTable *table;
+
+    assert(tableP != NULL);
+    assert(ref != NULL);
+
+    /* Make sure that a table with a free slot is
+     * at the head of the list.
+     */
+    if (*tableP != NULL) {
+        table = *tableP;
+        LargeHeapRefTable *prevTable;
+
+        /* Find an empty slot for this reference.
+         */
+        prevTable = NULL;
+        while (table != NULL && heapRefTableIsFull(&table->refs)) {
+            prevTable = table;
+            table = table->next;
+        }
+        if (table != NULL) {
+            if (prevTable != NULL) {
+                /* Move the table to the head of the list.
+                 */
+                prevTable->next = table->next;
+                table->next = *tableP;
+                *tableP = table;
+            }
+            /* else it's already at the head. */
+
+            goto insert;
+        }
+        /* else all tables are already full;
+         * fall through to the alloc case.
+         */
+    }
+
+    /* Allocate a new table.
+     */
+    table = calloc(1, sizeof(LargeHeapRefTable));
+    if (table == NULL) {
+        LOGE_HEAP("Can't allocate a new large ref table\n");
+        return false;
+    }
+    if (!dvmInitReferenceTable(&table->refs,
+                               kLargeHeapRefTableNElems,
+                               INT_MAX)) {
+        LOGE_HEAP("Can't initialize a new large ref table\n");
+        dvmHeapHeapTableFree(table);
+        return false;
+    }
+
+    /* Stick it at the head.
+     */
+    table->next = *tableP;
+    *tableP = table;
+
+insert:
+    /* Insert the reference.
+     */
+    assert(table == *tableP);
+    assert(table != NULL);
+    assert(!heapRefTableIsFull(&table->refs));
+    *table->refs.nextEntry++ = ref;
+
+    return true;
+}
+
+bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP, HeapRefTable *refs)
+{
+    LargeHeapRefTable *table;
+
+    /* Allocate a node.
+     */
+    table = calloc(1, sizeof(LargeHeapRefTable));
+    if (table == NULL) {
+        LOGE_HEAP("Can't allocate a new large ref table\n");
+        return false;
+    }
+    table->refs = *refs;
+
+    /* Insert the table into the list.
+     */
+    table->next = *tableP;
+    *tableP = table;
+
+    return true;
+}
+
+/* Frees everything associated with the LargeHeapRefTable.
+ */
+void dvmHeapFreeLargeTable(LargeHeapRefTable *table)
+{
+    while (table != NULL) {
+        LargeHeapRefTable *next = table->next;
+        dvmHeapFreeHeapRefTable(&table->refs);
+        dvmHeapHeapTableFree(table);
+        table = next;
+    }
+}
+
+Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable)
+{
+    LargeHeapRefTable *table;
+    Object *obj;
+
+    assert(pTable != NULL);
+
+    obj = NULL;
+    table = *pTable;
+    if (table != NULL) {
+        HeapRefTable *refs = &table->refs;
+
+        /* We should never have an empty table node in the list.
+         */
+        assert(dvmReferenceTableEntries(refs) != 0);
+
+        /* Remove and return the last entry in the list.
+         */
+        obj = *--refs->nextEntry;
+
+        /* If this was the last entry in the table node,
+         * free it and patch up the list.
+         */
+        if (refs->nextEntry == refs->table) {
+            *pTable = table->next;
+            dvmClearReferenceTable(refs);
+            dvmHeapHeapTableFree(table);
+        }
+    }
+
+    return obj;
+}
+
+void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table)
+{
+    while (table != NULL) {
+        Object **ref, **lastRef;
+
+        ref = table->refs.table;
+        lastRef = table->refs.nextEntry;
+        while (ref < lastRef) {
+            dvmMarkObjectNonNull(*ref++);
+        }
+        table = table->next;
+    }
+}
diff --git a/vm/alloc/HeapTable.h b/vm/alloc/HeapTable.h
new file mode 100644
index 0000000..55851b9
--- /dev/null
+++ b/vm/alloc/HeapTable.h
@@ -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.
+ */
+#ifndef _DALVIK_ALLOC_HEAP_TABLE
+#define _DALVIK_ALLOC_HEAP_TABLE
+
+#include "ReferenceTable.h"
+
+typedef ReferenceTable HeapRefTable;
+typedef struct LargeHeapRefTable LargeHeapRefTable;
+typedef struct HeapSource HeapSource;
+
+struct LargeHeapRefTable {
+    LargeHeapRefTable *next;
+    HeapRefTable refs;
+};
+
+bool dvmHeapInitHeapRefTable(HeapRefTable *refs);
+void dvmHeapFreeHeapRefTable(HeapRefTable *refs);
+void dvmHeapFreeLargeTable(LargeHeapRefTable *table);
+void dvmHeapHeapTableFree(void *ptr);
+bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref);
+void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table);
+bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP,
+        HeapRefTable *refs);
+Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable);
+
+#define dvmHeapAddToHeapRefTable(refs, ptr) \
+            dvmAddToReferenceTable((refs), (ptr))
+
+#define dvmHeapNumHeapRefTableEntries(refs) \
+            dvmReferenceTableEntries(refs)
+
+#define dvmHeapRemoveFromHeapRefTable(refs, ptr) \
+            dvmRemoveFromReferenceTable((refs), (refs)->table, (ptr))
+
+#endif  // _DALVIK_ALLOC_HEAP_TABLE
diff --git a/vm/alloc/HeapWorker.c b/vm/alloc/HeapWorker.c
new file mode 100644
index 0000000..42b8942
--- /dev/null
+++ b/vm/alloc/HeapWorker.c
@@ -0,0 +1,542 @@
+/*
+ * 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.
+ */
+
+/*
+ * An async worker thread to handle certain heap operations that need
+ * to be done in a separate thread to avoid synchronization problems.
+ * HeapWorkers and reference enqueuing are handled by this thread.
+ * The VM does all clearing.
+ */
+#include "Dalvik.h"
+#include "HeapInternal.h"
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <signal.h>
+#include <errno.h>  // for ETIMEDOUT, etc.
+
+static void* heapWorkerThreadStart(void* arg);
+
+/*
+ * Initialize any HeapWorker state that Heap.c
+ * cares about.  This lets the GC start before the
+ * HeapWorker thread is initialized.
+ */
+void dvmInitializeHeapWorkerState()
+{
+    assert(!gDvm.heapWorkerInitialized);
+
+    dvmInitMutex(&gDvm.heapWorkerLock);
+    pthread_cond_init(&gDvm.heapWorkerCond, NULL);
+    pthread_cond_init(&gDvm.heapWorkerIdleCond, NULL);
+
+    gDvm.heapWorkerInitialized = true;
+}
+
+/*
+ * Crank up the heap worker thread.
+ *
+ * Does not return until the thread is ready for business.
+ */
+bool dvmHeapWorkerStartup(void)
+{
+    assert(!gDvm.haltHeapWorker);
+    assert(!gDvm.heapWorkerReady);
+    assert(gDvm.heapWorkerHandle == 0);
+    assert(gDvm.heapWorkerInitialized);
+
+    /* use heapWorkerLock/heapWorkerCond to communicate readiness */
+    dvmLockMutex(&gDvm.heapWorkerLock);
+
+//BUG: If a GC happens in here or in the new thread while we hold the lock,
+//     the GC will deadlock when trying to acquire heapWorkerLock.
+    if (!dvmCreateInternalThread(&gDvm.heapWorkerHandle,
+                "HeapWorker", heapWorkerThreadStart, NULL))
+    {
+        dvmUnlockMutex(&gDvm.heapWorkerLock);
+        return false;
+    }
+
+    /*
+     * Wait for the heap worker to come up.  We know the thread was created,
+     * so this should not get stuck.
+     */
+    while (!gDvm.heapWorkerReady) {
+        dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
+    }
+
+    dvmUnlockMutex(&gDvm.heapWorkerLock);
+    return true;
+}
+
+/*
+ * Shut down the heap worker thread if it was started.
+ */
+void dvmHeapWorkerShutdown(void)
+{
+    void* threadReturn;
+
+    /* note: assuming that (pthread_t)0 is not a valid thread handle */
+    if (gDvm.heapWorkerHandle != 0) {
+        gDvm.haltHeapWorker = true;
+        dvmSignalHeapWorker(true);
+
+        /*
+         * We may not want to wait for the heapWorkers to complete.  It's
+         * a good idea to do so, in case they're holding some sort of OS
+         * resource that doesn't get reclaimed when the process exits
+         * (e.g. an open temp file).
+         */
+        if (pthread_join(gDvm.heapWorkerHandle, &threadReturn) != 0)
+            LOGW("HeapWorker thread join failed\n");
+        else if (gDvm.verboseShutdown)
+            LOGD("HeapWorker thread has shut down\n");
+
+        gDvm.heapWorkerReady = false;
+    }
+}
+
+/* Make sure that the HeapWorker thread hasn't spent an inordinate
+ * amount of time inside a finalizer.
+ *
+ * Aborts the VM if the thread appears to be wedged.
+ *
+ * The caller must hold the heapWorkerLock to guarantee an atomic
+ * read of the watchdog values.
+ */
+void dvmAssertHeapWorkerThreadRunning()
+{
+    if (gDvm.gcHeap->heapWorkerCurrentObject != NULL) {
+        static const u8 HEAP_WORKER_WATCHDOG_TIMEOUT = 10*1000*1000LL; // 10sec
+
+        u8 heapWorkerInterpStartTime = gDvm.gcHeap->heapWorkerInterpStartTime;
+        u8 now = dvmGetRelativeTimeUsec();
+        u8 delta = now - heapWorkerInterpStartTime;
+
+        if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT &&
+            (gDvm.debuggerActive || gDvm.nativeDebuggerActive))
+        {
+            /*
+             * Debugger suspension can block the thread indefinitely.  For
+             * best results we should reset this explicitly whenever the
+             * HeapWorker thread is resumed.  Unfortunately this is also
+             * affected by native debuggers, and we have no visibility
+             * into how they're manipulating us.  So, we ignore the
+             * watchdog and just reset the timer.
+             */
+            LOGI("Debugger is attached -- suppressing HeapWorker watchdog\n");
+            gDvm.gcHeap->heapWorkerInterpStartTime = now;   /* reset timer */
+        } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) {
+            /*
+             * Before we give up entirely, see if maybe we're just not
+             * getting any CPU time because we're stuck in a background
+             * process group.  If we successfully move the thread into the
+             * foreground we'll just leave it there (it doesn't do anything
+             * if the process isn't GCing).
+             */
+            dvmLockThreadList(NULL);
+            Thread* thread = dvmGetThreadByHandle(gDvm.heapWorkerHandle);
+            dvmUnlockThreadList();
+
+            if (thread != NULL) {
+                int priChangeFlags, threadPrio;
+                SchedPolicy threadPolicy;
+                priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread,
+                        &threadPrio, &threadPolicy);
+                if (priChangeFlags != 0) {
+                    LOGI("HeapWorker watchdog expired, raising priority"
+                         " and retrying\n");
+                    gDvm.gcHeap->heapWorkerInterpStartTime = now;
+                    return;
+                }
+            }
+
+            char* desc = dexProtoCopyMethodDescriptor(
+                    &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
+            LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s\n",
+                    delta / 1000,
+                    gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
+                    gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
+            free(desc);
+            dvmDumpAllThreads(true);
+
+            /* try to get a debuggerd dump from the target thread */
+            dvmNukeThread(thread);
+
+            /* abort the VM */
+            dvmAbort();
+        } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) {
+            char* desc = dexProtoCopyMethodDescriptor(
+                    &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
+            LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s\n",
+                    delta / 1000,
+                    gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
+                    gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
+            free(desc);
+        }
+    }
+}
+
+/*
+ * Acquires a mutex, transitioning to the VMWAIT state if the mutex is
+ * held.  This allows the thread to suspend while it waits for another
+ * thread to release the mutex.
+ */
+static void lockMutex(pthread_mutex_t *mu)
+{
+    Thread *self;
+    ThreadStatus oldStatus;
+
+    assert(mu != NULL);
+    if (dvmTryLockMutex(mu) != 0) {
+        self = dvmThreadSelf();
+        assert(self != NULL);
+        oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        dvmLockMutex(mu);
+        dvmChangeStatus(self, oldStatus);
+    }
+}
+
+static void callMethod(Thread *self, Object *obj, Method *method)
+{
+    JValue unused;
+
+    /* Keep track of the method we're about to call and
+     * the current time so that other threads can detect
+     * when this thread wedges and provide useful information.
+     */
+    gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec();
+    gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec();
+    gDvm.gcHeap->heapWorkerCurrentMethod = method;
+    gDvm.gcHeap->heapWorkerCurrentObject = obj;
+
+    /* Call the method.
+     *
+     * Don't hold the lock when executing interpreted
+     * code.  It may suspend, and the GC needs to grab
+     * heapWorkerLock.
+     */
+    dvmUnlockMutex(&gDvm.heapWorkerLock);
+    if (false) {
+        /* Log entry/exit; this will likely flood the log enough to
+         * cause "logcat" to drop entries.
+         */
+        char tmpTag[16];
+        sprintf(tmpTag, "HW%d", self->systemTid);
+        LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor);
+        dvmCallMethod(self, method, obj, &unused);
+        LOG(LOG_DEBUG, tmpTag, " done\n");
+    } else {
+        dvmCallMethod(self, method, obj, &unused);
+    }
+    /*
+     * Reacquire the heap worker lock in a suspend-friendly way.
+     */
+    lockMutex(&gDvm.heapWorkerLock);
+
+    gDvm.gcHeap->heapWorkerCurrentObject = NULL;
+    gDvm.gcHeap->heapWorkerCurrentMethod = NULL;
+    gDvm.gcHeap->heapWorkerInterpStartTime = 0LL;
+
+    /* Exceptions thrown during these calls interrupt
+     * the method, but are otherwise ignored.
+     */
+    if (dvmCheckException(self)) {
+#if DVM_SHOW_EXCEPTION >= 1
+        LOGI("Uncaught exception thrown by finalizer (will be discarded):\n");
+        dvmLogExceptionStackTrace();
+#endif
+        dvmClearException(self);
+    }
+}
+
+/* Process all enqueued heap work, including finalizers and reference
+ * enqueueing. Clearing has already been done by the VM.
+ *
+ * Caller must hold gDvm.heapWorkerLock.
+ */
+static void doHeapWork(Thread *self)
+{
+    Object *obj;
+    HeapWorkerOperation op;
+    int numFinalizersCalled, numReferencesEnqueued;
+
+    assert(gDvm.voffJavaLangObject_finalize >= 0);
+    assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL);
+
+    numFinalizersCalled = 0;
+    numReferencesEnqueued = 0;
+    while ((obj = dvmGetNextHeapWorkerObject(&op)) != NULL) {
+        Method *method = NULL;
+
+        /* Make sure the object hasn't been collected since
+         * being scheduled.
+         */
+        assert(dvmIsValidObject(obj));
+
+        /* Call the appropriate method(s).
+         */
+        if (op == WORKER_FINALIZE) {
+            numFinalizersCalled++;
+            method = obj->clazz->vtable[gDvm.voffJavaLangObject_finalize];
+            assert(dvmCompareNameDescriptorAndMethod("finalize", "()V",
+                            method) == 0);
+            assert(method->clazz != gDvm.classJavaLangObject);
+            callMethod(self, obj, method);
+        } else {
+            assert(op == WORKER_ENQUEUE);
+            assert(dvmGetFieldObject(
+                       obj, gDvm.offJavaLangRefReference_queue) != NULL);
+            assert(dvmGetFieldObject(
+                       obj, gDvm.offJavaLangRefReference_queueNext) == NULL);
+            numReferencesEnqueued++;
+            callMethod(self, obj,
+                       gDvm.methJavaLangRefReference_enqueueInternal);
+        }
+
+        /* Let the GC collect the object.
+         */
+        dvmReleaseTrackedAlloc(obj, self);
+    }
+    LOGV("Called %d finalizers\n", numFinalizersCalled);
+    LOGV("Enqueued %d references\n", numReferencesEnqueued);
+}
+
+/*
+ * The heap worker thread sits quietly until the GC tells it there's work
+ * to do.
+ */
+static void* heapWorkerThreadStart(void* arg)
+{
+    Thread *self = dvmThreadSelf();
+
+    UNUSED_PARAMETER(arg);
+
+    LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId);
+
+    /* tell the main thread that we're ready */
+    lockMutex(&gDvm.heapWorkerLock);
+    gDvm.heapWorkerReady = true;
+    dvmSignalCond(&gDvm.heapWorkerCond);
+    dvmUnlockMutex(&gDvm.heapWorkerLock);
+
+    lockMutex(&gDvm.heapWorkerLock);
+    while (!gDvm.haltHeapWorker) {
+        struct timespec trimtime;
+        bool timedwait = false;
+
+        /* We're done running interpreted code for now. */
+        dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+        /* Signal anyone who wants to know when we're done. */
+        dvmBroadcastCond(&gDvm.heapWorkerIdleCond);
+
+        /* Trim the heap if we were asked to. */
+        trimtime = gDvm.gcHeap->heapWorkerNextTrim;
+        if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
+            struct timespec now;
+
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+            clock_gettime(CLOCK_MONOTONIC, &now);       // relative time
+#else
+            struct timeval tvnow;
+            gettimeofday(&tvnow, NULL);                 // absolute time
+            now.tv_sec = tvnow.tv_sec;
+            now.tv_nsec = tvnow.tv_usec * 1000;
+#endif
+
+            if (trimtime.tv_sec < now.tv_sec ||
+                (trimtime.tv_sec == now.tv_sec &&
+                 trimtime.tv_nsec <= now.tv_nsec))
+            {
+                size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];
+
+                /*
+                 * Acquire the gcHeapLock.  The requires releasing the
+                 * heapWorkerLock before the gcHeapLock is acquired.
+                 * It is possible that the gcHeapLock may be acquired
+                 * during a concurrent GC in which case heapWorkerLock
+                 * is held by the GC and we are unable to make forward
+                 * progress.  We avoid deadlock by releasing the
+                 * gcHeapLock and then waiting to be signaled when the
+                 * GC completes.  There is no guarantee that the next
+                 * time we are run will coincide with GC inactivity so
+                 * the check and wait must be performed within a loop.
+                 */
+                dvmUnlockMutex(&gDvm.heapWorkerLock);
+                dvmLockHeap();
+                while (gDvm.gcHeap->gcRunning) {
+                    dvmWaitForConcurrentGcToComplete();
+                }
+                dvmLockMutex(&gDvm.heapWorkerLock);
+
+                memset(madvisedSizes, 0, sizeof(madvisedSizes));
+                dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
+                dvmLogMadviseStats(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
+
+                dvmUnlockHeap();
+
+                trimtime.tv_sec = 0;
+                trimtime.tv_nsec = 0;
+                gDvm.gcHeap->heapWorkerNextTrim = trimtime;
+            } else {
+                timedwait = true;
+            }
+        }
+
+        /* sleep until signaled */
+        if (timedwait) {
+            int cc __attribute__ ((__unused__));
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+            cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond,
+                    &gDvm.heapWorkerLock, &trimtime);
+#else
+            cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
+                    &gDvm.heapWorkerLock, &trimtime);
+#endif
+            assert(cc == 0 || cc == ETIMEDOUT);
+        } else {
+            dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
+        }
+
+        /*
+         * Return to the running state before doing heap work.  This
+         * will block if the GC has initiated a suspend.  We release
+         * the heapWorkerLock beforehand for the GC to make progress
+         * and wait to be signaled after the GC completes.  There is
+         * no guarantee that the next time we are run will coincide
+         * with GC inactivity so the check and wait must be performed
+         * within a loop.
+         */
+        dvmUnlockMutex(&gDvm.heapWorkerLock);
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+        dvmLockHeap();
+        while (gDvm.gcHeap->gcRunning) {
+            dvmWaitForConcurrentGcToComplete();
+        }
+        dvmLockMutex(&gDvm.heapWorkerLock);
+        dvmUnlockHeap();
+        LOGV("HeapWorker is awake\n");
+
+        /* Process any events in the queue.
+         */
+        doHeapWork(self);
+    }
+    dvmUnlockMutex(&gDvm.heapWorkerLock);
+
+    if (gDvm.verboseShutdown)
+        LOGD("HeapWorker thread shutting down\n");
+    return NULL;
+}
+
+/*
+ * Wake up the heap worker to let it know that there's work to be done.
+ */
+void dvmSignalHeapWorker(bool shouldLock)
+{
+    if (shouldLock) {
+        dvmLockMutex(&gDvm.heapWorkerLock);
+    }
+
+    dvmSignalCond(&gDvm.heapWorkerCond);
+
+    if (shouldLock) {
+        dvmUnlockMutex(&gDvm.heapWorkerLock);
+    }
+}
+
+/*
+ * Block until all pending heap worker work has finished.
+ */
+void dvmWaitForHeapWorkerIdle()
+{
+    assert(gDvm.heapWorkerReady);
+
+    dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+    dvmLockMutex(&gDvm.heapWorkerLock);
+
+    /* Wake up the heap worker and wait for it to finish. */
+    //TODO(http://b/issue?id=699704): This will deadlock if
+    //     called from finalize(), enqueue(), or clear().  We
+    //     need to detect when this is called from the HeapWorker
+    //     context and just give up.
+    dvmSignalHeapWorker(false);
+    dvmWaitCond(&gDvm.heapWorkerIdleCond, &gDvm.heapWorkerLock);
+
+    dvmUnlockMutex(&gDvm.heapWorkerLock);
+
+    dvmChangeStatus(NULL, THREAD_RUNNING);
+}
+
+/*
+ * Do not return until any pending heap work has finished.  This may
+ * or may not happen in the context of the calling thread.
+ * No exceptions will escape.
+ */
+void dvmRunFinalizationSync()
+{
+    if (gDvm.zygote) {
+        assert(!gDvm.heapWorkerReady);
+
+        /* When in zygote mode, there is no heap worker.
+         * Do the work in the current thread.
+         */
+        dvmLockMutex(&gDvm.heapWorkerLock);
+        doHeapWork(dvmThreadSelf());
+        dvmUnlockMutex(&gDvm.heapWorkerLock);
+    } else {
+        /* Outside of zygote mode, we can just ask the
+         * heap worker thread to do the work.
+         */
+        dvmWaitForHeapWorkerIdle();
+    }
+}
+
+/*
+ * Requests that dvmHeapSourceTrim() be called no sooner
+ * than timeoutSec seconds from now.  If timeoutSec
+ * is zero, any pending trim is cancelled.
+ *
+ * Caller must hold heapWorkerLock.
+ */
+void dvmScheduleHeapSourceTrim(size_t timeoutSec)
+{
+    GcHeap *gcHeap = gDvm.gcHeap;
+    struct timespec timeout;
+
+    if (timeoutSec == 0) {
+        timeout.tv_sec = 0;
+        timeout.tv_nsec = 0;
+        /* Don't wake up the thread just to tell it to cancel.
+         * If it wakes up naturally, we can avoid the extra
+         * context switch.
+         */
+    } else {
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+        clock_gettime(CLOCK_MONOTONIC, &timeout);
+        timeout.tv_sec += timeoutSec;
+#else
+        struct timeval now;
+        gettimeofday(&now, NULL);
+        timeout.tv_sec = now.tv_sec + timeoutSec;
+        timeout.tv_nsec = now.tv_usec * 1000;
+#endif
+        dvmSignalHeapWorker(false);
+    }
+    gcHeap->heapWorkerNextTrim = timeout;
+}
diff --git a/vm/alloc/HeapWorker.h b/vm/alloc/HeapWorker.h
new file mode 100644
index 0000000..45587ff
--- /dev/null
+++ b/vm/alloc/HeapWorker.h
@@ -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.
+ */
+/*
+ * Manage async heap tasks.
+ */
+#ifndef _DALVIK_ALLOC_HEAP_WORKER
+#define _DALVIK_ALLOC_HEAP_WORKER
+
+/*
+ * Initialize any HeapWorker state that Heap.c
+ * cares about.  This lets the GC start before the
+ * HeapWorker thread is initialized.
+ */
+void dvmInitializeHeapWorkerState(void);
+
+/*
+ * Initialization.  Starts/stops the worker thread.
+ */
+bool dvmHeapWorkerStartup(void);
+void dvmHeapWorkerShutdown(void);
+
+/*
+ * Tell the worker thread to wake up and do work.
+ * If shouldLock is false, the caller must have already
+ * acquired gDvm.heapWorkerLock.
+ */
+void dvmSignalHeapWorker(bool shouldLock);
+
+/*
+ * Block until all pending heap worker work has finished.
+ */
+void dvmWaitForHeapWorkerIdle(void);
+
+/*
+ * Does not return until any pending finalizers have been called.
+ * This may or may not happen in the context of the calling thread.
+ * No exceptions will escape.
+ *
+ * Used by zygote, which doesn't have a HeapWorker thread.
+ */
+void dvmRunFinalizationSync(void);
+
+/*
+ * Requests that dvmHeapSourceTrim() be called no sooner
+ * than timeoutSec seconds from now.  If timeoutSec
+ * is zero, any pending trim is cancelled.
+ *
+ * Caller must hold heapWorkerLock.
+ */
+void dvmScheduleHeapSourceTrim(size_t timeoutSec);
+
+/* Make sure that the HeapWorker thread hasn't spent an inordinate
+ * amount of time inside interpreted code.
+ *
+ * Aborts the VM if the thread appears to be wedged.
+ *
+ * The caller must hold the heapWorkerLock.
+ */
+void dvmAssertHeapWorkerThreadRunning();
+
+/*
+ * The type of operation for HeapWorker to perform on an object.
+ */
+typedef enum HeapWorkerOperation {
+    WORKER_FINALIZE = 0,
+    WORKER_ENQUEUE = 1,
+} HeapWorkerOperation;
+
+/*
+ * Called by the worker thread to get the next object
+ * to finalize/enqueue/clear.  Implemented in Heap.c.
+ *
+ * @param op The operation to perform on the returned object.
+ *           Must be non-NULL.
+ * @return The object to operate on, or NULL.
+ */
+Object *dvmGetNextHeapWorkerObject(HeapWorkerOperation *op);
+
+#endif /*_DALVIK_ALLOC_HEAP_WORKER*/
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
new file mode 100644
index 0000000..62a4ccc
--- /dev/null
+++ b/vm/alloc/MarkSweep.c
@@ -0,0 +1,986 @@
+/*
+ * 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/clz.h"
+#include "alloc/HeapBitmap.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>
+
+#define GC_LOG_TAG      LOG_TAG "-gc"
+
+#if LOG_NDEBUG
+#define LOGV_GC(...)    ((void)0)
+#define LOGD_GC(...)    ((void)0)
+#else
+#define LOGV_GC(...)    LOG(LOG_VERBOSE, GC_LOG_TAG, __VA_ARGS__)
+#define LOGD_GC(...)    LOG(LOG_DEBUG, GC_LOG_TAG, __VA_ARGS__)
+#endif
+
+#define LOGI_GC(...)    LOG(LOG_INFO, GC_LOG_TAG, __VA_ARGS__)
+#define LOGW_GC(...)    LOG(LOG_WARN, GC_LOG_TAG, __VA_ARGS__)
+#define LOGE_GC(...)    LOG(LOG_ERROR, GC_LOG_TAG, __VA_ARGS__)
+
+#define LOG_SCAN(...)   LOGV_GC("SCAN: " __VA_ARGS__)
+
+#define ALIGN_UP(x, n) (((size_t)(x) + (n) - 1) & ~((n) - 1))
+#define ALIGN_UP_TO_PAGE_SIZE(p) ALIGN_UP(p, SYSTEM_PAGE_SIZE)
+
+/* Do not cast the result of this to a boolean; the only set bit
+ * may be > 1<<8.
+ */
+static inline long isMarked(const void *obj, const GcMarkContext *ctx)
+{
+    return dvmHeapBitmapIsObjectBitSet(ctx->bitmap, obj);
+}
+
+static bool createMarkStack(GcMarkStack *stack)
+{
+    const Object **limit;
+    const char *name;
+    size_t size;
+
+    /* 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.
+     */
+    size = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) /
+            (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
+    size = ALIGN_UP_TO_PAGE_SIZE(size);
+    name = "dalvik-mark-stack";
+    limit = dvmAllocRegion(size, PROT_READ | PROT_WRITE, name);
+    if (limit == NULL) {
+        LOGE_GC("Could not mmap %zd-byte ashmem region '%s'", size, name);
+        return false;
+    }
+    stack->limit = limit;
+    stack->base = (const Object **)((uintptr_t)limit + size);
+    stack->top = stack->base;
+    return true;
+}
+
+static void destroyMarkStack(GcMarkStack *stack)
+{
+    munmap((char *)stack->limit,
+            (uintptr_t)stack->base - (uintptr_t)stack->limit);
+    memset(stack, 0, sizeof(*stack));
+}
+
+#define MARK_STACK_PUSH(stack, obj) \
+    do { \
+        *--(stack).top = (obj); \
+    } while (false)
+
+bool dvmHeapBeginMarkStep(GcMode mode)
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    if (!createMarkStack(&ctx->stack)) {
+        return false;
+    }
+    ctx->finger = NULL;
+    ctx->immuneLimit = dvmHeapSourceGetImmuneLimit(mode);
+    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.
+             */
+            MARK_STACK_PUSH(ctx->stack, obj);
+        }
+
+#if WITH_HPROF
+        if (gDvm.gcHeap->hprofContext != NULL) {
+            hprofMarkRootObject(gDvm.gcHeap->hprofContext, obj, 0);
+        }
+#endif
+    }
+}
+
+/* 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);
+    }
+}
+
+/* If the object hasn't already been marked, mark it and
+ * schedule it to be scanned for references.
+ *
+ * obj may not be NULL.  The macro dvmMarkObject() should
+ * be used in situations where a reference may be NULL.
+ *
+ * This function may only be called when marking the root
+ * set.  When recursing, use the internal markObject().
+ */
+void dvmMarkObjectNonNull(const Object *obj)
+{
+    assert(obj != NULL);
+    markObjectNonNull(obj, &gDvm.gcHeap->markContext, 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 allocated with ALLOC_NO_GC
+ * - Objects pending finalization (but not yet finalized)
+ * - 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;
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_STICKY_CLASS, 0);
+
+    LOG_SCAN("immune objects");
+    dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
+
+    LOG_SCAN("root class loader\n");
+    dvmGcScanRootClassLoader();
+    LOG_SCAN("primitive classes\n");
+    dvmGcScanPrimitiveClasses();
+
+    /* dvmGcScanRootThreadGroups() sets a bunch of
+     * different scan states internally.
+     */
+    HPROF_CLEAR_GC_SCAN_STATE();
+
+    LOG_SCAN("root thread groups\n");
+    dvmGcScanRootThreadGroups();
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_INTERNED_STRING, 0);
+
+    LOG_SCAN("interned strings\n");
+    dvmGcScanInternedStrings();
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_GLOBAL, 0);
+
+    LOG_SCAN("JNI global refs\n");
+    dvmGcMarkJniGlobalRefs();
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0);
+
+    LOG_SCAN("pending reference operations\n");
+    dvmHeapMarkLargeTableRefs(gcHeap->referenceOperations);
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
+
+    LOG_SCAN("pending finalizations\n");
+    dvmHeapMarkLargeTableRefs(gcHeap->pendingFinalizationRefs);
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_DEBUGGER, 0);
+
+    LOG_SCAN("debugger refs\n");
+    dvmGcMarkDebuggerRefs();
+
+    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_VM_INTERNAL, 0);
+
+    /* Mark any special objects we have sitting around.
+     */
+    LOG_SCAN("special objects\n");
+    dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
+    dvmMarkObjectNonNull(gDvm.internalErrorObj);
+    dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
+//TODO: scan object references sitting in gDvm;  use pointer begin & end
+
+    HPROF_CLEAR_GC_SCAN_STATE();
+}
+
+/*
+ * Callback applied to root references.  If the root location contains
+ * a white reference it is pushed on the mark stack and grayed.
+ */
+static void markObjectVisitor(void *addr, void *arg)
+{
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    obj = *(Object **)addr;
+    if (obj != NULL) {
+        markObjectNonNull(obj, arg, true);
+    }
+}
+
+/*
+ * Grays all references in the roots.
+ */
+void dvmHeapReMarkRootSet(void)
+{
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    assert(ctx->finger == (void *)ULONG_MAX);
+    dvmVisitRoots(markObjectVisitor, ctx);
+}
+
+/*
+ * Nothing past this point is allowed to use dvmMarkObject() or
+ * dvmMarkObjectNonNull(), which are for root-marking only.
+ * Scanning/recursion must use markObject(), which takes the finger
+ * into account.
+ */
+#undef dvmMarkObject
+#define dvmMarkObject __dont_use_dvmMarkObject__
+#define dvmMarkObjectNonNull __dont_use_dvmMarkObjectNonNull__
+
+/*
+ * Scans instance fields.
+ */
+static void scanInstanceFields(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) {
+            const int rshift = CLZ(refOffsets);
+            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+            markObject(dvmGetFieldObject((Object*)obj,
+                                          CLASS_OFFSET_FROM_CLZ(rshift)), ctx);
+        }
+    } else {
+        ClassObject *clazz;
+        int i;
+        for (clazz = obj->clazz; clazz != NULL; clazz = clazz->super) {
+            InstField *field = clazz->ifields;
+            for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+                void *addr = BYTE_OFFSET((Object *)obj, field->byteOffset);
+                markObject(((JValue *)addr)->l, ctx);
+            }
+        }
+    }
+}
+
+/*
+ * Scans the header, static field references, and interface
+ * pointers of a class object.
+ */
+static void scanClassObject(const ClassObject *obj, GcMarkContext *ctx)
+{
+    int i;
+
+    assert(obj != NULL);
+    assert(obj->obj.clazz == gDvm.classJavaLangClass);
+    assert(ctx != NULL);
+
+    markObject((Object *)obj->obj.clazz, ctx);
+    if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) {
+        markObject((Object *)obj->elementClass, ctx);
+    }
+    /* Do super and the interfaces contain Objects and not dex idx values? */
+    if (obj->status > CLASS_IDX) {
+        markObject((Object *)obj->super, ctx);
+    }
+    markObject(obj->classLoader, ctx);
+    /* Scan static field references. */
+    for (i = 0; i < obj->sfieldCount; ++i) {
+        char ch = obj->sfields[i].field.signature[0];
+        if (ch == '[' || ch == 'L') {
+            markObject(obj->sfields[i].value.l, ctx);
+        }
+    }
+    /* Scan the instance fields. */
+    scanInstanceFields((const Object *)obj, ctx);
+    /* Scan interface references. */
+    if (obj->status > CLASS_IDX) {
+        for (i = 0; i < obj->interfaceCount; ++i) {
+            markObject((Object *)obj->interfaces[i], 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 ArrayObject *obj, GcMarkContext *ctx)
+{
+    size_t i;
+
+    assert(obj != NULL);
+    assert(obj->obj.clazz != NULL);
+    assert(ctx != NULL);
+    /* Scan the class object reference. */
+    markObject((Object *)obj->obj.clazz, ctx);
+    if (IS_CLASS_FLAG_SET(obj->obj.clazz, CLASS_ISOBJECTARRAY)) {
+        /* Scan the array contents. */
+        Object **contents = (Object **)obj->contents;
+        for (i = 0; i < obj->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_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 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)
+{
+    size_t offset;
+
+    assert(ref != NULL);
+    assert(list != NULL);
+    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)
+{
+    Object *ref, *head;
+    size_t offset;
+
+    assert(list != NULL);
+    assert(*list != NULL);
+    offset = gDvm.offJavaLangRefReference_pendingNext;
+    head = dvmGetFieldObject(*list, offset);
+    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)
+{
+    GcHeap *gcHeap = gDvm.gcHeap;
+    Object *pending, *referent;
+    size_t pendingNextOffset, referentOffset;
+
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE));
+    assert(ctx != NULL);
+    pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    pending = dvmGetFieldObject(obj, pendingNextOffset);
+    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 (isPhantomReference(obj)) {
+            list = &gcHeap->phantomReferences;
+        }
+        assert(list != NULL);
+        enqueuePendingReference(obj, list);
+    }
+}
+
+/*
+ * Scans the header and field references of a data object.
+ */
+static void scanDataObject(DataObject *obj, GcMarkContext *ctx)
+{
+    assert(obj != NULL);
+    assert(obj->obj.clazz != NULL);
+    assert(ctx != NULL);
+    /* Scan the class object. */
+    markObject((Object *)obj->obj.clazz, ctx);
+    /* Scan the instance fields. */
+    scanInstanceFields((const Object *)obj, ctx);
+    if (IS_CLASS_FLAG_SET(obj->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(ctx != NULL);
+    assert(obj->clazz != NULL);
+#if WITH_HPROF
+    if (gDvm.gcHeap->hprofContext != NULL) {
+        hprofDumpHeapObject(gDvm.gcHeap->hprofContext, obj);
+    }
+#endif
+    /* Dispatch a type-specific scan routine. */
+    if (obj->clazz == gDvm.classJavaLangClass) {
+        scanClassObject((ClassObject *)obj, ctx);
+    } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+        scanArrayObject((ArrayObject *)obj, ctx);
+    } else {
+        scanDataObject((DataObject *)obj, ctx);
+    }
+}
+
+static void
+processMarkStack(GcMarkContext *ctx)
+{
+    const Object **const base = ctx->stack.base;
+
+    /* 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.
+     */
+    ctx->finger = (void *)ULONG_MAX;
+    while (ctx->stack.top != base) {
+        scanObject(*ctx->stack.top++, 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(u1 *base, u1 *limit, HeapBitmap *markBits)
+{
+    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;
+}
+
+/*
+ * Scan the card table looking for objects that have been grayed by
+ * the mutator.
+ */
+static void scanGrayObjects(GcMarkContext *ctx)
+{
+    GcHeap *h = gDvm.gcHeap;
+    HeapBitmap *markBits, *liveBits;
+    u1 *card, *baseCard, *limitCard;
+    size_t footprint;
+
+    markBits = ctx->bitmap;
+    liveBits = dvmHeapSourceGetLiveBits();
+    footprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+    baseCard = &h->cardTableBase[0];
+    limitCard = dvmCardFromAddr((u1 *)dvmHeapSourceGetBase() + footprint);
+    assert(limitCard <= &h->cardTableBase[h->cardTableLength]);
+    for (card = baseCard; card != limitCard; ++card) {
+        if (*card == GC_CARD_DIRTY) {
+            /*
+             * The card is dirty.  Scan all of the objects that
+             * intersect with the card address.
+             */
+            u1 *addr = dvmAddrFromCard(card);
+            /*
+             * Scan through all black objects that start on the
+             * current card.
+             */
+            u1 *limit = addr + GC_CARD_SIZE;
+            u1 *next = addr;
+            while (next < limit) {
+                Object *obj = nextGrayObject(next, limit, markBits);
+                if (obj == NULL)
+                    break;
+                scanObject(obj, ctx);
+                next = (u1*)obj + ALIGN_UP(objectSize(obj), HB_OBJECT_ALIGNMENT);
+            }
+        }
+    }
+}
+
+/*
+ * 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(void *addr, void *finger, void *arg)
+{
+    GcMarkContext *ctx = arg;
+    ctx->finger = (void *)finger;
+    scanObject(addr, 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);
+
+    /* We've walked the mark bitmaps.  Scan anything that's
+     * left on the mark stack.
+     */
+    processMarkStack(ctx);
+
+    LOG_SCAN("done with marked objects\n");
+}
+
+void dvmHeapReScanMarkedObjects(void)
+{
+    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)
+{
+    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);
+    if (!dvmHeapAddRefToLargeTable(&gDvm.gcHeap->referenceOperations, ref)) {
+        LOGE_HEAP("enqueueReference(): no room for any more "
+                  "reference operations\n");
+        dvmAbort();
+    }
+}
+
+/*
+ * 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.
+ */
+void dvmHandleSoftRefs(Object **list)
+{
+    GcMarkContext *ctx;
+    Object *ref, *referent;
+    Object *clear;
+    size_t referentOffset;
+    size_t counter;
+    bool marked;
+
+    ctx = &gDvm.gcHeap->markContext;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    clear = NULL;
+    counter = 0;
+    while (*list != NULL) {
+        ref = dequeuePendingReference(list);
+        referent = dvmGetFieldObject(ref, referentOffset);
+        if (referent == NULL) {
+            /* Referent was cleared by the user during marking. */
+            continue;
+        }
+        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.
+ */
+void dvmClearWhiteRefs(Object **list)
+{
+    GcMarkContext *ctx;
+    Object *ref, *referent;
+    size_t referentOffset;
+    bool doSignal;
+
+    ctx = &gDvm.gcHeap->markContext;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    doSignal = false;
+    while (*list != NULL) {
+        ref = dequeuePendingReference(list);
+        referent = dvmGetFieldObject(ref, referentOffset);
+        if (referent != NULL && !isMarked(referent, ctx)) {
+            /* Referent is white, clear it. */
+            clearReference(ref);
+            if (isEnqueuable(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);
+}
+
+/* Find unreachable objects that need to be finalized,
+ * and schedule them for finalization.
+ */
+void dvmHeapScheduleFinalizations()
+{
+    HeapRefTable newPendingRefs;
+    LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
+    Object **ref;
+    Object **lastRef;
+    size_t totalPendCount;
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+    /*
+     * All reachable objects have been marked.
+     * Any unmarked 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.
+        LOGE_GC("dvmHeapScheduleFinalizations(): no room for "
+                "pending finalizations\n");
+        dvmAbort();
+    }
+
+    /* Walk through finalizableRefs and move any unmarked 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 (!isMarked(*ref, ctx)) {
+                if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
+                    //TODO: add the current table and allocate
+                    //      a new, smaller one.
+                    LOGE_GC("dvmHeapScheduleFinalizations(): "
+                            "no room for any more pending finalizations: %zd\n",
+                            dvmHeapNumHeapRefTableEntries(&newPendingRefs));
+                    dvmAbort();
+                }
+                newPendCount++;
+            } else {
+                /* This ref is marked, 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;
+    }
+    LOGD_GC("dvmHeapScheduleFinalizations(): %zd finalizers triggered.\n",
+            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))
+    {
+        LOGE_GC("dvmHeapScheduleFinalizations(): can't insert new "
+                "pending finalizations\n");
+        dvmAbort();
+    }
+
+    //TODO: try compacting the main list with a memcpy loop
+
+    /* Mark 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) {
+        assert(*ref != NULL);
+        markObject(*ref, ctx);
+        ref++;
+    }
+    HPROF_CLEAR_GC_SCAN_STATE();
+    processMarkStack(ctx);
+    dvmSignalHeapWorker(false);
+}
+
+void dvmHeapFinishMarkStep()
+{
+    GcMarkContext *ctx;
+
+    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;
+}
+
+typedef struct {
+    size_t numObjects;
+    size_t numBytes;
+    bool isConcurrent;
+} SweepContext;
+
+static void sweepBitmapCallback(size_t numPtrs, void **ptrs, void *arg)
+{
+    SweepContext *ctx = 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 *object)
+{
+    return !isMarked((void *)((uintptr_t)object & ~(HB_OBJECT_ALIGNMENT-1)),
+            &gDvm.gcHeap->markContext);
+}
+
+/*
+ * Process all the internal system structures that behave like
+ * weakly-held objects.
+ */
+void dvmHeapSweepSystemWeaks(void)
+{
+    dvmGcDetachDeadInternedStrings(isUnmarkedObject);
+    dvmSweepMonitorList(&gDvm.monitorList, isUnmarkedObject);
+}
+
+/*
+ * Walk through the list of objects that haven't been marked and free
+ * them.  Assumes the bitmaps have been swapped.
+ */
+void dvmHeapSweepUnmarkedObjects(GcMode mode, bool isConcurrent,
+                                 size_t *numObjects, size_t *numBytes)
+{
+    HeapBitmap currMark[HEAP_SOURCE_MAX_HEAP_COUNT];
+    HeapBitmap currLive[HEAP_SOURCE_MAX_HEAP_COUNT];
+    SweepContext ctx;
+    size_t numBitmaps, numSweepBitmaps;
+    size_t i;
+
+    numBitmaps = dvmHeapSourceGetNumHeaps();
+    dvmHeapSourceGetObjectBitmaps(currLive, currMark, numBitmaps);
+    if (mode == GC_PARTIAL) {
+        numSweepBitmaps = 1;
+        assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == currLive[0].base);
+    } else {
+        numSweepBitmaps = numBitmaps;
+    }
+    ctx.numObjects = ctx.numBytes = 0;
+    ctx.isConcurrent = isConcurrent;
+    for (i = 0; i < numSweepBitmaps; i++) {
+        HeapBitmap* prevLive = &currMark[i];
+        HeapBitmap* prevMark = &currLive[i];
+        dvmHeapBitmapSweepWalk(prevLive, prevMark, 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..ffdfbd5
--- /dev/null
+++ b/vm/alloc/MarkSweep.h
@@ -0,0 +1,60 @@
+/*
+ * 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
+#define _DALVIK_ALLOC_MARK_SWEEP
+
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapSource.h"
+
+/* Downward-growing stack for better cache read behavior.
+ */
+typedef struct {
+    /* Lowest address (inclusive)
+     */
+    const Object **limit;
+
+    /* Current top of the stack (inclusive)
+     */
+    const Object **top;
+
+    /* Highest address (exclusive)
+     */
+    const Object **base;
+} GcMarkStack;
+
+/* This is declared publicly so that it can be included in gDvm.gcHeap.
+ */
+typedef struct {
+    HeapBitmap *bitmap;
+    GcMarkStack stack;
+    const char *immuneLimit;
+    const void *finger;   // only used while scanning/recursing.
+} GcMarkContext;
+
+bool dvmHeapBeginMarkStep(GcMode mode);
+void dvmHeapMarkRootSet(void);
+void dvmHeapReMarkRootSet(void);
+void dvmHeapScanMarkedObjects(void);
+void dvmHeapReScanMarkedObjects(void);
+void dvmHandleSoftRefs(Object **list);
+void dvmClearWhiteRefs(Object **list);
+void dvmHeapScheduleFinalizations(void);
+void dvmHeapFinishMarkStep(void);
+void dvmHeapSweepSystemWeaks(void);
+void dvmHeapSweepUnmarkedObjects(GcMode mode, bool isConcurrent,
+                                 size_t *numObjects, size_t *numBytes);
+
+#endif  // _DALVIK_ALLOC_MARK_SWEEP
diff --git a/vm/alloc/TEST/HeapBitmapTest/Makefile b/vm/alloc/TEST/HeapBitmapTest/Makefile
new file mode 100644
index 0000000..fe31b24
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/Makefile
@@ -0,0 +1,28 @@
+.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 ../../clz.h include/cutils/ashmem.h include/Dalvik.h
+	$(CC) $(CFLAGS) -c $< -o $@ -I ../.. -I include
+
+out/clz.o: ../../clz.c ../../clz.h
+	$(CC) $(CFLAGS) -c $< -o $@ -I ../..
+
+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..4c9f608
--- /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 LOGW(...) printf("W/" __VA_ARGS__)
+#define LOGE(...) 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.c b/vm/alloc/Verify.c
new file mode 100644
index 0000000..48168dd
--- /dev/null
+++ b/vm/alloc/Verify.c
@@ -0,0 +1,129 @@
+/*
+ * 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"
+
+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(void *ptr, void *arg)
+{
+    Object *obj = arg;
+    if (ptr == obj) {
+        LOGD("skipping %p == %p", ptr, obj);
+        return;
+    }
+    dvmVisitObject(dumpReferencesVisitor, ptr, &obj);
+    if (obj == NULL) {
+        LOGD("Found %p in the heap @ %p", arg, ptr);
+        dvmDumpObject(ptr);
+    }
+}
+
+static void dumpReferencesRootVisitor(void *ptr, void *arg)
+{
+    Object *obj = *(Object **)ptr;
+    Object *lookingFor = *(Object **)arg;
+    if (obj == lookingFor) {
+        LOGD("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 = arg;
+        if (*parent != NULL) {
+            LOGE("Verify of object %p failed", *parent);
+            dvmDumpObject(*parent);
+            *parent = NULL;
+        }
+        LOGE("Verify of reference %p @ %p failed", obj, addr);
+        dvmDumpObject(obj);
+    }
+}
+
+/*
+ * Verifies an object reference.
+ */
+void dvmVerifyObject(const Object *obj)
+{
+    Object *arg = (Object *)obj;
+    dvmVisitObject(verifyReference, (Object *)obj, &arg);
+    if (arg == NULL) {
+        dumpReferences(obj);
+        dvmAbort();
+    }
+}
+
+/*
+ * Helper function to call dvmVerifyObject from a bitmap walker.
+ */
+static void verifyBitmapCallback(void *ptr, void *arg)
+{
+    dvmVerifyObject(ptr);
+}
+
+/*
+ * Verifies the object references in a heap bitmap. Assumes the VM is
+ * suspended.
+ */
+void dvmVerifyBitmap(const HeapBitmap *bitmap)
+{
+    dvmHeapBitmapWalk(bitmap, verifyBitmapCallback, NULL);
+}
+
+/*
+ * Verifies references in the roots.
+ */
+void dvmVerifyRoots(void)
+{
+    dvmVisitRoots(verifyReference, NULL);
+}
diff --git a/vm/alloc/Verify.h b/vm/alloc/Verify.h
new file mode 100644
index 0000000..56d8958
--- /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
+#define _DALVIK_ALLOC_VERIFY
+
+/*
+ * 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 */
diff --git a/vm/alloc/Visit.c b/vm/alloc/Visit.c
new file mode 100644
index 0000000..0af27da
--- /dev/null
+++ b/vm/alloc/Visit.c
@@ -0,0 +1,205 @@
+/*
+ * 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/clz.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(Visitor *visitor, HashTable *table, void *arg)
+{
+    int i;
+
+    assert(visitor != NULL);
+    assert(table != NULL);
+    dvmHashTableLock(table);
+    for (i = 0; i < table->tableSize; ++i) {
+        HashEntry *entry = &table->pEntries[i];
+        if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
+            (*visitor)(&entry->data, arg);
+        }
+    }
+    dvmHashTableUnlock(table);
+}
+
+/*
+ * Visits all entries in the reference table.
+ */
+static void visitReferenceTable(Visitor *visitor, const ReferenceTable *table,
+                                void *arg)
+{
+    Object **entry;
+
+    assert(visitor != NULL);
+    assert(table != NULL);
+    for (entry = table->table; entry < table->nextEntry; ++entry) {
+        assert(entry != NULL);
+        (*visitor)(entry, arg);
+    }
+}
+
+/*
+ * Visits a large heap reference table.  These objects are list heads.
+ * As such, it is valid for table to be NULL.
+ */
+static void visitLargeHeapRefTable(Visitor *visitor, LargeHeapRefTable *table,
+                                   void *arg)
+{
+    assert(visitor != NULL);
+    for (; table != NULL; table = table->next) {
+        visitReferenceTable(visitor, &table->refs, arg);
+    }
+}
+
+/*
+ * Visits all stack slots. TODO: visit native methods.
+ */
+static void visitThreadStack(Visitor *visitor, Thread *thread, void *arg)
+{
+    const StackSaveArea *saveArea;
+    u4 *framePtr;
+
+    assert(visitor != NULL);
+    assert(thread != NULL);
+    framePtr = (u4 *)thread->curFrame;
+    for (; framePtr != NULL; framePtr = saveArea->prevFrame) {
+        Method *method;
+        saveArea = SAVEAREA_FROM_FP(framePtr);
+        method = (Method *)saveArea->method;
+        if (method != NULL && !dvmIsNativeMethod(method)) {
+            const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
+            const u1* regVector = NULL;
+            size_t i;
+
+            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 (i = 0; i < method->registersSize; ++i) {
+                    if (dvmIsValidObject((Object *)framePtr[i])) {
+                        (*visitor)(&framePtr[i], 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 (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.
+                         */
+                        (*visitor)(&framePtr[i], arg);
+                    }
+                }
+                dvmReleaseRegisterMapLine(pMap, regVector);
+            }
+        }
+        /*
+         * Don't fall into an infinite loop if things get corrupted.
+         */
+        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+               saveArea->prevFrame == NULL);
+    }
+}
+
+/*
+ * Visits all roots associated with a thread.
+ */
+static void visitThread(Visitor *visitor, Thread *thread, void *arg)
+{
+    assert(visitor != NULL);
+    assert(thread != NULL);
+    (*visitor)(&thread->threadObj, arg);
+    (*visitor)(&thread->exception, arg);
+    visitReferenceTable(visitor, &thread->internalLocalRefTable, arg);
+    visitReferenceTable(visitor, &thread->jniLocalRefTable, arg);
+    if (thread->jniMonitorRefTable.table) {
+        visitReferenceTable(visitor, &thread->jniMonitorRefTable, arg);
+    }
+    visitThreadStack(visitor, thread, arg);
+}
+
+/*
+ * Visits all threads on the thread list.
+ */
+static void visitThreads(Visitor *visitor, void *arg)
+{
+    Thread *thread;
+
+    assert(visitor != NULL);
+    dvmLockThreadList(dvmThreadSelf());
+    thread = gDvm.threadList;
+    while (thread) {
+        visitThread(visitor, thread, arg);
+        thread = thread->next;
+    }
+    dvmUnlockThreadList();
+}
+
+/*
+ * Visits roots.  TODO: visit all roots.
+ */
+void dvmVisitRoots(Visitor *visitor, void *arg)
+{
+    assert(visitor != NULL);
+    visitHashTable(visitor, gDvm.loadedClasses, arg);
+    visitHashTable(visitor, gDvm.dbgRegistry, arg);
+    visitHashTable(visitor, gDvm.internedStrings, arg);
+    visitHashTable(visitor, gDvm.literalStrings, arg);
+    visitReferenceTable(visitor, &gDvm.jniGlobalRefTable, arg);
+    visitReferenceTable(visitor, &gDvm.jniPinRefTable, arg);
+    visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, arg);
+    visitLargeHeapRefTable(visitor, gDvm.gcHeap->pendingFinalizationRefs, arg);
+    visitThreads(visitor, arg);
+    (*visitor)(&gDvm.outOfMemoryObj, arg);
+    (*visitor)(&gDvm.internalErrorObj, arg);
+    (*visitor)(&gDvm.noClassDefFoundErrorObj, arg);
+    /* TODO: visit cached global references. */
+}
diff --git a/vm/alloc/Visit.h b/vm/alloc/Visit.h
new file mode 100644
index 0000000..488c721
--- /dev/null
+++ b/vm/alloc/Visit.h
@@ -0,0 +1,38 @@
+/*
+ * 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
+#define _DALVIK_ALLOC_VISIT
+
+#include "Dalvik.h"
+
+/*
+ * Callback invoked with the address of a reference and a user
+ * supplied context argument.
+ */
+typedef void Visitor(void *addr, void *arg);
+
+/*
+ * Visits references in an object.
+ */
+void dvmVisitObject(Visitor *visitor, Object *obj, void *arg);
+
+/*
+ * Visits references in the root set.
+ */
+void dvmVisitRoots(Visitor *visitor, void *arg);
+
+#endif /* _DALVIK_ALLOC_VISIT */
diff --git a/vm/alloc/VisitInlines.h b/vm/alloc/VisitInlines.h
new file mode 100644
index 0000000..84a456a
--- /dev/null
+++ b/vm/alloc/VisitInlines.h
@@ -0,0 +1,180 @@
+/*
+ * 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
+#define _DALVIK_ALLOC_VISITINLINES
+
+/*
+ * 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 = BYTE_OFFSET(obj, offset);
+            (*visitor)(ref, arg);
+            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+        }
+    } else {
+        ClassObject *clazz;
+        for (clazz = obj->clazz; clazz != NULL; clazz = clazz->super) {
+            InstField *field = clazz->ifields;
+            int i;
+            for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+                size_t offset = field->byteOffset;
+                Object **ref = BYTE_OFFSET(obj, offset);
+                (*visitor)(ref, arg);
+            }
+        }
+    }
+}
+
+/*
+ * Visits the static fields of a class object.
+ */
+static void visitStaticFields(Visitor *visitor, ClassObject *clazz,
+                              void *arg)
+{
+    int i;
+
+    assert(visitor != NULL);
+    assert(clazz != NULL);
+    for (i = 0; i < clazz->sfieldCount; ++i) {
+        char ch = clazz->sfields[i].field.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)
+{
+    int i;
+
+    assert(visitor != NULL);
+    assert(clazz != NULL);
+    for (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 *classObj;
+    ClassStatus status;
+
+    assert(visitor != NULL);
+    assert(obj != NULL);
+    assert(obj->clazz != NULL);
+    assert(!strcmp(obj->clazz->descriptor, "Ljava/lang/Class;"));
+    classObj = (ClassObject *)obj;
+    (*visitor)(&obj->clazz, arg);
+    if (IS_CLASS_FLAG_SET(classObj, CLASS_ISARRAY)) {
+        (*visitor)(&classObj->elementClass, arg);
+    }
+    status = classObj->status;
+    if (status > CLASS_IDX) {
+        (*visitor)(&classObj->super, arg);
+    }
+    (*visitor)(&classObj->classLoader, arg);
+    visitFields(visitor, obj, arg);
+    visitStaticFields(visitor, classObj, arg);
+    if (status > CLASS_IDX) {
+        visitInterfaces(visitor, classObj, 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 **)array->contents;
+        size_t i;
+        for (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 = 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 (obj->clazz == gDvm.classJavaLangClass) {
+        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 */
diff --git a/vm/alloc/WriteBarrier.h b/vm/alloc/WriteBarrier.h
new file mode 100644
index 0000000..dd8f129
--- /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
+#define _DALVIK_ALLOC_WRITEBARRIER
+
+/*
+ * 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 */
diff --git a/vm/alloc/clz.c b/vm/alloc/clz.c
new file mode 100644
index 0000000..3488975
--- /dev/null
+++ b/vm/alloc/clz.c
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include "clz.h"
+
+/*
+ * Implementation of CLZ; intended to mimic gcc's __builtin_clz.
+ *
+ * Returns the number of leading zero bits, starting at the most
+ * significant bit position.  If the argument is zero, the result is
+ * undefined.
+ *
+ * (For best results, this file should always be compiled for ARM, not THUMB.)
+ */
+int dvmClzImpl(unsigned int x)
+{
+#ifdef HAVE_BUILTIN_CLZ
+    /*
+     * This file was compiled with flags that allow it to use the built-in
+     * CLZ (e.g. ARM mode for ARMv5 or later).
+     */
+    return __builtin_clz(x);
+#else
+    /*
+     * Built-in version not available.
+     */
+    if (!x) return 32;
+    int e = 31;
+    if (x&0xFFFF0000)   { e -=16; x >>=16; }
+    if (x&0x0000FF00)   { e -= 8; x >>= 8; }
+    if (x&0x000000F0)   { e -= 4; x >>= 4; }
+    if (x&0x0000000C)   { e -= 2; x >>= 2; }
+    if (x&0x00000002)   { e -= 1; }
+    return e;
+#endif
+}
diff --git a/vm/alloc/clz.h b/vm/alloc/clz.h
new file mode 100644
index 0000000..77fa6d4
--- /dev/null
+++ b/vm/alloc/clz.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+/*
+ * Implementation of clz(), which returns the number of leading zero bits,
+ * starting at the most significant bit position.  If the argument is zero,
+ * the result is undefined.
+ *
+ * On some platforms, gcc provides a __builtin_clz() function that uses
+ * an optimized implementation (e.g. the CLZ instruction on ARM).
+ *
+ * This gets a little tricky for ARM, because it's only available in ARMv5
+ * and above, and even on ARMv5 it's not available for THUMB code.  So we
+ * need to tailor this for every source file.
+ */
+#ifndef _DALVIK_CLZ
+
+#if defined(__arm__) && !defined(__thumb__)
+# include <machine/cpu-features.h>
+# if defined(__ARM_HAVE_CLZ)
+#  define CLZ(x) __builtin_clz(x)
+#  define HAVE_BUILTIN_CLZ
+# endif
+#endif
+
+#ifndef HAVE_BUILTIN_CLZ
+# define CLZ(x) dvmClzImpl(x)
+int dvmClzImpl(unsigned int x);
+#endif
+
+#endif // _DALVIK_CLZ
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
new file mode 100644
index 0000000..a7d634e
--- /dev/null
+++ b/vm/analysis/CodeVerify.c
@@ -0,0 +1,5782 @@
+/*
+ * 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.
+ *
+ * TODO: we do too much stuff in here that could be done in the static
+ * verification pass.  It's convenient, because we have all of the
+ * necessary information, but it's more efficient to do it over in
+ * DexVerify.c because in here we may have to process instructions
+ * multiple times.
+ */
+#include "Dalvik.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).
+ */
+typedef enum RegisterTrackingMode {
+    kTrackRegsBranches,
+    kTrackRegsGcPoints,
+    kTrackRegsAll
+} RegisterTrackingMode;
+
+/*
+ * 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.
+ */
+#define DEAD_CODE_SCAN  true
+
+static bool gDebugVerbose = false;      // TODO: remove this
+
+#if 0
+int gDvm__totalInstr = 0;
+int gDvm__gcInstr = 0;
+int gDvm__gcData = 0;
+int gDvm__gcSimpleData = 0;
+#endif
+
+/*
+ * Selectively enable verbose debug logging -- use this to activate
+ * dumpRegTypes() calls for all instructions in the specified method.
+ */
+static inline bool doVerboseLogging(const Method* meth) {
+    return false;       /* COMMENT OUT to enable verbose debugging */
+
+    const char* cd = "Landroid/net/http/Request;";
+    const char* mn = "readResponse";
+    const char* sg = "(Landroid/net/http/AndroidHttpClientConnection;)V";
+    return (strcmp(meth->clazz->descriptor, cd) == 0 &&
+            dvmCompareNameDescriptorAndMethod(mn, sg, meth) == 0);
+}
+
+#define SHOW_REG_DETAILS    (0 /*| 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 registers.
+ */
+typedef struct RegisterTable {
+    /*
+     * Array of RegType arrays, one per address in the method.  We only
+     * set the pointers for certain addresses, based on what we're trying
+     * to accomplish.
+     */
+    RegType**   addrRegs;
+
+    /*
+     * Number of registers we track for each instruction.  This is equal
+     * to the method's declared "registersSize" plus kExtraRegs.
+     */
+    int         insnRegCountPlus;
+
+    /*
+     * A single large alloc, with all of the storage needed for addrRegs.
+     */
+    RegType*    regAlloc;
+} RegisterTable;
+
+
+/* fwd */
+#ifndef NDEBUG
+static void checkMergeTab(void);
+#endif
+static bool isInitMethod(const Method* meth);
+static RegType getInvocationThis(const RegType* insnRegs,\
+    const int insnRegCount, const DecodedInstruction* pDecInsn,
+    VerifyError* pFailure);
+static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount,\
+    u4 vsrc, RegType checkType, VerifyError* pFailure);
+static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags,\
+    RegisterTable* regTable, UninitInstanceMap* uninitMap);
+static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,\
+    RegisterTable* regTable, RegType* workRegs, int insnIdx,
+    UninitInstanceMap* uninitMap, int* pStartGuess);
+static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2);
+static void dumpRegTypes(const Method* meth, const InsnFlags* insnFlags,\
+    const RegType* addrRegs, 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,
+};
+
+
+/*
+ * ===========================================================================
+ *      RegType and UninitInstanceMap utility functions
+ * ===========================================================================
+ */
+
+#define __  kRegTypeUnknown
+#define _U  kRegTypeUninit
+#define _X  kRegTypeConflict
+#define _F  kRegTypeFloat
+#define _0  kRegTypeZero
+#define _1  kRegTypeOne
+#define _Z  kRegTypeBoolean
+#define _b  kRegTypePosByte
+#define _B  kRegTypeByte
+#define _s  kRegTypePosShort
+#define _S  kRegTypeShort
+#define _C  kRegTypeChar
+#define _I  kRegTypeInteger
+#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.
+ *
+ * Because Dalvik does not draw a distinction between int and float, we
+ * have to 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  F  0  1  Z  b  B  s  S  C  I  J  j  D  d */
+    { /*_*/ __,_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 },
+    { /*F*/ _X,_X,_X,_F,_F,_F,_F,_F,_F,_F,_F,_F,_F,_X,_X,_X,_X },
+    { /*0*/ _X,_X,_X,_F,_0,_Z,_Z,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X },
+    { /*1*/ _X,_X,_X,_F,_Z,_1,_Z,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X },
+    { /*Z*/ _X,_X,_X,_F,_Z,_Z,_Z,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X },
+    { /*b*/ _X,_X,_X,_F,_b,_b,_b,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X },
+    { /*B*/ _X,_X,_X,_F,_B,_B,_B,_B,_B,_S,_S,_I,_I,_X,_X,_X,_X },
+    { /*s*/ _X,_X,_X,_F,_s,_s,_s,_s,_S,_s,_S,_C,_I,_X,_X,_X,_X },
+    { /*S*/ _X,_X,_X,_F,_S,_S,_S,_S,_S,_S,_S,_I,_I,_X,_X,_X,_X },
+    { /*C*/ _X,_X,_X,_F,_C,_C,_C,_C,_I,_C,_I,_C,_I,_X,_X,_X,_X },
+    { /*I*/ _X,_X,_X,_F,_I,_I,_I,_I,_I,_I,_I,_I,_I,_X,_X,_X,_X },
+    { /*J*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_J,_X,_J,_X },
+    { /*j*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_j,_X,_j },
+    { /*D*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_J,_X,_D,_X },
+    { /*d*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_j,_X,_d },
+};
+
+#undef __
+#undef _U
+#undef _X
+#undef _F
+#undef _0
+#undef _1
+#undef _Z
+#undef _b
+#undef _B
+#undef _s
+#undef _S
+#undef _C
+#undef _I
+#undef _J
+#undef _j
+#undef _D
+#undef _d
+
+#ifndef NDEBUG
+/*
+ * Verify symmetry in the conversion table.
+ */
+static void checkMergeTab(void)
+{
+    int i, j;
+
+    for (i = 0; i < kRegTypeMAX; i++) {
+        for (j = i; j < kRegTypeMAX; j++) {
+            if (gDvmMergeTab[i][j] != gDvmMergeTab[j][i]) {
+                LOGE("Symmetry violation: %d,%d vs %d,%d\n", 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.
+ *
+ * 32-bit int and float are interchangeable.
+ */
+static bool canConvertTo1nr(RegType srcType, RegType checkType)
+{
+    static const char convTab
+        [kRegType1nrEND-kRegType1nrSTART+1][kRegType1nrEND-kRegType1nrSTART+1] =
+    {
+        /* chk: F  0  1  Z  b  B  s  S  C  I */
+        { /*F*/ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+        { /*0*/ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
+        { /*1*/ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
+        { /*Z*/ 1, 0, 0, 1, 1, 1, 1, 1, 1, 1 },
+        { /*b*/ 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
+        { /*B*/ 1, 0, 0, 0, 0, 1, 0, 1, 0, 1 },
+        { /*s*/ 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
+        { /*S*/ 1, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
+        { /*C*/ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
+        { /*I*/ 1, 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)\n", 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 types are compatible.  In Dalvik, 64-bit doubles
+ * and longs are interchangeable.
+ */
+static bool canConvertTo2(RegType srcType, RegType checkType)
+{
+    return ((srcType == kRegTypeLongLo || srcType == kRegTypeDoubleLo) &&
+            (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.  As usual, "float" and "int" are interoperable.
+ *
+ * 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)
+{
+    if (instrType == targetType)
+        return true;            /* quick positive; most common case */
+
+    if ((instrType == kRegTypeInteger && targetType == kRegTypeFloat) ||
+        (instrType == kRegTypeFloat && targetType == kRegTypeInteger))
+    {
+        return true;
+    }
+
+    return false;
+}
+
+/*
+ * Convert a VM PrimitiveType enum value to the equivalent RegType value.
+ */
+static RegType primitiveTypeToRegType(PrimitiveType primType)
+{
+    static const struct {
+        RegType         regType;        /* type equivalent */
+        PrimitiveType   primType;       /* verification */
+    } convTab[] = {
+        /* must match order of enum in Object.h */
+        { kRegTypeBoolean,      PRIM_BOOLEAN },
+        { kRegTypeChar,         PRIM_CHAR },
+        { kRegTypeFloat,        PRIM_FLOAT },
+        { kRegTypeDoubleLo,     PRIM_DOUBLE },
+        { kRegTypeByte,         PRIM_BYTE },
+        { kRegTypeShort,        PRIM_SHORT },
+        { kRegTypeInteger,      PRIM_INT },
+        { kRegTypeLongLo,       PRIM_LONG },
+        // PRIM_VOID
+    };
+
+    if (primType < 0 || primType > (int) (sizeof(convTab) / sizeof(convTab[0])))
+    {
+        assert(false);
+        return kRegTypeUnknown;
+    }
+
+    assert(convTab[primType].primType == primType);
+    return convTab[primType].regType;
+}
+
+/*
+ * 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+.
+ */
+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 = 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);
+
+        if ((*insns & 0xff) == 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.
+ */
+int dvmSetUninitInstance(UninitInstanceMap* uninitMap, int addr,
+    ClassObject* clazz)
+{
+    int idx;
+
+    assert(clazz != NULL);
+
+    /* 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\n",
+                    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\n", addr);
+    assert(false);      // shouldn't happen
+    return -1;
+}
+
+/*
+ * Get the class object at the specified index.
+ */
+ClassObject* dvmGetUninitInstance(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\n");
+        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 dvmGetUninitInstance(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.
+     */
+
+    //LOGI("Looking up '%s'\n", typeStr);
+    ClassObject* clazz;
+    clazz = dvmFindClassNoInit(pDescriptor, meth->clazz->classLoader);
+    if (clazz == NULL) {
+        dvmClearOptException(dvmThreadSelf());
+        if (strchr(pDescriptor, '$') != NULL) {
+            LOGV("VFY: unable to find class referenced in signature (%s)\n",
+                pDescriptor);
+        } else {
+            LOG_VFY("VFY: unable to find class referenced in signature (%s)\n",
+                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'\n",
+                    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'\n", pDescriptor);
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+
+        if (clazz == NULL) {
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+    }
+
+    if (dvmIsPrimitiveClass(clazz)) {
+        LOG_VFY("VFY: invalid use of primitive type '%s'\n", 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 ';')\n", 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 ';')\n", 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;
+
+    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 = dvmSetUninitInstance(uninitMap, kUninitThisArgAddr,
+                            meth->clazz);
+            assert(uidx == 0);
+            regTypes[argStart + actualArgs] = regTypeFromUninitIndex(uidx);
+        } else {
+            regTypes[argStart + actualArgs] = regTypeFromClass(meth->clazz);
+        }
+        actualArgs++;
+    }
+
+    for (;;) {
+        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+
+        if (descriptor == NULL) {
+            break;
+        }
+
+        if (actualArgs >= expectedArgs) {
+            LOG_VFY("VFY: expected %d args, found more (%s)\n",
+                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'\n", *descriptor);
+            goto bad_sig;
+        }
+    }
+
+    if (actualArgs != expectedArgs) {
+        LOG_VFY("VFY: expected %d args, found %d\n", expectedArgs, actualArgs);
+        goto bad_sig;
+    }
+
+    const char* 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\n");
+//    return false;
+
+bad_sig:
+    {
+        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+        LOG_VFY("VFY: bad signature '%s' for %s.%s\n",
+            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, const RegType* insnRegs,
+    const int insnRegCount, const DecodedInstruction* pDecInsn,
+    UninitInstanceMap* uninitMap, MethodType methodType, bool isRange,
+    bool isSuper, VerifyError* pFailure)
+{
+    Method* resMethod;
+    char* sigOriginal = NULL;
+
+    /*
+     * Resolve the method.  This could be an abstract or concrete method
+     * depending on what sort of call we're making.
+     */
+    if (methodType == METHOD_INTERFACE) {
+        resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB);
+    } else {
+        resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType,
+            pFailure);
+    }
+    if (resMethod == NULL) {
+        /* failed; print a meaningful failure message */
+        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+        const DexMethodId* pMethodId;
+        const char* methodName;
+        char* methodDesc;
+        const char* classDescriptor;
+
+        pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB);
+        methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+        methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+        classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+        if (!gDvm.optimizing) {
+            char* dotMissingClass = dvmDescriptorToDot(classDescriptor);
+            char* dotMethClass = dvmDescriptorToDot(meth->clazz->descriptor);
+            //char* curMethodDesc =
+            //    dexProtoCopyMethodDescriptor(&meth->prototype);
+
+            LOGI("Could not find method %s.%s, referenced from method %s.%s\n",
+                dotMissingClass, methodName/*, methodDesc*/,
+                dotMethClass, meth->name/*, curMethodDesc*/);
+
+            free(dotMissingClass);
+            free(dotMethClass);
+            //free(curMethodDesc);
+        }
+
+        LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s\n",
+            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\n",
+                    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\n",
+            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\n",
+                    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);
+    const char* sig = sigOriginal;
+    int expectedArgs = pDecInsn->vA;
+    int actualArgs = 0;
+
+    if (!isRange && expectedArgs > 5) {
+        LOG_VFY("VFY: invalid arg count in non-range invoke (%d)\n",
+            pDecInsn->vA);
+        goto fail;
+    }
+    if (expectedArgs > meth->outsSize) {
+        LOG_VFY("VFY: invalid arg count (%d) exceeds outsSize (%d)\n",
+            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(insnRegs, insnRegCount, pDecInsn,
+                            pFailure);
+        if (!VERIFY_OK(*pFailure))
+            goto fail;
+
+        if (regTypeIsUninitReference(actualArgType) && resMethod->name[0] != '<')
+        {
+            LOG_VFY("VFY: 'this' arg must be initialized\n");
+            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'\n",
+                        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)\n",
+                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(insnRegs, insnRegCount, getReg,
+                    regTypeFromClass(clazz), pFailure);
+                if (!VERIFY_OK(*pFailure)) {
+                    LOG_VFY("VFY: bad arg %d (into %s)\n",
+                            actualArgs, clazz->descriptor);
+                    goto bad_sig;
+                }
+            }
+            actualArgs++;
+            break;
+        case '[':
+            {
+                ClassObject* clazz =
+                    lookupSignatureArrayClass(meth, &sig, pFailure);
+                if (!VERIFY_OK(*pFailure))
+                    goto bad_sig;
+                verifyRegisterType(insnRegs, insnRegCount, getReg,
+                    regTypeFromClass(clazz), pFailure);
+                if (!VERIFY_OK(*pFailure)) {
+                    LOG_VFY("VFY: bad arg %d (into %s)\n",
+                            actualArgs, clazz->descriptor);
+                    goto bad_sig;
+                }
+            }
+            actualArgs++;
+            break;
+        case 'Z':
+            verifyRegisterType(insnRegs, insnRegCount, getReg,
+                kRegTypeBoolean, pFailure);
+            actualArgs++;
+            break;
+        case 'C':
+            verifyRegisterType(insnRegs, insnRegCount, getReg,
+                kRegTypeChar, pFailure);
+            actualArgs++;
+            break;
+        case 'B':
+            verifyRegisterType(insnRegs, insnRegCount, getReg,
+                kRegTypeByte, pFailure);
+            actualArgs++;
+            break;
+        case 'I':
+            verifyRegisterType(insnRegs, insnRegCount, getReg,
+                kRegTypeInteger, pFailure);
+            actualArgs++;
+            break;
+        case 'S':
+            verifyRegisterType(insnRegs, insnRegCount, getReg,
+                kRegTypeShort, pFailure);
+            actualArgs++;
+            break;
+        case 'F':
+            verifyRegisterType(insnRegs, insnRegCount, getReg,
+                kRegTypeFloat, pFailure);
+            actualArgs++;
+            break;
+        case 'D':
+            verifyRegisterType(insnRegs, insnRegCount, getReg,
+                kRegTypeDoubleLo, pFailure);
+            actualArgs += 2;
+            break;
+        case 'J':
+            verifyRegisterType(insnRegs, insnRegCount, getReg,
+                kRegTypeLongLo, pFailure);
+            actualArgs += 2;
+            break;
+        default:
+            LOG_VFY("VFY: invocation target: bad signature type char '%c'\n",
+                *sig);
+            goto bad_sig;
+        }
+
+        sig++;
+    }
+    if (*sig != ')') {
+        char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+        LOG_VFY("VFY: invocation target: bad signature '%s'\n", desc);
+        free(desc);
+        goto bad_sig;
+    }
+
+    if (actualArgs != expectedArgs) {
+        LOG_VFY("VFY: expected %d args, found %d\n", 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\n",
+            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());
+        LOGV("VFY: unable to find class '%s' for field %s.%s, trying Object\n",
+            field->signature, meth->clazz->descriptor, field->name);
+        fieldClass = gDvm.classJavaLangObject;
+    } else {
+        assert(!dvmIsPrimitiveClass(fieldClass));
+    }
+    return fieldClass;
+}
+
+
+/*
+ * ===========================================================================
+ *      Register operations
+ * ===========================================================================
+ */
+
+/*
+ * Get the type of register N, verifying that the register is valid.
+ *
+ * Sets "*pFailure" appropriately if the register number is out of range.
+ */
+static inline RegType getRegisterType(const RegType* insnRegs,
+    const int insnRegCount, u4 vsrc, VerifyError* pFailure)
+{
+    if (vsrc >= (u4) insnRegCount) {
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return kRegTypeUnknown;
+    } else {
+        return insnRegs[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 RegType* insnRegs,
+    const int insnRegCount, u4 vsrc, VerifyError* pFailure)
+{
+    ClassObject* clazz = NULL;
+    RegType type;
+
+    /* get the element type of the array held in vsrc */
+    type = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure);
+    if (!VERIFY_OK(*pFailure))
+        goto bail;
+
+    /* 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)\n",
+            vsrc, type);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+    if (regTypeIsUninitReference(type)) {
+        LOG_VFY("VFY: register %u holds uninitialized reference\n", 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 RegType* insnRegs,
+    const int insnRegCount, const DecodedInstruction* pDecInsn,
+    VerifyError* pFailure)
+{
+    RegType thisType = kRegTypeUnknown;
+
+    if (pDecInsn->vA < 1) {
+        LOG_VFY("VFY: invoke lacks 'this'\n");
+        *pFailure = VERIFY_ERROR_GENERIC;
+        goto bail;
+    }
+
+    /* get the element type of the array held in vsrc */
+    thisType = getRegisterType(insnRegs, insnRegCount, pDecInsn->vC, pFailure);
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: failed to get 'this' from register %u\n", pDecInsn->vC);
+        goto bail;
+    }
+
+    if (!regTypeIsReference(thisType)) {
+        LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)\n",
+            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".
+ *
+ * Sets "*pFailure" if the register number is out of range.
+ */
+static void setRegisterType(RegType* insnRegs, const int insnRegCount,
+    u4 vdst, RegType newType, VerifyError* pFailure)
+{
+    //LOGD("set-reg v%u = %d\n", vdst, newType);
+    switch (newType) {
+    case kRegTypeUnknown:
+    case kRegTypeBoolean:
+    case kRegTypeOne:
+    case kRegTypeByte:
+    case kRegTypePosByte:
+    case kRegTypeShort:
+    case kRegTypePosShort:
+    case kRegTypeChar:
+    case kRegTypeInteger:
+    case kRegTypeFloat:
+    case kRegTypeZero:
+        if (vdst >= (u4) insnRegCount) {
+            *pFailure = VERIFY_ERROR_GENERIC;
+        } else {
+            insnRegs[vdst] = newType;
+        }
+        break;
+    case kRegTypeLongLo:
+    case kRegTypeDoubleLo:
+        if (vdst+1 >= (u4) insnRegCount) {
+            *pFailure = VERIFY_ERROR_GENERIC;
+        } else {
+            insnRegs[vdst] = newType;
+            insnRegs[vdst+1] = newType+1;
+        }
+        break;
+    case kRegTypeLongHi:
+    case kRegTypeDoubleHi:
+        /* should never set these explicitly */
+        *pFailure = VERIFY_ERROR_GENERIC;
+        break;
+
+    case kRegTypeUninit:
+    default:
+        if (regTypeIsReference(newType)) {
+            if (vdst >= (u4) insnRegCount) {
+                *pFailure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            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 - fall through */
+
+    case kRegTypeConflict:      // should only be set during a merge
+        LOG_VFY("Unexpected set type %d\n", newType);
+        assert(false);
+        *pFailure = VERIFY_ERROR_GENERIC;
+        break;
+    }
+}
+
+/*
+ * Verify that the contents of the specified register have the specified
+ * type (or can be converted to it through an implicit widening conversion).
+ *
+ * In theory we could use this to modify the type of the source register,
+ * e.g. a generic 32-bit constant, once used as a float, would thereafter
+ * remain a float.  There is no compelling reason to require this though.
+ *
+ * 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(const RegType* insnRegs, const int insnRegCount,
+    u4 vsrc, RegType checkType, VerifyError* pFailure)
+{
+    if (vsrc >= (u4) insnRegCount) {
+        *pFailure = VERIFY_ERROR_GENERIC;
+        return;
+    }
+
+    RegType srcType = insnRegs[vsrc];
+
+    //LOGD("check-reg v%u = %d\n", 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\n",
+                vsrc, srcType, checkType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+        break;
+    case kRegTypeLongLo:
+    case kRegTypeDoubleLo:
+        if (vsrc+1 >= (u4) insnRegCount) {
+            LOG_VFY("VFY: register2 v%u out of range (%d)\n",
+                vsrc, insnRegCount);
+            *pFailure = VERIFY_ERROR_GENERIC;
+        } else if (insnRegs[vsrc+1] != srcType+1) {
+            LOG_VFY("VFY: register2 v%u-%u values %d,%d\n",
+                vsrc, vsrc+1, insnRegs[vsrc], insnRegs[vsrc+1]);
+            *pFailure = VERIFY_ERROR_GENERIC;
+        } else if (!canConvertTo2(srcType, checkType)) {
+            LOG_VFY("VFY: register2 v%u type %d, wanted %d\n",
+                vsrc, srcType, checkType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+        break;
+
+    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\n", checkType);
+            assert(false);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        if (regTypeIsUninitReference(checkType)) {
+            LOG_VFY("VFY: uninitialized ref not expected as reg check\n");
+            *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\n", vsrc, srcType);
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        if (regTypeIsUninitReference(srcType)) {
+            LOG_VFY("VFY: register1 v%u holds uninitialized ref\n", 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\n",
+                            srcClass->descriptor, checkClass->descriptor);
+                    *pFailure = VERIFY_ERROR_GENERIC;
+                }
+                */
+            } else {
+                if (!dvmInstanceof(srcClass, checkClass)) {
+                    LOG_VFY("VFY: %s is not instance of %s\n",
+                            srcClass->descriptor, checkClass->descriptor);
+                    *pFailure = VERIFY_ERROR_GENERIC;
+                }
+            }
+        }
+        break;
+    }
+}
+
+/*
+ * Set the type of the "result" register.  Mostly this exists to expand
+ * "insnRegCount" to encompass the result register.
+ */
+static void setResultRegisterType(RegType* insnRegs, const int insnRegCount,
+    RegType newType, VerifyError* pFailure)
+{
+    setRegisterType(insnRegs, insnRegCount + kExtraRegs,
+        RESULT_REGISTER(insnRegCount), newType, pFailure);
+}
+
+
+/*
+ * 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(RegType* insnRegs, int insnRegCount,
+    UninitInstanceMap* uninitMap, RegType uninitType, VerifyError* pFailure)
+{
+    ClassObject* clazz;
+    RegType initType;
+    int i, changed;
+
+    clazz = dvmGetUninitInstance(uninitMap, regTypeToUninitIndex(uninitType));
+    if (clazz == NULL) {
+        LOGE("VFY: unable to find type=0x%x (idx=%d)\n",
+            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++;
+        }
+    }
+    //LOGD("VFY: marked %d registers as initialized\n", 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(RegType* insnRegs, int insnRegCount,
+    UninitInstanceMap* uninitMap, RegType uninitType)
+{
+    int i, changed;
+
+    changed = 0;
+    for (i = 0; i < insnRegCount; i++) {
+        if (insnRegs[i] == uninitType) {
+            insnRegs[i] = kRegTypeConflict;
+            changed++;
+        }
+    }
+
+    //if (changed)
+    //    LOGD("VFY: marked %d uninitialized registers as invalid\n", changed);
+}
+
+/*
+ * Find the start of the register set for the specified instruction in
+ * the current method.
+ */
+static inline RegType* getRegisterLine(const RegisterTable* regTable,
+    int insnIdx)
+{
+    return regTable->addrRegs[insnIdx];
+}
+
+/*
+ * Copy a bunch of registers.
+ */
+static inline void copyRegisters(RegType* dst, const RegType* src,
+    int numRegs)
+{
+    memcpy(dst, src, numRegs * sizeof(RegType));
+}
+
+/*
+ * Compare a bunch of registers.
+ *
+ * Returns 0 if they match.  Using this for a sort is unwise, since the
+ * value can change based on machine endianness.
+ */
+static inline int compareRegisters(const RegType* src1, const RegType* src2,
+    int numRegs)
+{
+    return memcmp(src1, src2, numRegs * sizeof(RegType));
+}
+
+/*
+ * 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.
+ */
+typedef enum TypeCategory {
+    kTypeCategoryUnknown = 0,
+    kTypeCategory1nr,           // byte, char, int, float, boolean
+    kTypeCategory2,             // long, double
+    kTypeCategoryRef,           // object reference
+} TypeCategory;
+
+/*
+ * 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 kRegTypeFloat:
+        case kRegTypeZero:
+        case kRegTypeOne:
+        case kRegTypeBoolean:
+        case kRegTypePosByte:
+        case kRegTypeByte:
+        case kRegTypePosShort:
+        case kRegTypeShort:
+        case kRegTypeChar:
+        case kRegTypeInteger:
+            break;
+        default:
+            *pFailure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        break;
+
+    case kTypeCategory2:
+        switch (type) {
+        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".
+ *
+ * "insnRegCount" is the number of registers available.  The "vdst" and
+ * "vsrc" values are checked against this.
+ */
+static void copyRegister1(RegType* insnRegs, int insnRegCount, u4 vdst,
+    u4 vsrc, TypeCategory cat, VerifyError* pFailure)
+{
+    RegType type = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure);
+    if (VERIFY_OK(*pFailure))
+        checkTypeCategory(type, cat, pFailure);
+    if (VERIFY_OK(*pFailure))
+        setRegisterType(insnRegs, insnRegCount, vdst, type, pFailure);
+
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copy1 v%u<-v%u type=%d cat=%d\n", vdst, vsrc, type, cat);
+    }
+}
+
+/*
+ * Implement category-2 "move" instructions.  Copy a 64-bit value from
+ * "vsrc" to "vdst".  This copies both halves of the register.
+ */
+static void copyRegister2(RegType* insnRegs, int insnRegCount, u4 vdst,
+    u4 vsrc, VerifyError* pFailure)
+{
+    RegType typel = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure);
+    RegType typeh = getRegisterType(insnRegs, insnRegCount, vsrc+1, pFailure);
+    if (VERIFY_OK(*pFailure)) {
+        checkTypeCategory(typel, kTypeCategory2, pFailure);
+        checkWidePair(typel, typeh, pFailure);
+    }
+    if (VERIFY_OK(*pFailure))
+        setRegisterType(insnRegs, insnRegCount, vdst, typel, pFailure);
+
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copy2 v%u<-v%u type=%d/%d\n", vdst, vsrc, typel, typeh);
+    }
+}
+
+/*
+ * Implement "move-result".  Copy the category-1 value from the result
+ * register to another register, and reset the result register.
+ *
+ * We can't just call copyRegister1 with an altered insnRegCount,
+ * because that would affect the test on "vdst" as well.
+ */
+static void copyResultRegister1(RegType* insnRegs, const int insnRegCount,
+    u4 vdst, TypeCategory cat, VerifyError* pFailure)
+{
+    RegType type;
+    u4 vsrc;
+
+    vsrc = RESULT_REGISTER(insnRegCount);
+    type = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc, pFailure);
+    if (VERIFY_OK(*pFailure))
+        checkTypeCategory(type, cat, pFailure);
+    if (VERIFY_OK(*pFailure)) {
+        setRegisterType(insnRegs, insnRegCount, vdst, type, pFailure);
+        insnRegs[vsrc] = kRegTypeUnknown;
+    }
+
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copyRes1 v%u<-v%u cat=%d type=%d\n",
+            vdst, vsrc, cat, type);
+    }
+}
+
+/*
+ * Implement "move-result-wide".  Copy the category-2 value from the result
+ * register to another register, and reset the result register.
+ *
+ * We can't just call copyRegister2 with an altered insnRegCount,
+ * because that would affect the test on "vdst" as well.
+ */
+static void copyResultRegister2(RegType* insnRegs, const int insnRegCount,
+    u4 vdst, VerifyError* pFailure)
+{
+    RegType typel, typeh;
+    u4 vsrc;
+
+    vsrc = RESULT_REGISTER(insnRegCount);
+    typel = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc,
+                pFailure);
+    typeh = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc+1,
+                pFailure);
+    if (VERIFY_OK(*pFailure)) {
+        checkTypeCategory(typel, kTypeCategory2, pFailure);
+        checkWidePair(typel, typeh, pFailure);
+    }
+    if (VERIFY_OK(*pFailure)) {
+        setRegisterType(insnRegs, insnRegCount, vdst, typel, pFailure);
+        insnRegs[vsrc] = kRegTypeUnknown;
+        insnRegs[vsrc+1] = kRegTypeUnknown;
+    }
+
+    if (!VERIFY_OK(*pFailure)) {
+        LOG_VFY("VFY: copyRes2 v%u<-v%u type=%d/%d\n",
+            vdst, vsrc, typel, typeh);
+    }
+}
+
+/*
+ * 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(RegType* insnRegs, const int insnRegCount,
+    DecodedInstruction* pDecInsn, RegType dstType, RegType srcType,
+    VerifyError* pFailure)
+{
+    verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType, pFailure);
+    setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure);
+}
+
+/*
+ * 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(RegType* insnRegs, const int insnRegCount,
+    u4 reg1, u4 reg2)
+{
+    RegType type1, type2;
+
+    type1 = insnRegs[reg1];
+    type2 = insnRegs[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(RegType* insnRegs, const int insnRegCount,
+    DecodedInstruction* pDecInsn, RegType dstType, RegType srcType,
+    bool checkBooleanOp, VerifyError* pFailure)
+{
+    verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType, pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        /* check vB with the call, then check the constant manually */
+        if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vB, pDecInsn->vB)
+            && (pDecInsn->vC == 0 || pDecInsn->vC == 1))
+        {
+            dstType = kRegTypeBoolean;
+        }
+    }
+    setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure);
+}
+
+/*
+ * 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(RegType* insnRegs, const int insnRegCount,
+    DecodedInstruction* pDecInsn, RegType dstType, RegType srcType1,
+    RegType srcType2, bool checkBooleanOp, VerifyError* pFailure)
+{
+    verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType1,
+        pFailure);
+    verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vC, srcType2,
+        pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vB, pDecInsn->vC))
+            dstType = kRegTypeBoolean;
+    }
+    setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure);
+}
+
+/*
+ * Verify types for a binary "2addr" operation.  "srcType1"/"srcType2"
+ * are verified against vA/vB, then "dstType" is stored into vA.
+ */
+static void checkBinop2addr(RegType* insnRegs, const int insnRegCount,
+    DecodedInstruction* pDecInsn, RegType dstType, RegType srcType1,
+    RegType srcType2, bool checkBooleanOp, VerifyError* pFailure)
+{
+    verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vA, srcType1,
+        pFailure);
+    verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType2,
+        pFailure);
+    if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+        assert(dstType == kRegTypeInteger);
+        if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vA, pDecInsn->vB))
+            dstType = kRegTypeBoolean;
+    }
+    setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure);
+}
+
+/*
+ * 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(RegType* workRegs, const int insnRegCount,
+    int reg, unsigned int shiftCount, bool isUnsignedShift,
+    VerifyError* pFailure)
+{
+    RegType srcType = getRegisterType(workRegs, insnRegCount, reg, pFailure);
+    RegType newType;
+
+    /* 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\n", shiftCount);
+        /* fail? */
+        return newType;
+    }
+
+    switch (srcType) {
+    case kRegTypeInteger:               /* 32-bit signed value */
+    case kRegTypeFloat:                 /* (allowed; treat same as int) */
+        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\n",
+            shiftCount, isUnsignedShift, srcType, newType);
+    } else {
+        LOGVV("not narrowed: %d(%d) --> %d\n",
+            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)\n",
+            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\n", 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[][].
+ *
+ * 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 i, numDims;
+
+    assert(c1->arrayDim > 0);
+    assert(c2->arrayDim > 0);
+
+    if (c1->arrayDim == c2->arrayDim) {
+        //commonElem = digForSuperclass(c1->elementClass, c2->elementClass);
+        commonElem = findCommonSuperclass(c1->elementClass, c2->elementClass);
+        numDims = c1->arrayDim;
+    } else {
+        if (c1->arrayDim < c2->arrayDim)
+            numDims = c1->arrayDim;
+        else
+            numDims = c2->arrayDim;
+        commonElem = c1->super;     // == java.lang.Object
+    }
+
+    /* walk from the element to the (multi-)dimensioned array type */
+    for (i = 0; i < numDims; i++) {
+        arrayClass = dvmFindArrayClassForElement(commonElem);
+        commonElem = arrayClass;
+    }
+
+    LOGVV("ArrayMerge '%s' + '%s' --> '%s'\n",
+        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 of non-primitive types, 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\n",
+                c1->descriptor, c2->descriptor, c1->descriptor);
+        return c1;
+    }
+    if (dvmIsInterfaceClass(c2) && dvmImplements(c1, c2)) {
+        if (gDebugVerbose)
+            LOGVV("COMMON/I2: %s + %s --> %s\n",
+                c1->descriptor, c2->descriptor, c2->descriptor);
+        return c2;
+    }
+
+    if (dvmIsArrayClass(c1) && dvmIsArrayClass(c2) &&
+        !dvmIsPrimitiveClass(c1->elementClass) &&
+        !dvmIsPrimitiveClass(c2->elementClass))
+    {
+        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.
+     *
+     * 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;
+}
+
+/*
+ * Control can transfer to "nextInsn".
+ *
+ * Merge the registers from "workRegs" into "regTypes" at "nextInsn", and
+ * set the "changed" flag on the target address if the registers have changed.
+ */
+static void updateRegisters(const Method* meth, InsnFlags* insnFlags,
+    RegisterTable* regTable, int nextInsn, const RegType* workRegs)
+{
+    RegType* targetRegs = getRegisterLine(regTable, nextInsn);
+    const int insnRegCount = meth->registersSize;
+
+#if 0
+    if (!dvmInsnIsBranchTarget(insnFlags, nextInsn)) {
+        LOGE("insnFlags[0x%x]=0x%08x\n", nextInsn, insnFlags[nextInsn]);
+        LOGE(" In %s.%s %s\n",
+            meth->clazz->descriptor, meth->name, meth->descriptor);
+        assert(false);
+    }
+#endif
+
+    if (!dvmInsnIsVisitedOrChanged(insnFlags, nextInsn)) {
+        /*
+         * We haven't processed this instruction before, and we haven't
+         * touched the registers here, so there's nothing to "merge".  Copy
+         * the registers over and mark it as changed.  (This is the only
+         * way a register can transition out of "unknown", so this is not
+         * just an optimization.)
+         */
+        LOGVV("COPY into 0x%04x\n", nextInsn);
+        copyRegisters(targetRegs, workRegs, insnRegCount + kExtraRegs);
+        dvmInsnSetChanged(insnFlags, nextInsn, true);
+    } else {
+        if (gDebugVerbose) {
+            LOGVV("MERGE into 0x%04x\n", nextInsn);
+            //dumpRegTypes(meth, insnFlags, targetRegs, 0, "targ", NULL, 0);
+            //dumpRegTypes(meth, insnFlags, workRegs, 0, "work", NULL, 0);
+        }
+        /* merge registers, set Changed only if different */
+        bool changed = false;
+        int i;
+
+        for (i = 0; i < insnRegCount + kExtraRegs; i++) {
+            targetRegs[i] = mergeTypes(targetRegs[i], workRegs[i], &changed);
+        }
+
+        if (gDebugVerbose) {
+            //LOGI(" RESULT (changed=%d)\n", changed);
+            //dumpRegTypes(meth, insnFlags, targetRegs, 0, "rslt", NULL, 0);
+        }
+
+        if (changed)
+            dvmInsnSetChanged(insnFlags, nextInsn, 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\n",
+            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\n", 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\n");
+            *pFailure = VERIFY_ERROR_GENERIC;
+            goto bail;
+        }
+        mustBeLocal = true;
+    }
+
+    if (!dvmInstanceof(objClass, instField->field.clazz)) {
+        LOG_VFY("VFY: invalid field access (field %s.%s, through %s ref)\n",
+                instField->field.clazz->descriptor, instField->field.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)\n",
+                    instField->field.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\n", 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\n",
+            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>\n");
+            *pFailure = VERIFY_ERROR_GENERIC;
+        }
+    } else {
+        if (!isInitMethod(meth)) {
+            LOG_VFY_METH(meth,
+                "VFY: can't modify final field outside <init>\n");
+            *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.  We could
+         * do something special if we can definitively identify it as a
+         * float, but there's no real value in doing so.
+         */
+        checkTypeCategory(regType, kTypeCategory1nr, pFailure);
+        if (!VERIFY_OK(*pFailure)) {
+            LOG_VFY_METH(meth, "Invalid reg type for array index (%d)\n",
+                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 RegType* insnRegs,
+    const int insnRegCount)
+{
+    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\n");
+            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\n");
+        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;
+    bool foundPossibleHandler = false;
+    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;
+                foundPossibleHandler = true;
+
+                if (handler->typeIdx == kDexNoIndex)
+                    clazz = gDvm.classJavaLangThrowable;
+                else
+                    clazz = dvmOptResolveClass(meth->clazz, handler->typeIdx,
+                                &localFailure);
+
+                if (clazz == NULL) {
+                    LOG_VFY("VFY: unable to resolve exception class %u (%s)\n",
+                        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 0x%x\n", insnIdx);
+        *pFailure = VERIFY_ERROR_GENERIC;
+    } else {
+        // TODO: verify the class is an instance of Throwable?
+    }
+
+    return commonSuper;
+}
+
+/*
+ * 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 storage we are effectively initializing the register
+ * information to kRegTypeUnknown.
+ */
+static bool initRegisterTable(const Method* meth, const InsnFlags* insnFlags,
+    RegisterTable* regTable, RegisterTrackingMode trackRegsFor)
+{
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    int i;
+
+    regTable->insnRegCountPlus = meth->registersSize + kExtraRegs;
+    regTable->addrRegs = (RegType**) calloc(insnsSize, sizeof(RegType*));
+    if (regTable->addrRegs == NULL)
+        return false;
+
+    assert(insnsSize > 0);
+
+    /*
+     * "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 = 0;
+    //int insnCount = 0;
+
+    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++;
+    }
+
+    regTable->regAlloc = (RegType*)
+        calloc(regTable->insnRegCountPlus * interestingCount, sizeof(RegType));
+    if (regTable->regAlloc == NULL)
+        return false;
+
+    RegType* regPtr = regTable->regAlloc;
+    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) {
+            regTable->addrRegs[i] = regPtr;
+            regPtr += regTable->insnRegCountPlus;
+        }
+    }
+
+    //LOGD("Tracking registers for %d, total %d of %d(%d) (%d%%)\n",
+    //    TRACK_REGS_FOR, interestingCount, insnCount, insnsSize,
+    //    (interestingCount*100) / insnCount);
+
+    assert(regPtr - regTable->regAlloc ==
+        regTable->insnRegCountPlus * interestingCount);
+    assert(regTable->addrRegs[0] != NULL);
+    return true;
+}
+
+
+/*
+ * 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,
+    const RegType* insnRegs, const int insnRegCount,
+    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);
+    }
+    //LOGI("filled-new-array: %s -> %d\n", 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(insnRegs, insnRegCount, getReg, expectedType,
+            pFailure);
+        if (!VERIFY_OK(*pFailure)) {
+            LOG_VFY("VFY: filled-new-array arg %u(%u) not valid\n", 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 verifier explicitly locks out breakpoint activity, 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;
+    const u2* oldInsns = meth->insns + insnIdx;
+    u2 oldInsn = *oldInsns;
+    bool result = false;
+
+    if (gDvm.optimizing)
+        LOGD("Weird: RFI during dexopt?");
+
+    //LOGD("  was 0x%04x\n", oldInsn);
+    u2* newInsns = (u2*) meth->insns + insnIdx;
+
+    /*
+     * Generate the new instruction out of the old.
+     *
+     * First, make sure this is an instruction we're expecting to stomp on.
+     */
+    switch (oldInsn & 0xff) {
+    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\n",
+            oldInsn & 0xff);
+        goto bail;
+    }
+
+    /* write a NOP over the third code unit, if necessary */
+    int width = dvmInsnGetWidth(insnFlags, insnIdx);
+    switch (width) {
+    case 2:
+        /* nothing to do */
+        break;
+    case 3:
+        dvmDexChangeDex2(meth->clazz->pDvmDex, newInsns+2, OP_NOP);
+        //newInsns[2] = OP_NOP;
+        break;
+    default:
+        /* whoops */
+        LOGE("ERROR: stomped a %d-unit instruction with a verifier error\n",
+            width);
+        dvmAbort();
+    }
+
+    /* encode the opcode, with the failure code in the high byte */
+    u2 newVal = OP_THROW_VERIFICATION_ERROR |
+        (failure << 8) | (refType << (8 + kVerifyErrorRefTypeShift));
+    //newInsns[0] = newVal;
+    dvmDexChangeDex2(meth->clazz->pDvmDex, newInsns, newVal);
+
+    result = true;
+
+bail:
+    return result;
+}
+
+
+/*
+ * ===========================================================================
+ *      Entry point and driver loop
+ * ===========================================================================
+ */
+
+/*
+ * Entry point for the detailed code-flow analysis.
+ */
+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));
+
+#ifndef NDEBUG
+    checkMergeTab();     // only need to do this if table gets updated
+#endif
+
+    /*
+     * We rely on these for verification of const-class, const-string,
+     * and throw instructions.  Make sure we have them.
+     */
+    if (gDvm.classJavaLangClass == NULL)
+        gDvm.classJavaLangClass =
+            dvmFindSystemClassNoInit("Ljava/lang/Class;");
+    if (gDvm.classJavaLangString == NULL)
+        gDvm.classJavaLangString =
+            dvmFindSystemClassNoInit("Ljava/lang/String;");
+    if (gDvm.classJavaLangThrowable == NULL) {
+        gDvm.classJavaLangThrowable =
+            dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
+        gDvm.offJavaLangThrowable_cause =
+            dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+                "cause", "Ljava/lang/Throwable;");
+    }
+    if (gDvm.classJavaLangObject == NULL)
+        gDvm.classJavaLangObject =
+            dvmFindSystemClassNoInit("Ljava/lang/Object;");
+
+    if (meth->registersSize * insnsSize > 4*1024*1024) {
+        LOG_VFY_METH(meth,
+            "VFY: warning: method is huge (regs=%d insnsSize=%d)\n",
+            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(meth, vdata->insnFlags, &regTable,
+            generateRegisterMap ? kTrackRegsGcPoints : kTrackRegsBranches))
+        goto bail;
+
+    vdata->addrRegs = NULL;     /* don't set this until we need it */
+
+    /*
+     * Initialize the types of the registers that correspond to the
+     * method arguments.  We can determine this from the method signature.
+     */
+    if (!setTypesFromSignature(meth, regTable.addrRegs[0], vdata->uninitMap))
+        goto bail;
+
+    /*
+     * Run the verifier.
+     */
+    if (!doCodeVerification(meth, vdata->insnFlags, &regTable, vdata->uninitMap))
+        goto bail;
+
+    /*
+     * Generate a register map.
+     */
+    if (generateRegisterMap) {
+        vdata->addrRegs = regTable.addrRegs;
+
+        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:
+    free(regTable.addrRegs);
+    free(regTable.regAlloc);
+    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.  In theory this could become quite large -- up to several
+ * megabytes for a monster function.  For self-preservation we reject
+ * anything that requires more than a certain amount of memory.  (Typical
+ * "large" should be on the order of 4K code units * 8 registers.)  This
+ * will likely have to be adjusted.
+ *
+ *
+ * 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(const Method* meth, InsnFlags* insnFlags,
+    RegisterTable* regTable, UninitInstanceMap* uninitMap)
+{
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    RegType workRegs[meth->registersSize + kExtraRegs];
+    bool result = false;
+    bool debugVerbose = false;
+    int insnIdx, startGuess;
+
+    /*
+     * Begin by marking the first instruction as "changed".
+     */
+    dvmInsnSetChanged(insnFlags, 0, true);
+
+    if (doVerboseLogging(meth)) {
+        IF_LOGI() {
+            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+            LOGI("Now verifying: %s.%s %s (ins=%d regs=%d)\n",
+                meth->clazz->descriptor, meth->name, desc,
+                meth->insSize, meth->registersSize);
+            LOGI(" ------ [0    4    8    12   16   20   24   28   32   36\n");
+            free(desc);
+        }
+        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)) {
+            RegType* insnRegs = getRegisterLine(regTable, insnIdx);
+            assert(insnRegs != NULL);
+            copyRegisters(workRegs, insnRegs, meth->registersSize + kExtraRegs);
+
+            if (debugVerbose) {
+                dumpRegTypes(meth, insnFlags, workRegs, insnIdx, NULL,uninitMap,
+                    SHOW_REG_DETAILS);
+            }
+
+        } else {
+            if (debugVerbose) {
+                dumpRegTypes(meth, insnFlags, workRegs, insnIdx, NULL,uninitMap,
+                    SHOW_REG_DETAILS);
+            }
+
+#ifndef NDEBUG
+            /*
+             * Sanity check: retrieve the stored register line (assuming
+             * a full table) and make sure it actually matches.
+             */
+            RegType* insnRegs = getRegisterLine(regTable, insnIdx);
+            if (insnRegs != NULL &&
+                compareRegisters(workRegs, insnRegs,
+                    meth->registersSize + kExtraRegs) != 0)
+            {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                LOG_VFY("HUH? workRegs diverged in %s.%s %s\n",
+                        meth->clazz->descriptor, meth->name, desc);
+                free(desc);
+                dumpRegTypes(meth, insnFlags, workRegs, 0, "work",
+                    uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
+                dumpRegTypes(meth, insnFlags, insnRegs, 0, "insn",
+                    uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
+            }
+#endif
+        }
+
+        //LOGI("process %s.%s %s %d\n",
+        //    meth->clazz->descriptor, meth->name, meth->descriptor, insnIdx);
+        if (!verifyInstruction(meth, insnFlags, regTable, workRegs, insnIdx,
+                uninitMap, &startGuess))
+        {
+            //LOGD("+++ %s bailing at %d\n", meth->name, insnIdx);
+            goto bail;
+        }
+
+#if 0
+        {
+            static const int gcMask = kInstrCanBranch | kInstrCanSwitch |
+                                      kInstrCanThrow | kInstrCanReturn;
+            OpCode opCode = *(meth->insns + insnIdx) & 0xff;
+            int flags = dexGetInstrFlags(gDvm.instrFlags, opCode);
+
+            /* 8, 16, 32, or 32*n -bit regs */
+            int regWidth = (meth->registersSize + 7) / 8;
+            if (regWidth == 3)
+                regWidth = 4;
+            if (regWidth > 4) {
+                regWidth = ((regWidth + 3) / 4) * 4;
+                if (false) {
+                    LOGW("WOW: %d regs -> %d  %s.%s\n",
+                        meth->registersSize, regWidth,
+                        meth->clazz->descriptor, meth->name);
+                    //x = true;
+                }
+            }
+
+            if ((flags & gcMask) != 0) {
+                /* this is a potential GC point */
+                gDvm__gcInstr++;
+
+                if (insnsSize < 256)
+                    gDvm__gcData += 1;
+                else
+                    gDvm__gcData += 2;
+                gDvm__gcData += regWidth;
+            }
+            gDvm__gcSimpleData += regWidth;
+
+            gDvm__totalInstr++;
+        }
+#endif
+
+        /*
+         * 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 rewritten "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.
+             */
+            int instr = meth->insns[insnIdx];
+            if (instr == kPackedSwitchSignature ||
+                instr == kSparseSwitchSignature ||
+                instr == kArrayDataSignature ||
+                (instr == OP_NOP &&
+                 (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_LOGD() {
+                    char* desc =
+                        dexProtoCopyMethodDescriptor(&meth->prototype);
+                    LOGD("VFY: dead code 0x%04x-%04x in %s.%s %s\n",
+                        deadStart, insnIdx-1,
+                        meth->clazz->descriptor, meth->name, desc);
+                    free(desc);
+                }
+
+                deadStart = -1;
+            }
+        }
+        if (deadStart >= 0) {
+            IF_LOGD() {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                LOGD("VFY: dead code 0x%04x-%04x in %s.%s %s\n",
+                    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, RegType* workRegs, int insnIdx,
+    UninitInstanceMap* uninitMap, int* pStartGuess)
+{
+    const int insnsSize = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns + insnIdx;
+    bool result = false;
+
+    /*
+     * Once we finish decoding the instruction, we need to figure out where
+     * we can go from here.  There are three possible ways to transfer
+     * control to another statement:
+     *
+     * (1) Continue to the next instruction.  Applies to all but
+     *     unconditional branches, method returns, and exception throws.
+     * (2) Branch to one or more possible locations.  Applies to branches
+     *     and switch statements.
+     * (3) Exception handlers.  Applies to any instruction that can
+     *     throw an exception that is handled by an encompassing "try"
+     *     block.
+     *
+     * We can also return, in which case there is no successor instruction
+     * from this point.
+     *
+     * The behavior can be determined from the InstructionFlags.
+     */
+
+    const DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+    RegType entryRegs[meth->registersSize + kExtraRegs];
+    ClassObject* resClass;
+    int 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(gDvm.instrFormat, insns, &decInsn);
+
+    int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
+
+    /*
+     * Make a copy of the previous register state.  If the instruction
+     * throws an exception, we merge *this* into the destination rather
+     * than workRegs, because we don't want the result from the "successful"
+     * code path (e.g. a check-cast that "improves" a type) to be visible
+     * to the exception handler.
+     */
+    if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+    {
+        copyRegisters(entryRegs, workRegs, meth->registersSize + kExtraRegs);
+    } else {
+#ifndef NDEBUG
+        memset(entryRegs, 0xdd,
+            (meth->registersSize + kExtraRegs) * 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\n");
+            failure = VERIFY_ERROR_GENERIC;
+        }
+        break;
+
+    case OP_MOVE:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_16:
+        copyRegister1(workRegs, insnRegCount, decInsn.vA, decInsn.vB,
+            kTypeCategory1nr, &failure);
+        break;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        copyRegister2(workRegs, insnRegCount, decInsn.vA, decInsn.vB, &failure);
+        break;
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_OBJECT_16:
+        copyRegister1(workRegs, insnRegCount, 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(workRegs, insnRegCount, decInsn.vA,
+            kTypeCategory1nr, &failure);
+        break;
+    case OP_MOVE_RESULT_WIDE:
+        copyResultRegister2(workRegs, insnRegCount, decInsn.vA, &failure);
+        break;
+    case OP_MOVE_RESULT_OBJECT:
+        copyResultRegister1(workRegs, 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(workRegs, insnRegCount, decInsn.vA,
+                regTypeFromClass(resClass), &failure);
+        }
+        break;
+
+    case OP_RETURN_VOID:
+        if (!checkConstructorReturn(meth, workRegs, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else if (getMethodReturnType(meth) != kRegTypeUnknown) {
+            LOG_VFY("VFY: return-void not expected\n");
+            failure = VERIFY_ERROR_GENERIC;
+        }
+        break;
+    case OP_RETURN:
+        if (!checkConstructorReturn(meth, workRegs, 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-32 not expected\n");
+
+            /* check the register contents */
+            returnType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+                &failure);
+            checkTypeCategory(returnType, kTypeCategory1nr, &failure);
+            if (!VERIFY_OK(failure))
+                LOG_VFY("VFY: return-32 on invalid register v%d\n", decInsn.vA);
+        }
+        break;
+    case OP_RETURN_WIDE:
+        if (!checkConstructorReturn(meth, workRegs, insnRegCount)) {
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            RegType returnType, returnTypeHi;
+
+            /* check the method signature */
+            returnType = getMethodReturnType(meth);
+            checkTypeCategory(returnType, kTypeCategory2, &failure);
+            if (!VERIFY_OK(failure))
+                LOG_VFY("VFY: return-wide not expected\n");
+
+            /* check the register contents */
+            returnType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+                &failure);
+            returnTypeHi = getRegisterType(workRegs, insnRegCount,
+                decInsn.vA +1, &failure);
+            if (VERIFY_OK(failure)) {
+                checkTypeCategory(returnType, kTypeCategory2, &failure);
+                checkWidePair(returnType, returnTypeHi, &failure);
+            }
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: return-wide on invalid register pair v%d\n",
+                    decInsn.vA);
+            }
+        }
+        break;
+    case OP_RETURN_OBJECT:
+        if (!checkConstructorReturn(meth, workRegs, 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\n");
+                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(workRegs, insnRegCount,
+                            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)\n",
+                            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(workRegs, insnRegCount, decInsn.vA,
+            dvmDetermineCat1Const((s4)decInsn.vB), &failure);
+        break;
+    case OP_CONST_HIGH16:
+        /* could be boolean, int, float, or a null reference */
+        setRegisterType(workRegs, insnRegCount, decInsn.vA,
+            dvmDetermineCat1Const((s4) decInsn.vB << 16), &failure);
+        break;
+    case OP_CONST_WIDE_16:
+    case OP_CONST_WIDE_32:
+    case OP_CONST_WIDE:
+    case OP_CONST_WIDE_HIGH16:
+        /* could be long or double; default to long and allow conversion */
+        setRegisterType(workRegs, insnRegCount, decInsn.vA,
+            kRegTypeLongLo, &failure);
+        break;
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+        assert(gDvm.classJavaLangString != NULL);
+        setRegisterType(workRegs, insnRegCount, decInsn.vA,
+            regTypeFromClass(gDvm.classJavaLangString), &failure);
+        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\n",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                regTypeFromClass(gDvm.classJavaLangClass), &failure);
+        }
+        break;
+
+    case OP_MONITOR_ENTER:
+    case OP_MONITOR_EXIT:
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+        if (VERIFY_OK(failure)) {
+            if (!regTypeIsReference(tmpType)) {
+                LOG_VFY("VFY: monitor op on non-object\n");
+                failure = VERIFY_ERROR_GENERIC;
+            }
+        }
+        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\n",
+                decInsn.vB, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            RegType origType;
+
+            origType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (!regTypeIsReference(origType)) {
+                LOG_VFY("VFY: check-cast on non-reference in v%u\n",decInsn.vA);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                regTypeFromClass(resClass), &failure);
+        }
+        break;
+    case OP_INSTANCE_OF:
+        /* make sure we're checking a reference type */
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (!regTypeIsReference(tmpType)) {
+            LOG_VFY("VFY: vB not a reference (%d)\n", 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\n",
+                decInsn.vC, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else {
+            /* result is boolean */
+            setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                kRegTypeBoolean, &failure);
+        }
+        break;
+
+    case OP_ARRAY_LENGTH:
+        resClass = getClassFromRegister(workRegs, insnRegCount,
+                        decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (resClass != NULL && !dvmIsArrayClass(resClass)) {
+            LOG_VFY("VFY: array-length on non-array\n");
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeInteger,
+            &failure);
+        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\n",
+                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\n",
+                    resClass->descriptor);
+                failure = VERIFY_ERROR_INSTANTIATION;
+                break;
+            }
+
+            /* add resolved class to uninit map if not already there */
+            int uidx = dvmSetUninitInstance(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(workRegs, insnRegCount, uninitMap,
+                uninitType);
+
+            /* add the new uninitialized reference to the register ste */
+            setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                uninitType, &failure);
+        }
+        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\n",
+                decInsn.vC, badClassDesc, meth->clazz->descriptor);
+            assert(failure != VERIFY_ERROR_GENERIC);
+        } else if (!dvmIsArrayClass(resClass)) {
+            LOG_VFY("VFY: new-array on non-array class\n");
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            /* make sure "size" register is valid type */
+            verifyRegisterType(workRegs, insnRegCount, decInsn.vB,
+                kRegTypeInteger, &failure);
+            /* set register type to array class */
+            setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                regTypeFromClass(resClass), &failure);
+        }
+        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\n",
+                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\n");
+            failure = VERIFY_ERROR_GENERIC;
+        } else {
+            bool isRange = (decInsn.opCode == OP_FILLED_NEW_ARRAY_RANGE);
+
+            /* check the arguments to the instruction */
+            verifyFilledNewArrayRegs(meth, workRegs, insnRegCount, &decInsn,
+                resClass, isRange, &failure);
+            /* filled-array result goes into "result" register */
+            setResultRegisterType(workRegs, insnRegCount,
+                regTypeFromClass(resClass), &failure);
+            justSetResult = true;
+        }
+        break;
+
+    case OP_CMPL_FLOAT:
+    case OP_CMPG_FLOAT:
+        verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeFloat,
+            &failure);
+        verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeFloat,
+            &failure);
+        setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean,
+            &failure);
+        break;
+    case OP_CMPL_DOUBLE:
+    case OP_CMPG_DOUBLE:
+        verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeDoubleLo,
+            &failure);
+        verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeDoubleLo,
+            &failure);
+        setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean,
+            &failure);
+        break;
+    case OP_CMP_LONG:
+        verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeLongLo,
+            &failure);
+        verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeLongLo,
+            &failure);
+        setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean,
+            &failure);
+        break;
+
+    case OP_THROW:
+        resClass = getClassFromRegister(workRegs, insnRegCount,
+                        decInsn.vA, &failure);
+        if (VERIFY_OK(failure) && resClass != NULL) {
+            if (!dvmInstanceof(resClass, gDvm.classJavaLangThrowable)) {
+                LOG_VFY("VFY: thrown class %s not instanceof Throwable\n",
+                        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(workRegs, insnRegCount, 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(workRegs, insnRegCount,
+                            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\n",
+                        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\n");
+                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)\n",
+                        arrayData[1], elemWidth);
+                failure = VERIFY_ERROR_GENERIC;
+            }
+        }
+        break;
+
+    case OP_IF_EQ:
+    case OP_IF_NE:
+        {
+            RegType type1, type2;
+
+            type1 = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+                        &failure);
+            type2 = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* both references? */
+            if (regTypeIsReference(type1) && regTypeIsReference(type2))
+                break;
+
+            /* both category-1nr? */
+            checkTypeCategory(type1, kTypeCategory1nr, &failure);
+            checkTypeCategory(type2, kTypeCategory1nr, &failure);
+            if (!VERIFY_OK(failure)) {
+                LOG_VFY("VFY: args to if-eq/if-ne must both be refs or cat1\n");
+                break;
+            }
+        }
+        break;
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (!VERIFY_OK(failure)) {
+            LOG_VFY("VFY: args to 'if' must be cat-1nr\n");
+            break;
+        }
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (!VERIFY_OK(failure)) {
+            LOG_VFY("VFY: args to 'if' must be cat-1nr\n");
+            break;
+        }
+        break;
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        if (regTypeIsReference(tmpType))
+            break;
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (!VERIFY_OK(failure))
+            LOG_VFY("VFY: expected cat-1 arg to if\n");
+        break;
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+        checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+        if (!VERIFY_OK(failure))
+            LOG_VFY("VFY: expected cat-1 arg to if\n");
+        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(workRegs, insnRegCount, decInsn.vC,
+                            &failure);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            resClass = getClassFromRegister(workRegs, insnRegCount,
+                            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\n",
+                        resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /* make sure array type matches instruction */
+                srcType = primitiveTypeToRegType(
+                                        resClass->elementClass->primitiveType);
+
+                if (!checkFieldArrayStore1nr(tmpType, srcType)) {
+                    LOG_VFY("VFY: invalid aget-1nr, array type=%d with"
+                            " inst type=%d (on %s)\n",
+                        srcType, tmpType, resClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+            }
+            setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                tmpType, &failure);
+        }
+        break;
+
+    case OP_AGET_WIDE:
+        {
+            RegType dstType, indexType;
+
+            indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+                            &failure);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            resClass = getClassFromRegister(workRegs, insnRegCount,
+                            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\n",
+                        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\n",
+                        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, and we don't really
+                 * discriminate between those during verification, so we
+                 * call it a long.
+                 */
+                dstType = kRegTypeLongLo;
+            }
+            setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                dstType, &failure);
+        }
+        break;
+
+    case OP_AGET_OBJECT:
+        {
+            RegType dstType, indexType;
+
+            indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+                            &failure);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* get the class of the array we're pulling an object from */
+            resClass = getClassFromRegister(workRegs, insnRegCount,
+                            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\n");
+                    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)\n",
+                        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(workRegs, insnRegCount, decInsn.vA,
+                dstType, &failure);
+        }
+        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(workRegs, insnRegCount, decInsn.vC,
+                            &failure);
+            checkArrayIndexType(meth, indexType, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* make sure the source register has the correct type */
+            srcType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+                            &failure);
+            if (!canConvertTo1nr(srcType, tmpType)) {
+                LOG_VFY("VFY: invalid reg type %d on aput instr (need %d)\n",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            resClass = getClassFromRegister(workRegs, insnRegCount,
+                            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\n", resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            /* verify that instruction matches array */
+            dstType = primitiveTypeToRegType(
+                                    resClass->elementClass->primitiveType);
+            assert(dstType != kRegTypeUnknown);
+
+            if (!checkFieldArrayStore1nr(tmpType, dstType)) {
+                LOG_VFY("VFY: invalid aput-1nr on %s (inst=%d dst=%d)\n",
+                        resClass->descriptor, tmpType, dstType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_APUT_WIDE:
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+                        &failure);
+        checkArrayIndexType(meth, tmpType, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+        if (VERIFY_OK(failure)) {
+            RegType typeHi =
+                getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure);
+            checkTypeCategory(tmpType, kTypeCategory2, &failure);
+            checkWidePair(tmpType, typeHi, &failure);
+        }
+        if (!VERIFY_OK(failure))
+            break;
+
+        resClass = getClassFromRegister(workRegs, insnRegCount,
+                        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\n",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            switch (resClass->elementClass->primitiveType) {
+            case PRIM_LONG:
+            case PRIM_DOUBLE:
+                /* these are okay */
+                break;
+            default:
+                LOG_VFY("VFY: invalid aput-wide on %s\n",
+                        resClass->descriptor);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_APUT_OBJECT:
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+                        &failure);
+        checkArrayIndexType(meth, tmpType, &failure);
+        if (!VERIFY_OK(failure))
+            break;
+
+        /* get the ref we're storing; Zero is okay, Uninit is not */
+        resClass = getClassFromRegister(workRegs, insnRegCount,
+                        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(workRegs, insnRegCount,
+                            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\n",
+                            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\n",
+                            resClass->descriptor, arrayClass->descriptor);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        break;
+
+    case OP_IGET:
+    case OP_IGET_VOLATILE:
+        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(workRegs, insnRegCount, decInsn.vB,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            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->field.signature[0]);
+            if (fieldType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, fieldType))
+            {
+                LOG_VFY("VFY: invalid iget-1nr of %s.%s (inst=%d field=%d)\n",
+                        instField->field.clazz->descriptor,
+                        instField->field.name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            setRegisterType(workRegs, insnRegCount, decInsn.vA, tmpType,
+                &failure);
+        }
+        break;
+    case OP_IGET_WIDE:
+    case OP_IGET_WIDE_VOLATILE:
+        {
+            RegType dstType;
+            InstField* instField;
+            RegType objType;
+
+            objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            /* check the type, which should be prim */
+            switch (instField->field.signature[0]) {
+            case 'D':
+                dstType = kRegTypeDoubleLo;
+                break;
+            case 'J':
+                dstType = kRegTypeLongLo;
+                break;
+            default:
+                LOG_VFY("VFY: invalid iget-wide of %s.%s\n",
+                        instField->field.clazz->descriptor,
+                        instField->field.name);
+                dstType = kRegTypeUnknown;
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                    dstType, &failure);
+            }
+        }
+        break;
+    case OP_IGET_OBJECT:
+    case OP_IGET_OBJECT_VOLATILE:
+        {
+            ClassObject* fieldClass;
+            InstField* instField;
+            RegType objType;
+
+            objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            fieldClass = getFieldClass(meth, &instField->field);
+            if (fieldClass == NULL) {
+                /* class not found or primitive type */
+                LOG_VFY("VFY: unable to recover field class from '%s'\n",
+                    instField->field.signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                assert(!dvmIsPrimitiveClass(fieldClass));
+                setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                    regTypeFromClass(fieldClass), &failure);
+            }
+        }
+        break;
+    case OP_IPUT:
+    case OP_IPUT_VOLATILE:
+        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(workRegs, insnRegCount, decInsn.vA,
+                        &failure);
+
+            /*
+             * javac generates synthetic functions that write byte values
+             * into boolean fields.
+             */
+            if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte)
+                srcType = kRegTypeBoolean;
+
+            /* 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)\n",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, &instField->field, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* get type of field we're storing into */
+            fieldType = primSigCharToRegType(instField->field.signature[0]);
+            if (fieldType == kRegTypeUnknown ||
+                !checkFieldArrayStore1nr(tmpType, fieldType))
+            {
+                LOG_VFY("VFY: invalid iput-1nr of %s.%s (inst=%d field=%d)\n",
+                        instField->field.clazz->descriptor,
+                        instField->field.name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_IPUT_WIDE:
+    case OP_IPUT_WIDE_VOLATILE:
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+        if (VERIFY_OK(failure)) {
+            RegType typeHi =
+                getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure);
+            checkTypeCategory(tmpType, kTypeCategory2, &failure);
+            checkWidePair(tmpType, typeHi, &failure);
+        }
+        if (VERIFY_OK(failure)) {
+            InstField* instField;
+            RegType objType;
+
+            objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, &instField->field, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* check the type, which should be prim */
+            switch (instField->field.signature[0]) {
+            case 'D':
+            case 'J':
+                /* these are okay (and interchangeable) */
+                break;
+            default:
+                LOG_VFY("VFY: invalid iput-wide of %s.%s\n",
+                        instField->field.clazz->descriptor,
+                        instField->field.name);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_IPUT_OBJECT:
+    case OP_IPUT_OBJECT_VOLATILE:
+        {
+            ClassObject* fieldClass;
+            ClassObject* valueClass;
+            InstField* instField;
+            RegType objType, valueType;
+
+            objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+                            &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, &instField->field, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            fieldClass = getFieldClass(meth, &instField->field);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'\n",
+                    instField->field.signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            valueType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (!regTypeIsReference(valueType)) {
+                LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)\n",
+                        decInsn.vA, instField->field.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\n",
+                        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)\n",
+                            valueClass->descriptor, fieldClass->descriptor,
+                            instField->field.clazz->descriptor,
+                            instField->field.name);
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+            }
+        }
+        break;
+
+    case OP_SGET:
+    case OP_SGET_VOLATILE:
+        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.  (We can't generally require an exact type match,
+             * because e.g. "int" and "float" are interchangeable.)
+             */
+            fieldType = primSigCharToRegType(staticField->field.signature[0]);
+            if (!checkFieldArrayStore1nr(tmpType, fieldType)) {
+                LOG_VFY("VFY: invalid sget-1nr of %s.%s (inst=%d actual=%d)\n",
+                    staticField->field.clazz->descriptor,
+                    staticField->field.name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            setRegisterType(workRegs, insnRegCount, decInsn.vA, tmpType,
+                &failure);
+        }
+        break;
+    case OP_SGET_WIDE:
+    case OP_SGET_WIDE_VOLATILE:
+        {
+            StaticField* staticField;
+            RegType dstType;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            /* check the type, which should be prim */
+            switch (staticField->field.signature[0]) {
+            case 'D':
+                dstType = kRegTypeDoubleLo;
+                break;
+            case 'J':
+                dstType = kRegTypeLongLo;
+                break;
+            default:
+                LOG_VFY("VFY: invalid sget-wide of %s.%s\n",
+                        staticField->field.clazz->descriptor,
+                        staticField->field.name);
+                dstType = kRegTypeUnknown;
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (VERIFY_OK(failure)) {
+                setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                    dstType, &failure);
+            }
+        }
+        break;
+    case OP_SGET_OBJECT:
+    case OP_SGET_OBJECT_VOLATILE:
+        {
+            StaticField* staticField;
+            ClassObject* fieldClass;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            fieldClass = getFieldClass(meth, &staticField->field);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'\n",
+                    staticField->field.signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            if (dvmIsPrimitiveClass(fieldClass)) {
+                LOG_VFY("VFY: attempt to get prim field with sget-object\n");
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+            setRegisterType(workRegs, insnRegCount, decInsn.vA,
+                regTypeFromClass(fieldClass), &failure);
+        }
+        break;
+    case OP_SPUT:
+    case OP_SPUT_VOLATILE:
+        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(workRegs, insnRegCount, decInsn.vA,
+                        &failure);
+
+            /*
+             * javac generates synthetic functions that write byte values
+             * into boolean fields.
+             */
+            if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte)
+                srcType = kRegTypeBoolean;
+
+            /* 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)\n",
+                    srcType, tmpType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, &staticField->field, &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->field.signature[0]);
+            if (!checkFieldArrayStore1nr(tmpType, fieldType)) {
+                LOG_VFY("VFY: invalid sput-1nr of %s.%s (inst=%d actual=%d)\n",
+                    staticField->field.clazz->descriptor,
+                    staticField->field.name, tmpType, fieldType);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_SPUT_WIDE:
+    case OP_SPUT_WIDE_VOLATILE:
+        tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+        if (VERIFY_OK(failure)) {
+            RegType typeHi =
+                getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure);
+            checkTypeCategory(tmpType, kTypeCategory2, &failure);
+            checkWidePair(tmpType, typeHi, &failure);
+        }
+        if (VERIFY_OK(failure)) {
+            StaticField* staticField;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, &staticField->field, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            /* check the type, which should be prim */
+            switch (staticField->field.signature[0]) {
+            case 'D':
+            case 'J':
+                /* these are okay */
+                break;
+            default:
+                LOG_VFY("VFY: invalid sput-wide of %s.%s\n",
+                        staticField->field.clazz->descriptor,
+                        staticField->field.name);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+        }
+        break;
+    case OP_SPUT_OBJECT:
+    case OP_SPUT_OBJECT_VOLATILE:
+        {
+            ClassObject* fieldClass;
+            ClassObject* valueClass;
+            StaticField* staticField;
+            RegType valueType;
+
+            staticField = getStaticField(meth, decInsn.vB, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            checkFinalFieldAccess(meth, &staticField->field, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            fieldClass = getFieldClass(meth, &staticField->field);
+            if (fieldClass == NULL) {
+                LOG_VFY("VFY: unable to recover field class from '%s'\n",
+                    staticField->field.signature);
+                failure = VERIFY_ERROR_GENERIC;
+                break;
+            }
+
+            valueType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+                        &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            if (!regTypeIsReference(valueType)) {
+                LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)\n",
+                        decInsn.vA, staticField->field.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\n",
+                        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)\n",
+                            valueClass->descriptor, fieldClass->descriptor,
+                            staticField->field.clazz->descriptor,
+                            staticField->field.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, workRegs, insnRegCount,
+                            &decInsn, uninitMap, METHOD_VIRTUAL, isRange,
+                            isSuper, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workRegs, insnRegCount, returnType, &failure);
+            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, workRegs, 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(workRegs, insnRegCount,
+                            &decInsn, &failure);
+                if (!VERIFY_OK(failure))
+                    break;
+
+                /* no null refs allowed (?) */
+                if (thisType == kRegTypeZero) {
+                    LOG_VFY("VFY: unable to initialize null ref\n");
+                    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\n");
+                    failure = VERIFY_ERROR_GENERIC;
+                    break;
+                }
+
+                /* arg must be an uninitialized reference */
+                if (!regTypeIsUninitReference(thisType)) {
+                    LOG_VFY("VFY: can only initialize the uninitialized\n");
+                    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(workRegs, insnRegCount, uninitMap,
+                    thisType, &failure);
+                if (!VERIFY_OK(failure))
+                    break;
+            }
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workRegs, insnRegCount,
+                returnType, &failure);
+            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, workRegs, insnRegCount,
+                            &decInsn, uninitMap, METHOD_STATIC, isRange,
+                            false, &failure);
+            if (!VERIFY_OK(failure))
+                break;
+
+            returnType = getMethodReturnType(calledMethod);
+            setResultRegisterType(workRegs, insnRegCount, returnType, &failure);
+            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, workRegs, 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(workRegs, insnRegCount,
+                        &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\n");
+                    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\n",
+                            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(workRegs, insnRegCount, returnType, &failure);
+            justSetResult = true;
+        }
+        break;
+
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, &failure);
+        break;
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, &failure);
+        break;
+    case OP_NEG_FLOAT:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeFloat, kRegTypeFloat, &failure);
+        break;
+    case OP_NEG_DOUBLE:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeDoubleLo, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_INT_TO_LONG:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeLongLo, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_FLOAT:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeFloat, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_DOUBLE:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeDoubleLo, kRegTypeInteger, &failure);
+        break;
+    case OP_LONG_TO_INT:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeLongLo, &failure);
+        break;
+    case OP_LONG_TO_FLOAT:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeFloat, kRegTypeLongLo, &failure);
+        break;
+    case OP_LONG_TO_DOUBLE:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeDoubleLo, kRegTypeLongLo, &failure);
+        break;
+    case OP_FLOAT_TO_INT:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeFloat, &failure);
+        break;
+    case OP_FLOAT_TO_LONG:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeLongLo, kRegTypeFloat, &failure);
+        break;
+    case OP_FLOAT_TO_DOUBLE:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeDoubleLo, kRegTypeFloat, &failure);
+        break;
+    case OP_DOUBLE_TO_INT:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_DOUBLE_TO_LONG:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeLongLo, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_DOUBLE_TO_FLOAT:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeFloat, kRegTypeDoubleLo, &failure);
+        break;
+    case OP_INT_TO_BYTE:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeByte, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_CHAR:
+        checkUnop(workRegs, insnRegCount, &decInsn,
+            kRegTypeChar, kRegTypeInteger, &failure);
+        break;
+    case OP_INT_TO_SHORT:
+        checkUnop(workRegs, insnRegCount, &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(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+        checkBinop(workRegs, insnRegCount, &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(workRegs, insnRegCount, &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(workRegs, insnRegCount, &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(workRegs, insnRegCount, &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(workRegs, insnRegCount, &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(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+        checkBinop2addr(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure);
+        break;
+    case OP_DIV_INT_2ADDR:
+        checkBinop2addr(workRegs, insnRegCount, &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(workRegs, insnRegCount, &decInsn,
+            kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure);
+        break;
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+        checkBinop2addr(workRegs, insnRegCount, &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(workRegs, insnRegCount, &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(workRegs, insnRegCount, &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(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+        checkLitop(workRegs, insnRegCount, &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(workRegs, insnRegCount, &decInsn,
+            kRegTypeInteger, kRegTypeInteger, false, &failure);
+        break;
+    case OP_SHR_INT_LIT8:
+        tmpType = adjustForRightShift(workRegs, insnRegCount,
+            decInsn.vB, decInsn.vC, false, &failure);
+        checkLitop(workRegs, insnRegCount, &decInsn,
+            tmpType, kRegTypeInteger, false, &failure);
+        break;
+    case OP_USHR_INT_LIT8:
+        tmpType = adjustForRightShift(workRegs, insnRegCount,
+            decInsn.vB, decInsn.vC, true, &failure);
+        checkLitop(workRegs, insnRegCount, &decInsn,
+            tmpType, kRegTypeInteger, false, &failure);
+        break;
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+        checkLitop(workRegs, insnRegCount, &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_INVOKE_DIRECT_EMPTY:
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        failure = VERIFY_ERROR_GENERIC;
+        break;
+
+    /* 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_F1:
+    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\n",
+                decInsn.opCode, insnIdx);
+            goto bail;
+        } else {
+            /* replace opcode and continue on */
+            LOGD("VFY: replacing opcode 0x%02x at 0x%04x\n",
+                decInsn.opCode, insnIdx);
+            if (!replaceFailingInstruction(meth, insnFlags, insnIdx, failure)) {
+                LOG_VFY_METH(meth, "VFY:  rejecting opcode 0x%02x at 0x%04x\n",
+                    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);
+        workRegs[reg] = workRegs[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 0x%x)\n",
+                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) != NULL) {
+            /*
+             * Merge registers into what we have for the next instruction,
+             * and set the "changed" flag if needed.
+             */
+            updateRegisters(meth, insnFlags, regTable, insnIdx+insnWidth,
+                workRegs);
+        } 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.
+     */
+    if ((nextFlags & kInstrCanBranch) != 0) {
+        bool isConditional;
+
+        if (!dvmGetBranchTarget(meth, insnFlags, insnIdx, &branchTarget,
+                &isConditional))
+        {
+            /* should never happen after static verification */
+            LOG_VFY_METH(meth, "VFY: bad branch at %d\n", 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 */
+        updateRegisters(meth, insnFlags, regTable, insnIdx+branchTarget,
+            workRegs);
+    }
+
+    /*
+     * 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;
+
+            updateRegisters(meth, insnFlags, regTable, absOffset, workRegs);
+        }
+    }
+
+    /*
+     * Handle instructions that can throw and that are sitting in a
+     * "try" block.  (If they're not in a "try" block when they throw,
+     * control transfers out of the method.)
+     */
+    if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+    {
+        const DexCode* pCode = dvmGetMethodCode(meth);
+        DexCatchIterator iterator;
+
+        if (dexFindCatchHandler(&iterator, pCode, insnIdx)) {
+            for (;;) {
+                DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+                if (handler == NULL) {
+                    break;
+                }
+
+                /* note we use entryRegs, not workRegs */
+                updateRegisters(meth, insnFlags, regTable, handler->address,
+                    entryRegs);
+            }
+        }
+    }
+
+    /*
+     * Update startGuess.  Advance to the next instruction of that's
+     * possible, otherwise use the branch target if one was found.  If
+     * neither of those exists we're in a return or throw; leave startGuess
+     * alone and let the caller sort it out.
+     */
+    if ((nextFlags & kInstrCanContinue) != 0) {
+        *pStartGuess = insnIdx + dvmInsnGetWidth(insnFlags, insnIdx);
+    } else if ((nextFlags & kInstrCanBranch) != 0) {
+        /* we're still okay if branchTarget is zero */
+        *pStartGuess = insnIdx + branchTarget;
+    }
+
+    assert(*pStartGuess >= 0 && *pStartGuess < 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)
+    {
+        LOGI("        %2d: '%s' %s\n", reg, name, descriptor);
+    }
+}
+
+/*
+ * Dump the register types for the specifed address to the log file.
+ */
+static void dumpRegTypes(const Method* meth, const InsnFlags* insnFlags,
+    const RegType* addrRegs, int addr, const char* addrName,
+    const UninitInstanceMap* uninitMap, int displayFlags)
+{
+    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';
+
+    //const RegType* addrRegs = getRegisterLine(regTable, addr);
+
+    for (i = 0; i < regCount + kExtraRegs; i++) {
+        char tch;
+
+        switch (addrRegs[i]) {
+        case kRegTypeUnknown:       tch = '.';  break;
+        case kRegTypeConflict:      tch = 'X';  break;
+        case kRegTypeFloat:         tch = 'F';  break;
+        case kRegTypeZero:          tch = '0';  break;
+        case kRegTypeOne:           tch = '1';  break;
+        case kRegTypeBoolean:       tch = 'Z';  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 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)
+        LOGI("%c%s %s\n", branchTarget ? '>' : ' ', addrName, regChars);
+    else
+        LOGI("%c0x%04x %s\n", branchTarget ? '>' : ' ', addr, regChars);
+
+    if (displayFlags & DRT_SHOW_REF_TYPES) {
+        for (i = 0; i < regCount + kExtraRegs; i++) {
+            if (regTypeIsReference(addrRegs[i]) && addrRegs[i] != kRegTypeZero)
+            {
+                ClassObject* clazz;
+
+                clazz = regTypeReferenceToClass(addrRegs[i], uninitMap);
+                assert(dvmValidateObject((Object*)clazz));
+                if (i < regCount) {
+                    LOGI("        %2d: 0x%08x %s%s\n",
+                        i, addrRegs[i],
+                        regTypeIsUninitReference(addrRegs[i]) ? "[U]" : "",
+                        clazz->descriptor);
+                } else {
+                    LOGI("        RS: 0x%08x %s%s\n",
+                        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..a7ddc95
--- /dev/null
+++ b/vm/analysis/CodeVerify.h
@@ -0,0 +1,308 @@
+/*
+ * 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
+#define _DALVIK_CODEVERIFY
+
+#include "analysis/VerifySubs.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    kRegTypeFloat
+    kRegTypeFloat,
+    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 */
+    kRegTypePosByte,        /* byte, known positive (can become char) */
+    kRegTypeByte,
+    kRegTypePosShort,       /* short, known positive (can become char) */
+    kRegTypeShort,
+    kRegTypeChar,
+    kRegTypeInteger,
+#define kRegType1nrEND      kRegTypeInteger
+
+    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;
+
+/*
+ * Table that maps uninitialized instances to classes, based on the
+ * address of the new-instance instruction.
+ */
+typedef struct UninitInstanceMap {
+    int numEntries;
+    struct {
+        int             addr;   /* code offset, or -1 for method arg ("this") */
+        ClassObject*    clazz;  /* class created at this address */
+    } map[1];
+} UninitInstanceMap;
+#define kUninitThisArgAddr  (-1)
+#define kUninitThisArgSlot  0
+
+/*
+ * Various bits of data generated by the verifier, wrapped up in a package
+ * for ease of use by the register map generator.
+ */
+typedef 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 SRegType arrays, 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.
+     */
+    RegType**       addrRegs;
+} VerifierData;
+
+
+/* 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);
+
+/*
+ * Associate a class with an address.  Returns the map slot index, or -1
+ * if the address isn't listed in the map (shouldn't happen) or if a
+ * different class is already associated with the address (shouldn't
+ * happen either).
+ */
+//int dvmSetUninitInstance(UninitInstanceMap* uninitMap, int addr,
+//    ClassObject* clazz);
+
+/*
+ * Return the class associated with an uninitialized reference.  Pass in
+ * the map index.
+ */
+//ClassObject* dvmGetUninitInstance(const UninitInstanceMap* uninitMap, int idx);
+
+/*
+ * Clear the class associated with an uninitialized reference.  Pass in
+ * the map index.
+ */
+//void dvmClearUninitInstance(UninitInstanceMap* uninitMap, int idx);
+
+
+/*
+ * Verify bytecode in "meth".  "insnFlags" should be populated with
+ * instruction widths and "in try" flags.
+ */
+bool dvmVerifyCodeFlow(VerifierData* vdata);
+
+#endif /*_DALVIK_CODEVERIFY*/
diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c
new file mode 100644
index 0000000..c00810c
--- /dev/null
+++ b/vm/analysis/DexPrepare.c
@@ -0,0 +1,1446 @@
+/*
+ * 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 <zlib.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/* fwd */
+static bool rewriteDex(u1* addr, int len, u4* pHeaderFlags,
+    DexClassLookup** ppClassLookup);
+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);
+
+
+/*
+ * 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) {
+                LOGE("Can't open dex cache '%s': %s\n",
+                    cacheFileName, strerror(errno));
+            }
+            return fd;
+        }
+        readOnly = true;
+    }
+
+    /*
+     * 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.
+     */
+    int oldStatus;
+    LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
+        cacheFileName, fd, isBootstrap);
+    oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+    cc = flock(fd, LOCK_EX | LOCK_NB);
+    if (cc != 0) {
+        LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
+        cc = flock(fd, LOCK_EX);
+    }
+    dvmChangeStatus(NULL, oldStatus);
+    if (cc != 0) {
+        LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
+        close(fd);
+        return -1;
+    }
+    LOGV("DexOpt:  locked cache file\n");
+
+    /*
+     * 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) {
+        LOGE("Can't stat open file '%s'\n", cacheFileName);
+        LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+        goto close_fail;
+    }
+    cc = stat(cacheFileName, &fileStat);
+    if (cc != 0 ||
+        fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
+    {
+        LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
+        LOGVV("DexOpt: unlocking cache file %s\n", 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) {
+            LOGW("DexOpt: file has zero length and isn't writable\n");
+            goto close_fail;
+        }
+        cc = dexOptCreateEmptyHeader(fd);
+        if (cc != 0)
+            goto close_fail;
+        *pNewFile = true;
+        LOGV("DexOpt: successfully initialized new cache file\n");
+    } 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)
+            expectOpt = expectVerify;
+        else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
+            expectOpt = true;
+
+        LOGV("checking deps, expecting vfy=%d opt=%d\n",
+            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) {
+                    LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
+                        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.
+             */
+            LOGD("ODEX file is stale or bad; removing and retrying (%s)\n",
+                cacheFileName);
+            if (ftruncate(fd, 0) != 0) {
+                LOGW("Warning: unable to truncate cache file '%s': %s\n",
+                    cacheFileName, strerror(errno));
+                /* keep going */
+            }
+            if (unlink(cacheFileName) != 0) {
+                LOGW("Warning: unable to remove cache file '%s': %d %s\n",
+                    cacheFileName, errno, strerror(errno));
+                /* keep going; permission failure should probably be fatal */
+            }
+            LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+            flock(fd, LOCK_UN);
+            close(fd);
+            goto retry;
+        } else {
+            LOGV("DexOpt: good deps in cache file\n");
+        }
+    }
+
+    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\n", 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;
+
+    LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);
+
+    pid_t pid;
+
+    /*
+     * This could happen if something in our bootclasspath, which we thought
+     * was all optimized, got rejected.
+     */
+    if (gDvm.optimizing) {
+        LOGW("Rejecting recursive optimization attempt on '%s'\n", 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);
+        char* argv[argc+1];             // last entry is NULL
+        char values[argc][kMaxIntLen];
+        char* execFile;
+        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) {
+            LOGW("ANDROID_ROOT not set, defaulting to /system\n");
+            androidRoot = "/system";
+        }
+        execFile = malloc(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, argv);
+        else
+            execv(execFile, argv);
+
+        LOGE("execv '%s'%s failed: %s\n", execFile,
+            kUseValgrind ? " [valgrind]" : "", strerror(errno));
+        exit(1);
+    } else {
+        LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
+        int status;
+        pid_t gotPid;
+        int oldStatus;
+
+        /*
+         * Wait for the optimization process to finish.  We go into VMWAIT
+         * mode here so GC suspension won't have to wait for us.
+         */
+        oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+        while (true) {
+            gotPid = waitpid(pid, &status, 0);
+            if (gotPid == -1 && errno == EINTR) {
+                LOGD("waitpid interrupted, retrying\n");
+            } else {
+                break;
+            }
+        }
+        dvmChangeStatus(NULL, oldStatus);
+        if (gotPid != pid) {
+            LOGE("waitpid failed: wanted %d, got %d: %s\n",
+                (int) pid, (int) gotPid, strerror(errno));
+            return false;
+        }
+
+        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+            LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
+            return true;
+        } else {
+            LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
+                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;
+    u4 headerFlags = 0;
+
+    assert(gDvm.optimizing);
+
+    LOGV("Continuing optimization (%s, isb=%d, vfy=%d, opt=%d)\n",
+        fileName, isBootstrap, doVerify, doOpt);
+
+    assert(dexOffset >= 0);
+
+    /* quick test so we don't blow up on empty file */
+    if (dexLength < (int) sizeof(DexHeader)) {
+        LOGE("too small to be DEX\n");
+        return false;
+    }
+    if (dexOffset < (int) sizeof(DexOptHeader)) {
+        LOGE("not enough room for opt header\n");
+        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) {
+            LOGE("unable to mmap DEX cache: %s\n", strerror(errno));
+            goto bail;
+        }
+
+        /*
+         * 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 sets "headerFlags" and creates the class lookup table as
+         * part of doing the processing.
+         */
+        success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
+                    &headerFlags, &pClassLookup);
+
+        if (success) {
+            DvmDex* pDvmDex = NULL;
+            u1* dexAddr = ((u1*) mapAddr) + dexOffset;
+
+            if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
+                LOGE("Unable to create DexFile\n");
+                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) {
+                        LOGE("Failed generating register maps\n");
+                        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) {
+            LOGW("msync failed: %s\n", 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) {
+            LOGE("munmap failed: %s\n", 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) {
+        LOGE("lseek to EOF failed: %s\n", strerror(errno));
+        goto bail;
+    }
+    adjOffset = (depsOffset + 7) & ~(0x07);
+    if (adjOffset != depsOffset) {
+        LOGV("Adjusting deps start from %d to %d\n",
+            (int) depsOffset, (int) adjOffset);
+        depsOffset = adjOffset;
+        lseek(fd, depsOffset, SEEK_SET);
+    }
+
+    /*
+     * Append the dependency list.
+     */
+    if (writeDependencies(fd, modWhen, crc) != 0) {
+        LOGW("Failed writing dependencies\n");
+        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) {
+        LOGV("Adjusting opt start from %d to %d\n",
+            (int) optOffset, (int) adjOffset);
+        optOffset = adjOffset;
+        lseek(fd, optOffset, SEEK_SET);
+    }
+
+    /*
+     * Append any optimized pre-computed data structures.
+     */
+    if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {
+        LOGW("Failed writing opt data\n");
+        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;
+
+    optHdr.flags = headerFlags;
+    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;
+
+    LOGV("Successfully wrote DEX header\n");
+    result = true;
+
+    //dvmRegisterMapDumpStats();
+
+bail:
+    dvmFreeRegisterMapBuilder(pRegMapBuilder);
+    free(pClassLookup);
+    return result;
+}
+
+
+/*
+ * Perform in-place rewrites on a memory-mapped DEX file.
+ *
+ * This happens in a short-lived child process, so we can go nutty with
+ * loading classes and allocating memory.
+ */
+static bool rewriteDex(u1* addr, int len, u4* pHeaderFlags,
+    DexClassLookup** ppClassLookup)
+{
+    u8 prepWhen, loadWhen, verifyOptWhen;
+    DvmDex* pDvmDex = NULL;
+    bool doVerify, doOpt;
+    bool result = false;
+
+    *pHeaderFlags = 0;
+
+    /* if the DEX is in the wrong byte order, swap it now */
+    if (dexSwapAndVerify(addr, len) != 0)
+        goto bail;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+    *pHeaderFlags |= DEX_OPT_FLAG_BIG;
+#endif
+
+    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)
+        doOpt = doVerify;
+    else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
+        doOpt = true;
+
+    /* TODO: decide if this is actually useful */
+    if (doVerify)
+        *pHeaderFlags |= DEX_FLAG_VERIFIED;
+    if (doOpt)
+        *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
+
+    /*
+     * Now that the DEX file can be read directly, create a DexFile struct
+     * for it.
+     */
+    if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
+        LOGE("Unable to create DexFile\n");
+        goto bail;
+    }
+
+    /*
+     * Create the class lookup table.  This will eventually be appended
+     * to the end of the .odex.
+     */
+    *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
+    if (*ppClassLookup == NULL)
+        goto bail;
+
+    /*
+     * 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;
+    }
+
+    /* this is needed for the next part */
+    pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
+
+    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();
+
+    /*
+     * 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();
+
+    const char* msgStr = "???";
+    if (doVerify && doOpt)
+        msgStr = "verify+opt";
+    else if (doVerify)
+        msgStr = "verify";
+    else if (doOpt)
+        msgStr = "opt";
+    LOGD("DexOpt: load %dms, %s %dms\n",
+        (int) (loadWhen - prepWhen) / 1000,
+        msgStr,
+        (int) (verifyOptWhen - loadWhen) / 1000);
+
+    result = true;
+
+bail:
+    /* free up storage */
+    dvmDexFileFree(pDvmDex);
+
+    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;
+
+    LOGV("DexOpt: +++ trying to load %d classes\n", count);
+
+    dvmSetBootPathExtraDex(pDvmDex);
+
+    /*
+     * 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.  Take care of that here.  (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 (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
+        LOGE("ERROR: java.lang.Class does not exist!\n");
+        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);
+
+        LOGV("+++  loading '%s'", classDescriptor);
+        //newClass = dvmDefineClass(pDexFile, classDescriptor,
+        //        NULL);
+        newClass = dvmFindSystemClassNoInit(classDescriptor);
+        if (newClass == NULL) {
+            LOGV("DexOpt: failed loading '%s'\n", 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.
+             */
+            LOGD("DexOpt: '%s' has an earlier definition; blocking out\n",
+                classDescriptor);
+            SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
+        } else {
+            loaded++;
+        }
+    }
+    LOGV("DexOpt: +++ successfully loaded %d classes\n", 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;
+
+    /*
+     * Create a data structure for use by the bytecode optimizer.  We
+     * stuff it into a global so we don't have to pass it around as
+     * a function argument.
+     *
+     * We could create this at VM startup, but there's no need to do so
+     * unless we're optimizing, which means we're in dexopt, and we're
+     * only going to call here once.
+     */
+    if (doOpt) {
+        gDvm.inlineSubs = dvmCreateInlineSubsTable();
+        if (gDvm.inlineSubs == NULL)
+            return;
+    }
+
+    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
+            LOGV("DexOpt: not optimizing unavailable class '%s'\n",
+                classDescriptor);
+        }
+    }
+
+    if (gDvm.inlineSubs != NULL) {
+        dvmFreeInlineSubsTable(gDvm.inlineSubs);
+        gDvm.inlineSubs = NULL;
+    }
+}
+
+/*
+ * 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;
+
+    classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+    /*
+     * First, try to verify it.
+     */
+    if (doVerify) {
+        if (clazz->pDvmDex->pDexFile != pDexFile) {
+            LOGD("DexOpt: not verifying '%s': multiple definitions\n",
+                classDescriptor);
+        } else {
+            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
+                LOGV("DexOpt: '%s' failed verification\n", classDescriptor);
+            }
+        }
+    }
+
+    if (doOpt) {
+        if (!verified && gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED) {
+            LOGV("DexOpt: not optimizing '%s': not verified\n",
+                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:
+        LOGE("DexOpt: unexpected cpe kind %d\n", 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:
+        LOGE("unexpected cpe kind %d\n", 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) {
+        LOGE("DexOpt: failed to seek to start of file: %s\n", 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) {
+        LOGE("DexOpt: failed reading opt header: %s\n", strerror(errno));
+        goto bail;
+    } else if (actual != sizeof(optHdr)) {
+        LOGE("DexOpt: failed reading opt header (got %d of %zd)\n",
+            (int) actual, sizeof(optHdr));
+        goto bail;
+    }
+
+    magic = optHdr.magic;
+    if (memcmp(magic, DEX_MAGIC, 4) == 0) {
+        /* somebody probably pointed us at the wrong file */
+        LOGD("DexOpt: expected optimized DEX, found unoptimized\n");
+        goto bail;
+    } else if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
+        /* not a DEX file, or previous attempt was interrupted */
+        LOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
+            magic[0], magic[1], magic[2], magic[3]);
+        goto bail;
+    }
+    if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+        LOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)\n",
+            magic[4], magic[5], magic[6], magic[7]);
+        goto bail;
+    }
+    if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
+        LOGW("DexOpt: weird deps length %d, bailing\n", optHdr.depsLength);
+        goto bail;
+    }
+
+    /*
+     * Do the header flags match up with what we want?
+     *
+     * This is useful because it allows us to automatically regenerate
+     * a file when settings change (e.g. verification is now mandatory),
+     * but can cause difficulties if the bootstrap classes we depend upon
+     * were handled differently than the current options specify.  We get
+     * upset because they're not verified or optimized, but we're not able
+     * to regenerate them because the installer won't let us.
+     *
+     * (This is also of limited value when !sourceAvail.)
+     *
+     * So, for now, we essentially ignore "expectVerify" and "expectOpt"
+     * by limiting the match mask.
+     *
+     * 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 (expectVerify)
+        expectedFlags |= DEX_FLAG_VERIFIED;
+    if (expectOpt)
+        expectedFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
+    if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
+        LOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)\n",
+            expectedFlags, optHdr.flags, matchMask);
+        goto bail;
+    }
+
+    posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
+    if (posn < 0) {
+        LOGW("DexOpt: seek to deps failed: %s\n", strerror(errno));
+        goto bail;
+    }
+
+    /*
+     * Read all of the dependency stuff into memory.
+     */
+    depData = (u1*) malloc(optHdr.depsLength);
+    if (depData == NULL) {
+        LOGW("DexOpt: unable to allocate %d bytes for deps\n",
+            optHdr.depsLength);
+        goto bail;
+    }
+    actual = read(fd, depData, optHdr.depsLength);
+    if (actual < 0) {
+        LOGW("DexOpt: failed reading deps: %s\n", strerror(errno));
+        goto bail;
+    } else if (actual != (ssize_t) optHdr.depsLength) {
+        LOGW("DexOpt: failed reading deps: got %d of %d\n",
+            (int) actual, optHdr.depsLength);
+        goto bail;
+    }
+
+    /*
+     * Verify simple items.
+     */
+    const u1* ptr;
+    u4 val;
+
+    ptr = depData;
+    val = read4LE(&ptr);
+    if (sourceAvail && val != modWhen) {
+        LOGI("DexOpt: source file mod time mismatch (%08x vs %08x)\n",
+            val, modWhen);
+        goto bail;
+    }
+    val = read4LE(&ptr);
+    if (sourceAvail && val != crc) {
+        LOGI("DexOpt: source file CRC mismatch (%08x vs %08x)\n", val, crc);
+        goto bail;
+    }
+    val = read4LE(&ptr);
+    if (val != DALVIK_VM_BUILD) {
+        LOGD("DexOpt: VM build version mismatch (%d vs %d)\n",
+            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);
+    LOGV("+++ DexOpt: numDeps = %d\n", 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 */
+            LOGI("DexOpt: not all deps represented\n");
+            goto bail;
+        }
+
+        storedStrLen = read4LE(&ptr);
+        if (len != storedStrLen ||
+            strcmp(cacheFileName, (const char*) ptr) != 0)
+        {
+            LOGI("DexOpt: mismatch dep name: '%s' vs. '%s'\n",
+                cacheFileName, ptr);
+            goto bail;
+        }
+
+        ptr += storedStrLen;
+
+        if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
+            LOGI("DexOpt: mismatch dep signature for '%s'\n", cacheFileName);
+            goto bail;
+        }
+        ptr += kSHA1DigestLen;
+
+        LOGV("DexOpt: dep match on '%s'\n", cacheFileName);
+
+        numDeps--;
+    }
+
+    if (numDeps != 0) {
+        /* more entries in deps list than in classpath */
+        LOGI("DexOpt: Some deps went away\n");
+        goto bail;
+    }
+
+    // consumed all data and no more?
+    if (ptr != depData + optHdr.depsLength) {
+        LOGW("DexOpt: Spurious dep data? %d vs %d\n",
+            (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 */
+
+        LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName);
+
+        numDeps++;
+        bufLen += strlen(cacheFileName) +1;
+    }
+
+    bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
+
+    buf = 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) {
+            LOGE("DexOpt: overran buffer\n");
+            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);
+
+    LOGV("Writing chunk, type=%.4s size=%d\n", (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);
+        LOGV("size was %d, inserting %d pad bytes\n", 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) {
+        LOGE("Unable to seek to start of checksum area (%ld): %s\n",
+            (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) {
+            LOGE("Read failed (%d) while computing checksum (len=%zu): %s\n",
+                (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..bfa5fb5
--- /dev/null
+++ b/vm/analysis/DexPrepare.h
@@ -0,0 +1,122 @@
+/*
+ * 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
+#define _DALVIK_DEXPREPARE
+
+/*
+ * Global DEX optimizer control.  Determines the circumstances in which we
+ * try to rewrite instructions in the DEX file.
+ */
+typedef enum DexOptimizerMode {
+    OPTIMIZE_MODE_UNKNOWN = 0,
+    OPTIMIZE_MODE_NONE,         /* never optimize */
+    OPTIMIZE_MODE_VERIFIED,     /* only optimize verified classes (default) */
+    OPTIMIZE_MODE_ALL           /* optimize all classes */
+} DexOptimizerMode;
+
+/* 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.
+ */
+typedef 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 */
+} VerifyError;
+
+/*
+ * 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.
+ */
+typedef enum VerifyErrorRefType {
+    VERIFY_ERROR_REF_CLASS  = 0,
+    VERIFY_ERROR_REF_FIELD  = 1,
+    VERIFY_ERROR_REF_METHOD = 2,
+} VerifyErrorRefType;
+
+#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);
+
+#endif /*_DALVIK_DEXPREPARE*/
diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c
new file mode 100644
index 0000000..b934481
--- /dev/null
+++ b/vm/analysis/DexVerify.c
@@ -0,0 +1,697 @@
+/*
+ * 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"
+
+
+/* fwd */
+static bool verifyMethod(Method* meth);
+static bool verifyInstructions(VerifierData* vdata);
+
+
+/*
+ * Initialize some things we need for verification.
+ */
+bool dvmVerificationStartup(void)
+{
+    gDvm.instrWidth = dexCreateInstrWidthTable();
+    gDvm.instrFormat = dexCreateInstrFormatTable();
+    gDvm.instrFlags = dexCreateInstrFlagsTable();
+    if (gDvm.instrWidth == NULL || gDvm.instrFormat == NULL ||
+        gDvm.instrFlags == NULL)
+    {
+        LOGE("Unable to create instruction tables\n");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Free up some things we needed for verification.
+ */
+void dvmVerificationShutdown(void)
+{
+    free(gDvm.instrWidth);
+    free(gDvm.instrFormat);
+    free(gDvm.instrFlags);
+}
+
+
+/*
+ * 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)) {
+        LOGD("Ignoring duplicate verify attempt on %s\n", clazz->descriptor);
+        return true;
+    }
+
+    for (i = 0; i < clazz->directMethodCount; i++) {
+        if (!verifyMethod(&clazz->directMethods[i])) {
+            LOG_VFY("Verifier rejected class %s\n", clazz->descriptor);
+            return false;
+        }
+    }
+    for (i = 0; i < clazz->virtualMethodCount; i++) {
+        if (!verifyMethod(&clazz->virtualMethods[i])) {
+            LOG_VFY("Verifier rejected class %s\n", clazz->descriptor);
+            return false;
+        }
+    }
+
+    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 dvmComputeCodeWidths():
+ * - 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;
+    int newInstanceCount;
+
+    /*
+     * 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;
+
+    /*
+     * 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\n");
+            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)\n",
+            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(dvmGetMethodInsnsSize(meth), 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 new-instance instructions while we're
+     * at it.
+     */
+    if (!dvmComputeCodeWidths(meth, vdata.insnFlags, &newInstanceCount))
+        goto bail;
+
+    /*
+     * Allocate a map to hold the classes of uninitialized instances.
+     */
+    vdata.uninitMap = dvmCreateUninitInstanceMap(meth, vdata.insnFlags,
+        newInstanceCount);
+    if (vdata.uninitMap == NULL)
+        goto bail;
+
+    /*
+     * Set the "in try" flags for all instructions guarded by a "try" block.
+     */
+    if (!dvmSetTryFlags(meth, vdata.insnFlags))
+        goto bail;
+
+    /*
+     * Perform static instruction verification.
+     */
+    if (!verifyInstructions(&vdata))
+        goto bail;
+
+    /*
+     * Do code-flow analysis.  Do this after verifying the branch targets
+     * so we don't need to worry about it here.
+     *
+     * If there are no registers, we don't need to do much in the way of
+     * analysis, but we still need to verify that nothing actually tries
+     * to use a register.
+     */
+    if (!dvmVerifyCodeFlow(&vdata)) {
+        //LOGD("+++ %s failed code flow\n", meth->name);
+        goto bail;
+    }
+
+success:
+    result = true;
+
+bail:
+    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, int curOffset)
+{
+    const int insnCount = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns + curOffset;
+    const u2* arrayData;
+    int valueCount, valueWidth, tableSize;
+    int offsetToArrayData;
+
+    assert(curOffset >= 0 && curOffset < insnCount);
+
+    /* make sure the start of the array data table is in range */
+    offsetToArrayData = insns[1] | (((s4)insns[2]) << 16);
+    if (curOffset + offsetToArrayData < 0 ||
+        curOffset + offsetToArrayData + 2 >= insnCount)
+    {
+        LOG_VFY_METH(meth,
+            "VFY: invalid array data start: at %d, data offset %d, count %d\n",
+            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_METH(meth,
+            "VFY: unaligned array data table: at %d, data offset %d\n",
+            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_METH(meth,
+            "VFY: invalid array data end: at %d, data offset %d, end %d, "
+            "count %d\n",
+            curOffset, offsetToArrayData,
+            curOffset + offsetToArrayData + tableSize,
+            insnCount);
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * Decode the current instruction.
+ */
+static void decodeInstruction(const Method* meth, int insnIdx,
+    DecodedInstruction* pDecInsn)
+{
+    dexDecodeInstruction(gDvm.instrFormat, meth->insns + insnIdx, pDecInsn);
+}
+
+
+/*
+ * 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 Method* meth, int insnIdx)
+{
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+    DecodedInstruction decInsn;
+    const char* classDescriptor;
+    u4 idx;
+
+    decodeInstruction(meth, insnIdx, &decInsn);
+    idx = decInsn.vB;       // 2nd item
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
+            idx, pDvmDex->pHeader->typeIdsSize);
+        return false;
+    }
+
+    classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
+    if (classDescriptor[0] != 'L') {
+        LOG_VFY_METH(meth, "VFY: can't call new-instance on type '%s'\n",
+            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 Method* meth, int insnIdx)
+{
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+    DecodedInstruction decInsn;
+    const char* classDescriptor;
+    u4 idx;
+
+    decodeInstruction(meth, insnIdx, &decInsn);
+    idx = decInsn.vC;       // 3rd item
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
+            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_METH(meth, "VFY: can't new-array class '%s' (not an array)\n",
+            classDescriptor);
+        return false;
+    } else if (bracketCount > 255) {
+        /* It is illegal to create an array of more than 255 dimensions. */
+        LOG_VFY_METH(meth, "VFY: can't new-array class '%s' (exceeds limit)\n",
+            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 Method* meth, int insnIdx, bool useB)
+{
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+    DecodedInstruction decInsn;
+    u4 idx;
+
+    decodeInstruction(meth, insnIdx, &decInsn);
+    if (useB)
+        idx = decInsn.vB;
+    else
+        idx = decInsn.vC;
+    if (idx >= pDvmDex->pHeader->typeIdsSize) {
+        LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
+            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 Method* meth, int insnIdx, bool useB)
+{
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+    DecodedInstruction decInsn;
+    u4 idx;
+
+    decodeInstruction(meth, insnIdx, &decInsn);
+    if (useB)
+        idx = decInsn.vB;
+    else
+        idx = decInsn.vC;
+    if (idx >= pDvmDex->pHeader->fieldIdsSize) {
+        LOG_VFY_METH(meth,
+            "VFY: bad field index %d (max %d) at offset 0x%04x\n",
+            idx, pDvmDex->pHeader->fieldIdsSize, insnIdx);
+        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 Method* meth, int insnIdx)
+{
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+    DecodedInstruction decInsn;
+
+    decodeInstruction(meth, insnIdx, &decInsn);
+    if (decInsn.vB >= pDvmDex->pHeader->methodIdsSize) {
+        LOG_VFY_METH(meth, "VFY: bad method index %d (max %d)\n",
+            decInsn.vB, pDvmDex->pHeader->methodIdsSize);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Perform static checks on a string constant instruction.  All we do
+ * here is ensure that the string index is in the valid range.
+ */
+static bool checkStringIndex(const Method* meth, int insnIdx)
+{
+    DvmDex* pDvmDex = meth->clazz->pDvmDex;
+    DecodedInstruction decInsn;
+
+    decodeInstruction(meth, insnIdx, &decInsn);
+    if (decInsn.vB >= pDvmDex->pHeader->stringIdsSize) {
+        LOG_VFY_METH(meth, "VFY: bad string index %d (max %d)\n",
+            decInsn.vB, pDvmDex->pHeader->stringIdsSize);
+        return false;
+    }
+
+    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
+ * - (CF) operands referencing constant pool entries must be valid
+ * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid
+ * - (new) verify operands of "quick" field ops
+ * - (CF) operands of method invocation instructions must be valid
+ * - (new) verify operands of "quick" method invoke ops
+ * - (CF) only invoke-direct can call a method starting with '<'
+ * - (CF) <clinit> must never be called explicitly
+ * - (CF) operands of instanceof, checkcast, new (and variants) must be valid
+ * - new-array[-type] limited to 255 dimensions
+ * - can't use "new" on an array class
+ * - (?) limit dimensions in multi-array creation
+ * - (CF) local variable load/store register values must be in valid range
+ *
+ * v3 4.11.1.2
+ * - branches must be within the bounds of the code array
+ * - targets of all control-flow instructions are the start of an instruction
+ * - (CF) register accesses fall within range of allocated registers
+ * - (N/A) access to constant pool must be of appropriate type
+ * - (CF) code does not end in the middle of an instruction
+ * - (CF) execution cannot fall off the end of the code
+ * - (earlier) for each exception handler, the "try" area must begin and
+ *   end at the start of an instruction (end can be at the end of the code)
+ * - (earlier) for each exception handler, the handler must start at a valid
+ *   instruction
+ *
+ * TODO: move some of the "CF" items in here for better performance (the
+ * code-flow analysis sometimes has to process the same instruction several
+ * times).
+ */
+static bool verifyInstructions(VerifierData* vdata)
+{
+    const Method* meth = vdata->method;
+    InsnFlags* insnFlags = vdata->insnFlags;
+    const size_t insnCount = vdata->insnsSize;
+    const u2* insns = meth->insns;
+    int i;
+
+    /* the start of the method is a "branch target" */
+    dvmInsnSetBranchTarget(insnFlags, 0, true);
+
+    for (i = 0; i < (int) insnCount; /**/) {
+        /*
+         * These types of instructions can be GC points.  To support precise
+         * GC, all such instructions must export the PC in the interpreter,
+         * or the GC won't be able to identify the current PC for the thread.
+         */
+        static const int gcMask = kInstrCanBranch | kInstrCanSwitch |
+            kInstrCanThrow | kInstrCanReturn;
+
+        int width = dvmInsnGetWidth(insnFlags, i);
+        OpCode opcode = *insns & 0xff;
+        InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode);
+
+        if ((opFlags & gcMask) != 0) {
+            /*
+             * This instruction is probably a GC point.  Branch instructions
+             * only qualify if they go backward, so we need to check the
+             * offset.
+             */
+            int offset = -1;
+            bool unused;
+            if (dvmGetBranchTarget(meth, insnFlags, i, &offset, &unused)) {
+                if (offset <= 0) {
+                    dvmInsnSetGcPoint(insnFlags, i, true);
+                }
+            } else {
+                /* not a branch target */
+                dvmInsnSetGcPoint(insnFlags, i, true);
+            }
+        }
+
+        switch (opcode) {
+        case OP_NOP:
+            /* plain no-op or switch table data; nothing to do here */
+            break;
+
+        case OP_CONST_STRING:
+        case OP_CONST_STRING_JUMBO:
+            if (!checkStringIndex(meth, i))
+                return false;
+            break;
+
+        case OP_CONST_CLASS:
+        case OP_CHECK_CAST:
+            if (!checkTypeIndex(meth, i, true))
+                return false;
+            break;
+        case OP_INSTANCE_OF:
+            if (!checkTypeIndex(meth, i, false))
+                return false;
+            break;
+
+        case OP_PACKED_SWITCH:
+        case OP_SPARSE_SWITCH:
+            /* verify the associated table */
+            if (!dvmCheckSwitchTargets(meth, insnFlags, i))
+                return false;
+            break;
+
+        case OP_FILL_ARRAY_DATA:
+            /* verify the associated table */
+            if (!checkArrayData(meth, i))
+                return false;
+            break;
+
+        case OP_GOTO:
+        case OP_GOTO_16:
+        case OP_IF_EQ:
+        case OP_IF_NE:
+        case OP_IF_LT:
+        case OP_IF_GE:
+        case OP_IF_GT:
+        case OP_IF_LE:
+        case OP_IF_EQZ:
+        case OP_IF_NEZ:
+        case OP_IF_LTZ:
+        case OP_IF_GEZ:
+        case OP_IF_GTZ:
+        case OP_IF_LEZ:
+            /* check the destination */
+            if (!dvmCheckBranchTarget(meth, insnFlags, i, false))
+                return false;
+            break;
+        case OP_GOTO_32:
+            /* check the destination; self-branch is okay */
+            if (!dvmCheckBranchTarget(meth, insnFlags, i, true))
+                return false;
+            break;
+
+        case OP_NEW_INSTANCE:
+            if (!checkNewInstance(meth, i))
+                return false;
+            break;
+
+        case OP_NEW_ARRAY:
+            if (!checkNewArray(meth, i))
+                return false;
+            break;
+
+        case OP_FILLED_NEW_ARRAY:
+            if (!checkTypeIndex(meth, i, true))
+                return false;
+            break;
+        case OP_FILLED_NEW_ARRAY_RANGE:
+            if (!checkTypeIndex(meth, i, true))
+                return false;
+            break;
+
+        case OP_IGET:
+        case OP_IGET_WIDE:
+        case OP_IGET_OBJECT:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+        case OP_IPUT:
+        case OP_IPUT_WIDE:
+        case OP_IPUT_OBJECT:
+        case OP_IPUT_BOOLEAN:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_SHORT:
+            /* check the field index */
+            if (!checkFieldIndex(meth, i, false))
+                return false;
+            break;
+        case OP_SGET:
+        case OP_SGET_WIDE:
+        case OP_SGET_OBJECT:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_BYTE:
+        case OP_SGET_CHAR:
+        case OP_SGET_SHORT:
+        case OP_SPUT:
+        case OP_SPUT_WIDE:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_SHORT:
+            /* check the field index */
+            if (!checkFieldIndex(meth, i, true))
+                return false;
+            break;
+
+        case OP_INVOKE_VIRTUAL:
+        case OP_INVOKE_SUPER:
+        case OP_INVOKE_DIRECT:
+        case OP_INVOKE_STATIC:
+        case OP_INVOKE_INTERFACE:
+        case OP_INVOKE_VIRTUAL_RANGE:
+        case OP_INVOKE_SUPER_RANGE:
+        case OP_INVOKE_DIRECT_RANGE:
+        case OP_INVOKE_STATIC_RANGE:
+        case OP_INVOKE_INTERFACE_RANGE:
+            /* check the method index */
+            if (!checkMethodIndex(meth, i))
+                return false;
+            break;
+
+        case OP_EXECUTE_INLINE:
+        case OP_INVOKE_DIRECT_EMPTY:
+        case OP_IGET_QUICK:
+        case OP_IGET_WIDE_QUICK:
+        case OP_IGET_OBJECT_QUICK:
+        case OP_IPUT_QUICK:
+        case OP_IPUT_WIDE_QUICK:
+        case OP_IPUT_OBJECT_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK:
+        case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+        case OP_INVOKE_SUPER_QUICK:
+        case OP_INVOKE_SUPER_QUICK_RANGE:
+            LOG_VFY("VFY: not expecting optimized instructions\n");
+            return false;
+            break;
+
+        default:
+            /* nothing to do */
+            break;
+        }
+
+        assert(width > 0);
+        i += width;
+        insns += width;
+    }
+
+    /* make sure the last instruction ends at the end of the insn area */
+    if (i != (int) insnCount) {
+        LOG_VFY_METH(meth,
+            "VFY: code did not end when expected (end at %d, count %d)\n",
+            i, insnCount);
+        return false;
+    }
+
+    return true;
+}
diff --git a/vm/analysis/DexVerify.h b/vm/analysis/DexVerify.h
new file mode 100644
index 0000000..ab2af52
--- /dev/null
+++ b/vm/analysis/DexVerify.h
@@ -0,0 +1,54 @@
+/*
+ * 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
+#define _DALVIK_DEXVERIFY
+
+/*
+ * 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.
+ */
+typedef enum {
+    VERIFY_MODE_UNKNOWN = 0,
+    VERIFY_MODE_NONE,
+    VERIFY_MODE_REMOTE,
+    VERIFY_MODE_ALL
+} DexClassVerifyMode;
+
+bool dvmVerificationStartup(void);
+void dvmVerificationShutdown(void);
+
+/*
+ * Perform verification on all classes loaded from this DEX file.  If
+ * enabled, it must happen before optimization.
+ */
+bool dvmVerifyAllClasses(DexFile* pDexFile);
+
+/*
+ * Verify a single class.
+ */
+bool dvmVerifyClass(ClassObject* clazz);
+
+/*
+ * Release the storage associated with a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap);
+
+#endif /*_DALVIK_DEXVERIFY*/
diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c
new file mode 100644
index 0000000..7ad4e45
--- /dev/null
+++ b/vm/analysis/Optimize.c
@@ -0,0 +1,1108 @@
+/*
+ * 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 <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 bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
+    OpCode volatileOpc);
+static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc);
+static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
+static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
+static bool rewriteExecuteInline(Method* method, u2* insns,
+    MethodType methodType);
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+    MethodType methodType);
+
+
+/*
+ * Create a table of inline substitutions.
+ *
+ * TODO: this is currently just a linear array.  We will want to put this
+ * into a hash table as the list size increases.
+ */
+InlineSub* dvmCreateInlineSubsTable(void)
+{
+    const InlineOperation* ops = dvmGetInlineOpsTable();
+    const int count = dvmGetInlineOpsTableLength();
+    InlineSub* table;
+    Method* method;
+    ClassObject* clazz;
+    int i, tableIndex;
+
+    /*
+     * Allocate for optimism: one slot per entry, plus an end-of-list marker.
+     */
+    table = malloc(sizeof(InlineSub) * (count+1));
+
+    tableIndex = 0;
+    for (i = 0; i < count; i++) {
+        clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
+        if (clazz == NULL) {
+            LOGV("DexOpt: can't inline for class '%s': not found\n",
+                ops[i].classDescriptor);
+            dvmClearOptException(dvmThreadSelf());
+        } else {
+            /*
+             * Method could be virtual or direct.  Try both.  Don't use
+             * the "hier" versions.
+             */
+            method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
+                        ops[i].methodSignature);
+            if (method == NULL)
+                method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
+                        ops[i].methodSignature);
+            if (method == NULL) {
+                LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
+                    ops[i].classDescriptor, ops[i].methodName,
+                    ops[i].methodSignature);
+            } else {
+                if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
+                    LOGW("DexOpt: WARNING: inline op on non-final class/method "
+                         "%s.%s\n",
+                        clazz->descriptor, method->name);
+                    /* fail? */
+                }
+                if (dvmIsSynchronizedMethod(method) ||
+                    dvmIsDeclaredSynchronizedMethod(method))
+                {
+                    LOGW("DexOpt: WARNING: inline op on synchronized method "
+                         "%s.%s\n",
+                        clazz->descriptor, method->name);
+                    /* fail? */
+                }
+
+                table[tableIndex].method = method;
+                table[tableIndex].inlineIdx = i;
+                tableIndex++;
+
+                LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
+                    ops[i].classDescriptor, ops[i].methodName,
+                    ops[i].methodSignature);
+            }
+        }
+    }
+
+    /* mark end of table */
+    table[tableIndex].method = NULL;
+    LOGV("DexOpt: inline table has %d entries\n", tableIndex);
+
+    return table;
+}
+
+/*
+ * Release inline sub data structure.
+ */
+void dvmFreeInlineSubsTable(InlineSub* inlineSubs)
+{
+    free(inlineSubs);
+}
+
+
+/*
+ * 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 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)
+{
+    u4 insnsSize;
+    u2* insns;
+    u2 inst;
+
+    if (!gDvm.optimizing && !essentialOnly) {
+        /* unexpected; will force copy-on-write of a lot of pages */
+        LOGD("NOTE: doing full bytecode optimization outside dexopt\n");
+    }
+
+    if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
+        return;
+
+    insns = (u2*) method->insns;
+    assert(insns != NULL);
+    insnsSize = dvmGetMethodInsnsSize(method);
+
+    while (insnsSize > 0) {
+        OpCode quickOpc, volatileOpc = OP_NOP;
+        int width;
+        bool notMatched = false;
+
+        inst = *insns & 0xff;
+
+        /* "essential" substitutions, always checked */
+        switch (inst) {
+        case OP_IGET:
+        case OP_IGET_BOOLEAN:
+        case OP_IGET_BYTE:
+        case OP_IGET_CHAR:
+        case OP_IGET_SHORT:
+            quickOpc = OP_IGET_QUICK;
+            if (gDvm.dexOptForSmp)
+                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 (gDvm.dexOptForSmp)
+                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 (gDvm.dexOptForSmp)
+                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 (gDvm.dexOptForSmp)
+                volatileOpc = OP_IPUT_OBJECT_VOLATILE;
+rewrite_inst_field:
+            if (essentialOnly)
+                quickOpc = OP_NOP;
+            if (quickOpc != OP_NOP || volatileOpc != OP_NOP)
+                rewriteInstField(method, insns, quickOpc, volatileOpc);
+            break;
+
+        case OP_SGET_WIDE:
+            volatileOpc = OP_SGET_WIDE_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT_WIDE:
+            volatileOpc = OP_SPUT_WIDE_VOLATILE;
+rewrite_static_field:
+            rewriteStaticField(method, insns, volatileOpc);
+            break;
+        default:
+            notMatched = true;
+            break;
+        }
+
+        if (notMatched && gDvm.dexOptForSmp) {
+            /* additional "essential" substitutions for an SMP device */
+            switch (inst) {
+            case OP_SGET:
+            case OP_SGET_BOOLEAN:
+            case OP_SGET_BYTE:
+            case OP_SGET_CHAR:
+            case OP_SGET_SHORT:
+                volatileOpc = OP_SGET_VOLATILE;
+                goto rewrite_static_field2;
+            case OP_SGET_OBJECT:
+                volatileOpc = OP_SGET_OBJECT_VOLATILE;
+                goto rewrite_static_field2;
+            case OP_SPUT:
+            case OP_SPUT_BOOLEAN:
+            case OP_SPUT_BYTE:
+            case OP_SPUT_CHAR:
+            case OP_SPUT_SHORT:
+                volatileOpc = OP_SPUT_VOLATILE;
+                goto rewrite_static_field2;
+            case OP_SPUT_OBJECT:
+                volatileOpc = OP_SPUT_OBJECT_VOLATILE;
+rewrite_static_field2:
+                rewriteStaticField(method, insns, volatileOpc);
+                notMatched = false;
+                break;
+            default:
+                assert(notMatched);
+                break;
+            }
+        }
+
+        /* non-essential substitutions */
+        if (notMatched && !essentialOnly) {
+            switch (inst) {
+            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:
+                if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) {
+                    rewriteEmptyDirectInvoke(method, insns);
+                }
+                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 */
+                ;
+            }
+        }
+
+        width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
+        assert(width > 0);
+
+        insns += width;
+        insnsSize -= width;
+    }
+
+    assert(insnsSize == 0);
+}
+
+/*
+ * Update a 16-bit code unit in "meth".
+ */
+static inline void updateCode(const Method* meth, u2* ptr, u2 newVal)
+{
+    if (gDvm.optimizing) {
+        /* dexopt time, alter the output directly */
+        *ptr = newVal;
+    } else {
+        /* runtime, toggle the page read/write status */
+        dvmDexChangeDex2(meth->clazz->pDvmDex, ptr, newVal);
+    }
+}
+
+/*
+ * 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 */
+            LOGV("DexOpt: class %d (%s) not found\n",
+                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)) {
+        LOGI("DexOpt: not resolving ambiguous class '%s'\n",
+            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) {
+        LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
+            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) {
+            LOGD("DexOpt: couldn't find field %s.%s\n",
+                resClass->descriptor,
+                dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_FIELD;
+            return NULL;
+        }
+        if (dvmIsStaticField(&resField->field)) {
+            LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
+                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->field.clazz);
+    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+    untweakLoader(referrer, resField->field.clazz);
+    if (!allowed) {
+        LOGI("DexOpt: access denied from %s to field %s.%s\n",
+            referrer->descriptor, resField->field.clazz->descriptor,
+            resField->field.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;
+        }
+
+        resField = (StaticField*)dvmFindFieldHier(resClass,
+                    dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+                    dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+        if (resField == NULL) {
+            LOGD("DexOpt: couldn't find static field\n");
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_FIELD;
+            return NULL;
+        }
+        if (!dvmIsStaticField(&resField->field)) {
+            LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
+                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.
+         *
+         * 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->field.clazz);
+    bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+    untweakLoader(referrer, resField->field.clazz);
+    if (!allowed) {
+        LOGI("DexOpt: access denied from %s to field %s.%s\n",
+            referrer->descriptor, resField->field.clazz->descriptor,
+            resField->field.name);
+        if (pFailure != NULL)
+            *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+        return NULL;
+    }
+
+    return resField;
+}
+
+
+/*
+ * Rewrite an iget/iput instruction.  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 bool 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) {
+        LOGI("DexOpt: unable to optimize instance field ref "
+             "0x%04x at 0x%02x in %s.%s\n",
+            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return false;
+    }
+
+    if (instField->byteOffset >= 65536) {
+        LOGI("DexOpt: field offset exceeds 64K (%d)\n", instField->byteOffset);
+        return false;
+    }
+
+    if (volatileOpc != OP_NOP && dvmIsVolatileField(&instField->field)) {
+        updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
+        LOGV("DexOpt: rewrote ifield access %s.%s --> volatile\n",
+            instField->field.clazz->descriptor, instField->field.name);
+    } else if (quickOpc != OP_NOP) {
+        updateCode(method, insns, (insns[0] & 0xff00) | (u2) quickOpc);
+        updateCode(method, insns+1, (u2) instField->byteOffset);
+        LOGV("DexOpt: rewrote ifield access %s.%s --> %d\n",
+            instField->field.clazz->descriptor, instField->field.name,
+            instField->byteOffset);
+    } else {
+        LOGV("DexOpt: no rewrite of ifield access %s.%s\n",
+            instField->field.clazz->descriptor, instField->field.name);
+    }
+
+    return true;
+}
+
+/*
+ * Rewrite an sget/sput instruction.  These all have the form:
+ *   op vAA, field@BBBB
+ *
+ * Where vAA holds the value, and BBBB is the field reference constant
+ * pool offset.  There is no "quick" form of static field accesses, so
+ * this is only useful for volatile fields.
+ *
+ * "method" is the referring method.
+ */
+static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc)
+{
+    ClassObject* clazz = method->clazz;
+    u2 fieldIdx = insns[1];
+    StaticField* staticField;
+
+    assert(volatileOpc != OP_NOP);
+
+    staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL);
+    if (staticField == NULL) {
+        LOGI("DexOpt: unable to optimize static field ref "
+             "0x%04x at 0x%02x in %s.%s\n",
+            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return false;
+    }
+
+    if (dvmIsVolatileField(&staticField->field)) {
+        updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
+        LOGV("DexOpt: rewrote sfield access %s.%s --> volatile\n",
+            staticField->field.clazz->descriptor, staticField->field.name);
+    }
+
+    return true;
+}
+
+/*
+ * 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)\n", 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.
+             */
+            LOGV("DexOpt: can't find called method's class (?.%s)\n",
+                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 */
+            LOGW("DexOpt: method is in an interface\n");
+            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) {
+            LOGV("DexOpt: couldn't find method '%s'\n",
+                dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+            if (pFailure != NULL)
+                *pFailure = VERIFY_ERROR_NO_METHOD;
+            return NULL;
+        }
+        if (methodType == METHOD_STATIC) {
+            if (!dvmIsStaticMethod(resMethod)) {
+                LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
+                    resClass->descriptor, resMethod->name);
+                if (pFailure != NULL)
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                return NULL;
+            }
+        } else if (methodType == METHOD_VIRTUAL) {
+            if (dvmIsStaticMethod(resMethod)) {
+                LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
+                    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)) {
+            LOGW("DexOpt: pure-abstract method '%s' in %s\n",
+                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)\n",
+        methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+    /* access allowed? */
+    tweakLoader(referrer, resMethod->clazz);
+    bool allowed = dvmCheckMethodAccess(referrer, resMethod);
+    untweakLoader(referrer, resMethod->clazz);
+    if (!allowed) {
+        IF_LOGI() {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
+                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.  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 bool 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) {
+        LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
+            methodIdx,
+            (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return false;
+    }
+
+    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.
+     */
+    updateCode(method, insns, (insns[0] & 0xff00) | (u2) newOpc);
+    updateCode(method, insns+1, baseMethod->methodIndex);
+
+    //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
+    //    method->clazz->descriptor, method->name,
+    //    baseMethod->clazz->descriptor, baseMethod->name);
+
+    return true;
+}
+
+/*
+ * Rewrite invoke-direct, which has the form:
+ *   op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * There isn't a lot we can do to make this faster, but in some situations
+ * we can make it go away entirely.
+ *
+ * This must only be used when the invoked method does nothing and has
+ * no return value (the latter being very important for verification).
+ */
+static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
+{
+    ClassObject* clazz = method->clazz;
+    Method* calledMethod;
+    u2 methodIdx = insns[1];
+
+    calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
+    if (calledMethod == NULL) {
+        LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
+            methodIdx,
+            (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return false;
+    }
+
+    /* TODO: verify that java.lang.Object() is actually empty! */
+    if (calledMethod->clazz == gDvm.classJavaLangObject &&
+        dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
+    {
+        /*
+         * Replace with "empty" instruction.  DO NOT disturb anything
+         * else about it, as we want it to function the same as
+         * OP_INVOKE_DIRECT when debugging is enabled.
+         */
+        assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
+        updateCode(method, insns,
+            (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY);
+
+        //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
+        //    method->clazz->descriptor, method->name,
+        //    calledMethod->clazz->descriptor, calledMethod->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;
+    int i;
+
+    LOGVV("--- resolving interface method %d (referrer=%s)\n",
+        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 */
+            LOGI("Interface method not part of interface class\n");
+            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'\n",
+            methodName, methodSig, resClass->descriptor);
+        resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
+        if (resMethod == NULL) {
+            /* scan superinterfaces and superclass interfaces */
+            LOGVV("+++ did not resolve immediately\n");
+            for (i = 0; i < resClass->iftableCount; i++) {
+                resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
+                                methodName, &proto);
+                if (resMethod != NULL)
+                    break;
+            }
+
+            if (resMethod == NULL) {
+                LOGVV("+++ unable to resolve method %s\n", methodName);
+                return NULL;
+            }
+        } else {
+            LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
+                resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
+        }
+
+        /* we're expecting this to be abstract */
+        if (!dvmIsAbstractMethod(resMethod)) {
+            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+            LOGW("Found non-abstract interface method %s.%s %s\n",
+                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)\n",
+        methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+    /* interface methods are always public; no need to check access */
+
+    return resMethod;
+}
+
+/*
+ * See if the method being called can be rewritten as an inline operation.
+ * Works for invoke-virtual, invoke-direct, and invoke-static.
+ *
+ * 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) {
+        LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
+        return false;
+    }
+
+    while (inlineSubs->method != NULL) {
+        /*
+        if (extra) {
+            LOGI("comparing %p vs %p %s.%s %s\n",
+                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);
+            updateCode(method, insns,
+                (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE);
+            updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
+
+            //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
+            //    method->clazz->descriptor, method->name,
+            //    calledMethod->clazz->descriptor, calledMethod->name);
+            return true;
+        }
+
+        inlineSubs++;
+    }
+
+    return false;
+}
+
+/*
+ * See if the method being called can be rewritten as an inline operation.
+ * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
+ *
+ * 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) {
+        LOGV("+++ DexOpt inline/range: can't find %d\n", 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);
+            updateCode(method, insns,
+                (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE);
+            updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
+
+            //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
+            //    method->clazz->descriptor, method->name,
+            //    calledMethod->clazz->descriptor, calledMethod->name);
+            return true;
+        }
+
+        inlineSubs++;
+    }
+
+    return false;
+}
diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h
new file mode 100644
index 0000000..30f7eef
--- /dev/null
+++ b/vm/analysis/Optimize.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.
+ */
+
+/*
+ * Bytecode optimization declarations.
+ */
+#ifndef _DALVIK_OPTIMIZE
+#define _DALVIK_OPTIMIZE
+
+/*
+ * Prep data structures.
+ */
+InlineSub* dvmCreateInlineSubsTable(void);
+void dvmFreeInlineSubsTable(InlineSub* inlineSubs);
+
+/*
+ * Entry point from DEX preparation.
+ */
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly);
+
+/*
+ * 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*/
diff --git a/vm/analysis/RegisterMap.c b/vm/analysis/RegisterMap.c
new file mode 100644
index 0000000..f7d92cd
--- /dev/null
+++ b/vm/analysis/RegisterMap.c
@@ -0,0 +1,3272 @@
+/*
+ * 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 "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
+typedef 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;
+} MapStats;
+#endif
+
+/*
+ * Prepare some things.
+ */
+bool dvmRegisterMapStartup(void)
+{
+#ifdef REGISTER_MAP_STATS
+    MapStats* pStats = calloc(1, sizeof(MapStats));
+    gDvm.registerMapStats = pStats;
+#endif
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmRegisterMapShutdown(void)
+{
+#ifdef REGISTER_MAP_STATS
+    free(gDvm.registerMapStats);
+#endif
+}
+
+/*
+ * Write stats to log file.
+ */
+void dvmRegisterMapDumpStats(void)
+{
+#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;
+    }
+
+    LOGI("Register Map gcPointGap stats (diff count=%d, total=%d):\n",
+        pStats->gcGapCount, pStats->totalGcPointCount);
+    assert(pStats->gcPointGap[0] == 0);
+    for (i = 1; i <= end; i++) {
+        LOGI(" %2d %d\n", i, pStats->gcPointGap[i]);
+    }
+
+
+    for (end = kMaxDiffBits-1; end >= 0; end--) {
+        if (pStats->numDiffBits[end] != 0)
+            break;
+    }
+
+    LOGI("Register Map bit difference stats:\n");
+    for (i = 0; i <= end; i++) {
+        LOGI(" %2d %d\n", i, pStats->numDiffBits[i]);
+    }
+
+
+    LOGI("Register Map update position stats (lt16=%d ge16=%d):\n",
+        pStats->updateLT16, pStats->updateGE16);
+    for (i = 0; i < kNumUpdatePosns; i++) {
+        LOGI(" %2d %d\n", 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) {
+        LOGE("ERROR: register map can't handle %d registers\n",
+            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 */
+        LOGE("ERROR: register map can't handle %d gc points in one method\n",
+            gcPointCount);
+        goto bail;
+    }
+
+    /*
+     * Allocate a buffer to hold the map data.
+     */
+    bufSize = kHeaderSize + gcPointCount * (bytesForAddr + regWidth);
+
+    LOGV("+++ grm: %s.%s (adr=%d gpc=%d rwd=%d bsz=%d)\n",
+        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->addrRegs[i] != NULL);
+            if (format == kRegMapFormatCompact8) {
+                *mapData++ = i;
+            } else /*kRegMapFormatCompact16*/ {
+                *mapData++ = i & 0xff;
+                *mapData++ = i >> 8;
+            }
+            outputTypeVector(vdata->addrRegs[i], vdata->insnRegCount, mapData);
+            mapData += regWidth;
+        }
+    }
+
+    LOGV("mapData=%p pMap=%p bufSize=%d\n", 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) {
+                LOGE("Map failed to uncompress - %s.%s\n",
+                    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) {
+                    LOGE("Map comparison failed - %s.%s\n",
+                        vdata->method->clazz->descriptor,
+                        vdata->method->name);
+                    free(pCompMap);
+                    /* bad - compression is broken */
+                    dvmAbort();
+                }
+
+                /* verify succeeded */
+                free(pUncompMap);
+            }
+        }
+
+        if (REGISTER_MAP_VERBOSE) {
+            LOGD("Good compress on %s.%s\n",
+                vdata->method->clazz->descriptor,
+                vdata->method->name);
+        }
+        free(pMap);
+        pMap = pCompMap;
+    } else {
+        if (REGISTER_MAP_VERBOSE) {
+            LOGD("Unable to compress %s.%s (ent=%d rw=%d)\n",
+                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 */
+        LOGE("Can only dump Compact8 / Compact16 maps (not %d)\n", 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';
+
+        LOGD("  %04x %s %s\n", 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);
+            LOGI("Map for %s.%s %s\n", vdata->method->clazz->descriptor,
+                vdata->method->name, desc);
+            free(desc);
+
+            dumpMap = true;
+        }
+    }
+
+    if ((vdata->method->registersSize + 7) / 8 != pMap->regWidth) {
+        LOGE("GLITCH: registersSize=%d, regWidth=%d\n",
+            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 */
+            LOGE("GLITCH: bad format (%d)", format);
+            dvmAbort();
+        }
+
+        const RegType* regs = vdata->addrRegs[addr];
+        if (regs == NULL) {
+            LOGE("GLITCH: addr %d has no data\n", 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) {
+                LOGE("GLITCH: addr %d reg %d: bit=%d reg=%d(%d)\n",
+                    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:
+        LOGE("Bad register map format %d\n", 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)) {
+            LOGW("Warning: no map available for %s.%s\n",
+                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) {
+        LOGE("Too many methods in %s\n", 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)\n",
+                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\n", clazz->descriptor,
+                clazz->directMethodCount, clazz->virtualMethodCount,
+                (ptr - basePtr) - offsetTable[idx]);
+        } else {
+            LOGV("%4d NOT mapadding '%s'\n", idx, classDescriptor);
+            assert(offsetTable[idx] == 0);
+        }
+    }
+
+    if (ptr - basePtr >= (int)length) {
+        /* a bit late */
+        LOGE("Buffer overrun\n");
+        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;
+    }
+
+    LOGV("TOTAL size of register maps: %d\n", 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) {
+        LOGE("bad class index (%d vs %d)\n", classIdx, pClassPool->numClasses);
+        dvmAbort();
+    }
+
+    u4 classOffset = pClassPool->classDataOffset[classIdx];
+    if (classOffset == 0) {
+        LOGV("+++ no map for classIdx=%d\n", 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 = *pPtr;
+
+    *pPtr = /*align32*/(((u1*) pMap) + computeRegisterMapSize(pMap));
+    LOGVV("getNext: %p -> %p (f=0x%x w=%d e=%d)\n",
+        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:
+        LOGE("Unknown format %d\n", 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) {
+        LOGI("compareMaps: size mismatch (%zd vs %zd)\n", size1, size2);
+        return -1;
+    }
+
+    if (memcmp(pMap1, pMap2, size1) != 0) {
+        LOGI("compareMaps: content mismatch\n");
+        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) {
+            LOGE("GLITCH: dvmGetExpandedRegisterMap not called at GC time\n");
+            dvmAbort();
+        }
+    }
+
+    RegisterMapFormat format = dvmRegisterMapGetFormat(curMap);
+    switch (format) {
+    case kRegMapFormatCompact8:
+    case kRegMapFormatCompact16:
+        if (REGISTER_MAP_VERBOSE) {
+            if (dvmRegisterMapGetOnHeap(curMap)) {
+                LOGD("RegMap: already expanded: %s.%s\n",
+                    method->clazz->descriptor, method->name);
+            } else {
+                LOGD("RegMap: stored w/o compression: %s.%s\n",
+                    method->clazz->descriptor, method->name);
+            }
+        }
+        return curMap;
+    case kRegMapFormatDifferential:
+        newMap = uncompressMapDifferential(curMap);
+        break;
+    default:
+        LOGE("Unknown format %d in dvmGetExpandedRegisterMap\n", format);
+        dvmAbort();
+        newMap = NULL;      // make gcc happy
+    }
+
+    if (newMap == NULL) {
+        LOGE("Map failed to uncompress (fmt=%d) %s.%s\n",
+            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);
+        LOGD("RMAP: count=%d size=%d\n",
+            pStats->numExpandedMaps, pStats->totalExpandedMapSize);
+    }
+#endif
+
+    IF_LOGV() {
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        LOGV("Expanding map -> %s.%s:%s\n",
+            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 */
+            LOGE("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) {
+                LOGE("GLITCH: address went backward (0x%04x->0x%04x, %s.%s)\n",
+                    prevAddr, addr, method->clazz->descriptor, method->name);
+            } else if (addrDiff > kMaxGcPointGap) {
+                if (REGISTER_MAP_VERBOSE) {
+                    LOGI("HEY: addrDiff is %d, max %d (0x%04x->0x%04x %s.%s)\n",
+                        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 */
+                            LOGE("WEIRD: bit=%d (%d/%d), prev=%02x cur=%02x\n",
+                                bit, regByte, method->registersSize,
+                                prev, cur);
+                            assert(false);
+                        }
+                        int idx = (int) (bitNum * div);
+                        if (!(idx >= 0 && idx < kNumUpdatePosns)) {
+                            LOGE("FAIL: bitNum=%d (of %d) div=%.3f idx=%d\n",
+                                bitNum, method->registersSize, div, idx);
+                            assert(false);
+                        }
+                        pStats->updatePosn[idx]++;
+                    }
+                }
+            }
+
+            if (numDiff > kMaxDiffBits) {
+                if (REGISTER_MAP_VERBOSE) {
+                    LOGI("WOW: numDiff is %d, max %d\n", 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* tmpBuf = NULL;
+    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:
+        LOGE("ERROR: can't compress map with format=%d\n", format);
+        goto bail;
+    }
+
+    regWidth = dvmRegisterMapGetRegWidth(pMap);
+    numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+    if (debug) {
+        LOGI("COMPRESS: %s.%s aw=%d rw=%d ne=%d\n",
+            meth->clazz->descriptor, meth->name,
+            addrWidth, regWidth, numEntries);
+        dumpRegisterMap(pMap, -1);
+    }
+
+    if (numEntries <= 1) {
+        LOGV("Can't compress map with 0 or 1 entries\n");
+        goto bail;
+    }
+
+    /*
+     * 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.
+     */
+    tmpBuf = (u1*) malloc(origSize + (1 + 3 + regWidth));
+    if (tmpBuf == NULL)
+        goto bail;
+
+    tmpPtr = tmpBuf;
+
+    const u1* mapData = pMap->data;
+    const u1* prevBits;
+    u2 addr, prevAddr;
+
+    addr = *mapData++;
+    if (addrWidth > 1)
+        addr |= (*mapData++) << 8;
+
+    if (addr >= 128) {
+        LOGV("Can't compress map with starting address >= 128\n");
+        goto bail;
+    }
+
+    /*
+     * 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.
+     */
+    int entry;
+    for (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)
+            LOGI(" addr=0x%04x ent=%d (aw=%d)\n", 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)
+                LOGI(" : small %d, key=0x%02x\n", addrDiff, key);
+        } else {
+            /* large difference, output escape code */
+            key = 0x07;                 /* escape code for AAA */
+            if (debug)
+                LOGI(" : large %d, key=0x%02x\n", addrDiff, key);
+        }
+
+        int numBitsChanged, firstBitChanged, lebSize;
+
+        lebSize = computeBitDiff(prevBits, mapData, regWidth,
+            &firstBitChanged, &numBitsChanged, NULL);
+
+        if (debug) {
+            LOGI(" : diff fbc=%d nbc=%d ls=%d (rw=%d)\n",
+                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) LOGI(" : no bits changed\n");
+        } else if (numBitsChanged == 1 && firstBitChanged < 16) {
+            /* set B to 0 and CCCC to the index of the changed bit */
+            key |= firstBitChanged << 4;
+            if (debug) LOGI(" : 1 low bit changed\n");
+        } else if (numBitsChanged < 15 && lebSize < regWidth) {
+            /* set B to 1 and CCCC to the number of bits */
+            key |= 0x08 | (numBitsChanged << 4);
+            if (debug) LOGI(" : some bits changed\n");
+        } else {
+            /* set B to 1 and CCCC to 0x0f so we store the entire vector */
+            key |= 0x08 | 0xf0;
+            if (debug) LOGI(" : encode original\n");
+        }
+
+        /*
+         * 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 >= origSize) {
+            if (debug) {
+                LOGD("Compressed size >= original (%d vs %d): %s.%s\n",
+                    tmpPtr - tmpBuf, origSize,
+                    meth->clazz->descriptor, meth->name);
+            }
+            goto bail;
+        }
+    }
+
+    /*
+     * 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;
+    int newMapSize;
+
+    newMapSize = kHeaderSize + unsignedLeb128Size(newDataSize) + newDataSize;
+    if (newMapSize >= origSize) {
+        if (debug) {
+            LOGD("Final comp size >= original (%d vs %d): %s.%s\n",
+                newMapSize, origSize, meth->clazz->descriptor, meth->name);
+        }
+        goto bail;
+    }
+
+    pNewMap = (RegisterMap*) malloc(newMapSize);
+    if (pNewMap == NULL)
+        goto bail;
+    dvmRegisterMapSetFormat(pNewMap, kRegMapFormatDifferential);
+    dvmRegisterMapSetOnHeap(pNewMap, true);
+    dvmRegisterMapSetRegWidth(pNewMap, regWidth);
+    dvmRegisterMapSetNumEntries(pNewMap, numEntries);
+
+    tmpPtr = pNewMap->data;
+    tmpPtr = writeUnsignedLeb128(tmpPtr, newDataSize);
+    memcpy(tmpPtr, tmpBuf, newDataSize);
+
+    if (REGISTER_MAP_VERBOSE) {
+        LOGD("Compression successful (%d -> %d) from aw=%d rw=%d ne=%d\n",
+            computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap),
+            addrWidth, regWidth, numEntries);
+    }
+
+bail:
+    free(tmpBuf);
+    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)
+{
+    RegisterMap* pNewMap = NULL;
+    static const int kHeaderSize = offsetof(RegisterMap, data);
+    u1 format = dvmRegisterMapGetFormat(pMap);
+    RegisterMapFormat newFormat;
+    int regWidth, numEntries, newAddrWidth, newMapSize;
+
+    if (format != kRegMapFormatDifferential) {
+        LOGE("Not differential (%d)\n", format);
+        goto bail;
+    }
+
+    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) {
+        LOGI("Expanding to map aw=%d rw=%d ne=%d\n",
+            newAddrWidth, regWidth, numEntries);
+    }
+    newMapSize = kHeaderSize + (newAddrWidth + regWidth) * numEntries;
+    pNewMap = (RegisterMap*) malloc(newMapSize);
+    if (pNewMap == NULL)
+        goto bail;
+
+    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) {
+        LOGE("ERROR: output %d bytes, expected %d\n",
+            dstPtr - (u1*) pNewMap, newMapSize);
+        goto bail;
+    }
+
+    if (srcPtr - srcStart != expectedSrcLen) {
+        LOGE("ERROR: consumed %d bytes, expected %d\n",
+            srcPtr - srcStart, expectedSrcLen);
+        goto bail;
+    }
+
+    if (REGISTER_MAP_VERBOSE) {
+        LOGD("Expansion successful (%d -> %d)\n",
+            computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap));
+    }
+
+    return pNewMap;
+
+bail:
+    free(pNewMap);
+    return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ *      Just-in-time generation
+ * ===========================================================================
+ */
+
+#if 0   /* incomplete implementation; may be removed entirely in the future */
+
+/*
+Notes on just-in-time RegisterMap generation
+
+Generating RegisterMap tables as part of verification is convenient because
+we generate most of what we need to know as part of doing the verify.
+The negative aspect of doing it this way is that we must store the
+result in the DEX file (if we're verifying ahead of time) or in memory
+(if verifying during class load) for every concrete non-native method,
+even if we never actually need the map during a GC.
+
+A simple but compact encoding of register map data increases the size of
+optimized DEX files by about 25%, so size considerations are important.
+
+We can instead generate the RegisterMap at the point where it is needed.
+In a typical application we only need to convert about 2% of the loaded
+methods, and we can generate type-precise roots reasonably quickly because
+(a) we know the method has already been verified and hence can make a
+lot of assumptions, and (b) we don't care what type of object a register
+holds, just whether or not it holds a reference, and hence can skip a
+lot of class resolution gymnastics.
+
+There are a couple of problems with this approach however.  First, to
+get good performance we really want an implementation that is largely
+independent from the verifier, which means some duplication of effort.
+Second, we're dealing with post-dexopt code, which contains "quickened"
+instructions.  We can't process those without either tracking type
+information (which slows us down) or storing additional data in the DEX
+file that allows us to reconstruct the original instructions (adds ~5%
+to the size of the ODEX).
+
+
+Implementation notes...
+
+Both type-precise and live-precise information can be generated knowing
+only whether or not a register holds a reference.  We don't need to
+know what kind of reference or whether the object has been initialized.
+Not only can we skip many of the fancy steps in the verifier, we can
+initialize from simpler sources, e.g. the initial registers and return
+type are set from the "shorty" signature rather than the full signature.
+
+The short-term storage needs for just-in-time register map generation can
+be much lower because we can use a 1-byte SRegType instead of a 4-byte
+RegType.  On the other hand, if we're not doing type-precise analysis
+in the verifier we only need to store register contents at every branch
+target, rather than every GC point (which are much more frequent).
+
+Whether it happens in the verifier or independently, because this is done
+with native heap allocations that may be difficult to return to the system,
+an effort should be made to minimize memory use.
+*/
+
+/*
+ * This is like RegType in the verifier, but simplified.  It holds a value
+ * from the reg type enum, or kRegTypeReference.
+ */
+typedef u1 SRegType;
+#define kRegTypeReference kRegTypeMAX
+
+/*
+ * We need an extra "pseudo register" to hold the return type briefly.  It
+ * can be category 1 or 2, so we need two slots.
+ */
+#define kExtraRegs  2
+#define RESULT_REGISTER(_insnRegCountPlus)  (_insnRegCountPlus - kExtraRegs)
+
+/*
+ * Working state.
+ */
+typedef struct WorkState {
+    /*
+     * The method we're working on.
+     */
+    const Method* method;
+
+    /*
+     * Number of instructions in the method.
+     */
+    int         insnsSize;
+
+    /*
+     * Number of registers we track for each instruction.  This is equal
+     * to the method's declared "registersSize" plus kExtraRegs.
+     */
+    int         insnRegCountPlus;
+
+    /*
+     * Instruction widths and flags, one entry per code unit.
+     */
+    InsnFlags*  insnFlags;
+
+    /*
+     * Array of SRegType arrays, one entry per code unit.  We only need
+     * to create an entry when an instruction starts at this address.
+     * We can further reduce this to instructions that are GC points.
+     *
+     * We could just go ahead and allocate one per code unit, but for
+     * larger methods that can represent a significant bit of short-term
+     * storage.
+     */
+    SRegType**  addrRegs;
+
+    /*
+     * A single large alloc, with all of the storage needed for addrRegs.
+     */
+    SRegType*   regAlloc;
+} WorkState;
+
+// fwd
+static bool generateMap(WorkState* pState, RegisterMap* pMap);
+static bool analyzeMethod(WorkState* pState);
+static bool handleInstruction(WorkState* pState, SRegType* workRegs,\
+    int insnIdx, int* pStartGuess);
+static void updateRegisters(WorkState* pState, int nextInsn,\
+    const SRegType* workRegs);
+
+
+/*
+ * Set instruction flags.
+ */
+static bool setInsnFlags(WorkState* pState, int* pGcPointCount)
+{
+    const Method* meth = pState->method;
+    InsnFlags* insnFlags = pState->insnFlags;
+    int insnsSize = pState->insnsSize;
+    const u2* insns = meth->insns;
+    int gcPointCount = 0;
+    int offset;
+
+    /* set the widths */
+    if (!dvmComputeCodeWidths(meth, pState->insnFlags, NULL))
+        return false;
+
+    /* mark "try" regions and exception handler branch targets */
+    if (!dvmSetTryFlags(meth, pState->insnFlags))
+        return false;
+
+    /* the start of the method is a "branch target" */
+    dvmInsnSetBranchTarget(insnFlags, 0, true);
+
+    /*
+     * Run through the instructions, looking for switches and branches.
+     * Mark their targets.
+     *
+     * We don't really need to "check" these instructions -- the verifier
+     * already did that -- but the additional overhead isn't significant
+     * enough to warrant making a second copy of the "Check" function.
+     *
+     * Mark and count GC points while we're at it.
+     */
+    for (offset = 0; offset < insnsSize; offset++) {
+        static int gcMask = kInstrCanBranch | kInstrCanSwitch |
+            kInstrCanThrow | kInstrCanReturn;
+        u1 opcode = insns[offset] & 0xff;
+        InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode);
+
+        if (opFlags & kInstrCanBranch) {
+            if (!dvmCheckBranchTarget(meth, insnFlags, offset, true))
+                return false;
+        }
+        if (opFlags & kInstrCanSwitch) {
+            if (!dvmCheckSwitchTargets(meth, insnFlags, offset))
+                return false;
+        }
+
+        if ((opFlags & gcMask) != 0) {
+            dvmInsnSetGcPoint(pState->insnFlags, offset, true);
+            gcPointCount++;
+        }
+    }
+
+    *pGcPointCount = gcPointCount;
+    return true;
+}
+
+/*
+ * Generate the register map for a method.
+ *
+ * Returns a pointer to newly-allocated storage.
+ */
+RegisterMap* dvmGenerateRegisterMap(const Method* meth)
+{
+    WorkState* pState = NULL;
+    RegisterMap* pMap = NULL;
+    RegisterMap* result = NULL;
+    SRegType* regPtr;
+
+    pState = (WorkState*) calloc(1, sizeof(WorkState));
+    if (pState == NULL)
+        goto bail;
+
+    pMap = (RegisterMap*) calloc(1, sizeof(RegisterMap));
+    if (pMap == NULL)
+        goto bail;
+
+    pState->method = meth;
+    pState->insnsSize = dvmGetMethodInsnsSize(meth);
+    pState->insnRegCountPlus = meth->registersSize + kExtraRegs;
+
+    pState->insnFlags = calloc(sizeof(InsnFlags), pState->insnsSize);
+    pState->addrRegs = calloc(sizeof(SRegType*), pState->insnsSize);
+
+    /*
+     * Set flags on instructions, and calculate the number of code units
+     * that happen to be GC points.
+     */
+    int gcPointCount;
+    if (!setInsnFlags(pState, &gcPointCount))
+        goto bail;
+
+    if (gcPointCount == 0) {
+        /* the method doesn't allocate or call, and never returns? unlikely */
+        LOG_VFY_METH(meth, "Found do-nothing method\n");
+        goto bail;
+    }
+
+    pState->regAlloc = (SRegType*)
+        calloc(sizeof(SRegType), pState->insnsSize * gcPointCount);
+    regPtr = pState->regAlloc;
+
+    /*
+     * For each instruction that is a GC point, set a pointer into the
+     * regAlloc buffer.
+     */
+    int offset;
+    for (offset = 0; offset < pState->insnsSize; offset++) {
+        if (dvmInsnIsGcPoint(pState->insnFlags, offset)) {
+            pState->addrRegs[offset] = regPtr;
+            regPtr += pState->insnRegCountPlus;
+        }
+    }
+    assert(regPtr - pState->regAlloc == pState->insnsSize * gcPointCount);
+    assert(pState->addrRegs[0] != NULL);
+
+    /*
+     * Compute the register map.
+     */
+    if (!generateMap(pState, pMap))
+        goto bail;
+
+    /* success */
+    result = pMap;
+    pMap = NULL;
+
+bail:
+    if (pState != NULL) {
+        free(pState->insnFlags);
+        free(pState->addrRegs);
+        free(pState->regAlloc);
+        free(pState);
+    }
+    if (pMap != NULL)
+        dvmFreeRegisterMap(pMap);
+    return result;
+}
+
+/*
+ * Release the storage associated with a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap)
+{
+    if (pMap == NULL)
+        return;
+}
+
+
+/*
+ * Create the RegisterMap using the provided state.
+ */
+static bool generateMap(WorkState* pState, RegisterMap* pMap)
+{
+    bool result = false;
+
+    /*
+     * Analyze the method and store the results in WorkState.
+     */
+    if (!analyzeMethod(pState))
+        goto bail;
+
+    /*
+     * Convert the analyzed data into a RegisterMap.
+     */
+    // TODO
+
+    result = true;
+
+bail:
+    return result;
+}
+
+/*
+ * Set the register types for the method arguments.  We can pull the values
+ * out of the "shorty" signature.
+ */
+static bool setTypesFromSignature(WorkState* pState)
+{
+    const Method* meth = pState->method;
+    int argReg = meth->registersSize - meth->insSize;   /* first arg */
+    SRegType* pRegs = pState->addrRegs[0];
+    SRegType* pCurReg = &pRegs[argReg];
+    const char* ccp;
+
+    /*
+     * Include "this" pointer, if appropriate.
+     */
+    if (!dvmIsStaticMethod(meth)) {
+        *pCurReg++ = kRegTypeReference;
+    }
+
+    ccp = meth->shorty +1;      /* skip first byte, which holds return type */
+    while (*ccp != 0) {
+        switch (*ccp) {
+        case 'L':
+        //case '[':
+            *pCurReg++ = kRegTypeReference;
+            break;
+        case 'Z':
+            *pCurReg++ = kRegTypeBoolean;
+            break;
+        case 'C':
+            *pCurReg++ = kRegTypeChar;
+            break;
+        case 'B':
+            *pCurReg++ = kRegTypeByte;
+            break;
+        case 'I':
+            *pCurReg++ = kRegTypeInteger;
+            break;
+        case 'S':
+            *pCurReg++ = kRegTypeShort;
+            break;
+        case 'F':
+            *pCurReg++ = kRegTypeFloat;
+            break;
+        case 'D':
+            *pCurReg++ = kRegTypeDoubleLo;
+            *pCurReg++ = kRegTypeDoubleHi;
+            break;
+        case 'J':
+            *pCurReg++ = kRegTypeLongLo;
+            *pCurReg++ = kRegTypeLongHi;
+            break;
+        default:
+            assert(false);
+            return false;
+        }
+    }
+
+    assert(pCurReg - pRegs == meth->insSize);
+    return true;
+}
+
+/*
+ * Find the start of the register set for the specified instruction in
+ * the current method.
+ */
+static inline SRegType* getRegisterLine(const WorkState* pState, int insnIdx)
+{
+    return pState->addrRegs[insnIdx];
+}
+
+/*
+ * Copy a set of registers.
+ */
+static inline void copyRegisters(SRegType* dst, const SRegType* src,
+    int numRegs)
+{
+    memcpy(dst, src, numRegs * sizeof(SRegType));
+}
+
+/*
+ * Compare a set of registers.  Returns 0 if they match.
+ */
+static inline int compareRegisters(const SRegType* src1, const SRegType* src2,
+    int numRegs)
+{
+    return memcmp(src1, src2, numRegs * sizeof(SRegType));
+}
+
+/*
+ * Run through the instructions repeatedly until we have exercised all
+ * possible paths.
+ */
+static bool analyzeMethod(WorkState* pState)
+{
+    const Method* meth = pState->method;
+    SRegType workRegs[pState->insnRegCountPlus];
+    InsnFlags* insnFlags = pState->insnFlags;
+    int insnsSize = pState->insnsSize;
+    int insnIdx, startGuess;
+    bool result = false;
+
+    /*
+     * Initialize the types of the registers that correspond to method
+     * arguments.
+     */
+    if (!setTypesFromSignature(pState))
+        goto bail;
+
+    /*
+     * Mark the first instruction as "changed".
+     */
+    dvmInsnSetChanged(insnFlags, 0, true);
+    startGuess = 0;
+
+    if (true) {
+        IF_LOGI() {
+            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+            LOGI("Now mapping: %s.%s %s (ins=%d regs=%d)\n",
+                meth->clazz->descriptor, meth->name, desc,
+                meth->insSize, meth->registersSize);
+            LOGI(" ------ [0    4    8    12   16   20   24   28   32   36\n");
+            free(desc);
+        }
+    }
+
+    /*
+     * Continue until no instructions are marked "changed".
+     */
+    while (true) {
+        /*
+         * Find the first marked one.  Use "startGuess" as a way to find
+         * one quickly.
+         */
+        for (insnIdx = startGuess; insnIdx < insnsSize; insnIdx++) {
+            if (dvmInsnIsChanged(insnFlags, insnIdx))
+                break;
+        }
+
+        if (insnIdx == insnsSize) {
+            if (startGuess != 0) {
+                /* try again, starting from the top */
+                startGuess = 0;
+                continue;
+            } else {
+                /* all flags are clear */
+                break;
+            }
+        }
+
+        /*
+         * We carry the working set of registers from instruction to
+         * instruction.  If this address can be the target of a branch
+         * (or throw) instruction, or if we're skipping around chasing
+         * "changed" flags, we need to load the set of registers from
+         * the table.
+         *
+         * Because we always prefer to continue on to the next instruction,
+         * we should never have a situation where we have a stray
+         * "changed" flag set on an instruction that isn't a branch target.
+         */
+        if (dvmInsnIsBranchTarget(insnFlags, insnIdx)) {
+            SRegType* insnRegs = getRegisterLine(pState, insnIdx);
+            assert(insnRegs != NULL);
+            copyRegisters(workRegs, insnRegs, pState->insnRegCountPlus);
+
+        } else {
+#ifndef NDEBUG
+            /*
+             * Sanity check: retrieve the stored register line (assuming
+             * a full table) and make sure it actually matches.
+             */
+            SRegType* insnRegs = getRegisterLine(pState, insnIdx);
+            if (insnRegs != NULL &&
+                compareRegisters(workRegs, insnRegs,
+                                 pState->insnRegCountPlus) != 0)
+            {
+                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+                LOG_VFY("HUH? workRegs diverged in %s.%s %s\n",
+                        meth->clazz->descriptor, meth->name, desc);
+                free(desc);
+            }
+#endif
+        }
+
+        /*
+         * Update the register sets altered by this instruction.
+         */
+        if (!handleInstruction(pState, workRegs, insnIdx, &startGuess)) {
+            goto bail;
+        }
+
+        dvmInsnSetVisited(insnFlags, insnIdx, true);
+        dvmInsnSetChanged(insnFlags, insnIdx, false);
+    }
+
+    // TODO - add dead code scan to help validate this code?
+
+    result = true;
+
+bail:
+    return result;
+}
+
+/*
+ * Get a pointer to the method being invoked.
+ *
+ * Returns NULL on failure.
+ */
+static Method* getInvokedMethod(const Method* meth,
+    const DecodedInstruction* pDecInsn, MethodType methodType)
+{
+    Method* resMethod;
+    char* sigOriginal = NULL;
+
+    /*
+     * Resolve the method.  This could be an abstract or concrete method
+     * depending on what sort of call we're making.
+     */
+    if (methodType == METHOD_INTERFACE) {
+        resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB);
+    } else {
+        resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType);
+    }
+    if (resMethod == NULL) {
+        /* failed; print a meaningful failure message */
+        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+        const DexMethodId* pMethodId;
+        const char* methodName;
+        char* methodDesc;
+        const char* classDescriptor;
+
+        pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB);
+        methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+        methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+        classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+        LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s\n",
+            dvmMethodTypeStr(methodType), pDecInsn->vB,
+            classDescriptor, methodName, methodDesc);
+        free(methodDesc);
+        return NULL;
+    }
+
+    return resMethod;
+}
+
+/*
+ * Return the register type for the method.  Since we don't care about
+ * the actual type, we can just look at the "shorty" signature.
+ *
+ * Returns kRegTypeUnknown for "void".
+ */
+static SRegType getMethodReturnType(const Method* meth)
+{
+    SRegType type;
+
+    switch (meth->shorty[0]) {
+    case 'I':
+        type = kRegTypeInteger;
+        break;
+    case 'C':
+        type = kRegTypeChar;
+        break;
+    case 'S':
+        type = kRegTypeShort;
+        break;
+    case 'B':
+        type = kRegTypeByte;
+        break;
+    case 'Z':
+        type = kRegTypeBoolean;
+        break;
+    case 'V':
+        type = kRegTypeUnknown;
+        break;
+    case 'F':
+        type = kRegTypeFloat;
+        break;
+    case 'D':
+        type = kRegTypeDoubleLo;
+        break;
+    case 'J':
+        type = kRegTypeLongLo;
+        break;
+    case 'L':
+    //case '[':
+        type = kRegTypeReference;
+        break;
+    default:
+        /* we verified signature return type earlier, so this is impossible */
+        assert(false);
+        type = kRegTypeConflict;
+        break;
+    }
+
+    return type;
+}
+
+/*
+ * Copy a category 1 register.
+ */
+static inline void copyRegister1(SRegType* insnRegs, u4 vdst, u4 vsrc)
+{
+    insnRegs[vdst] = insnRegs[vsrc];
+}
+
+/*
+ * Copy a category 2 register.  Note the source and destination may overlap.
+ */
+static inline void copyRegister2(SRegType* insnRegs, u4 vdst, u4 vsrc)
+{
+    //memmove(&insnRegs[vdst], &insnRegs[vsrc], sizeof(SRegType) * 2);
+    SRegType r1 = insnRegs[vsrc];
+    SRegType r2 = insnRegs[vsrc+1];
+    insnRegs[vdst] = r1;
+    insnRegs[vdst+1] = r2;
+}
+
+/*
+ * Set the type of a category 1 register.
+ */
+static inline void setRegisterType(SRegType* insnRegs, u4 vdst, SRegType type)
+{
+    insnRegs[vdst] = type;
+}
+
+/*
+ * Decode the specified instruction and update the register info.
+ */
+static bool handleInstruction(WorkState* pState, SRegType* workRegs,
+    int insnIdx, int* pStartGuess)
+{
+    const Method* meth = pState->method;
+    const u2* insns = meth->insns + insnIdx;
+    InsnFlags* insnFlags = pState->insnFlags;
+    bool result = false;
+
+    /*
+     * Once we finish decoding the instruction, we need to figure out where
+     * we can go from here.  There are three possible ways to transfer
+     * control to another statement:
+     *
+     * (1) Continue to the next instruction.  Applies to all but
+     *     unconditional branches, method returns, and exception throws.
+     * (2) Branch to one or more possible locations.  Applies to branches
+     *     and switch statements.
+     * (3) Exception handlers.  Applies to any instruction that can
+     *     throw an exception that is handled by an encompassing "try"
+     *     block.  (We simplify this to be any instruction that can
+     *     throw any exception.)
+     *
+     * We can also return, in which case there is no successor instruction
+     * from this point.
+     *
+     * The behavior can be determined from the InstrFlags.
+     */
+    DecodedInstruction decInsn;
+    SRegType entryRegs[pState->insnRegCountPlus];
+    const int insnRegCountPlus = pState->insnRegCountPlus;
+    bool justSetResult = false;
+    int branchTarget = 0;
+    SRegType tmpType;
+
+    dexDecodeInstruction(gDvm.instrFormat, insns, &decInsn);
+    const int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
+
+    /*
+     * Make a copy of the previous register state.  If the instruction
+     * throws an exception, we merge *this* into the destination rather
+     * than workRegs, because we don't want the result from the "successful"
+     * code path (e.g. a check-cast that "improves" a type) to be visible
+     * to the exception handler.
+     */
+    if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+    {
+        copyRegisters(entryRegs, workRegs, insnRegCountPlus);
+    }
+
+    switch (decInsn.opCode) {
+    case OP_NOP:
+        break;
+
+    case OP_MOVE:
+    case OP_MOVE_FROM16:
+    case OP_MOVE_16:
+    case OP_MOVE_OBJECT:
+    case OP_MOVE_OBJECT_FROM16:
+    case OP_MOVE_OBJECT_16:
+        copyRegister1(workRegs, decInsn.vA, decInsn.vB);
+        break;
+    case OP_MOVE_WIDE:
+    case OP_MOVE_WIDE_FROM16:
+    case OP_MOVE_WIDE_16:
+        copyRegister2(workRegs, decInsn.vA, decInsn.vB);
+        break;
+
+    /*
+     * The move-result instructions copy data out of a "pseudo-register"
+     * with the results from the last method invocation.  In practice we
+     * might want to hold the result in an actual CPU register, so the
+     * Dalvik spec requires that these only appear immediately after an
+     * invoke or filled-new-array.
+     *
+     * These calls invalidate the "result" register.  (This is now
+     * redundant with the reset done below, but it can make the debug info
+     * easier to read in some cases.)
+     */
+    case OP_MOVE_RESULT:
+    case OP_MOVE_RESULT_OBJECT:
+        copyRegister1(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus));
+        break;
+    case OP_MOVE_RESULT_WIDE:
+        copyRegister2(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus));
+        break;
+
+    case OP_MOVE_EXCEPTION:
+        /*
+         * This statement can only appear as the first instruction in an
+         * exception handler (though not all exception handlers need to
+         * have one of these).  We verify that as part of extracting the
+         * exception type from the catch block list.
+         */
+        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+        break;
+
+    case OP_RETURN_VOID:
+    case OP_RETURN:
+    case OP_RETURN_WIDE:
+    case OP_RETURN_OBJECT:
+        break;
+
+    case OP_CONST_4:
+    case OP_CONST_16:
+    case OP_CONST:
+        /* could be boolean, int, float, or a null reference */
+        setRegisterType(workRegs, decInsn.vA,
+            dvmDetermineCat1Const((s4)decInsn.vB));
+        break;
+    case OP_CONST_HIGH16:
+        /* could be boolean, int, float, or a null reference */
+        setRegisterType(workRegs, decInsn.vA,
+            dvmDetermineCat1Const((s4) decInsn.vB << 16));
+        break;
+    case OP_CONST_WIDE_16:
+    case OP_CONST_WIDE_32:
+    case OP_CONST_WIDE:
+    case OP_CONST_WIDE_HIGH16:
+        /* could be long or double; default to long and allow conversion */
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+    case OP_CONST_STRING:
+    case OP_CONST_STRING_JUMBO:
+    case OP_CONST_CLASS:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+        break;
+
+    case OP_MONITOR_ENTER:
+    case OP_MONITOR_EXIT:
+        break;
+
+    case OP_CHECK_CAST:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+        break;
+    case OP_INSTANCE_OF:
+        /* result is boolean */
+        setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
+        break;
+
+    case OP_ARRAY_LENGTH:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+        break;
+
+    case OP_NEW_INSTANCE:
+    case OP_NEW_ARRAY:
+        /* add the new uninitialized reference to the register ste */
+        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+        break;
+    case OP_FILLED_NEW_ARRAY:
+    case OP_FILLED_NEW_ARRAY_RANGE:
+        setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+            kRegTypeReference);
+        justSetResult = true;
+        break;
+
+    case OP_CMPL_FLOAT:
+    case OP_CMPG_FLOAT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
+        break;
+    case OP_CMPL_DOUBLE:
+    case OP_CMPG_DOUBLE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
+        break;
+    case OP_CMP_LONG:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
+        break;
+
+    case OP_THROW:
+    case OP_GOTO:
+    case OP_GOTO_16:
+    case OP_GOTO_32:
+    case OP_PACKED_SWITCH:
+    case OP_SPARSE_SWITCH:
+        break;
+
+    case OP_FILL_ARRAY_DATA:
+        break;
+
+    case OP_IF_EQ:
+    case OP_IF_NE:
+    case OP_IF_LT:
+    case OP_IF_GE:
+    case OP_IF_GT:
+    case OP_IF_LE:
+    case OP_IF_EQZ:
+    case OP_IF_NEZ:
+    case OP_IF_LTZ:
+    case OP_IF_GEZ:
+    case OP_IF_GTZ:
+    case OP_IF_LEZ:
+        break;
+
+    case OP_AGET:
+        tmpType = kRegTypeInteger;
+        goto aget_1nr_common;
+    case OP_AGET_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto aget_1nr_common;
+    case OP_AGET_BYTE:
+        tmpType = kRegTypeByte;
+        goto aget_1nr_common;
+    case OP_AGET_CHAR:
+        tmpType = kRegTypeChar;
+        goto aget_1nr_common;
+    case OP_AGET_SHORT:
+        tmpType = kRegTypeShort;
+        goto aget_1nr_common;
+aget_1nr_common:
+        setRegisterType(workRegs, decInsn.vA, tmpType);
+        break;
+
+    case OP_AGET_WIDE:
+        /*
+         * We know this is either long or double, and we don't really
+         * discriminate between those during verification, so we
+         * call it a long.
+         */
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+
+    case OP_AGET_OBJECT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+        break;
+
+    case OP_APUT:
+    case OP_APUT_BOOLEAN:
+    case OP_APUT_BYTE:
+    case OP_APUT_CHAR:
+    case OP_APUT_SHORT:
+    case OP_APUT_WIDE:
+    case OP_APUT_OBJECT:
+        break;
+
+    case OP_IGET:
+        tmpType = kRegTypeInteger;
+        goto iget_1nr_common;
+    case OP_IGET_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto iget_1nr_common;
+    case OP_IGET_BYTE:
+        tmpType = kRegTypeByte;
+        goto iget_1nr_common;
+    case OP_IGET_CHAR:
+        tmpType = kRegTypeChar;
+        goto iget_1nr_common;
+    case OP_IGET_SHORT:
+        tmpType = kRegTypeShort;
+        goto iget_1nr_common;
+iget_1nr_common:
+        setRegisterType(workRegs, decInsn.vA, tmpType);
+        break;
+
+    case OP_IGET_WIDE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+
+    case OP_IGET_OBJECT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+        break;
+
+    case OP_IPUT:
+    case OP_IPUT_BOOLEAN:
+    case OP_IPUT_BYTE:
+    case OP_IPUT_CHAR:
+    case OP_IPUT_SHORT:
+    case OP_IPUT_WIDE:
+    case OP_IPUT_OBJECT:
+        break;
+
+    case OP_SGET:
+        tmpType = kRegTypeInteger;
+        goto sget_1nr_common;
+    case OP_SGET_BOOLEAN:
+        tmpType = kRegTypeBoolean;
+        goto sget_1nr_common;
+    case OP_SGET_BYTE:
+        tmpType = kRegTypeByte;
+        goto sget_1nr_common;
+    case OP_SGET_CHAR:
+        tmpType = kRegTypeChar;
+        goto sget_1nr_common;
+    case OP_SGET_SHORT:
+        tmpType = kRegTypeShort;
+        goto sget_1nr_common;
+sget_1nr_common:
+        setRegisterType(workRegs, decInsn.vA, tmpType);
+        break;
+
+    case OP_SGET_WIDE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+
+    case OP_SGET_OBJECT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+        break;
+
+    case OP_SPUT:
+    case OP_SPUT_BOOLEAN:
+    case OP_SPUT_BYTE:
+    case OP_SPUT_CHAR:
+    case OP_SPUT_SHORT:
+    case OP_SPUT_WIDE:
+    case OP_SPUT_OBJECT:
+        break;
+
+    case OP_INVOKE_VIRTUAL:
+    case OP_INVOKE_VIRTUAL_RANGE:
+    case OP_INVOKE_SUPER:
+    case OP_INVOKE_SUPER_RANGE:
+        {
+            Method* calledMethod;
+
+            calledMethod = getInvokedMethod(meth, &decInsn, METHOD_VIRTUAL);
+            if (calledMethod == NULL)
+                goto bail;
+            setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+                getMethodReturnType(calledMethod));
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_DIRECT:
+    case OP_INVOKE_DIRECT_RANGE:
+        {
+            Method* calledMethod;
+
+            calledMethod = getInvokedMethod(meth, &decInsn, METHOD_DIRECT);
+            if (calledMethod == NULL)
+                goto bail;
+            setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+                getMethodReturnType(calledMethod));
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_STATIC:
+    case OP_INVOKE_STATIC_RANGE:
+        {
+            Method* calledMethod;
+
+            calledMethod = getInvokedMethod(meth, &decInsn, METHOD_STATIC);
+            if (calledMethod == NULL)
+                goto bail;
+            setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+                getMethodReturnType(calledMethod));
+            justSetResult = true;
+        }
+        break;
+    case OP_INVOKE_INTERFACE:
+    case OP_INVOKE_INTERFACE_RANGE:
+        {
+            Method* absMethod;
+
+            absMethod = getInvokedMethod(meth, &decInsn, METHOD_INTERFACE);
+            if (absMethod == NULL)
+                goto bail;
+            setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+                getMethodReturnType(absMethod));
+            justSetResult = true;
+        }
+        break;
+
+    case OP_NEG_INT:
+    case OP_NOT_INT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+        break;
+    case OP_NEG_LONG:
+    case OP_NOT_LONG:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+    case OP_NEG_FLOAT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+        break;
+    case OP_NEG_DOUBLE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+        break;
+    case OP_INT_TO_LONG:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+    case OP_INT_TO_FLOAT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+        break;
+    case OP_INT_TO_DOUBLE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+        break;
+    case OP_LONG_TO_INT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+        break;
+    case OP_LONG_TO_FLOAT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+        break;
+    case OP_LONG_TO_DOUBLE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+        break;
+    case OP_FLOAT_TO_INT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+        break;
+    case OP_FLOAT_TO_LONG:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+    case OP_FLOAT_TO_DOUBLE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+        break;
+    case OP_DOUBLE_TO_INT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+        break;
+    case OP_DOUBLE_TO_LONG:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+    case OP_DOUBLE_TO_FLOAT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+        break;
+    case OP_INT_TO_BYTE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeByte);
+        break;
+    case OP_INT_TO_CHAR:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeChar);
+        break;
+    case OP_INT_TO_SHORT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeShort);
+        break;
+
+    case OP_ADD_INT:
+    case OP_SUB_INT:
+    case OP_MUL_INT:
+    case OP_REM_INT:
+    case OP_DIV_INT:
+    case OP_SHL_INT:
+    case OP_SHR_INT:
+    case OP_USHR_INT:
+    case OP_AND_INT:
+    case OP_OR_INT:
+    case OP_XOR_INT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+        break;
+    case OP_ADD_LONG:
+    case OP_SUB_LONG:
+    case OP_MUL_LONG:
+    case OP_DIV_LONG:
+    case OP_REM_LONG:
+    case OP_AND_LONG:
+    case OP_OR_LONG:
+    case OP_XOR_LONG:
+    case OP_SHL_LONG:
+    case OP_SHR_LONG:
+    case OP_USHR_LONG:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+    case OP_ADD_FLOAT:
+    case OP_SUB_FLOAT:
+    case OP_MUL_FLOAT:
+    case OP_DIV_FLOAT:
+    case OP_REM_FLOAT:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+        break;
+    case OP_ADD_DOUBLE:
+    case OP_SUB_DOUBLE:
+    case OP_MUL_DOUBLE:
+    case OP_DIV_DOUBLE:
+    case OP_REM_DOUBLE:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+        break;
+    case OP_ADD_INT_2ADDR:
+    case OP_SUB_INT_2ADDR:
+    case OP_MUL_INT_2ADDR:
+    case OP_REM_INT_2ADDR:
+    case OP_SHL_INT_2ADDR:
+    case OP_SHR_INT_2ADDR:
+    case OP_USHR_INT_2ADDR:
+    case OP_AND_INT_2ADDR:
+    case OP_OR_INT_2ADDR:
+    case OP_XOR_INT_2ADDR:
+    case OP_DIV_INT_2ADDR:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+        break;
+    case OP_ADD_LONG_2ADDR:
+    case OP_SUB_LONG_2ADDR:
+    case OP_MUL_LONG_2ADDR:
+    case OP_DIV_LONG_2ADDR:
+    case OP_REM_LONG_2ADDR:
+    case OP_AND_LONG_2ADDR:
+    case OP_OR_LONG_2ADDR:
+    case OP_XOR_LONG_2ADDR:
+    case OP_SHL_LONG_2ADDR:
+    case OP_SHR_LONG_2ADDR:
+    case OP_USHR_LONG_2ADDR:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+        break;
+    case OP_ADD_FLOAT_2ADDR:
+    case OP_SUB_FLOAT_2ADDR:
+    case OP_MUL_FLOAT_2ADDR:
+    case OP_DIV_FLOAT_2ADDR:
+    case OP_REM_FLOAT_2ADDR:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+        break;
+    case OP_ADD_DOUBLE_2ADDR:
+    case OP_SUB_DOUBLE_2ADDR:
+    case OP_MUL_DOUBLE_2ADDR:
+    case OP_DIV_DOUBLE_2ADDR:
+    case OP_REM_DOUBLE_2ADDR:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+        break;
+    case OP_ADD_INT_LIT16:
+    case OP_RSUB_INT:
+    case OP_MUL_INT_LIT16:
+    case OP_DIV_INT_LIT16:
+    case OP_REM_INT_LIT16:
+    case OP_AND_INT_LIT16:
+    case OP_OR_INT_LIT16:
+    case OP_XOR_INT_LIT16:
+    case OP_ADD_INT_LIT8:
+    case OP_RSUB_INT_LIT8:
+    case OP_MUL_INT_LIT8:
+    case OP_DIV_INT_LIT8:
+    case OP_REM_INT_LIT8:
+    case OP_SHL_INT_LIT8:
+    case OP_SHR_INT_LIT8:
+    case OP_USHR_INT_LIT8:
+    case OP_AND_INT_LIT8:
+    case OP_OR_INT_LIT8:
+    case OP_XOR_INT_LIT8:
+        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+        break;
+
+
+    /*
+     * See comments in analysis/CodeVerify.c re: why some of these are
+     * annoying to deal with.  It's worse in this implementation, because
+     * we're not keeping any information about the classes held in each
+     * reference register.
+     *
+     * Handling most of these would require retaining the field/method
+     * reference info that we discarded when the instructions were
+     * quickened.  This is feasible but not currently supported.
+     */
+    case OP_EXECUTE_INLINE:
+    case OP_EXECUTE_INLINE_RANGE:
+    case OP_INVOKE_DIRECT_EMPTY:
+    case OP_IGET_QUICK:
+    case OP_IGET_WIDE_QUICK:
+    case OP_IGET_OBJECT_QUICK:
+    case OP_IPUT_QUICK:
+    case OP_IPUT_WIDE_QUICK:
+    case OP_IPUT_OBJECT_QUICK:
+    case OP_IGET_WIDE_VOLATILE:
+    case OP_IPUT_WIDE_VOLATILE:
+    case OP_SGET_WIDE_VOLATILE:
+    case OP_SPUT_WIDE_VOLATILE:
+    case OP_INVOKE_VIRTUAL_QUICK:
+    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+    case OP_INVOKE_SUPER_QUICK:
+    case OP_INVOKE_SUPER_QUICK_RANGE:
+        dvmAbort();     // not implemented, shouldn't be here
+        break;
+
+
+    /* these should never appear */
+    case OP_UNUSED_3E:
+    case OP_UNUSED_3F:
+    case OP_UNUSED_40:
+    case OP_UNUSED_41:
+    case OP_UNUSED_42:
+    case OP_UNUSED_43:
+    case OP_UNUSED_73:
+    case OP_UNUSED_79:
+    case OP_UNUSED_7A:
+    case OP_UNUSED_E3:
+    case OP_UNUSED_E4:
+    case OP_UNUSED_E5:
+    case OP_UNUSED_E6:
+    case OP_UNUSED_E7:
+    case OP_BREAKPOINT:
+    case OP_UNUSED_ED:
+    case OP_UNUSED_F1:
+    case OP_UNUSED_FC:
+    case OP_UNUSED_FD:
+    case OP_UNUSED_FE:
+    case OP_UNUSED_FF:
+        dvmAbort();
+        break;
+
+    /*
+     * DO NOT add a "default" clause here.  Without it the compiler will
+     * complain if an instruction is missing (which is desirable).
+     */
+    }
+
+
+    /*
+     * If we didn't just set the result register, clear it out.  This
+     * isn't so important here, but does help ensure that our output matches
+     * the verifier.
+     */
+    if (!justSetResult) {
+        int reg = RESULT_REGISTER(pState->insnRegCountPlus);
+        workRegs[reg] = workRegs[reg+1] = kRegTypeUnknown;
+    }
+
+    /*
+     * Handle "continue".  Tag the next consecutive instruction.
+     */
+    if ((nextFlags & kInstrCanContinue) != 0) {
+        int insnWidth = dvmInsnGetWidth(insnFlags, insnIdx);
+
+        /*
+         * We want to update the registers and set the "changed" flag on the
+         * next instruction (if necessary).  We aren't storing register
+         * changes for all addresses, so for non-GC-point targets we just
+         * compare "entry" vs. "work" to see if we've changed anything.
+         */
+        if (getRegisterLine(pState, insnIdx+insnWidth) != NULL) {
+            updateRegisters(pState, insnIdx+insnWidth, workRegs);
+        } else {
+            /* if not yet visited, or regs were updated, set "changed" */
+            if (!dvmInsnIsVisited(insnFlags, insnIdx+insnWidth) ||
+                compareRegisters(workRegs, entryRegs,
+                    pState->insnRegCountPlus) != 0)
+            {
+                dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true);
+            }
+        }
+    }
+
+    /*
+     * Handle "branch".  Tag the branch target.
+     */
+    if ((nextFlags & kInstrCanBranch) != 0) {
+        bool isConditional;
+
+        dvmGetBranchTarget(meth, insnFlags, insnIdx, &branchTarget,
+                &isConditional);
+        assert(isConditional || (nextFlags & kInstrCanContinue) == 0);
+        assert(!isConditional || (nextFlags & kInstrCanContinue) != 0);
+
+        updateRegisters(pState, insnIdx+branchTarget, workRegs);
+    }
+
+    /*
+     * Handle "switch".  Tag all possible branch targets.
+     */
+    if ((nextFlags & kInstrCanSwitch) != 0) {
+        int offsetToSwitch = insns[1] | (((s4)insns[2]) << 16);
+        const u2* switchInsns = insns + offsetToSwitch;
+        int switchCount = switchInsns[1];
+        int offsetToTargets, targ;
+
+        if ((*insns & 0xff) == OP_PACKED_SWITCH) {
+            /* 0=sig, 1=count, 2/3=firstKey */
+            offsetToTargets = 4;
+        } else {
+            /* 0=sig, 1=count, 2..count*2 = keys */
+            assert((*insns & 0xff) == OP_SPARSE_SWITCH);
+            offsetToTargets = 2 + 2*switchCount;
+        }
+
+        /* verify each switch target */
+        for (targ = 0; targ < switchCount; targ++) {
+            int offset, absOffset;
+
+            /* offsets are 32-bit, and only partly endian-swapped */
+            offset = switchInsns[offsetToTargets + targ*2] |
+                     (((s4) switchInsns[offsetToTargets + targ*2 +1]) << 16);
+            absOffset = insnIdx + offset;
+            assert(absOffset >= 0 && absOffset < pState->insnsSize);
+
+            updateRegisters(pState, absOffset, workRegs);
+        }
+    }
+
+    /*
+     * Handle instructions that can throw and that are sitting in a
+     * "try" block.  (If they're not in a "try" block when they throw,
+     * control transfers out of the method.)
+     */
+    if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+    {
+        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+        const DexCode* pCode = dvmGetMethodCode(meth);
+        DexCatchIterator iterator;
+
+        if (dexFindCatchHandler(&iterator, pCode, insnIdx)) {
+            while (true) {
+                DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+                if (handler == NULL)
+                    break;
+
+                /* note we use entryRegs, not workRegs */
+                updateRegisters(pState, handler->address, entryRegs);
+            }
+        }
+    }
+
+    /*
+     * Update startGuess.  Advance to the next instruction of that's
+     * possible, otherwise use the branch target if one was found.  If
+     * neither of those exists we're in a return or throw; leave startGuess
+     * alone and let the caller sort it out.
+     */
+    if ((nextFlags & kInstrCanContinue) != 0) {
+        *pStartGuess = insnIdx + dvmInsnGetWidth(insnFlags, insnIdx);
+    } else if ((nextFlags & kInstrCanBranch) != 0) {
+        /* we're still okay if branchTarget is zero */
+        *pStartGuess = insnIdx + branchTarget;
+    }
+
+    assert(*pStartGuess >= 0 && *pStartGuess < pState->insnsSize &&
+        dvmInsnGetWidth(insnFlags, *pStartGuess) != 0);
+
+    result = true;
+
+bail:
+    return result;
+}
+
+
+/*
+ * Merge two SRegType values.
+ *
+ * Sets "*pChanged" to "true" if the result doesn't match "type1".
+ */
+static SRegType mergeTypes(SRegType type1, SRegType type2, bool* pChanged)
+{
+    SRegType result;
+
+    /*
+     * Check for trivial case so we don't have to hit memory.
+     */
+    if (type1 == type2)
+        return type1;
+
+    /*
+     * Use the table if we can, and reject any attempts to merge something
+     * from the table with a reference type.
+     *
+     * The uninitialized table entry at index zero *will* show up as a
+     * simple kRegTypeUninit value.  Since this cannot be merged with
+     * anything but itself, the rules do the right thing.
+     */
+    if (type1 < kRegTypeMAX) {
+        if (type2 < kRegTypeMAX) {
+            result = gDvmMergeTab[type1][type2];
+        } else {
+            /* simple + reference == conflict, usually */
+            if (type1 == kRegTypeZero)
+                result = type2;
+            else
+                result = kRegTypeConflict;
+        }
+    } else {
+        if (type2 < kRegTypeMAX) {
+            /* reference + simple == conflict, usually */
+            if (type2 == kRegTypeZero)
+                result = type1;
+            else
+                result = kRegTypeConflict;
+        } else {
+            /* merging two references */
+            assert(type1 == type2);
+            result = type1;
+        }
+    }
+
+    if (result != type1)
+        *pChanged = true;
+    return result;
+}
+
+/*
+ * Control can transfer to "nextInsn".
+ *
+ * Merge the registers from "workRegs" into "addrRegs" at "nextInsn", and
+ * set the "changed" flag on the target address if the registers have changed.
+ */
+static void updateRegisters(WorkState* pState, int nextInsn,
+    const SRegType* workRegs)
+{
+    const Method* meth = pState->method;
+    InsnFlags* insnFlags = pState->insnFlags;
+    const int insnRegCountPlus = pState->insnRegCountPlus;
+    SRegType* targetRegs = getRegisterLine(pState, nextInsn);
+
+    if (!dvmInsnIsVisitedOrChanged(insnFlags, nextInsn)) {
+        /*
+         * We haven't processed this instruction before, and we haven't
+         * touched the registers here, so there's nothing to "merge".  Copy
+         * the registers over and mark it as changed.  (This is the only
+         * way a register can transition out of "unknown", so this is not
+         * just an optimization.)
+         */
+        LOGVV("COPY into 0x%04x\n", nextInsn);
+        copyRegisters(targetRegs, workRegs, insnRegCountPlus);
+        dvmInsnSetChanged(insnFlags, nextInsn, true);
+    } else {
+        /* merge registers, set Changed only if different */
+        LOGVV("MERGE into 0x%04x\n", nextInsn);
+        bool changed = false;
+        int i;
+
+        for (i = 0; i < insnRegCountPlus; i++) {
+            targetRegs[i] = mergeTypes(targetRegs[i], workRegs[i], &changed);
+        }
+
+        if (changed)
+            dvmInsnSetChanged(insnFlags, nextInsn, true);
+    }
+}
+
+#endif /*#if 0*/
diff --git a/vm/analysis/RegisterMap.h b/vm/analysis/RegisterMap.h
new file mode 100644
index 0000000..7897d45
--- /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
+#define _DALVIK_REGISTERMAP
+
+#include "analysis/VerifySubs.h"
+#include "analysis/CodeVerify.h"
+
+/*
+ * Format enumeration for RegisterMap data area.
+ */
+typedef 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 */
+} RegisterMapFormat;
+
+/*
+ * 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 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.
+ */
+typedef struct RegisterMapMethodPool {
+    u2      methodCount;            /* chiefly used as a sanity check */
+
+    /* stream of per-method data starts here */
+    u4      methodData[1];
+} RegisterMapMethodPool;
+
+/*
+ * 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.
+ */
+typedef struct RegisterMapClassPool {
+    u4      numClasses;
+
+    /* offset table starts here, 32-bit aligned; offset==0 means no data */
+    u4      classDataOffset[1];
+} RegisterMapClassPool;
+
+/*
+ * 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.
+ */
+typedef struct RegisterMapBuilder {
+    /* public */
+    void*       data;
+    size_t      size;
+
+    /* private */
+    MemMapping  memMap;
+} RegisterMapBuilder;
+
+/*
+ * 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*/
diff --git a/vm/analysis/VerifySubs.c b/vm/analysis/VerifySubs.c
new file mode 100644
index 0000000..8334a0c
--- /dev/null
+++ b/vm/analysis/VerifySubs.c
@@ -0,0 +1,457 @@
+/*
+ * 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/DexCatch.h"
+#include "libdex/InstrUtils.h"
+
+
+/*
+ * Compute the width of the instruction at each address in the instruction
+ * stream.  Addresses that are in the middle of an instruction, or that
+ * are part of switch table data, are not set (so the caller should probably
+ * initialize "insnFlags" to zero).
+ *
+ * If "pNewInstanceCount" is not NULL, it will be set to the number of
+ * new-instance instructions in the method.
+ *
+ * Performs some static checks, notably:
+ * - opcode of first instruction begins at index 0
+ * - only documented instructions may appear
+ * - each instruction follows the last
+ * - last byte of last instruction is at (code_length-1)
+ *
+ * Logs an error and returns "false" on failure.
+ */
+bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags,
+    int* pNewInstanceCount)
+{
+    size_t insnCount = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns;
+    bool result = false;
+    int newInstanceCount = 0;
+    int i;
+
+
+    for (i = 0; i < (int) insnCount; /**/) {
+        size_t width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
+        if (width == 0) {
+            LOG_VFY_METH(meth,
+                "VFY: invalid post-opt instruction (0x%04x)\n", *insns);
+            goto bail;
+        }
+
+        if ((*insns & 0xff) == OP_NEW_INSTANCE)
+            newInstanceCount++;
+
+        if (width > 65535) {
+            LOG_VFY_METH(meth, "VFY: insane width %d\n", width);
+            goto bail;
+        }
+
+        insnFlags[i] |= width;
+        i += width;
+        insns += width;
+    }
+    if (i != (int) dvmGetMethodInsnsSize(meth)) {
+        LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)\n",
+            i, dvmGetMethodInsnsSize(meth));
+        goto bail;
+    }
+
+    result = true;
+    if (pNewInstanceCount != NULL)
+        *pNewInstanceCount = newInstanceCount;
+
+bail:
+    return result;
+}
+
+/*
+ * Set the "in try" flags for all instructions protected by "try" statements.
+ * Also sets the "branch target" flags for exception handlers.
+ *
+ * Call this after widths have been set in "insnFlags".
+ *
+ * Returns "false" if something in the exception table looks fishy, but
+ * we're expecting the exception table to be somewhat sane.
+ */
+bool dvmSetTryFlags(const Method* meth, InsnFlags* insnFlags)
+{
+    u4 insnsSize = dvmGetMethodInsnsSize(meth);
+    const DexCode* pCode = dvmGetMethodCode(meth);
+    u4 triesSize = pCode->triesSize;
+    const DexTry* pTries;
+    u4 handlersSize;
+    u4 offset;
+    u4 i;
+
+    if (triesSize == 0) {
+        return true;
+    }
+
+    pTries = dexGetTries(pCode);
+    handlersSize = dexGetHandlersSize(pCode);
+
+    for (i = 0; i < triesSize; i++) {
+        const DexTry* pTry = &pTries[i];
+        u4 start = pTry->startAddr;
+        u4 end = start + pTry->insnCount;
+        u4 addr;
+
+        if ((start >= end) || (start >= insnsSize) || (end > insnsSize)) {
+            LOG_VFY_METH(meth,
+                "VFY: bad exception entry: startAddr=%d endAddr=%d (size=%d)\n",
+                start, end, insnsSize);
+            return false;
+        }
+
+        if (dvmInsnGetWidth(insnFlags, start) == 0) {
+            LOG_VFY_METH(meth,
+                "VFY: 'try' block starts inside an instruction (%d)\n",
+                start);
+            return false;
+        }
+
+        for (addr = start; addr < end;
+            addr += dvmInsnGetWidth(insnFlags, addr))
+        {
+            assert(dvmInsnGetWidth(insnFlags, addr) != 0);
+            dvmInsnSetInTry(insnFlags, addr, true);
+        }
+    }
+
+    /* Iterate over each of the handlers to verify target addresses. */
+    offset = dexGetFirstHandlerOffset(pCode);
+    for (i = 0; i < handlersSize; i++) {
+        DexCatchIterator iterator;
+        dexCatchIteratorInit(&iterator, pCode, offset);
+
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+            u4 addr;
+
+            if (handler == NULL) {
+                break;
+            }
+
+            addr = handler->address;
+            if (dvmInsnGetWidth(insnFlags, addr) == 0) {
+                LOG_VFY_METH(meth,
+                    "VFY: exception handler starts at bad address (%d)\n",
+                    addr);
+                return false;
+            }
+
+            dvmInsnSetBranchTarget(insnFlags, addr, true);
+        }
+
+        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+    }
+
+    return true;
+}
+
+/*
+ * Verify a switch table.  "curOffset" is the offset of the switch
+ * instruction.
+ */
+bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags,
+    int curOffset)
+{
+    const s4 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 >= 0 && curOffset < insnCount);
+
+    /* make sure the start of the switch is in range */
+    offsetToSwitch = insns[1] | ((s4) insns[2]) << 16;
+    if (curOffset + offsetToSwitch < 0 ||
+        curOffset + offsetToSwitch + 2 >= insnCount)
+    {
+        LOG_VFY_METH(meth,
+            "VFY: invalid switch start: at %d, switch offset %d, count %d\n",
+            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_METH(meth,
+            "VFY: unaligned switch table: at %d, switch offset %d\n",
+            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_METH(meth,
+            "VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)\n",
+            switchInsns[0], expectedSignature);
+        return false;
+    }
+
+    /* make sure the end of the switch is in range */
+    if (curOffset + offsetToSwitch + tableSize > (u4) insnCount) {
+        LOG_VFY_METH(meth,
+            "VFY: invalid switch end: at %d, switch offset %d, end %d, count %d\n",
+            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_METH(meth,
+                    "VFY: invalid packed switch: last key=%d, this=%d\n",
+                    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 >= insnCount ||
+            !dvmInsnIsOpcode(insnFlags, absOffset))
+        {
+            LOG_VFY_METH(meth,
+                "VFY: invalid switch target %d (-> 0x%x) at 0x%x[%d]\n",
+                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.
+ */
+bool dvmCheckBranchTarget(const Method* meth, InsnFlags* insnFlags,
+    int curOffset, bool selfOkay)
+{
+    const int insnCount = dvmGetMethodInsnsSize(meth);
+    int offset, absOffset;
+    bool isConditional;
+
+    if (!dvmGetBranchTarget(meth, insnFlags, curOffset, &offset,
+            &isConditional))
+        return false;
+
+    if (!selfOkay && offset == 0) {
+        LOG_VFY_METH(meth, "VFY: branch offset of zero not allowed at 0x%x\n",
+            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 0x%x +%d\n",
+            curOffset, offset);
+        return false;
+    }
+    absOffset = curOffset + offset;
+    if (absOffset < 0 || absOffset >= insnCount ||
+        !dvmInsnIsOpcode(insnFlags, absOffset))
+    {
+        LOG_VFY_METH(meth,
+            "VFY: invalid branch target %d (-> 0x%x) at 0x%x\n",
+            offset, absOffset, curOffset);
+        return false;
+    }
+    dvmInsnSetBranchTarget(insnFlags, absOffset, true);
+
+    return true;
+}
+
+
+/*
+ * Output a code verifier warning message.  For the pre-verifier it's not
+ * a big deal if something fails (and it may even be expected), but if
+ * we're doing just-in-time verification it's significant.
+ */
+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\n",
+            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;
+
+    char* dotMissingClass = dvmDescriptorToDot(missingClassDescr);
+    char* dotFromClass = dvmDescriptorToDot(meth->clazz->descriptor);
+    //char* methodDescr = dexProtoCopyMethodDescriptor(&meth->prototype);
+
+    LOGE("Could not find class '%s', referenced from method %s.%s\n",
+        dotMissingClass, dotFromClass, meth->name/*, methodDescr*/);
+
+    free(dotMissingClass);
+    free(dotFromClass);
+    //free(methodDescr);
+}
+
+/*
+ * Extract the relative offset from a branch instruction.
+ *
+ * Returns "false" on failure (e.g. this isn't a branch instruction).
+ */
+bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
+    int curOffset, int* pOffset, bool* pConditional)
+{
+    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;
+}
+
+/*
+ * Given a 32-bit constant, return the most-restricted RegType enum entry
+ * that can hold the value.
+ */
+char dvmDetermineCat1Const(s4 value)
+{
+    if (value < -32768)
+        return kRegTypeInteger;
+    else if (value < -128)
+        return kRegTypeShort;
+    else if (value < 0)
+        return kRegTypeByte;
+    else if (value == 0)
+        return kRegTypeZero;
+    else if (value == 1)
+        return kRegTypeOne;
+    else if (value < 128)
+        return kRegTypePosByte;
+    else if (value < 32768)
+        return kRegTypePosShort;
+    else if (value < 65536)
+        return kRegTypeChar;
+    else
+        return kRegTypeInteger;
+}
diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h
new file mode 100644
index 0000000..9e05e9d
--- /dev/null
+++ b/vm/analysis/VerifySubs.h
@@ -0,0 +1,79 @@
+/*
+ * 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
+#define _DALVIK_VERIFYSUBS
+
+/*
+ * 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);
+
+/* check switch targets and set the "branch target" flag for destinations */
+bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags,
+    int curOffset);
+
+/* verify branch target and set "branch target" flag on the destination */
+bool dvmCheckBranchTarget(const Method* meth, InsnFlags* insnFlags,
+    int curOffset, bool selfOkay);
+
+/* 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 target from a branch instruction */
+bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
+    int curOffset, int* pOffset, bool* pConditional);
+
+/* return a RegType enumeration value that "value" just fits into */
+char dvmDetermineCat1Const(s4 value);
+
+#endif /*_DALVIK_VERIFYSUBS*/
diff --git a/vm/arch/arm/CallEABI.S b/vm/arch/arm/CallEABI.S
new file mode 100644
index 0000000..e0d4f5c
--- /dev/null
+++ b/vm/arch/arm/CallEABI.S
@@ -0,0 +1,424 @@
+/*
+ * 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 hardware FP
+ *   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.)
+ *
+ * TODO: could reduce register-saving overhead for "fast" case, since we
+ * don't use a couple of registers.  Another thought is to rearrange the
+ * arguments such that r0/r1 get passed in on the stack, allowing us to
+ * use r0/r1 freely here and then load them with a single ldm.  Might be
+ * faster than saving/restoring other registers so that we can leave r0/r1
+ * undisturbed.
+ *
+ * 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.
+ */
+dvmPlatformInvoke:
+    .fnstart
+    @ Save regs.  Same style as gcc with "-fomit-frame-pointer" -- we don't
+    @ disturb "fp" in case somebody else wants it.  Copy "sp" to r4 and use
+    @ that to access local vars.
+    @
+    @ 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).  Easier to just push an even number
+    @ of registers.
+    mov     ip, sp                      @ ip<- original stack pointer
+    .save {r4, r5, r6, r7, r8, r9, ip, lr}
+    stmfd   sp!, {r4, r5, r6, r7, r8, r9, ip, lr}
+
+    mov     r4, ip                      @ r4<- original stack pointer
+
+    @ 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
+
+    cmp     r1, #0                      @ Is this a static method?
+    ldr     r9, [r4]                    @ r9<- argv
+
+    @ Not static: set r1 to *argv++ ("this"), and set argc--.
+    @
+    @ Note the "this" pointer is not included in the method signature.
+    ldreq   r1, [r9], #4
+    subeq   r3, r3, #1
+
+    @ 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  original stack pointer
+     *   r5-r8 (available)
+     *   r9  argv
+     */
+.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     r5, r2, lsr #28             @ r5<- 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)
+     *  r4  original saved sp
+     *  r5  return type (enum DalvikJniReturnType)
+     *  r9  original argv
+     *
+     * 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]
+
+    @ call the method
+    ldr     ip, [r4, #8]                @ func
+#ifdef __ARM_HAVE_BLX
+    blx     ip
+#else
+    mov     lr, pc
+    bx      ip
+#endif
+
+    @ 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.
+    @
+    @ 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     r5, #0                      @ DALVIK_JNI_RETURN_VOID?
+    ldrne   ip, [r4, #12]               @ pReturn
+    stmneia ip, {r0-r1}                 @ pReturn->j <- r0/r1
+
+    @ Restore the registers we saved and return (restores lr into pc, and
+    @ the initial stack pointer into sp).
+#ifdef __ARM_HAVE_PC_INTERWORK
+    ldmdb   r4, {r4, r5, r6, r7, r8, r9, sp, pc}
+#else
+    ldmdb   r4, {r4, r5, r6, r7, r8, r9, sp, lr}
+    bx      lr
+#endif
+    .fnend
+
+
+
+    /*
+     * "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  original stack pointer
+     *   r5-r8 (available)
+     *   r9  argv
+     */
+.Lno_arg_info:
+    mov     r5, r2, lsr #28             @ r5<- return type
+    ldr     r6, [r4, #4]                @ r6<- short signature
+    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, [r4, #4]                @ r6<- signature
+    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     r6, #'D'
+    cmpne   r6, #'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
+
+
+
+#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
+#ifdef __ARM_HAVE_PC_INTERWORK
+    ldmfd   sp!, {r0, r1, r2, r3, ip, pc}
+#else
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+#endif
+    .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..2463d3c
--- /dev/null
+++ b/vm/arch/arm/CallOldABI.S
@@ -0,0 +1,173 @@
+/*
+ * 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.
+    @
+#ifdef __ARM_HAVE_PC_INTERWORK
+    mov     lr, pc
+    ldr     pc, [r4, #8]
+#else
+    ldr     ip, [r4, #8]
+    blx     ip
+#endif
+
+    @ 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}
+
+#ifdef __ARM_HAVE_PC_INTERWORK
+    @ 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}
+#else
+    ldmfd   r5, {r4, r5, r6, fp, sp, lr}
+    bx      lr
+#endif
+
+#endif /*__ARM_EABI__*/
diff --git a/vm/arch/arm/HintsEABI.c b/vm/arch/arm/HintsEABI.c
new file mode 100644
index 0000000..3e27e5a
--- /dev/null
+++ b/vm/arch/arm/HintsEABI.c
@@ -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.c b/vm/arch/generic/Call.c
new file mode 100644
index 0000000..a39b761
--- /dev/null
+++ b/vm/arch/generic/Call.c
@@ -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.
+ */
+/*
+ * 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 'F': return &ffi_type_float;
+    case 'D': return &ffi_type_double;
+    case 'J': return &ffi_type_sint64;
+    case '[':
+    case 'L': return &ffi_type_pointer;
+    default:  return &ffi_type_uint32;
+    }
+}
+
+/*
+ * 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;
+    const char* sig;
+    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) {
+        LOGE("ffi_prep_cif failed\n");
+        dvmAbort();
+    }
+
+    ffi_call(&cif, FFI_FN(func), pReturn, values);
+}
diff --git a/vm/arch/generic/Hints.c b/vm/arch/generic/Hints.c
new file mode 100644
index 0000000..7b08aeb
--- /dev/null
+++ b/vm/arch/generic/Hints.c
@@ -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/sh/CallSH4ABI.S b/vm/arch/sh/CallSH4ABI.S
new file mode 100644
index 0000000..f8b2ddc
--- /dev/null
+++ b/vm/arch/sh/CallSH4ABI.S
@@ -0,0 +1,400 @@
+/*
+ * 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.
+ */
+/*
+ * Invoking JNI native method via SH4 ABI.
+ * This inplementation follows the spec found in following URL.
+ * http://www.ecos.sourceware.org/docs-1.3.1/ref/gnupro-ref/sh/SH_ch01.html#pgfId-461254
+
+ * This version supports SH4A little endian.
+ */
+    .text
+    .align 4
+    .type  dvmPlatformInvoke, #function
+    .globl dvmPlatformInvoke
+
+/*
+ * @param r4 void* pEnv  (used as scrach after invoking method)
+ * @param r5 ClassObject* clazz
+ * @param r6 int argInfo
+ * @param r7 int argc
+ * @param r15[0] const u4 * argv
+ * @param r15[1] const char * shorty
+ * @param r15[2] void * func
+ * @param r15[3] JValue * pReturn
+ *
+ * @remark r0,r1  Scratch before invoking method.
+ *                Return value after invoking method.
+ * @remark r2  shorty pointer
+ * @remark r3  argv pointer before invoking method.
+ *             pReturn after invoking method.
+ * @remark r8-11 Don't touch.
+ * @remark r12 status of r5-7
+ * @remark r13 status of fr4-11
+ * @remark r14 Keep stack pointer.
+ */
+dvmPlatformInvoke:
+    ## save preserved regsiters
+    mov.l   r14, @-r15
+    mov     r15, r14
+    add     #4, r14             /* r14 = original r15 = stack pointer */
+    mov.l   r13, @-r15
+    mov.l   r12, @-r15
+    sts.l   pr, @-r15
+
+    # fetch arguments
+    mov.l   @r14, r3            /* argv */
+    mov.l   @(4,r14), r2        /* shorty for argumnets */
+    mov     #1, r0              /* shorty's 1st byte specify ret value type. */
+    add     r0, r2
+
+### initialize local variables
+
+    ## r12 ... status of r6, and r7
+    ##          bit 1 << 0 : if r6 is available, it contains 1.
+    ##          bit 1 << 1 : if r7 is available, it contains 1.
+    ##  Note : r4 is always used to pass pEnv.
+    ##         r5 is always used for clazz or object
+    mov     #3, r12             /* b0000-0111 : r5-7 avialble. */
+
+    ## r13 ... status of fr4-fr11
+    ##          bit 1 << 0 : if fr4 is available, it contains 1.
+    ##          bit 1 << 1 : if fr5 is available, it contains 1.
+    ##      ...
+    ##          bit 1 << 7 : if fr11 is available, it contains 1.
+    mov     #0xFF, r13          /* b1111-1111 : fr4-11 avialble. */
+
+### put arguments
+
+    ## ... keep pEnv in r4 as is.
+
+    ## check clazz
+    mov     #0, r0
+    cmp/eq  r0, r5
+    bf      arg_loop            /* if r5 has clazz, keep it as is */
+    mov.l   @r3+, r5            /* put object arg in r5 */
+
+    ## other args
+arg_loop:
+one_arg_handled:
+    mov.b   @r2+, r0
+    cmp/eq  #0, r0              /* if (*shorty == '\0) */
+    bf      process_one_arg
+    bra     arg_end             /* no argument left */
+    nop
+
+process_one_arg:
+
+    ## check arg type
+
+    cmp/eq  #'F', r0
+    bt      jfloat_arg
+
+    cmp/eq  #'D', r0
+    bt      jdouble_arg
+
+    cmp/eq  #'J', r0
+    bt      jlong_arg
+
+    ## other 32bit arg types
+    mov     r12, r0
+    cmp/eq  #0, r0
+    bt      put_32bit_on_stack  /* r6-7 not available */
+
+    tst     #1, r0
+    bt      j32_arg_1
+    mov.l   @r3+, r6            /* put one arg in r6 */
+    mov     #1, r0              /* r6 is not available now. */
+    not     r0, r0
+    and     r0, r12
+    bra     one_arg_handled
+    nop
+j32_arg_1:
+    tst     #2, r0
+    bt      j32_arg_fatal_error
+    mov.l   @r3+, r7            /* put one arg in r7 */
+    mov     #2, r0              /* r7 is not available now. */
+    not     r0, r0
+    and     r0, r12
+    bra     one_arg_handled
+    nop
+
+j32_arg_fatal_error:
+    bra     j32_arg_fatal_error
+    nop
+
+jlong_arg:
+    mov     r12, r0
+    cmp/eq  #0, r0
+    bt      put_64bit_on_stack  /* r6-7 not available */
+
+    and     #3, r0
+    cmp/eq  #3, r0
+    bf      put_64bit_on_stack  /* consequent two registers not available. */
+    mov.l   @r3+, r6            /* put one arg in r6 and r7 */
+    mov.l   @r3+, r7
+    mov     #3, r0              /* r6 and r7 are not available now. */
+    not     r0, r0
+    and     r0, r12
+    bra     one_arg_handled
+    nop
+
+    # utility routines are placed here make short range jumps available.
+put_32bit_on_stack:
+    mov.l   @r3+, r0
+    mov.l   r0, @-r15
+    bra     one_arg_handled
+    nop
+
+put_64bit_on_stack:
+    mov.l   @r3+, r0
+    mov.l   r0, @-r15           /* Pay attention that the endianness is */
+    mov.l   @r3+, r0            /* once reversed.  It is corrected when the */
+    mov.l   r0, @-r15           /* arguments on stack are revesred before */
+    bra     one_arg_handled     /* jni call */
+    nop
+
+jdouble_arg:
+    mov     r13, r0
+    cmp/eq  #0, r0
+    bt      put_64bit_on_stack  /* fr4-11 not available */
+
+    and     #3, r0
+    cmp/eq  #3, r0
+    bf      jdouble_arg_1
+
+    fmov.s  @r3+, fr5           /* put one arg to drX */
+    fmov.s  @r3+, fr4
+    mov     #3, r0              /* fr4-frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jdouble_arg_1:
+    mov     r13, r0
+    and     #12, r0
+    cmp/eq  #12, r0
+    bf      jdouble_arg_2
+
+    fmov.s  @r3+, fr7           /* put one arg to drX */
+    fmov.s  @r3+, fr6
+    mov     #15, r0             /* fr4-frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jdouble_arg_2:
+    mov     r13, r0
+    and     #48, r0
+    cmp/eq  #48, r0
+    bf      jdouble_arg_3
+    fmov.s  @r3+, fr9           /* put one arg to drX */
+    fmov.s  @r3+, fr8
+    mov     #63, r0             /* fr4-frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jdouble_arg_3:
+    mov     r13, r0
+    and     #192, r0
+    cmp/eq  #192, r0
+    bf      put_64bit_on_stack
+    fmov.s  @r3+, fr11          /* put one arg to drX */
+    fmov.s  @r3+, fr10
+    mov     #0, r13             /* fr4-fr11 all not available now. */
+    bra     one_arg_handled
+    nop
+
+jfloat_arg:
+    mov     r13, r0
+    cmp/eq  #0, r0
+    bt      put_32bit_on_stack  /* fr4-11 not available */
+
+    tst     #2, r0
+    bt      jfloat_arg_1
+    fmov.s  @r3+, fr5           /* put one arg to frX */
+    mov     #2, r0              /* frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jfloat_arg_1:
+    tst     #1, r0
+    bt      jfloat_arg_2
+    fmov.s  @r3+, fr4           /* put one arg to frX */
+    mov     #1, r0              /* frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jfloat_arg_2:
+    tst     #8, r0
+    bt      jfloat_arg_3
+    fmov.s  @r3+, fr7           /* put one arg to frX */
+    mov     #8, r0              /* frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jfloat_arg_3:
+    tst     #4, r0
+    bt      jfloat_arg_4
+    fmov.s  @r3+, fr6           /* put one arg to frX */
+    mov     #4, r0              /* frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jfloat_arg_4:
+    tst     #32, r0
+    bt      jfloat_arg_5
+    fmov.s  @r3+, fr9           /* put one arg to frX */
+    mov     #32, r0             /* frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jfloat_arg_5:
+    tst     #16, r0
+    bt      jfloat_arg_6
+    fmov.s  @r3+, fr8           /* put one arg to frX */
+    mov     #16, r0             /* frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jfloat_arg_6:
+    tst     #128, r0
+    bt      jfloat_arg_7
+    fmov.s  @r3+, fr11          /* put one arg to frX */
+    mov     #127, r0            /* frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jfloat_arg_7:
+    tst     #64, r0
+    bt      jfloat_fatal_error
+    fmov.s  @r3+, fr10          /* put one arg to frX */
+    mov     #64, r0             /* frX not available now. */
+    not     r0, r0
+    and     r0, r13
+    bra     one_arg_handled
+    nop
+
+jfloat_fatal_error:
+    bra     jfloat_fatal_error:
+    nop
+
+arg_end:
+
+
+### reverse the variables on stack
+    mov     r14, r12            /* points to first arg on stack */
+    add     #-20, r12
+    mov     r15, r13            /* points to last arg on stack */
+arg_rev_loop:
+    cmp/hs  r12, r13            /* When r13 >= r12 (unsigned), 1->T */
+    bt      arg_rev_end
+    mov.l   @r12, r0
+    mov.l   @r13, r1
+    mov.l   r0, @r13
+    mov.l   r1, @r12
+    add     #-4, r12
+    add     #4, r13
+    bra     arg_rev_loop
+    nop
+
+arg_rev_end:
+
+### invoke the JNI function.
+
+    mov.l   @(8,r14), r0
+    jsr     @r0
+    nop
+
+### pass the return value
+
+    /*
+     * r0 and r1 keep return value.
+     */
+
+    ## fetch data
+    mov.l   @(4,r14), r2        /* reload shorty */
+    mov.b   @r2, r2             /* first byte specifyes return value type. */
+    mov.l   @(12,r14), r3       /* pReturn */
+
+    ## check return value types
+
+    mov     #'V', r4
+    cmp/eq  r4, r2
+    bt      end
+
+    mov     #'F', r4
+    cmp/eq  r4, r2
+    bt      jfloat_ret
+
+    mov     #'D', r4
+    cmp/eq  r4, r2
+    bt      jdouble_ret
+
+    mov     #'J', r4
+    cmp/eq  r4, r2
+    bt      jlong_ret
+
+    ## fall-through for other 32 bit java types.
+
+    ## load return values
+j32_ret:
+    bra     end
+    mov.l   r0, @r3             /* delay slot */
+
+jfloat_ret:
+    bra     end
+    fmov.s  fr0, @r3            /* delay slot */
+
+jdouble_ret:
+    fmov.s  fr1, @r3
+    mov     #4, r0
+    bra     end
+    fmov.s  fr0, @(r0,r3)       /* delay slot */
+
+jlong_ret:
+    mov.l   r0, @r3
+    bra     end
+    mov.l   r1, @(4,r3)         /* delay slot */
+
+end:
+    ## restore preserved registers
+    mov     r14, r15
+    add     #-16, r15
+    lds.l   @r15+, pr
+    mov.l   @r15+, r12
+    mov.l   @r15+, r13
+    mov.l   @r15+, r14
+
+    rts                         /* dvmPlatformInvoke returns void. */
+    nop
diff --git a/vm/arch/x86-atom/Call386ABI.S b/vm/arch/x86-atom/Call386ABI.S
new file mode 100644
index 0000000..f996168
--- /dev/null
+++ b/vm/arch/x86-atom/Call386ABI.S
@@ -0,0 +1,189 @@
+   /* 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.
+    */
+
+   /*
+    * File: CallABI.S
+    *
+    * Code: facilitates call to native code C and C++ routines.
+    *
+    */
+
+   /*
+    * 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.
+    */
+
+   /*
+    * On entry:
+    *   4(%sp)    JNIEnv (can be left alone)
+    *   8(%esp)   clazz (NULL for virtual method calls, non-NULL for static)
+    *   12(%esp)  arg info
+    *   16(%esp)  argc (number of 32-bit values in argv)
+    *   20(%esp)  argv
+    *   24(%esp)  short signature
+    *   28(%esp)  func
+    *   32(%esp)  pReturn
+    *
+    * For a virtual method call, the "this" reference is in argv[0].
+    *
+    * argInfo (32-bit int) layout:
+    *
+    *   SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+    *
+    *   S - if set, argInfo hints are invalid
+    *   R - return type enumeration (see jniInternal.h)
+    *       VOID   -> 0
+    *       FLOAT  -> 1
+    *       DOUBLE -> 2
+    *       S8     -> 3
+    *       S4     -> 4
+    *    H - target-specific hints (see below for details)
+    *
+    * IA32 ABI JNI hint format
+    *
+    *       ZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+    *
+    *   Z - reserved
+    *   A - size of the variable argument block in 32-bit words
+    */
+
+    .text
+    .align  4
+    .global dvmPlatformInvoke
+    .type   dvmPlatformInvoke, %function
+
+
+dvmPlatformInvoke:
+CallABI_ENTER:
+
+   /*
+    * Save registers.
+    */
+
+    movl        %ebp, -4(%esp)
+    movl        %ebx, -8(%esp)          # save %ebx
+    movl        %esi, -12(%esp)         # save %esi
+    movl        %edi, -16(%esp)         # save %edi
+    lea         (%esp), %ebp
+
+   /*
+    * Check if argInfo is valid. Is always valid so should remove this check?
+    */
+
+    movzwl      12(%ebp), %ecx          # %ecx<- argsize in words
+    movl        12(%ebp), %ebx          # %ebx<- argInfo
+
+    shl         $2, %ecx                # %ecx<- argsize in bytes
+    subl        %ecx, %esp              # %esp<- expanded for arg region
+
+   /*
+    * Prepare for 16 byte alignment
+    */
+
+    and         $0xfffffff0, %esp
+    subl        $24, %esp
+
+
+    movl        8(%ebp), %eax           # %eax<- clazz
+    cmpl        $0, %eax                # Check virtual or static
+    movl        4(%ebp), %ecx           # %ecx<- JNIEnv
+    movl        20(%ebp), %esi          # %esi<- argV
+    jne         1f                      # Branch if static
+    movl        (%esi), %eax            # get the this pointer
+    addl        $4, %esi                # %esi<- update past this
+
+1:
+    movl        %ecx, -8(%esp)          # push JNIEnv as arg #1
+    movl        %eax, -4(%esp)          # push clazz or this as arg #2
+    lea         -8(%esp), %esp
+
+   /*
+    * Copy arguments
+    */
+
+    movzwl      %bx, %ecx               # %ecx<- %bx; argsize in words
+    lea         8(%esp), %edi           # %edi<- stack location for arguments
+    cld
+    rep         movsl                   # move %ecx arguments to 8(%esp)
+    call        *28(%ebp)
+    sarl        $28, %ebx               # %ebx<- SRRR (low 4 bits)
+    je          CallABI_EXIT            # exit call
+    cmpl        $2, %ebx
+    movl        32(%ebp), %ecx          # %ecx<- return pointer
+    je          2f                      # handle double return
+    jl          1f                      # handle float return
+
+   /*
+    *  If a native function returns a result smaller than 8-bytes
+    *  then higher bytes may contain garbage.
+    *  This code does type-checking based on size of return result.
+    *  We zero higher bytes instead of allowing the garbage to go through.
+    */
+
+    cmpl        $3,%ebx
+    je          S8
+    cmpl        $4,%ebx
+    je          S4
+    cmpl        $7,%ebx
+    je          S1
+    cmpl        $6,%ebx
+    jne         S2
+U2:
+    movzwl      %ax, %eax
+    movl        %eax, (%ecx)            # save 32-bit return
+    jmp         CallABI_EXIT            # exit call
+
+S1:
+    movsbl      %al, %eax
+    movl        %eax, (%ecx)            # save 32-bit return
+    jmp         CallABI_EXIT            # exit call
+S2:
+    movswl      %ax, %eax
+    movl        %eax, (%ecx)            # save 32-bit return
+    jmp         CallABI_EXIT            # exit call
+S4:
+    cltd
+    movl        %eax, (%ecx)            # save 32-bit return
+    jmp         CallABI_EXIT            # exit call
+S8:
+    movl        %edx, 4(%ecx)           # save 64-bit return
+    movl        %eax, (%ecx)            # save 32-bit return
+    jmp         CallABI_EXIT            # exit call
+
+2:
+    fstpl       (%ecx)                  # save double return
+    jmp         CallABI_EXIT            # exit call
+1:
+    fstps       (%ecx)                  # save float return
+
+CallABI_EXIT:
+    lea         (%ebp), %esp
+    movl        -16(%ebp), %edi         # restore %edi
+    movl        -12(%ebp), %esi         # restore %esi
+    movl        -8(%ebp), %ebx          # restore %ebx
+    movl        -4(%ebp), %ebp          # restore caller base pointer
+    ret                                 # return
diff --git a/vm/arch/x86-atom/Hints386ABI.c b/vm/arch/x86-atom/Hints386ABI.c
new file mode 100644
index 0000000..dd2fc69
--- /dev/null
+++ b/vm/arch/x86-atom/Hints386ABI.c
@@ -0,0 +1,79 @@
+   /* 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 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 IA32-specific hints for the standard 32-bit 386 ABI.
+    * All arguments have 32-bit alignment.  Padding is not an issue.
+    *
+    * IA32 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.
+    */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+u4 dvmPlatformInvokeHints(const DexProto* proto)  {
+
+const char* sig = dexProtoGetShorty(proto);
+unsigned int wordCount = 0;
+char sigByte;
+
+ while (1) {
+
+   /*
+    * Move past return type; dereference sigByte
+    */
+
+    sigByte = *(++sig);
+    if (sigByte == '\0') { break; }
+    ++wordCount;
+
+    if (sigByte == 'D' || sigByte == 'J') {
+      ++wordCount;
+    }
+ }
+
+/*
+ * Check for Dex file limitation and return
+ */
+
+ if (wordCount > 0xFFFF) { return DALVIK_JNI_NO_ARG_INFO; }
+ return wordCount;
+
+}
diff --git a/vm/arch/x86/Call386ABI.S b/vm/arch/x86/Call386ABI.S
new file mode 100644
index 0000000..c98876c
--- /dev/null
+++ b/vm/arch/x86/Call386ABI.S
@@ -0,0 +1,174 @@
+/*
+ * 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
+    .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, spill & align to 16b */
+    pushl    %ebp
+    movl     %esp,%ebp
+    pushl    %edi
+    pushl    %esi
+    pushl    %ebx
+    subl     $12,%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 and grow (preserving alignment) */
+    movl     %ebx,%ecx
+    leal     12(,%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:
+    pushl    %eax
+    pushl    %ecx
+/* 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.c b/vm/arch/x86/Hints386ABI.c
new file mode 100644
index 0000000..2a0a1d8
--- /dev/null
+++ b/vm/arch/x86/Hints386ABI.c
@@ -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.c b/vm/compiler/Compiler.c
new file mode 100644
index 0000000..60f060c
--- /dev/null
+++ b/vm/compiler/Compiler.c
@@ -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 <sys/mman.h>
+#include <errno.h>
+#include <cutils/ashmem.h>
+
+#include "Dalvik.h"
+#include "interp/Jit.h"
+#include "CompilerInternals.h"
+
+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;
+}
+
+/*
+ * Attempt to enqueue a work order, returning true if successful.
+ * This routine will not block, but simply return if it couldn't
+ * aquire the lock or if the queue is full.
+ *
+ * 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;
+
+    if (dvmTryLockMutex(&gDvmJit.compilerLock)) {
+        return false;  // Couldn't acquire the lock
+    }
+
+    /*
+     * Return if queue or code cache is full.
+     */
+    if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
+        gDvmJit.codeCacheFull == true) {
+        result = false;
+        goto unlockAndExit;
+    }
+
+    for (numWork = gDvmJit.compilerQueueLength,
+           i = gDvmJit.compilerWorkDequeueIndex;
+         numWork > 0;
+         numWork--) {
+        /* Already enqueued */
+        if (gDvmJit.compilerWorkQueue[i++].pc == pc)
+            goto unlockAndExit;
+        /* 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 = NULL;
+    newOrder->result.codeAddress = NULL;
+    newOrder->result.discardResult =
+        (kind == kWorkOrderTraceDebug) ? true : false;
+    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);
+
+unlockAndExit:
+    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)
+{
+    extern void dvmCompilerTemplateStart(void);
+    extern void dmvCompilerTemplateEnd(void);
+    int fd;
+
+    /* Allocate the code cache */
+    fd = ashmem_create_region("dalvik-jit-code-cache", gDvmJit.codeCacheSize);
+    if (fd < 0) {
+        LOGE("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) {
+        LOGE("Failed to mmap the JIT code cache: %s\n", strerror(errno));
+        return false;
+    }
+
+    gDvmJit.pageSizeMask = getpagesize() - 1;
+
+    /* This can be found through "dalvik-jit-code-cache" in /proc/<pid>/maps */
+    // LOGD("Code cache starts at %p", gDvmJit.codeCache);
+
+    /* Copy the template code into the beginning of the code cache */
+    int templateSize = (intptr_t) dmvCompilerTemplateEnd -
+                       (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 */
+    cacheflush((intptr_t) gDvmJit.codeCache,
+               (intptr_t) gDvmJit.codeCache + templateSize, 0);
+
+    int result = mprotect(gDvmJit.codeCache, gDvmJit.codeCacheSize,
+                          PROTECT_CODE_CACHE_ATTRS);
+
+    if (result == -1) {
+        LOGE("Failed to remove the write permission for the code cache");
+        dvmAbort();
+    }
+
+    return true;
+}
+
+static void crawlDalvikStack(Thread *thread, bool print)
+{
+    void *fp = thread->curFrame;
+    StackSaveArea* saveArea = NULL;
+    int stackLevel = 0;
+
+    if (print) {
+        LOGD("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(fp)) {
+                LOGD("  #%d: break frame (%p)",
+                     stackLevel, saveArea->returnAddr);
+            }
+            else {
+                LOGD("  #%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 */
+    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++;
+        }
+    }
+
+    if (inJit) {
+        LOGD("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);
+
+    /* 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.
+     */
+    memset((char *) gDvmJit.codeCache + gDvmJit.templateSize,
+           0,
+           gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
+    cacheflush((intptr_t) gDvmJit.codeCache,
+               (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed, 0);
+
+    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);
+
+    /* All clear now */
+    gDvmJit.codeCacheFull = false;
+
+    dvmUnlockMutex(&gDvmJit.compilerLock);
+
+    LOGD("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();
+}
+
+bool compilerThreadStartup(void)
+{
+    JitEntry *pJitTable = NULL;
+    unsigned char *pJitProfTable = 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;
+    }
+
+    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) {
+        LOGE("jit table allocation failed\n");
+        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 syncronized 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) {
+        LOGE("jit prof table allocation failed\n");
+        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);
+
+    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;
+    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);
+            LOGD("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)
+                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) {
+                    LOGD("Compiler shutdown in progress - discarding request");
+                } else if (!gDvmJit.codeCacheFull) {
+                    bool compileOK = false;
+                    jmp_buf jmpBuf;
+                    work.bailPtr = &jmpBuf;
+                    bool aborted = setjmp(jmpBuf);
+                    if (!aborted) {
+                        compileOK = dvmCompilerDoWork(&work);
+                    }
+                    if (aborted || !compileOK) {
+                        dvmCompilerArenaReset();
+                    } else if (!work.result.discardResult &&
+                               work.result.codeAddress) {
+                        /* Make sure that proper code addr is installed */
+                        assert(work.result.codeAddress != NULL);
+                        dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
+                                          work.result.instructionSet);
+                    }
+                }
+                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)
+        LOGD("Compiler thread shutting down\n");
+    return NULL;
+}
+
+bool dvmCompilerStartup(void)
+{
+
+    dvmInitMutex(&gDvmJit.compilerLock);
+    dvmInitMutex(&gDvmJit.compilerICPatchLock);
+    dvmInitMutex(&gDvmJit.codeCacheProtectionLock);
+    dvmLockMutex(&gDvmJit.compilerLock);
+    pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
+    pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
+
+    /* 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;
+
+    if (gDvm.verboseShutdown) {
+        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)
+            LOGW("Compiler thread join failed\n");
+        else if (gDvm.verboseShutdown)
+            LOGD("Compiler thread has shut down\n");
+    }
+
+    /* 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 dvmCompilerStateRefresh()
+{
+    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;
+    }
+
+    dvmLockMutex(&gDvmJit.tableLock);
+    jitActive = gDvmJit.pProfTable != NULL;
+    jitActivate = !(gDvm.debuggerActive || (gDvm.activeProfilers > 0));
+
+    if (jitActivate && !jitActive) {
+        gDvmJit.pProfTable = gDvmJit.pProfTableCopy;
+    } else if (!jitActivate && jitActive) {
+        gDvmJit.pProfTable = NULL;
+        needUnchain = true;
+    }
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    if (needUnchain)
+        dvmJitUnchainAll();
+}
diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h
new file mode 100644
index 0000000..739d517
--- /dev/null
+++ b/vm/compiler/Compiler.h
@@ -0,0 +1,296 @@
+/*
+ * 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 <Thread.h>
+#include <setjmp.h>
+
+#ifndef _DALVIK_VM_COMPILER
+#define _DALVIK_VM_COMPILER
+
+/*
+ * Uncomment the following to enable JIT signature breakpoint
+ * #define SIGNATURE_BREAKPOINT
+ */
+
+#define MAX_JIT_RUN_LEN                 64
+#define COMPILER_WORK_QUEUE_SIZE        100
+#define COMPILER_IC_PATCH_QUEUE_SIZE    64
+
+/* 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 */
+#if !defined(WITH_JIT_TUNING)
+#define PROTECT_CODE_CACHE_ATTRS       (PROT_READ | PROT_EXEC)
+#define UNPROTECT_CODE_CACHE_ATTRS     (PROT_READ | PROT_EXEC | PROT_WRITE)
+#else
+/* When doing JIT profiling always grant the write permission */
+#define PROTECT_CODE_CACHE_ATTRS       (PROT_READ | PROT_EXEC |                \
+                                  (gDvmJit.profile ? PROT_WRITE : 0))
+#define UNPROTECT_CODE_CACHE_ATTRS     (PROT_READ | PROT_EXEC | PROT_WRITE)
+#endif
+
+/* 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_THUMB2EE,
+    DALVIK_JIT_X86
+} JitInstructionSetType;
+
+/* Description of a compiled trace. */
+typedef struct JitTranslationInfo {
+    void *codeAddress;
+    JitInstructionSetType instructionSet;
+    bool discardResult;         // Used for debugging divergence and IC patching
+    bool methodCompilationAborted;  // Cannot compile the whole method
+    Thread *requestingThread;   // For debugging purpose
+} 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)
+} 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 */
+    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 */
+} ICPatchWorkOrder;
+
+/* States of the dbg interpreter when serving a JIT-related request */
+typedef 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
+    kJitSingleStep = 6,        // Single step interpretation
+    kJitSingleStepEnd = 7,     // Done with single step, ready return to mterp
+    kJitDone = 8,              // Ready to leave the debug interpreter
+} JitState;
+
+#if defined(WITH_SELF_VERIFICATION)
+typedef 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
+} SelfVerificationState;
+#endif
+
+typedef 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
+} jitHint;
+
+/*
+ * Element of a Jit trace description. If the isCode bit is set, it describes
+ * a contiguous sequence of Dalvik byte codes.
+ */
+typedef struct {
+    unsigned isCode:1;       // If set denotes code fragments
+    unsigned numInsts:8;     // Number of Byte codes in run
+    unsigned runEnd:1;       // Run ends with last byte code
+    jitHint  hint:6;         // Hint to apply to final code of run
+    u2    startOffset;       // Starting offset for trace run
+} JitCodeDesc;
+
+/*
+ * A complete list of trace runs passed to the compiler looks like the
+ * following:
+ *   frag1
+ *   frag2
+ *   frag3
+ *   meta1
+ *   meta2
+ *   frag4
+ *
+ * frags 1-4 have the "isCode" field set, and metas 1-2 are plain pointers or
+ * pointers to auxiliary data structures as long as the LSB is null.
+ * 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
+ * type of "this" and the currently resolved method pointer are two instances
+ * of meta information stored there.
+ */
+typedef union {
+    JitCodeDesc frag;
+    void*       meta;
+} JitTraceRun;
+
+/*
+ * 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 */
+} 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)
+
+/* Vectors to provide optimization hints */
+typedef enum JitOptimizationHints {
+    kJitOptNoLoop = 0,          // Disable loop formation/optimization
+} JitOptimizationHints;
+
+#define JIT_OPT_NO_LOOP         (1 << kJitOptNoLoop)
+
+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);
+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(struct CompilationUnit *cUnit, 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 dvmCompilerSortAndPrintTraceProfiles(void);
+void dvmCompilerPerformSafePointChecks(void);
+void dvmCompilerInlineMIR(struct CompilationUnit *cUnit);
+void dvmInitializeSSAConversion(struct CompilationUnit *cUnit);
+int dvmConvertSSARegToDalvik(struct CompilationUnit *cUnit, int ssaReg);
+bool dvmCompilerLoopOpt(struct CompilationUnit *cUnit);
+void dvmCompilerNonLoopAnalysis(struct CompilationUnit *cUnit);
+void dvmCompilerFindLiveIn(struct CompilationUnit *cUnit,
+                           struct BasicBlock *bb);
+void dvmCompilerDoSSAConversion(struct CompilationUnit *cUnit,
+                                struct BasicBlock *bb);
+void dvmCompilerDoConstantPropagation(struct CompilationUnit *cUnit,
+                                      struct BasicBlock *bb);
+void dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+                                       struct BasicBlock *bb);
+char *dvmCompilerGetDalvikDisassembly(DecodedInstruction *insn, char *note);
+char *dvmCompilerGetSSAString(struct CompilationUnit *cUnit,
+                              struct SSARepresentation *ssaRep);
+void dvmCompilerDataFlowAnalysisDispatcher(struct CompilationUnit *cUnit,
+                void (*func)(struct CompilationUnit *, struct BasicBlock *));
+void dvmCompilerStateRefresh(void);
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const struct JitEntry *desc);
+void *dvmCompilerGetInterpretTemplate();
+#endif /* _DALVIK_VM_COMPILER */
diff --git a/vm/compiler/CompilerIR.h b/vm/compiler/CompilerIR.h
new file mode 100644
index 0000000..82b97e5
--- /dev/null
+++ b/vm/compiler/CompilerIR.h
@@ -0,0 +1,243 @@
+/*
+ * 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
+#define _DALVIK_VM_COMPILER_IR
+
+#include "codegen/Optimizer.h"
+
+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 (-1)
+
+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,
+    kMethodEntryBlock,
+    kTraceEntryBlock,
+    kDalvikByteCode,
+    kTraceExitBlock,
+    kMethodExitBlock,
+    kPCReconstruction,
+    kExceptionHandling,
+} BBType;
+
+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 = 256,
+    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
+} 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)
+
+typedef struct CallsiteInfo {
+    const ClassObject *clazz;
+    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;
+
+typedef struct BasicBlock {
+    int id;
+    int visited;
+    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 *next;            // Serial link for book keeping purposes
+    struct BasicBlockDataFlow *dataFlowInfo;
+} BasicBlock;
+
+struct LoopAnalysis;
+struct RegisterPool;
+
+typedef enum AssemblerStatus {
+    kSuccess,
+    kRetryAll,
+    kRetryHalve
+} AssemblerStatus;
+
+typedef struct CompilationUnit {
+    int numInsts;
+    int numBlocks;
+    BasicBlock **blockList;
+    const Method *method;
+    const JitTraceDescription *traceDesc;
+    LIR *firstLIRInsn;
+    LIR *lastLIRInsn;
+    LIR *wordList;
+    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 executionCount;                // Add code to count trace executions
+    bool hasLoop;                       // Contains a loop
+    bool hasInvoke;                     // Contains an invoke instruction
+    bool heapMemOp;                     // Mark mem ops for self verification
+    bool wholeMethod;
+    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;
+} 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);
+
+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 */
diff --git a/vm/compiler/CompilerInternals.h b/vm/compiler/CompilerInternals.h
new file mode 100644
index 0000000..9a30b34
--- /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
+#define _DALVIK_VM_COMPILER_INTERNAL
+
+#include "Dalvik.h"
+#include "CompilerUtility.h"
+#include "codegen/CompilerCodegen.h"
+#include "interp/Jit.h"
+
+#endif /* _DALVIK_VM_COMPILER_INTERNAL */
diff --git a/vm/compiler/CompilerUtility.h b/vm/compiler/CompilerUtility.h
new file mode 100644
index 0000000..551edb8
--- /dev/null
+++ b/vm/compiler/CompilerUtility.h
@@ -0,0 +1,57 @@
+/*
+ * 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
+#define _DALVIK_VM_COMPILER_UTILITY
+
+#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;
+    void **elemList;
+} GrowableList;
+
+#define GET_ELEM_N(LIST, TYPE, N) (((TYPE*) LIST->elemList)[N])
+
+struct LIR;
+
+void dvmInitGrowableList(GrowableList *gList, size_t initLength);
+void dvmInsertGrowableList(GrowableList *gList, void *elem);
+BitVector* dvmCompilerAllocBitVector(int startBits, bool expandable);
+bool dvmCompilerSetBit(BitVector* pBits, int num);
+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);
+
+#endif /* _DALVIK_COMPILER_UTILITY */
diff --git a/vm/compiler/Dataflow.c b/vm/compiler/Dataflow.c
new file mode 100644
index 0000000..89c5b35
--- /dev/null
+++ b/vm/compiler/Dataflow.c
@@ -0,0 +1,1459 @@
+/*
+ * 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/OpCodeNames.h"
+
+/*
+ * Main table containing data flow attributes for each bytecode. The first
+ * 256 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_DIRECT_EMPTY
+    DF_NOP,
+
+    // F1 OP_UNUSED_F1
+    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(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(DecodedInstruction *insn,
+                                      char *note)
+{
+    char buffer[256];
+    int opcode = insn->opCode;
+    int dfAttributes = dvmCompilerDataFlowAttributes[opcode];
+    char *ret;
+
+    buffer[0] = 0;
+    strcpy(buffer, dexGetOpcodeName(opcode));
+
+    if (note)
+        strcat(buffer, note);
+
+    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 {
+            snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vB);
+        }
+        if (dfAttributes & DF_C_IS_REG) {
+            snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vC);
+        }
+        else {
+            snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vC);
+        }
+    }
+    int length = strlen(buffer) + 1;
+    ret = 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 = 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 handleLiveInDef(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.
+ */
+void dvmCompilerFindLiveIn(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+    BitVector *useV, *defV, *liveInV;
+
+    if (bb->blockType != kDalvikByteCode &&
+        bb->blockType != kTraceEntryBlock) {
+        return;
+    }
+
+    useV = bb->dataFlowInfo->useV =
+        dvmCompilerAllocBitVector(cUnit->method->registersSize, false);
+    defV = bb->dataFlowInfo->defV =
+        dvmCompilerAllocBitVector(cUnit->method->registersSize, false);
+    liveInV = bb->dataFlowInfo->liveInV =
+        dvmCompilerAllocBitVector(cUnit->method->registersSize, 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) {
+            handleLiveInDef(defV, dInsn->vA);
+            if (dfAttributes & DF_DA_WIDE) {
+                handleLiveInDef(defV, dInsn->vA+1);
+            }
+        }
+    }
+}
+
+/* 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, (void *) 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 = 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 = 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 */
+void dvmCompilerDoSSAConversion(CompilationUnit *cUnit, BasicBlock *bb)
+{
+    MIR *mir;
+
+    if (bb->blockType != kDalvikByteCode && bb->blockType != kTraceEntryBlock) {
+        return;
+    }
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        mir->ssaRep = 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 = dvmCompilerNew(sizeof(int) * numUses, false);
+            mir->ssaRep->fpUse = 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 = dvmCompilerNew(sizeof(int) * numDefs, false);
+            mir->ssaRep->fpDef = 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);
+            }
+        }
+    }
+
+    bb->dataFlowInfo->dalvikToSSAMap =
+        dvmCompilerNew(sizeof(int) * cUnit->method->registersSize, false);
+
+    /* Take a snapshot of Dalvik->SSA mapping at the end of each block */
+    memcpy(bb->dataFlowInfo->dalvikToSSAMap, cUnit->dalvikToSSAMap,
+           sizeof(int) * cUnit->method->registersSize);
+}
+
+/* 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;
+}
+
+void 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 */
+}
+
+void 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 != kTraceEntryBlock) {
+        return;
+    }
+
+    /* If the bb doesn't have a phi it cannot contain an induction variable */
+    if (bb->firstMIRInsn == NULL ||
+        bb->firstMIRInsn->dalvikInsn.opCode != kMirOpPhi) {
+        return;
+    }
+
+    /* 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 (phi->dalvikInsn.opCode != 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 =
+                        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, (void *) 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 =
+                    dvmCompilerNew(sizeof(InductionVariableInfo),
+                                   false);
+                InductionVariableInfo *ivInfoOld = NULL ;
+
+                for (i = 0; i < ivList->numUsed; i++) {
+                    ivInfoOld = 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, (void *) ivInfo);
+            }
+        }
+    }
+}
+
+/* Setup the basic data structures for SSA conversion */
+void dvmInitializeSSAConversion(CompilationUnit *cUnit)
+{
+    int i;
+    int numDalvikReg = cUnit->method->registersSize;
+
+    cUnit->ssaToDalvikMap = 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,
+                              (void *) 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 = 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
+     */
+    for (i = 0; i < cUnit->numBlocks; i++) {
+        BasicBlock *bb = cUnit->blockList[i];
+        if (bb->blockType == kDalvikByteCode ||
+            bb->blockType == kTraceEntryBlock) {
+            bb->dataFlowInfo = dvmCompilerNew(sizeof(BasicBlockDataFlow), true);
+        }
+    }
+}
+
+void dvmCompilerDataFlowAnalysisDispatcher(CompilationUnit *cUnit,
+                void (*func)(CompilationUnit *, BasicBlock *))
+{
+    int i;
+    for (i = 0; i < cUnit->numBlocks; i++) {
+        BasicBlock *bb = cUnit->blockList[i];
+        (*func)(cUnit, bb);
+    }
+}
+
+/* Main entry point to do SSA conversion for non-loop traces */
+void dvmCompilerNonLoopAnalysis(CompilationUnit *cUnit)
+{
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion);
+}
diff --git a/vm/compiler/Dataflow.h b/vm/compiler/Dataflow.h
new file mode 100644
index 0000000..f3d3984
--- /dev/null
+++ b/vm/compiler/Dataflow.h
@@ -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.
+ */
+
+#ifndef _DALVIK_VM_DATAFLOW
+#define _DALVIK_VM_DATAFLOW
+
+#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;
+
+typedef struct InductionVariableInfo {
+    int ssaReg;
+    int basicSSAReg;
+    int m;
+    int c;
+    int inc;
+} 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 */
diff --git a/vm/compiler/Frontend.c b/vm/compiler/Frontend.c
new file mode 100644
index 0000000..525ec72
--- /dev/null
+++ b/vm/compiler/Frontend.c
@@ -0,0 +1,1313 @@
+/*
+ * 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/OpCode.h"
+#include "interp/Jit.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+
+/*
+ * Parse an instruction, return the length of the instruction
+ */
+static inline int parseInsn(const u2 *codePtr, DecodedInstruction *decInsn,
+                            bool printMe)
+{
+    u2 instr = *codePtr;
+    OpCode opcode = instr & 0xff;
+    int insnWidth;
+
+    // Don't parse instruction data
+    if (opcode == OP_NOP && instr != 0) {
+        return 0;
+    } else {
+        insnWidth = gDvm.instrWidth[opcode];
+        if (insnWidth < 0) {
+            insnWidth = -insnWidth;
+        }
+    }
+
+    dexDecodeInstruction(gDvm.instrFormat, codePtr, decInsn);
+    if (printMe) {
+        char *decodedString = dvmCompilerGetDalvikDisassembly(decInsn, NULL);
+        LOGD("%p: %#06x %s\n", codePtr, opcode, decodedString);
+    }
+    return insnWidth;
+}
+
+#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 = dexGetInstrFlags(gDvm.instrFlags, dalvikInsn->opCode);
+    int dalvikOpCode = dalvikInsn->opCode;
+
+    if ((flags & kInstrInvoke) &&
+        (dalvikOpCode != OP_INVOKE_DIRECT_EMPTY)) {
+        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 = 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) {
+        LOGE("%s%s is inlinable%s", method->clazz->descriptor, method->name,
+             (attributes & METHOD_IS_EMPTY) ? " empty" : "");
+    }
+
+    if (attributes & METHOD_IS_LEAF) {
+        LOGE("%s%s is leaf %d%s", method->clazz->descriptor, method->name,
+             insnSize, insnSize < 5 ? " (small)" : "");
+    }
+
+    if (attributes & (METHOD_IS_GETTER | METHOD_IS_SETTER)) {
+        LOGE("%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)) {
+        LOGE("%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.
+ */
+bool filterMethodByCallGraph(Thread *thread, const char *curMethodName)
+{
+    /* Crawl the Dalvik stack frames and compare the method name*/
+    StackSaveArea *ssaPtr = ((StackSaveArea *) thread->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) {
+                LOGD("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;
+}
+
+/*
+ * 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->frag.startOffset;
+    unsigned int numInsts = currRun->frag.numInsts;
+    const u2 *codePtr = dexCode->insns + curOffset;
+    int traceSize = 0;  // # of half-words
+    const u2 *startCodePtr = codePtr;
+    BasicBlock *startBB, *curBB, *lastBB;
+    int numBlocks = 0;
+    static int compilationId;
+    CompilationUnit cUnit;
+#if defined(WITH_JIT_TUNING)
+    CompilerMethodStats *methodStats;
+#endif
+
+    /* If we've already compiled this trace, just return success */
+    if (dvmJitGetCodeAddr(startCodePtr) && !info->discardResult) {
+        /*
+         * Make sure the codeAddress is NULL so that it won't clobber the
+         * existing entry.
+         */
+        info->codeAddress = NULL;
+        return true;
+    }
+
+    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;
+
+    /* Initialize the profile flag */
+    cUnit.executionCount = gDvmJit.profile;
+
+    /* Setup the method */
+    cUnit.method = desc->method;
+
+    /* Initialize the PC reconstruction list */
+    dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
+
+    /* Identify traces that we don't want to compile */
+    if (gDvmJit.methodTable) {
+        int len = strlen(desc->method->clazz->descriptor) +
+                  strlen(desc->method->name) + 1;
+        char *fullSignature = 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.includeSelectedMethod != methodFound) {
+            cUnit.allSingleStep = true;
+        } else {
+            /* Compile the trace as normal */
+
+            /* Print the method we cherry picked */
+            if (gDvmJit.includeSelectedMethod == true) {
+                cUnit.printMe = true;
+            }
+        }
+    }
+
+    /* Allocate the entry block */
+    lastBB = startBB = curBB = dvmCompilerNewBB(kTraceEntryBlock);
+    curBB->startOffset = curOffset;
+    curBB->id = numBlocks++;
+
+    curBB = dvmCompilerNewBB(kDalvikByteCode);
+    curBB->startOffset = curOffset;
+    curBB->id = numBlocks++;
+
+    /* Make the first real dalvik block the fallthrough of the entry block */
+    startBB->fallThrough = curBB;
+    lastBB->next = curBB;
+    lastBB = curBB;
+
+    if (cUnit.printMe) {
+        LOGD("--------\nCompiler: Building trace for %s, offset 0x%x\n",
+             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 = 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 = dexGetInstrFlags(gDvm.instrFlags, insn->dalvikInsn.opCode);
+
+        if ((flags & kInstrInvoke) &&
+            (insn->dalvikInsn.opCode != OP_INVOKE_DIRECT_EMPTY)) {
+            assert(numInsts == 1);
+            CallsiteInfo *callsiteInfo =
+                dvmCompilerNew(sizeof(CallsiteInfo), true);
+            callsiteInfo->clazz = currRun[1].meta;
+            callsiteInfo->method = currRun[2].meta;
+            insn->meta.callsiteInfo = callsiteInfo;
+        }
+
+        /* Instruction limit reached - terminate the trace here */
+        if (cUnit.numInsts >= numMaxInsts) {
+            break;
+        }
+        if (--numInsts == 0) {
+            if (currRun->frag.runEnd) {
+                break;
+            } else {
+                /* Advance to the next trace description (ie non-meta info) */
+                do {
+                    currRun++;
+                } while (!currRun->frag.isCode);
+
+                /* Dummy end-of-run marker seen */
+                if (currRun->frag.numInsts == 0) {
+                    break;
+                }
+
+                curBB = dvmCompilerNewBB(kDalvikByteCode);
+                lastBB->next = curBB;
+                lastBB = curBB;
+                curBB->id = numBlocks++;
+                curOffset = currRun->frag.startOffset;
+                numInsts = currRun->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.
+     */
+    for (curBB = startBB; curBB; curBB = curBB->next) {
+        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 = dexGetInstrFlags(gDvm.instrFlags,
+                                     lastInsn->dalvikInsn.opCode);
+
+        if (flags & kInstrInvoke) {
+            cUnit.hasInvoke = true;
+        }
+
+        /* No backward branch in the trace - start searching the next BB */
+        for (searchBB = curBB->next; searchBB; searchBB = searchBB->next) {
+            if (targetOffset == searchBB->startOffset) {
+                curBB->taken = searchBB;
+            }
+            if (fallThroughOffset == searchBB->startOffset) {
+                curBB->fallThrough = searchBB;
+
+                /*
+                 * 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.
+         *
+         * NOTE: INVOKE_DIRECT_EMPTY is actually not an invoke but a nop
+         */
+        curBB->needFallThroughBranch =
+            ((flags & (kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
+                       kInstrInvoke)) == 0) ||
+            (lastInsn->dalvikInsn.opCode == OP_INVOKE_DIRECT_EMPTY);
+
+        /* Only form a loop if JIT_OPT_NO_LOOP is not set */
+        if (curBB->taken == NULL &&
+            curBB->fallThrough == NULL &&
+            flags == (kInstrCanBranch | kInstrCanContinue) &&
+            fallThroughOffset == startBB->startOffset &&
+            JIT_OPT_NO_LOOP != (optHints & JIT_OPT_NO_LOOP)) {
+            BasicBlock *loopBranch = curBB;
+            BasicBlock *exitBB;
+            BasicBlock *exitChainingCell;
+
+            if (cUnit.printMe) {
+                LOGD("Natural loop detected!");
+            }
+            exitBB = dvmCompilerNewBB(kTraceExitBlock);
+            lastBB->next = exitBB;
+            lastBB = exitBB;
+
+            exitBB->startOffset = targetOffset;
+            exitBB->id = numBlocks++;
+            exitBB->needFallThroughBranch = true;
+
+            loopBranch->taken = exitBB;
+#if defined(WITH_SELF_VERIFICATION)
+            BasicBlock *backwardCell =
+                dvmCompilerNewBB(kChainingCellBackwardBranch);
+            lastBB->next = backwardCell;
+            lastBB = backwardCell;
+
+            backwardCell->startOffset = startBB->startOffset;
+            backwardCell->id = numBlocks++;
+            loopBranch->fallThrough = backwardCell;
+#elif defined(WITH_JIT_TUNING)
+            if (gDvmJit.profile) {
+                BasicBlock *backwardCell =
+                    dvmCompilerNewBB(kChainingCellBackwardBranch);
+                lastBB->next = backwardCell;
+                lastBB = backwardCell;
+
+                backwardCell->startOffset = startBB->startOffset;
+                backwardCell->id = numBlocks++;
+                loopBranch->fallThrough = backwardCell;
+            } else {
+                loopBranch->fallThrough = startBB->next;
+            }
+#else
+            loopBranch->fallThrough = startBB->next;
+#endif
+
+            /* Create the chaining cell as the fallthrough of the exit block */
+            exitChainingCell = dvmCompilerNewBB(kChainingCellNormal);
+            lastBB->next = exitChainingCell;
+            lastBB = exitChainingCell;
+
+            exitChainingCell->startOffset = targetOffset;
+            exitChainingCell->id = numBlocks++;
+
+            exitBB->fallThrough = exitChainingCell;
+
+            cUnit.hasLoop = true;
+        }
+
+        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);
+                lastBB->next = caseChain;
+                lastBB = caseChain;
+
+                caseChain->startOffset = lastInsn->offset + targets[i];
+                caseChain->id = numBlocks++;
+            }
+
+            /* One more chaining cell for the default case */
+            BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal);
+            lastBB->next = caseChain;
+            lastBB = caseChain;
+
+            caseChain->startOffset = lastInsn->offset + lastInsn->width;
+            caseChain->id = numBlocks++;
+        /* Fallthrough block not included in the trace */
+        } else if (!isUnconditionalBranch(lastInsn) &&
+                   curBB->fallThrough == NULL) {
+            /*
+             * 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) {
+                lastBB->next = dvmCompilerNewBB(kChainingCellHot);
+            } else {
+                lastBB->next = dvmCompilerNewBB(kChainingCellNormal);
+            }
+            lastBB = lastBB->next;
+            lastBB->id = numBlocks++;
+            lastBB->startOffset = fallThroughOffset;
+            curBB->fallThrough = lastBB;
+        }
+        /* Target block not included in the trace */
+        if (curBB->taken == NULL &&
+            (isGoto(lastInsn) || isInvoke ||
+            (targetOffset != UNKNOWN_TARGET && targetOffset != curOffset))) {
+            BasicBlock *newBB;
+            if (isInvoke) {
+                /* Monomorphic callee */
+                if (callee) {
+                    newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton);
+                    newBB->startOffset = 0;
+                    newBB->containingMethod = callee;
+                /* Will resolve at runtime */
+                } else {
+                    newBB = dvmCompilerNewBB(kChainingCellInvokePredicted);
+                    newBB->startOffset = 0;
+                }
+            /* For unconditional branches, request a hot chaining cell */
+            } else {
+#if !defined(WITH_SELF_VERIFICATION)
+                newBB = dvmCompilerNewBB(flags & kInstrUnconditional ?
+                                                  kChainingCellHot :
+                                                  kChainingCellNormal);
+                newBB->startOffset = targetOffset;
+#else
+                /* Handle branches that branch back into the block */
+                if (targetOffset >= curBB->firstMIRInsn->offset &&
+                    targetOffset <= curBB->lastMIRInsn->offset) {
+                    newBB = dvmCompilerNewBB(kChainingCellBackwardBranch);
+                } else {
+                    newBB = dvmCompilerNewBB(flags & kInstrUnconditional ?
+                                                      kChainingCellHot :
+                                                      kChainingCellNormal);
+                }
+                newBB->startOffset = targetOffset;
+#endif
+            }
+            newBB->id = numBlocks++;
+            curBB->taken = newBB;
+            lastBB->next = newBB;
+            lastBB = newBB;
+        }
+    }
+
+    /* Now create a special block to host PC reconstruction code */
+    lastBB->next = dvmCompilerNewBB(kPCReconstruction);
+    lastBB = lastBB->next;
+    lastBB->id = numBlocks++;
+
+    /* And one final block that publishes the PC and raise the exception */
+    lastBB->next = dvmCompilerNewBB(kExceptionHandling);
+    lastBB = lastBB->next;
+    lastBB->id = numBlocks++;
+
+    if (cUnit.printMe) {
+        char* signature = dexProtoCopyMethodDescriptor(&desc->method->prototype);
+        LOGD("TRACEINFO (%d): 0x%08x %s%s.%s 0x%x %d of %d, %d blocks",
+            compilationId,
+            (intptr_t) desc->method->insns,
+            desc->method->clazz->descriptor,
+            desc->method->name,
+            signature,
+            desc->trace[0].frag.startOffset,
+            traceSize,
+            dexCode->insnsSize,
+            numBlocks);
+        free(signature);
+    }
+
+    BasicBlock **blockList;
+
+    cUnit.traceDesc = desc;
+    cUnit.numBlocks = numBlocks;
+    blockList = cUnit.blockList =
+        dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true);
+
+    int i;
+
+    for (i = 0, curBB = startBB; i < numBlocks; i++) {
+        blockList[i] = curBB;
+        curBB = curBB->next;
+    }
+    /* Make sure all blocks are added to the cUnit */
+    assert(curBB == NULL);
+
+    /* 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);
+    }
+
+    /* Preparation for SSA conversion */
+    dvmInitializeSSAConversion(&cUnit);
+
+    if (cUnit.hasLoop) {
+        /*
+         * Loop is not optimizable (for example lack of a single induction
+         * variable), punt and recompile the trace with loop optimization
+         * disabled.
+         */
+        bool loopOpt = dvmCompilerLoopOpt(&cUnit);
+        if (loopOpt == false) {
+            if (cUnit.printMe) {
+                LOGD("Loop is not optimizable - retry codegen");
+            }
+            /* Reset the compiler resource pool */
+            dvmCompilerArenaReset();
+            return dvmCompileTrace(desc, cUnit.numInsts, info, bailPtr,
+                                   optHints | JIT_OPT_NO_LOOP);
+        }
+    }
+    else {
+        dvmCompilerNonLoopAnalysis(&cUnit);
+    }
+
+    dvmCompilerInitializeRegAlloc(&cUnit);  // Needs to happen after SSA naming
+
+    if (cUnit.printMe) {
+        dvmCompilerDumpCompilationUnit(&cUnit);
+    }
+
+    /* Allocate Registers */
+    dvmCompilerRegAlloc(&cUnit);
+
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(&cUnit);
+
+    /* Convert LIR into machine code. Loop for recoverable retries */
+    do {
+        dvmCompilerAssembleLIR(&cUnit, info);
+        cUnit.assemblerRetries++;
+        if (cUnit.printMe && cUnit.assemblerStatus != kSuccess)
+            LOGD("Assembler abort #%d on %d",cUnit.assemblerRetries,
+                  cUnit.assemblerStatus);
+    } while (cUnit.assemblerStatus == kRetryAll);
+
+    if (cUnit.printMe) {
+        dvmCompilerCodegenDump(&cUnit);
+        LOGD("End %s%s, %d Dalvik instructions",
+             desc->method->clazz->descriptor, desc->method->name,
+             cUnit.numInsts);
+    }
+
+    /* Reset the compiler resource pool */
+    dvmCompilerArenaReset();
+
+    if (cUnit.assemblerStatus == kRetryHalve) {
+        /* Halve the instruction count and start from the top */
+        return dvmCompileTrace(desc, cUnit.numInsts / 2, info, bailPtr,
+                               optHints);
+    }
+
+    assert(cUnit.assemblerStatus == kSuccess);
+#if defined(WITH_JIT_TUNING)
+    methodStats->nativeSize += cUnit.totalSize;
+#endif
+    return info->codeAddress != NULL;
+}
+
+/*
+ * 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 = (void*)
+              (method->clazz->pDvmDex->pResClasses[insn->vB]);
+
+            /* Class hasn't been initialized yet */
+            if (classPtr == NULL) {
+                return false;
+            }
+            return true;
+        }
+        case OP_SGET_OBJECT:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_CHAR:
+        case OP_SGET_BYTE:
+        case OP_SGET_SHORT:
+        case OP_SGET:
+        case OP_SGET_WIDE:
+        case OP_SPUT_OBJECT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_SHORT:
+        case OP_SPUT:
+        case OP_SPUT_WIDE: {
+            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;
+    }
+}
+
+/*
+ * 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(CompilationUnit *cUnit, const Method *method,
+                      JitTranslationInfo *info)
+{
+    const DexCode *dexCode = dvmGetMethodCode(method);
+    const u2 *codePtr = dexCode->insns;
+    const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+    int blockID = 0;
+    unsigned int curOffset = 0;
+
+    /* If we've already compiled this trace, just return success */
+    if (dvmJitGetCodeAddr(codePtr) && !info->discardResult) {
+        return true;
+    }
+
+    /* Doing method-based compilation */
+    cUnit->wholeMethod = true;
+
+    BasicBlock *firstBlock = dvmCompilerNewBB(kDalvikByteCode);
+    firstBlock->id = blockID++;
+
+    /* Allocate the bit-vector to track the beginning of basic blocks */
+    BitVector *bbStartAddr = dvmCompilerAllocBitVector(dexCode->insnsSize+1,
+                                                       false);
+    dvmCompilerSetBit(bbStartAddr, 0);
+
+    int numInvokeTargets = 0;
+
+    /*
+     * Sequentially go through every instruction first and put them in a single
+     * basic block. Identify block boundaries at the mean time.
+     */
+    while (codePtr < codeEnd) {
+        MIR *insn = dvmCompilerNew(sizeof(MIR), true);
+        insn->offset = curOffset;
+        int width = parseInsn(codePtr, &insn->dalvikInsn, false);
+        bool isInvoke = false;
+        const Method *callee;
+        insn->width = width;
+
+        /* Terminate when the data section is seen */
+        if (width == 0)
+            break;
+
+        if (!dvmCompilerCanIncludeThisInstruction(cUnit->method,
+						  &insn->dalvikInsn)) {
+            return false;
+        }
+
+        dvmCompilerAppendMIR(firstBlock, insn);
+        /*
+         * Check whether this is a block ending instruction and whether it
+         * suggests the start of a new block
+         */
+        unsigned int target = curOffset;
+
+        /*
+         * If findBlockBoundary returns true, it means the current instruction
+         * is terminating the current block. If it is a branch, the target
+         * address will be recorded in target.
+         */
+        if (findBlockBoundary(method, insn, curOffset, &target, &isInvoke,
+                              &callee)) {
+            dvmCompilerSetBit(bbStartAddr, curOffset + width);
+            /* Each invoke needs a chaining cell block */
+            if (isInvoke) {
+                numInvokeTargets++;
+            }
+            /* A branch will end the current block */
+            else if (target != curOffset && target != UNKNOWN_TARGET) {
+                dvmCompilerSetBit(bbStartAddr, target);
+            }
+        }
+
+        codePtr += width;
+        /* each bit represents 16-bit quantity */
+        curOffset += width;
+    }
+
+    /*
+     * The number of blocks will be equal to the number of bits set to 1 in the
+     * bit vector minus 1, because the bit representing the location after the
+     * last instruction is set to one.
+     *
+     * We also add additional blocks for invoke chaining and the number is
+     * denoted by numInvokeTargets.
+     */
+    int numBlocks = dvmCountSetBits(bbStartAddr);
+    if (dvmIsBitSet(bbStartAddr, dexCode->insnsSize)) {
+        numBlocks--;
+    }
+
+    BasicBlock **blockList;
+    blockList = cUnit->blockList =
+        dvmCompilerNew(sizeof(BasicBlock *) * (numBlocks + numInvokeTargets),
+                       true);
+
+    /*
+     * Register the first block onto the list and start splitting it into
+     * sub-blocks.
+     */
+    blockList[0] = firstBlock;
+    cUnit->numBlocks = 1;
+
+    int i;
+    for (i = 0; i < numBlocks; i++) {
+        MIR *insn;
+        BasicBlock *curBB = blockList[i];
+        curOffset = curBB->lastMIRInsn->offset;
+
+        for (insn = curBB->firstMIRInsn->next; insn; insn = insn->next) {
+            /* Found the beginning of a new block, see if it is created yet */
+            if (dvmIsBitSet(bbStartAddr, insn->offset)) {
+                int j;
+                for (j = 0; j < cUnit->numBlocks; j++) {
+                    if (blockList[j]->firstMIRInsn->offset == insn->offset)
+                        break;
+                }
+
+                /* Block not split yet - do it now */
+                if (j == cUnit->numBlocks) {
+                    BasicBlock *newBB = dvmCompilerNewBB(kDalvikByteCode);
+                    newBB->id = blockID++;
+                    newBB->firstMIRInsn = insn;
+                    newBB->startOffset = insn->offset;
+                    newBB->lastMIRInsn = curBB->lastMIRInsn;
+                    curBB->lastMIRInsn = insn->prev;
+                    insn->prev->next = NULL;
+                    insn->prev = NULL;
+
+                    /*
+                     * If the insn is not an unconditional branch, set up the
+                     * fallthrough link.
+                     */
+                    if (!isUnconditionalBranch(curBB->lastMIRInsn)) {
+                        curBB->fallThrough = newBB;
+                    }
+
+                    /*
+                     * Fallthrough block of an invoke instruction needs to be
+                     * aligned to 4-byte boundary (alignment instruction to be
+                     * inserted later.
+                     */
+                    if (dexGetInstrFlags(gDvm.instrFlags,
+                           curBB->lastMIRInsn->dalvikInsn.opCode) &
+                        kInstrInvoke) {
+                        newBB->isFallThroughFromInvoke = true;
+                    }
+
+                    /* enqueue the new block */
+                    blockList[cUnit->numBlocks++] = newBB;
+                    break;
+                }
+            }
+        }
+    }
+
+    if (numBlocks != cUnit->numBlocks) {
+        LOGE("Expect %d vs %d basic blocks\n", numBlocks, cUnit->numBlocks);
+        dvmCompilerAbort(cUnit);
+    }
+
+    /* Connect the basic blocks through the taken links */
+    for (i = 0; i < numBlocks; i++) {
+        BasicBlock *curBB = blockList[i];
+        MIR *insn = curBB->lastMIRInsn;
+        unsigned int target = insn->offset;
+        bool isInvoke = false;
+        const Method *callee = NULL;
+
+        findBlockBoundary(method, insn, target, &target, &isInvoke, &callee);
+
+        /* Found a block ended on a branch (not invoke) */
+        if (isInvoke == false && target != insn->offset) {
+            int j;
+            /* Forward branch */
+            if (target > insn->offset) {
+                j = i + 1;
+            } else {
+                /* Backward branch */
+                j = 0;
+            }
+            for (; j < numBlocks; j++) {
+                if (blockList[j]->firstMIRInsn->offset == target) {
+                    curBB->taken = blockList[j];
+                    break;
+                }
+            }
+        }
+
+        if (isInvoke) {
+            BasicBlock *newBB;
+            /* Monomorphic callee */
+            if (callee) {
+                newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton);
+                newBB->startOffset = 0;
+                newBB->containingMethod = callee;
+            /* Will resolve at runtime */
+            } else {
+                newBB = dvmCompilerNewBB(kChainingCellInvokePredicted);
+                newBB->startOffset = 0;
+            }
+            newBB->id = blockID++;
+            curBB->taken = newBB;
+            /* enqueue the new block */
+            blockList[cUnit->numBlocks++] = newBB;
+        }
+    }
+
+    if (cUnit->numBlocks != numBlocks + numInvokeTargets) {
+        LOGE("Expect %d vs %d total blocks\n", numBlocks + numInvokeTargets,
+             cUnit->numBlocks);
+        dvmCompilerDumpCompilationUnit(cUnit);
+        dvmCompilerAbort(cUnit);
+    }
+
+    /* Set the instruction set to use (NOTE: later components may change it) */
+    cUnit->instructionSet = dvmCompilerInstructionSet();
+
+    /* Preparation for SSA conversion */
+    dvmInitializeSSAConversion(cUnit);
+
+    /* SSA analysis */
+    dvmCompilerNonLoopAnalysis(cUnit);
+
+    /* Needs to happen after SSA naming */
+    dvmCompilerInitializeRegAlloc(cUnit);
+
+    /* Allocate Registers */
+    dvmCompilerRegAlloc(cUnit);
+
+    /* Convert MIR to LIR, etc. */
+    dvmCompilerMIR2LIR(cUnit);
+
+    /* Convert LIR into machine code. */
+    dvmCompilerAssembleLIR(cUnit, info);
+
+    if (cUnit->assemblerStatus != kSuccess) {
+        return false;
+    }
+
+    dvmCompilerDumpCompilationUnit(cUnit);
+
+    dvmCompilerArenaReset();
+
+    return info->codeAddress != NULL;
+}
diff --git a/vm/compiler/InlineTransformation.c b/vm/compiler/InlineTransformation.c
new file mode 100644
index 0000000..ce45b8b
--- /dev/null
+++ b/vm/compiler/InlineTransformation.c
@@ -0,0 +1,366 @@
+/*
+ * 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/OpCodeNames.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 void inlineGetter(CompilationUnit *cUnit,
+                         const Method *calleeMethod,
+                         MIR *invokeMIR,
+                         BasicBlock *invokeBB,
+                         bool isPredicted,
+                         bool isRange)
+{
+    BasicBlock *moveResultBB = invokeBB->fallThrough;
+    MIR *moveResultMIR = moveResultBB->firstMIRInsn;
+    MIR *newGetterMIR = dvmCompilerNew(sizeof(MIR), true);
+    DecodedInstruction getterInsn;
+
+    dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &getterInsn);
+
+    if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
+        return;
+
+    /*
+     * 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;
+    }
+
+    int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opCode];
+
+    /* Expecting vA to be the destination register */
+    if (dfFlags & (DF_UA | DF_UA_WIDE)) {
+        LOGE("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 = gDvm.instrWidth[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 = dvmCompilerNew(sizeof(MIR), true);
+        *invokeMIRSlow = *invokeMIR;
+        invokeMIR->dalvikInsn.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;
+}
+
+static void inlineSetter(CompilationUnit *cUnit,
+                         const Method *calleeMethod,
+                         MIR *invokeMIR,
+                         BasicBlock *invokeBB,
+                         bool isPredicted,
+                         bool isRange)
+{
+    MIR *newSetterMIR = dvmCompilerNew(sizeof(MIR), true);
+    DecodedInstruction setterInsn;
+
+    dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &setterInsn);
+
+    if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
+        return;
+
+    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 = gDvm.instrWidth[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 = dvmCompilerNew(sizeof(MIR), true);
+        *invokeMIRSlow = *invokeMIR;
+        invokeMIR->dalvikInsn.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;
+}
+
+static void tryInlineSingletonCallsite(CompilationUnit *cUnit,
+                                       const Method *calleeMethod,
+                                       MIR *invokeMIR,
+                                       BasicBlock *invokeBB,
+                                       bool isRange)
+{
+    /* Not a Java method */
+    if (dvmIsNativeMethod(calleeMethod)) return;
+
+    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;
+    }
+
+    if (methodStats->attributes & METHOD_IS_GETTER) {
+        inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange);
+        return;
+    } else if (methodStats->attributes & METHOD_IS_SETTER) {
+        inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange);
+        return;
+    }
+}
+
+static void inlineEmptyVirtualCallee(CompilationUnit *cUnit,
+                                     const Method *calleeMethod,
+                                     MIR *invokeMIR,
+                                     BasicBlock *invokeBB)
+{
+    MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
+    *invokeMIRSlow = *invokeMIR;
+    invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction;
+
+    dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
+    invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+}
+
+static void tryInlineVirtualCallsite(CompilationUnit *cUnit,
+                                     const Method *calleeMethod,
+                                     MIR *invokeMIR,
+                                     BasicBlock *invokeBB,
+                                     bool isRange)
+{
+    /* Not a Java method */
+    if (dvmIsNativeMethod(calleeMethod)) return;
+
+    CompilerMethodStats *methodStats =
+        dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+
+    /* Empty callee - do nothing by checking the clazz pointer */
+    if (methodStats->attributes & METHOD_IS_EMPTY) {
+        inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, invokeBB);
+        return;
+    }
+
+    if (methodStats->attributes & METHOD_IS_GETTER) {
+        inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange);
+        return;
+    } else if (methodStats->attributes & METHOD_IS_SETTER) {
+        inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange);
+        return;
+    }
+}
+
+
+void dvmCompilerInlineMIR(CompilationUnit *cUnit)
+{
+    int i;
+    bool isRange = false;
+
+    /*
+     * Analyze the basic block containing an invoke to see if it can be inlined
+     */
+    for (i = 0; i < cUnit->numBlocks; i++) {
+        BasicBlock *bb = cUnit->blockList[i];
+        if (bb->blockType != kDalvikByteCode)
+            continue;
+        MIR *lastMIRInsn = bb->lastMIRInsn;
+        int opCode = lastMIRInsn->dalvikInsn.opCode;
+        int flags = dexGetInstrFlags(gDvm.instrFlags, opCode);
+
+        /* No invoke - continue */
+        if ((flags & kInstrInvoke) == 0)
+            continue;
+
+        /* Not a real invoke - continue */
+        if (opCode == OP_INVOKE_DIRECT_EMPTY)
+            continue;
+
+        /*
+         * If the invoke itself is selected for single stepping, don't bother
+         * to inline it.
+         */
+        if (SINGLE_STEP_OP(opCode))
+            continue;
+
+        const Method *calleeMethod;
+
+        switch (opCode) {
+            case OP_INVOKE_SUPER:
+            case OP_INVOKE_DIRECT:
+            case OP_INVOKE_STATIC:
+            case OP_INVOKE_SUPER_QUICK:
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            case OP_INVOKE_SUPER_RANGE:
+            case OP_INVOKE_DIRECT_RANGE:
+            case OP_INVOKE_STATIC_RANGE:
+            case OP_INVOKE_SUPER_QUICK_RANGE:
+                isRange = true;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            default:
+                calleeMethod = NULL;
+                break;
+        }
+
+        if (calleeMethod) {
+            tryInlineSingletonCallsite(cUnit, calleeMethod, lastMIRInsn, bb,
+                                       isRange);
+            return;
+        }
+
+        switch (opCode) {
+            case OP_INVOKE_VIRTUAL:
+            case OP_INVOKE_VIRTUAL_QUICK:
+            case OP_INVOKE_INTERFACE:
+                isRange = false;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            case OP_INVOKE_VIRTUAL_RANGE:
+            case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+            case OP_INVOKE_INTERFACE_RANGE:
+                isRange = true;
+                calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+                break;
+            default:
+                break;
+        }
+
+        if (calleeMethod) {
+            tryInlineVirtualCallsite(cUnit, calleeMethod, lastMIRInsn, bb,
+                                     isRange);
+            return;
+        }
+    }
+}
diff --git a/vm/compiler/IntermediateRep.c b/vm/compiler/IntermediateRep.c
new file mode 100644
index 0000000..825a690
--- /dev/null
+++ b/vm/compiler/IntermediateRep.c
@@ -0,0 +1,121 @@
+/*
+ * 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)
+{
+    BasicBlock *bb = dvmCompilerNew(sizeof(BasicBlock), true);
+    bb->blockType = blockType;
+    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.c b/vm/compiler/Loop.c
new file mode 100644
index 0000000..918d955
--- /dev/null
+++ b/vm/compiler/Loop.c
@@ -0,0 +1,526 @@
+/*
+ * 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)
+
+/*
+ * Given the current simple natural loops, the phi node placement can be
+ * determined in the following fashion:
+ *                    entry (B0)
+ *              +---v   v
+ *              |  loop body (B1)
+ *              |       v
+ *              |  loop back (B2)
+ *              +---+   v
+ *                     exit (B3)
+ *
+ *  1) Add live-ins of B1 to B0 as defs
+ *  2) The intersect of defs(B0)/defs(B1) and defs(B2)/def(B0) are the variables
+ *     that need PHI nodes in B1.
+ */
+static void handlePhiPlacement(CompilationUnit *cUnit)
+{
+    BasicBlock *entry = cUnit->blockList[0];
+    BasicBlock *loopBody = cUnit->blockList[1];
+    BasicBlock *loopBranch = cUnit->blockList[2];
+    dvmCopyBitVector(entry->dataFlowInfo->defV,
+                     loopBody->dataFlowInfo->liveInV);
+
+    BitVector *phiV = dvmCompilerAllocBitVector(cUnit->method->registersSize,
+                                                false);
+    dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV,
+                           loopBody->dataFlowInfo->defV);
+    dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV,
+                           loopBranch->dataFlowInfo->defV);
+
+    /* Insert the PHI MIRs */
+    int i;
+    for (i = 0; i < cUnit->method->registersSize; i++) {
+        if (!dvmIsBitSet(phiV, i)) {
+            continue;
+        }
+        MIR *phi = dvmCompilerNew(sizeof(MIR), true);
+        phi->dalvikInsn.opCode = kMirOpPhi;
+        phi->dalvikInsn.vA = i;
+        dvmCompilerPrependMIR(loopBody, phi);
+    }
+}
+
+static void fillPhiNodeContents(CompilationUnit *cUnit)
+{
+    BasicBlock *entry = cUnit->blockList[0];
+    BasicBlock *loopBody = cUnit->blockList[1];
+    BasicBlock *loopBranch = cUnit->blockList[2];
+    MIR *mir;
+
+    for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) {
+        if (mir->dalvikInsn.opCode != kMirOpPhi) break;
+        int dalvikReg = mir->dalvikInsn.vA;
+
+        mir->ssaRep->numUses = 2;
+        mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * 2, false);
+        mir->ssaRep->uses[0] =
+            DECODE_REG(entry->dataFlowInfo->dalvikToSSAMap[dalvikReg]);
+        mir->ssaRep->uses[1] =
+            DECODE_REG(loopBranch->dataFlowInfo->dalvikToSSAMap[dalvikReg]);
+    }
+
+
+}
+
+#if 0
+/* Debugging routines */
+static void dumpConstants(CompilationUnit *cUnit)
+{
+    int i;
+    for (i = 0; i < cUnit->numSSARegs; i++) {
+        if (dvmIsBitSet(cUnit->isConstantV, i)) {
+            int subNReg = dvmConvertSSARegToDalvik(cUnit, i);
+            LOGE("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;
+    int *ssaToDalvikMap = (int *) cUnit->ssaToDalvikMap->elemList;
+
+    for (i = 0; i < ivList->numUsed; i++) {
+        InductionVariableInfo *ivInfo = ivList->elemList[i];
+        /* Basic IV */
+        if (ivInfo->ssaReg == ivInfo->basicSSAReg) {
+            LOGE("BIV %d: s%d(v%d) + %d", i,
+                 ivInfo->ssaReg,
+                 ssaToDalvikMap[ivInfo->ssaReg] & 0xffff,
+                 ivInfo->inc);
+        /* Dependent IV */
+        } else {
+            LOGE("DIV %d: s%d(v%d) = %d * s%d(v%d) + %d", i,
+                 ivInfo->ssaReg,
+                 ssaToDalvikMap[ivInfo->ssaReg] & 0xffff,
+                 ivInfo->m,
+                 ivInfo->basicSSAReg,
+                 ssaToDalvikMap[ivInfo->basicSSAReg] & 0xffff,
+                 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));
+        LOGE("Array access %d", i);
+        LOGE("  arrayReg %d", arrayReg);
+        LOGE("  idxReg %d", idxReg);
+        LOGE("  endReg %d", loopAnalysis->endConditionReg);
+        LOGE("  maxC %d", arrayAccessInfo->maxC);
+        LOGE("  minC %d", arrayAccessInfo->minC);
+        LOGE("  opcode %d", loopAnalysis->loopBranchOpcode);
+    }
+}
+
+#endif
+
+/*
+ * 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) If it is a count-up loop, the condition is GE/GT, or LE/LT/LEZ/LTZ for
+ *    a count-down loop.
+ *
+ * Return false if the loop is not optimizable.
+ */
+static bool isLoopOptimizable(CompilationUnit *cUnit)
+{
+    unsigned int i;
+    BasicBlock *loopBranch = cUnit->blockList[2];
+    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;
+        }
+    }
+
+    MIR *branch = loopBranch->lastMIRInsn;
+    OpCode opCode = branch->dalvikInsn.opCode;
+
+    /*
+     * If the instruction is not accessing the IV as the first operand, return
+     * false.
+     */
+    if (branch->ssaRep->numUses == 0 || branch->ssaRep->numDefs != 0) {
+        return false;
+    }
+
+    /*
+     * If the first operand of the comparison is not the basic induction
+     * variable, return false.
+     */
+    if (branch->ssaRep->uses[0] != loopAnalysis->ssaBIV) {
+        return false;
+    }
+
+    if (loopAnalysis->isCountUpLoop) {
+        /*
+         * If the condition op is not > or >=, this is not an optimization
+         * candidate.
+         */
+        if (opCode != OP_IF_GT && opCode != OP_IF_GE) {
+            return false;
+        }
+        /*
+         * If the comparison is not between the BIV and a loop invariant,
+         * return false. endReg 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
+         */
+        int endReg = dvmConvertSSARegToDalvik(cUnit, branch->ssaRep->uses[1]);
+        if (DECODE_SUB(endReg) != 0 &&
+            !dvmIsBitSet(cUnit->isConstantV, branch->ssaRep->uses[1])) {
+            return false;
+        }
+        loopAnalysis->endConditionReg = DECODE_REG(endReg);
+    } else  {
+        /*
+         * If the condition op is not < or <=, this is not an optimization
+         * candidate.
+         */
+        if (opCode == OP_IF_LT || opCode == OP_IF_LE) {
+            /*
+             * If the comparison is not between the BIV and a loop invariant,
+             * return false.
+             */
+            int endReg = dvmConvertSSARegToDalvik(cUnit,
+                                                  branch->ssaRep->uses[1]);
+
+            if (DECODE_SUB(endReg) != 0) {
+                return false;
+            }
+            loopAnalysis->endConditionReg = DECODE_REG(endReg);
+        } else if (opCode != OP_IF_LTZ && opCode != OP_IF_LEZ) {
+            return false;
+        }
+    }
+    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 =
+                    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,
+                                      arrayAccessInfo);
+            }
+            break;
+        }
+    }
+}
+
+/* Returns true if the loop body cannot throw any exceptions */
+static bool doLoopBodyCodeMotion(CompilationUnit *cUnit)
+{
+    BasicBlock *loopBody = cUnit->blockList[1];
+    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 (dInsn->opCode > 255) continue;
+
+        int instrFlags = dexGetInstrFlags(gDvm.instrFlags, 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;
+                    LOGE("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->blockList[0];
+    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 = dvmCompilerNew(sizeof(MIR), true);
+        rangeCheckMIR->dalvikInsn.opCode = (loopAnalysis->isCountUpLoop) ?
+            kMirOpNullNRangeUpCheck : 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 = dvmCompilerNew(sizeof(MIR), true);
+            boundCheckMIR->dalvikInsn.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 = dvmCompilerNew(sizeof(MIR), true);
+                boundCheckMIR->dalvikInsn.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 = dvmCompilerNew(sizeof(MIR), true);
+                    boundCheckMIR->dalvikInsn.opCode = kMirOpPunt;
+                    dvmCompilerAppendMIR(entry, boundCheckMIR);
+                }
+            } else if (loopAnalysis->loopBranchOpcode == OP_IF_LEZ) {
+                /* Array index will fall below 0 */
+                if (globalMinC < -1) {
+                    MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+                    boundCheckMIR->dalvikInsn.opCode = kMirOpPunt;
+                    dvmCompilerAppendMIR(entry, boundCheckMIR);
+                }
+            } else {
+                LOGE("Jit: bad case in genHoistedChecks");
+                dvmCompilerAbort(cUnit);
+            }
+        }
+
+    }
+}
+
+/*
+ * Main entry point to do loop optimization.
+ * Return false if sanity checks for loop formation/optimization failed.
+ */
+bool dvmCompilerLoopOpt(CompilationUnit *cUnit)
+{
+    LoopAnalysis *loopAnalysis = dvmCompilerNew(sizeof(LoopAnalysis), true);
+
+    assert(cUnit->blockList[0]->blockType == kTraceEntryBlock);
+    assert(cUnit->blockList[2]->blockType == kDalvikByteCode);
+    assert(cUnit->blockList[3]->blockType == kTraceExitBlock);
+
+    cUnit->loopAnalysis = loopAnalysis;
+    /*
+     * Find live-in variables to the loop body so that we can fake their
+     * definitions in the entry block.
+     */
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLiveIn);
+
+    /* Insert phi nodes to the loop body */
+    handlePhiPlacement(cUnit);
+
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion);
+    fillPhiNodeContents(cUnit);
+
+    /* Constant propagation */
+    cUnit->isConstantV = dvmAllocBitVector(cUnit->numSSARegs, false);
+    cUnit->constantValues = dvmCompilerNew(sizeof(int) * cUnit->numSSARegs,
+                                           true);
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+                                          dvmCompilerDoConstantPropagation);
+    DEBUG_LOOP(dumpConstants(cUnit);)
+
+    /* Find induction variables - basic and dependent */
+    loopAnalysis->ivList = dvmCompilerNew(sizeof(GrowableList), true);
+    dvmInitGrowableList(loopAnalysis->ivList, 4);
+    loopAnalysis->isIndVarV = dvmAllocBitVector(cUnit->numSSARegs, false);
+    dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+                                          dvmCompilerFindInductionVariables);
+    DEBUG_LOOP(dumpIVList(cUnit);)
+
+    /* If the loop turns out to be non-optimizable, return early */
+    if (!isLoopOptimizable(cUnit))
+        return false;
+
+    loopAnalysis->arrayAccessInfo = 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;
+}
diff --git a/vm/compiler/Loop.h b/vm/compiler/Loop.h
new file mode 100644
index 0000000..0de2baf
--- /dev/null
+++ b/vm/compiler/Loop.h
@@ -0,0 +1,37 @@
+/*
+ * 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
+#define _DALVIK_VM_LOOP
+
+#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;
+
+#endif /* _DALVIK_VM_LOOP */
diff --git a/vm/compiler/Ralloc.c b/vm/compiler/Ralloc.c
new file mode 100644
index 0000000..f227527
--- /dev/null
+++ b/vm/compiler/Ralloc.c
@@ -0,0 +1,159 @@
+/*
+ * 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"
+
+typedef struct LiveRange {
+    int ssaName;
+    bool active;
+    int first;
+    int last;
+} LiveRange;
+
+int computeLiveRange(LiveRange *list, BasicBlock *bb, int seqNum)
+{
+    MIR *mir;
+    int i;
+
+    if (bb->blockType != kDalvikByteCode &&
+        bb->blockType != kTraceEntryBlock)
+        return seqNum;
+
+    for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+        SSARepresentation *ssaRep = mir->ssaRep;
+        mir->seqNum = seqNum;
+        if (ssaRep) {
+            for (i=0; i< ssaRep->numUses; i++) {
+                int reg = ssaRep->uses[i];
+                list[reg].first = MIN(list[reg].first, seqNum);
+                list[reg].active = true;
+            }
+            for (i=0; i< ssaRep->numDefs; i++) {
+                int reg = ssaRep->defs[i];
+                list[reg].last = MAX(list[reg].last, seqNum + 1);
+                list[reg].active = true;
+            }
+            seqNum += 2;
+        }
+    }
+    return seqNum;
+}
+
+/*
+ * 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 != kTraceEntryBlock)
+        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;
+            }
+        }
+    }
+}
+
+/*
+ * Determine whether to use simple or aggressive register allocation.  In
+ * general, loops and full methods will get aggressive.
+ */
+static bool simpleTrace(CompilationUnit *cUnit)
+{
+    //TODO: flesh out
+    return true;
+}
+
+/*
+ * Target-independent register allocation.  Requires target-dependent
+ * helper functions and assumes free list, temp list and spill region.
+ * Uses a variant of linear scan and produces a mapping between SSA names
+ * and location.  Location may be original Dalvik register, hardware
+ * register or spill location.
+ *
+ * Method:
+ *    0.  Allocate the structure to hold the SSA name life ranges
+ *    1.  Number each MIR instruction, counting by 2.
+ *        +0 -> The "read" of the operands
+ *        +1 -> The definition of the target resource
+ *    2.  Compute live ranges for all SSA names *not* including the
+ *        subscript 0 original Dalvik names.  Phi functions ignored
+ *        at this point.
+ *    3.  Sort the live range list by lowest range start.
+ *    4.  Process and remove all Phi functions.
+ *        o If there is no live range collisions among all operands and
+ *          the target of a Phi function, collapse operands and target
+ *          and rewrite using target SSA name.
+ *        o If there is a collision, introduce copies.
+ *    5.  Allocate in order of increasing live range start.
+ */
+static const RegLocation freshLoc = {kLocDalvikFrame, 0, 0, INVALID_REG,
+                                     INVALID_REG, INVALID_SREG};
+void dvmCompilerRegAlloc(CompilationUnit *cUnit)
+{
+    int i;
+    int seqNum = 0;
+    LiveRange *ranges;
+    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;
+
+    /* Do type inference pass */
+    for (i=0; i < cUnit->numBlocks; i++) {
+        inferTypes(cUnit, cUnit->blockList[i]);
+    }
+
+    if (simpleTrace(cUnit)) {
+        /*
+         * Just rename everything back to subscript 0 names and don't do
+         * any explicit promotion.  Local allocator will opportunistically
+         * promote on the fly.
+         */
+        for (i=0; i < cUnit->numSSARegs; i++) {
+            cUnit->regLocation[i].sRegLow =
+                DECODE_REG(dvmConvertSSARegToDalvik(cUnit, loc[i].sRegLow));
+        }
+    } else {
+        // Compute live ranges
+        ranges = dvmCompilerNew(cUnit->numSSARegs * sizeof(*ranges), true);
+        for (i=0; i < cUnit->numSSARegs; i++)
+            ranges[i].active = false;
+        seqNum = computeLiveRange(ranges, cUnit->blockList[i], seqNum);
+        //TODO: phi squash & linear scan promotion
+    }
+}
diff --git a/vm/compiler/Utility.c b/vm/compiler/Utility.c
new file mode 100644
index 0000000..711d4cf
--- /dev/null
+++ b/vm/compiler/Utility.c
@@ -0,0 +1,316 @@
+/*
+ * 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) {
+        LOGE("No memory left to create compiler heap memory\n");
+        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) {
+            LOGE("Arena allocation failure");
+            dvmAbort();
+        }
+        newArena->blockSize = blockSize;
+        newArena->bytesAllocated = 0;
+        newArena->next = NULL;
+        currentArena->next = newArena;
+        currentArena = newArena;
+        numArenaBlocks++;
+        if (numArenaBlocks > 10)
+            LOGI("Total arena pages for JIT: %d", numArenaBlocks);
+        goto retry;
+    }
+    return NULL;
+}
+
+/* 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 = (void **) dvmCompilerNew(sizeof(void *) * 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;
+    }
+    void *newArray = dvmCompilerNew(sizeof(void *) * newLength, true);
+    memcpy(newArray, gList->elemList, sizeof(void *) * gList->numAllocated);
+    gList->numAllocated = newLength;
+    gList->elemList = newArray;
+}
+
+/* Insert a new element into the growable list */
+void dvmInsertGrowableList(GrowableList *gList, void *elem)
+{
+    assert(gList->numAllocated != 0);
+    if (gList->numUsed == gList->numAllocated) {
+        expandGrowableList(gList);
+    }
+    gList->elemList[gList->numUsed++] = elem;
+}
+
+/* Debug Utility - dump a compilation unit */
+void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit)
+{
+    int i;
+    BasicBlock *bb;
+    char *blockTypeNames[] = {
+        "Normal Chaining Cell",
+        "Hot Chaining Cell",
+        "Singleton Chaining Cell",
+        "Predicted Chaining Cell",
+        "Backward Branch",
+        "Chaining Cell Gap",
+        "N/A",
+        "Method Entry Block",
+        "Trace Entry Block",
+        "Code Block",
+        "Trace Exit Block",
+        "Method Exit Block",
+        "PC Reconstruction",
+        "Exception Handling",
+    };
+
+    LOGD("Compiling %s %s", cUnit->method->clazz->descriptor,
+         cUnit->method->name);
+    LOGD("%d insns", dvmGetMethodInsnsSize(cUnit->method));
+    LOGD("%d blocks in total", cUnit->numBlocks);
+
+    for (i = 0; i < cUnit->numBlocks; i++) {
+        bb = cUnit->blockList[i];
+        LOGD("Block %d (%s) (insn %04x - %04x%s)\n",
+             bb->id,
+             blockTypeNames[bb->blockType],
+             bb->startOffset,
+             bb->lastMIRInsn ? bb->lastMIRInsn->offset : bb->startOffset,
+             bb->lastMIRInsn ? "" : " empty");
+        if (bb->taken) {
+            LOGD("  Taken branch: block %d (%04x)\n",
+                 bb->taken->id, bb->taken->startOffset);
+        }
+        if (bb->fallThrough) {
+            LOGD("  Fallthrough : block %d (%04x)\n",
+                 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) {
+        LOGD("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));
+    LOGD("%d compilations using %d + %d bytes",
+         gDvmJit.numCompilations,
+         gDvmJit.templateSize,
+         gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
+    LOGD("Compiler arena uses %d blocks (%d bytes each)",
+         numArenaBlocks, ARENA_DEFAULT_SIZE);
+    LOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength,
+         gDvmJit.compilerMaxQueued);
+    dvmJitStats();
+    dvmCompilerArchDump();
+    if (gDvmJit.methodStatsTable) {
+        dvmHashForeach(gDvmJit.methodStatsTable, dumpMethodStats,
+                       &totalMethodStats);
+        LOGD("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(int startBits, bool expandable)
+{
+    BitVector* bv;
+    int count;
+
+    assert(sizeof(bv->storage[0]) == 4);        /* assuming 32-bit units */
+    assert(startBits >= 0);
+
+    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, int num)
+{
+    assert(num >= 0);
+    if (num >= pBits->storageSize * (int)sizeof(u4) * 8) {
+        if (!pBits->expandable)
+            return false;
+
+        int newSize = (num + 31) >> 5;
+        assert(newSize > pBits->storageSize);
+        u4 *newStorage = 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;
+}
+
+void dvmDebugBitVector(char *msg, const BitVector *bv, int length)
+{
+    int i;
+
+    LOGE("%s", msg);
+    for (i = 0; i < length; i++) {
+        if (dvmIsBitSet(bv, i)) {
+            LOGE("Bit %d is set", i);
+        }
+    }
+}
+
+void dvmCompilerAbort(CompilationUnit *cUnit)
+{
+    LOGE("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);
+}
diff --git a/vm/compiler/codegen/CompilerCodegen.h b/vm/compiler/codegen/CompilerCodegen.h
new file mode 100644
index 0000000..7379d50
--- /dev/null
+++ b/vm/compiler/codegen/CompilerCodegen.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.
+ */
+
+#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 */
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit);
+
+/* Assemble LIR into machine code */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info);
+
+/* 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* dvmJitChain(void *tgtAddr, u4* branchAddr);
+u4* dvmJitUnchain(void *codeAddr);
+void dvmJitUnchainAll(void);
+void dvmCompilerPatchInlineCache(void);
+
+/* Implemented in codegen/<target>/Ralloc.c */
+void dvmCompilerRegAlloc(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);
+
+#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..d42fe87
--- /dev/null
+++ b/vm/compiler/codegen/Optimizer.h
@@ -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.
+ */
+
+#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.
+ */
+typedef enum optControlVector {
+    kLoadStoreElimination = 0,
+    kLoadHoisting,
+    kTrackLiveTemps,
+    kSuppressLoads,
+    kMethodInlining,
+} optControlVector;
+
+/* 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/arm/ArchUtility.c b/vm/compiler/codegen/arm/ArchUtility.c
new file mode 100644
index 0000000..2e68459
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArchUtility.c
@@ -0,0 +1,387 @@
+/*
+ * 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/OpCodeNames.h"
+#include "ArmLIR.h"
+
+static char *shiftNames[4] = {
+    "lsl",
+    "lsr",
+    "asr",
+    "ror"};
+
+/* Decode and print a ARM register name */
+static char * decodeRegList(int vector, char *buf)
+{
+    int i;
+    bool printed = false;
+    buf[0] = 0;
+    for (i = 0; i < 8; i++, vector >>= 1) {
+        if (vector & 0x1) {
+            if (printed) {
+                sprintf(buf + strlen(buf), ", r%d", i);
+            } else {
+                printed = true;
+                sprintf(buf, "r%d", i);
+            }
+        }
+    }
+    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(char *fmt, ArmLIR *lir, char* buf,
+                            unsigned char *baseAddr, int size)
+{
+    int i;
+    char *bufEnd = &buf[size-1];
+    char *fmtEnd = &fmt[strlen(fmt)];
+    char tbuf[256];
+    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 [0x%x]", operand, operand);
+                       break;
+                   case 'm':
+                       operand = expandImmediate(operand);
+                       sprintf(tbuf,"%d [0x%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",
+                               (int) baseAddr + lir->generic.offset + 4 +
+                               (operand << 1));
+                       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(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 ");
+        }
+        if (armLIR && (mask & ENCODE_DALVIK_REG)) {
+            sprintf(buf + strlen(buf), "dr%d%s", armLIR->aliasInfo & 0xffff,
+                    (armLIR->aliasInfo & 0x80000000) ? "(+1)" : "");
+        }
+    }
+    if (buf[0]) {
+        LOGD("%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:
+            LOGD("-------- end of chaining cells (0x%04x)\n", offset);
+            break;
+        case kArmPseudoBarrier:
+            LOGD("-------- BARRIER");
+            break;
+        case kArmPseudoExtended:
+            LOGD("-------- %s\n", (char *) dest);
+            break;
+        case kArmPseudoSSARep:
+            DUMP_SSA_REP(LOGD("-------- %s\n", (char *) dest));
+            break;
+        case kArmPseudoTargetLabel:
+            break;
+        case kArmPseudoChainingCellBackwardBranch:
+            LOGD("-------- chaining cell (backward branch): 0x%04x\n", dest);
+            break;
+        case kArmPseudoChainingCellNormal:
+            LOGD("-------- chaining cell (normal): 0x%04x\n", dest);
+            break;
+        case kArmPseudoChainingCellHot:
+            LOGD("-------- chaining cell (hot): 0x%04x\n", dest);
+            break;
+        case kArmPseudoChainingCellInvokePredicted:
+            LOGD("-------- chaining cell (predicted)\n");
+            break;
+        case kArmPseudoChainingCellInvokeSingleton:
+            LOGD("-------- chaining cell (invoke singleton): %s/%p\n",
+                 ((Method *)dest)->name,
+                 ((Method *)dest)->insns);
+            break;
+        case kArmPseudoEntryBlock:
+            LOGD("-------- entry offset: 0x%04x\n", dest);
+            break;
+        case kArmPseudoDalvikByteCodeBoundary:
+            LOGD("-------- dalvik offset: 0x%04x @ %s\n", dest,
+                 (char *) lir->operands[1]);
+            break;
+        case kArmPseudoExitBlock:
+            LOGD("-------- exit offset: 0x%04x\n", dest);
+            break;
+        case kArmPseudoPseudoAlign4:
+            LOGD("%p (%04x): .align4\n", baseAddr + offset, offset);
+            break;
+        case kArmPseudoPCReconstructionCell:
+            LOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x\n", dest,
+                 lir->operands[1]);
+            break;
+        case kArmPseudoPCReconstructionBlockLabel:
+            /* Do nothing */
+            break;
+        case kArmPseudoEHBlockLabel:
+            LOGD("Exception_Handling:\n");
+            break;
+        case kArmPseudoNormalBlockLabel:
+            LOGD("L%#06x:\n", dest);
+            break;
+        default:
+            if (lir->isNop && !dumpNop) {
+                break;
+            }
+            buildInsnString(EncodingMap[lir->opCode].name, lir, opName,
+                            baseAddr, 256);
+            buildInsnString(EncodingMap[lir->opCode].fmt, lir, buf, baseAddr,
+                            256);
+            LOGD("%p (%04x): %-8s%s%s\n",
+                 baseAddr + offset, offset, opName, buf,
+                 lir->isNop ? "(nop)" : "");
+            break;
+    }
+
+    if (lir->useMask && (!lir->isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->useMask, "use"));
+    }
+    if (lir->defMask && (!lir->isNop || dumpNop)) {
+        DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+                                               lir->defMask, "def"));
+    }
+}
+
+/* Dump instructions and constant pool contents */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit)
+{
+    LOGD("Dumping LIR insns\n");
+    LIR *lirInsn;
+    ArmLIR *armLIR;
+
+    LOGD("installed code is at %p\n", cUnit->baseAddr);
+    LOGD("total size is %d bytes\n", cUnit->totalSize);
+    for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
+        dvmDumpLIRInsn(lirInsn, cUnit->baseAddr);
+    }
+    for (lirInsn = cUnit->wordList; lirInsn; lirInsn = lirInsn->next) {
+        armLIR = (ArmLIR *) lirInsn;
+        LOGD("%p (%04x): .word (0x%x)\n",
+             (char*)cUnit->baseAddr + armLIR->generic.offset,
+             armLIR->generic.offset,
+             armLIR->operands[0]);
+    }
+}
diff --git a/vm/compiler/codegen/arm/ArmLIR.h b/vm/compiler/codegen/arm/ArmLIR.h
new file mode 100644
index 0000000..24f5240
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArmLIR.h
@@ -0,0 +1,789 @@
+/*
+ * 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 "compiler/CompilerInternals.h"
+
+#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H
+#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_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 (rGLUE) is reserved [holds current &interpState]
+ * 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;
+    int numCoreRegs;
+    RegisterInfo *coreRegs;
+    int numFPRegs;
+    RegisterInfo *FPRegs;
+} RegisterPool;
+
+typedef enum ResourceEncodingPos {
+    kGPReg0     = 0,
+    kRegSP      = 13,
+    kRegLR      = 14,
+    kRegPC      = 15,
+    kFPReg0     = 16,
+    kRegEnd     = 48,
+    kCCode      = kRegEnd,
+    kFPStatus,
+    kDalvikReg,
+    kLiteral,
+    kFrameRef,
+    kHeapRef,
+    kLitPoolRef
+} 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)
+
+    /* Must alias */
+#define ENCODE_DALVIK_REG       (1ULL << kDalvikReg)
+#define ENCODE_LITERAL          (1ULL << kLiteral)
+
+    /* May alias */
+#define ENCODE_FRAME_REF        (1ULL << kFrameRef)
+#define ENCODE_HEAP_REF         (1ULL << kHeapRef)
+#define ENCODE_LITPOOL_REF      (1ULL << kLitPoolRef)
+
+#define ENCODE_ALL              (~0ULL)
+#define ENCODE_MEM_DEF          (ENCODE_FRAME_REF | ENCODE_HEAP_REF)
+#define ENCODE_MEM_USE          (ENCODE_FRAME_REF | ENCODE_HEAP_REF \
+                                 | ENCODE_LITPOOL_REF)
+
+#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;
+
+typedef enum NativeRegisterPool {
+    r0 = 0,
+    r1 = 1,
+    r2 = 2,
+    r3 = 3,
+    r4PC = 4,
+    rFP = 5,
+    rGLUE = 6,
+    r7 = 7,
+    r8 = 8,
+    r9 = 9,
+    r10 = 10,
+    r11 = 11,
+    r12 = 12,
+    r13 = 13,
+    rlr = 14,
+    rpc = 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] */
+    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] */
+    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    [1110100010101101] 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] */
+    kThumb2Umull,        /* umull [111110111010] rn[19-16], rdlo[15-12]
+                                  rdhi[11-8] [0000] 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] */
+
+    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;
+    char *name;
+    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]
+    bool isNop;         // LIR is optimized away
+    bool branchInsertSV;// mark for insertion of branch before this instruction,
+                        // used to identify mem ops for self verification mode
+    int age;            // default is 0, set lazily by the optimizer
+    int size;           // 16-bit unit size (1 for thumb, 1 or 2 for thumb2)
+    int aliasInfo;      // For Dalvik register access & 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/Assemble.c b/vm/compiler/codegen/arm/Assemble.c
new file mode 100644
index 0000000..4f54f1e
--- /dev/null
+++ b/vm/compiler/codegen/arm/Assemble.c
@@ -0,0 +1,2651 @@
+/*
+ * 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/OpCode.h"
+#include "libdex/OpCodeNames.h"
+
+#include "../../CompilerInternals.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+#include <unistd.h>             /* for cacheflush */
+#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(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(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,          0xe8ad0000,
+                 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, [rpc, #!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(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(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),
+};
+
+/*
+ * The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is
+ * not ready. Since r5 (rFP) 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 literal pool to the codegen stream */
+static void installDataContent(CompilationUnit *cUnit)
+{
+    int *dataPtr = (int *) ((char *) cUnit->baseAddr + cUnit->dataOffset);
+    ArmLIR *dataLIR = (ArmLIR *) cUnit->wordList;
+    while (dataLIR) {
+        *dataPtr++ = dataLIR->operands[0];
+        dataLIR = NEXT_LIR(dataLIR);
+    }
+}
+
+/* Returns the size of a Jit trace description */
+static int jitTraceDescriptionSize(const JitTraceDescription *desc)
+{
+    int runCount;
+    /* Trace end is always of non-meta type (ie isCode == true) */
+    for (runCount = 0; ; runCount++) {
+        if (desc->trace[runCount].frag.isCode &&
+            desc->trace[runCount].frag.runEnd)
+           break;
+    }
+    return sizeof(JitTraceDescription) + ((runCount+1) * sizeof(JitTraceRun));
+}
+
+/*
+ * 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->isNop) {
+            continue;
+        }
+
+        if (lir->opCode == kThumbLdrPcRel ||
+            lir->opCode == kThumb2LdrPcRel12 ||
+            lir->opCode == kThumbAddPcRel ||
+            ((lir->opCode == kThumb2Vldrs) && (lir->operands[1] == rpc))) {
+            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) {
+                LOGE("PC-rel distance is not multiples of 4: %d\n", delta);
+                dvmCompilerAbort(cUnit);
+            }
+            if ((lir->opCode == kThumb2LdrPcRel12) && (delta > 4091)) {
+                return kRetryHalve;
+            } else if (delta > 1020) {
+                return kRetryHalve;
+            }
+            if (lir->opCode == kThumb2Vldrs) {
+                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 = 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);
+                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)) {
+                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) {
+                LOGE("Unconditional branch distance out of range: %d\n", 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;
+        }
+
+        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;
+}
+
+#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) {
+                LOGD("Signature match starting from offset %#x (%d words)",
+                     i*4, gDvmJit.signatureBreakpointSize);
+                int descSize = jitTraceDescriptionSize(cUnit->traceDesc);
+                JitTraceDescription *newCopy =
+                    (JitTraceDescription *) malloc(descSize);
+                memcpy(newCopy, cUnit->traceDesc, descSize);
+                dvmCompilerWorkEnqueue(NULL, kWorkOrderTraceDebug, newCopy);
+                break;
+            }
+        }
+    }
+}
+#endif
+
+/*
+ * 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 (if present) executionCount is at
+ * codeAddress - 6.
+ *
+ *      +----------------------------+
+ *      | Execution count            |  -> [Optional] 4 bytes
+ *      +----------------------------+
+ *   +--| Offset to chain cell counts|  -> 2 bytes
+ *   |  +----------------------------+
+ *   |  | Code body                  |  -> Start address for translation
+ *   |  |                            |     variable in 2-byte chunks
+ *   |  .                            .     (JitTable's codeAddress points here)
+ *   |  .                            .
+ *   |  |                            |
+ *   |  +----------------------------+
+ *   |  | Chaining Cells             |  -> 12/16 bytes each, must be 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
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *      | Literal pool               |  -> 4-byte aligned, variable size
+ *      .                            .
+ *      .                            .
+ *      |                            |
+ *      +----------------------------+
+ *
+ * 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)
+{
+    LIR *lir;
+    ArmLIR *armLIR;
+    int offset = 0;
+    int i;
+    ChainCellCounts chainCellCounts;
+    int descSize =
+        cUnit->wholeMethod ? 0 : jitTraceDescriptionSize(cUnit->traceDesc);
+    int chainingCellGap;
+
+    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->isNop) {
+            armLIR->size = EncodingMap[armLIR->opCode].size * 2;
+            offset += armLIR->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;
+
+    /*
+     * 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 */
+    u4 chainCellOffset = offset;
+    ArmLIR *chainCellOffsetLIR = (ArmLIR *) cUnit->chainCellOffsetLIR;
+    assert(chainCellOffsetLIR);
+    assert(chainCellOffset < 0x10000);
+    assert(chainCellOffsetLIR->opCode == kArm16BitData &&
+           chainCellOffsetLIR->operands[0] == CHAIN_CELL_OFFSET_TAG);
+
+    /*
+     * Replace the CHAIN_CELL_OFFSET_TAG with the real value. If trace
+     * profiling is enabled, subtract 4 (occupied by the counter word) from
+     * the absolute offset as the value stored in chainCellOffsetLIR is the
+     * delta from &chainCellOffsetLIR to &ChainCellCounts.
+     */
+    chainCellOffsetLIR->operands[0] =
+        gDvmJit.profile ? (chainCellOffset - 4) : chainCellOffset;
+
+    offset += sizeof(chainCellCounts) + descSize;
+
+    assert((offset & 0x3) == 0);  /* Should still be word aligned */
+
+    /* Set up offsets for literals */
+    cUnit->dataOffset = offset;
+
+    for (lir = cUnit->wordList; lir; lir = lir->next) {
+        lir->offset = offset;
+        offset += 4;
+    }
+
+    cUnit->totalSize = offset;
+
+    if (gDvmJit.codeCacheByteUsed + cUnit->totalSize > gDvmJit.codeCacheSize) {
+        gDvmJit.codeCacheFull = true;
+        cUnit->baseAddr = NULL;
+        return;
+    }
+
+    /* Allocate enough space for the code block */
+    cUnit->codeBuffer = dvmCompilerNew(chainCellOffset, true);
+    if (cUnit->codeBuffer == NULL) {
+        LOGE("Code buffer allocation failure\n");
+        cUnit->baseAddr = NULL;
+        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) {
+                /* 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:
+             LOGE("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;
+
+    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++;
+
+    /* 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 */
+    installDataContent(cUnit);
+
+    /* Flush dcache and invalidate the icache to maintain coherence */
+    cacheflush((long)cUnit->baseAddr,
+               (long)((char *) cUnit->baseAddr + offset), 0);
+    UPDATE_CODE_CACHE_PATCHES();
+
+    PROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+    /* 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;
+}
+
+/*
+ * 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(
+            LOGD("Jit Runtime: chaining 0x%x to 0x%x\n",
+                 (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;
+        cacheflush((long)branchAddr, (long)branchAddr + 4, 0);
+        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,
+            (void*) &cellAddr->clazz);
+        cacheflush((intptr_t) cellAddr, (intptr_t) (cellAddr+1), 0);
+        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++;
+        gDvmJit.compilerICPatchQueue[index].cellAddr = cellAddr;
+        gDvmJit.compilerICPatchQueue[index].cellContent = *newContent;
+#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,
+                                          InterpState *interpState,
+                                          PredictedChainingCell *cell,
+                                          const ClassObject *clazz)
+{
+    int newRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+#if defined(WITH_SELF_VERIFICATION)
+    newRechainCount = PREDICTED_CHAIN_COUNTER_AVOID;
+    goto done;
+#else
+    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 = (void *) PREDICTED_CHAIN_FAKE_CLAZZ;
+
+        UPDATE_CODE_CACHE_PATCHES();
+        PROTECT_CODE_CACHE(cell, sizeof(*cell));
+        goto done;
+    }
+    int tgtAddr = (int) dvmJitGetCodeAddr(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(
+            LOGD("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 = interpState->icRechainCount;
+    }
+
+    int baseAddr = (int) cell + 4;   // PC is cur_addr + 4
+    int branchOffset = tgtAddr - baseAddr;
+
+    newCell.branch = assembleChainingBranch(branchOffset, true);
+    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);
+#endif
+done:
+    interpState->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);
+
+    //LOGD("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++) {
+        PredictedChainingCell *cellAddr =
+            gDvmJit.compilerICPatchQueue[i].cellAddr;
+        PredictedChainingCell *cellContent =
+            &gDvmJit.compilerICPatchQueue[i].cellContent;
+
+        COMPILER_TRACE_CHAINING(
+            LOGD("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 */
+    cacheflush((long) minAddr, (long) (maxAddr+1), 0);
+    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.
+ */
+u4* dvmJitUnchain(void* codeAddr)
+{
+    u2* pChainCellOffset = (u2*)((char*)codeAddr - 3);
+    u2 chainCellOffset = *pChainCellOffset;
+    ChainCellCounts *pChainCellCounts =
+          (ChainCellCounts*)((char*)codeAddr + chainCellOffset - 3);
+    int cellSize;
+    u4* pChainCells;
+    u4* pStart;
+    u4 newInst;
+    int i,j;
+    PredictedChainingCell *predChainCell;
+
+    /* Get total count of chain cells */
+    for (i = 0, cellSize = 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);
+        }
+    }
+
+    if (cellSize == 0)
+        return (u4 *) pChainCellCounts;
+
+    /* Locate the beginning of the chain cell region */
+    pStart = 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:
+                    LOGE("Unexpected chaining type: %d", i);
+                    dvmAbort();  // dvmAbort OK here - can't safely recover
+            }
+            COMPILER_TRACE_CHAINING(
+                LOGD("Jit Runtime: unchaining 0x%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;
+    unsigned int i;
+    if (gDvmJit.pJitEntryTable != NULL) {
+        COMPILER_TRACE_CHAINING(LOGD("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].codeAddress &&
+                   (gDvmJit.pJitEntryTable[i].codeAddress !=
+                    dvmCompilerGetInterpretTemplate())) {
+                u4* lastAddress;
+                lastAddress =
+                      dvmJitUnchain(gDvmJit.pJitEntryTable[i].codeAddress);
+                if (lowAddress == NULL ||
+                      (u4*)gDvmJit.pJitEntryTable[i].codeAddress < lowAddress)
+                    lowAddress = lastAddress;
+                if (lastAddress > highAddress)
+                    highAddress = lastAddress;
+            }
+        }
+        cacheflush((long)lowAddress, (long)highAddress, 0);
+        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;
+}
+
+static char *getTraceBase(const JitEntry *p)
+{
+    return (char*)p->codeAddress -
+        (6 + (p->u.info.instructionSet == DALVIK_JIT_ARM ? 0 : 1));
+}
+
+/* Dumps profile info for a single trace */
+static int dumpTraceProfile(JitEntry *p, bool silent, bool reset,
+                            unsigned long sum)
+{
+    ChainCellCounts* pCellCounts;
+    char* traceBase;
+    u4* pExecutionCount;
+    u4 executionCount;
+    u2* pCellOffset;
+    JitTraceDescription *desc;
+    const Method* method;
+    int idx;
+
+    traceBase = getTraceBase(p);
+
+    if (p->codeAddress == NULL) {
+        if (!silent)
+            LOGD("TRACEPROFILE 0x%08x 0 NULL 0 0", (int)traceBase);
+        return 0;
+    }
+    if (p->codeAddress == dvmCompilerGetInterpretTemplate()) {
+        if (!silent)
+            LOGD("TRACEPROFILE 0x%08x 0 INTERPRET_ONLY  0 0", (int)traceBase);
+        return 0;
+    }
+
+    pExecutionCount = (u4*) (traceBase);
+    executionCount = *pExecutionCount;
+    if (reset) {
+        *pExecutionCount =0;
+    }
+    if (silent) {
+        return executionCount;
+    }
+    pCellOffset = (u2*) (traceBase + 4);
+    pCellCounts = (ChainCellCounts*) ((char *)pCellOffset + *pCellOffset);
+    desc = (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+    method = desc->method;
+    char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+    jitProfileAddrToLine addrToLine = {0, desc->trace[0].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);
+
+    LOGD("TRACEPROFILE 0x%08x % 10d %5.2f%% [%#x(+%d), %d] %s%s;%s",
+         (int)traceBase,
+         executionCount,
+         ((float ) executionCount) / sum * 100.0,
+         desc->trace[0].frag.startOffset,
+         desc->trace[0].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].frag.isCode && !desc->trace[idx].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].frag.isCode) {
+        const Method *method = desc->trace[idx+1].meta;
+        char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+        /* Print the callee info in the trace */
+        LOGD("    -> %s%s;%s", method->clazz->descriptor, method->name,
+             methodDesc);
+    }
+
+    return executionCount;
+}
+
+/* Create a copy of the trace descriptor of an existing compilation */
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+                                            const JitEntry *knownEntry)
+{
+    const JitEntry *jitEntry = knownEntry ? knownEntry : dvmFindJitEntry(pc);
+    if (jitEntry == NULL) return NULL;
+
+    /* Find out the startint point */
+    char *traceBase = getTraceBase(jitEntry);
+
+    /* Then find out the starting point of the chaining cell */
+    u2 *pCellOffset = (u2*) (traceBase + 4);
+    ChainCellCounts *pCellCounts =
+        (ChainCellCounts*) ((char *)pCellOffset + *pCellOffset);
+
+    /* From there we can find out the starting point of the trace descriptor */
+    JitTraceDescription *desc =
+        (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+
+    /* Now make a copy and return */
+    int descSize = jitTraceDescriptionSize(desc);
+    JitTraceDescription *newCopy = (JitTraceDescription *) malloc(descSize);
+    memcpy(newCopy, desc, descSize);
+    return newCopy;
+}
+
+/* Handy function to retrieve the profile count */
+static inline int getProfileCount(const JitEntry *entry)
+{
+    if (entry->dPC == 0 || entry->codeAddress == 0 ||
+        entry->codeAddress == dvmCompilerGetInterpretTemplate())
+        return 0;
+
+    u4 *pExecutionCount = (u4 *) getTraceBase(entry);
+
+    return *pExecutionCount;
+}
+
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+    const JitEntry *jitEntry1 = entry1;
+    const JitEntry *jitEntry2 = entry2;
+
+    int count1 = getProfileCount(jitEntry1);
+    int 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 = 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;
+    }
+
+    LOGD("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]);
+        dvmCompilerWorkEnqueue(sortedEntries[i].dPC,
+                               kWorkOrderTraceDebug, desc);
+    }
+
+    free(sortedEntries);
+done:
+    dvmUnlockMutex(&gDvmJit.tableLock);
+    return;
+}
+
+#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:
+            LOGE("*** ERROR: BAD SIZE IN selfVerificationLoad: %d", size);
+            data = 0;
+            dvmAbort();
+    }
+
+    //LOGD("*** HEAP LOAD: Addr: 0x%x Data: 0x%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;
+        }
+    }
+
+    //LOGD("*** HEAP LOAD DOUBLEWORD: Addr: 0x%x Data: 0x%x Data2: 0x%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;
+
+    //LOGD("*** HEAP STORE: Addr: 0x%x Data: 0x%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:
+            LOGE("*** 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;
+
+    //LOGD("*** HEAP STORE DOUBLEWORD: Addr: 0x%x Data: 0x%x, Data2: 0x%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);
+        //LOGD("*** THUMB2 - Addr: 0x%x Insn: 0x%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 {
+                    LOGE("*** 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:
+                LOGE("*** ERROR: UNRECOGNIZED THUMB2 MEM OP: %x", opcode12);
+                offset = 0;
+                dvmAbort();
+        }
+
+        // Handle the decoded mem op accordingly
+        if (store) {
+            if (size == kSVVariable) {
+                LOGD("*** 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) {
+                LOGD("*** 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 {
+        //LOGD("*** THUMB - Addr: 0x%x Insn: 0x%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:
+                LOGE("*** 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..d6eb421
--- /dev/null
+++ b/vm/compiler/codegen/arm/CalloutHelper.h
@@ -0,0 +1,135 @@
+/*
+ * 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"
+
+#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H
+#define _DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_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 */
+float __aeabi_i2f(int op1);             // OP_INT_TO_FLOAT
+int __aeabi_f2iz(float op1);            // OP_FLOAT_TO_INT
+float __aeabi_d2f(double op1);          // OP_DOUBLE_TO_FLOAT
+double __aeabi_f2d(float op1);          // OP_FLOAT_TO_DOUBLE
+double __aeabi_i2d(int op1);            // OP_INT_TO_DOUBLE
+int __aeabi_d2iz(double op1);           // OP_DOUBLE_TO_INT
+float __aeabi_l2f(long op1);            // OP_LONG_TO_FLOAT
+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 */
+float __aeabi_fadd(float a, float b);   // OP_ADD_FLOAT[_2ADDR]
+float __aeabi_fsub(float a, float b);   // OP_SUB_FLOAT[_2ADDR]
+float __aeabi_fdiv(float a, float b);   // OP_DIV_FLOAT[_2ADDR]
+float __aeabi_fmul(float a, float b);   // OP_MUL_FLOAT[_2ADDR]
+float fmodf(float a, float b);          // OP_REM_FLOAT[_2ADDR]
+
+/* Double-precision FP arithmetics */
+double __aeabi_dadd(double a, double b); // OP_ADD_DOUBLE[_2ADDR]
+double __aeabi_dsub(double a, double b); // OP_SUB_DOUBLE[_2ADDR]
+double __aeabi_ddiv(double a, double b); // OP_DIV_DOUBLE[_2ADDR]
+double __aeabi_dmul(double a, double b); // OP_MUL_DOUBLE[_2ADDR]
+double fmod(double a, double b);         // OP_REM_DOUBLE[_2ADDR]
+
+/* Integer arithmetics */
+int __aeabi_idivmod(int op1, int op2);  // OP_REM_INT[_2ADDR|_LIT8|_LIT16]
+int __aeabi_idiv(int op1, int op2);     // OP_DIV_INT[_2ADDR|_LIT8|_LIT16]
+
+/* Long long arithmetics - OP_REM_LONG[_2ADDR] & OP_DIV_LONG[_2ADDR] */
+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);
+
+/*
+ * Switch dispatch offset calculation for OP_PACKED_SWITCH & OP_SPARSE_SWITCH
+ * Used in CodegenDriver.c
+ * static s8 findPackedSwitchIndex(const u2* switchData, int testVal, int pc);
+ * static s8 findSparseSwitchIndex(const u2* switchData, int testVal, int pc);
+ */
+
+/*
+ * 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.
+ *
+ *      org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod
+ *      javaLangString_charAt
+ *      javaLangString_compareTo
+ *      javaLangString_equals
+ *      javaLangString_indexOf_I
+ *      javaLangString_indexOf_II
+ *      javaLangString_length
+ *      javaLangMath_abs_int
+ *      javaLangMath_abs_long
+ *      javaLangMath_abs_float
+ *      javaLangMath_abs_double
+ *      javaLangMath_min_int
+ *      javaLangMath_max_int
+ *      javaLangMath_sqrt
+ *      javaLangMath_cos
+ *      javaLangMath_sin
+ */
+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..be74e3f
--- /dev/null
+++ b/vm/compiler/codegen/arm/Codegen.h
@@ -0,0 +1,88 @@
+/*
+ * 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(WITH_DEADLOCK_PREDICTION) || defined(WITH_MONITOR_TRACKING) || \
+    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 */
+void dvmSelfVerificationMemOpDecode(int lr, int* sp);
+#endif
+
+/*
+ * Architecture-dependent register allocation routines implemented in
+ * Thumb[2]/Ralloc.c
+ */
+extern int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+                                         bool fpHint, int regClass);
+
+extern int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+                                     int regClass);
+
+extern ArmLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest,
+                                          int rSrc);
+
+extern ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
+extern void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo,
+                                   int destHi, int srcLo, int srcHi);
+
+extern void dvmCompilerSetupResourceMasks(ArmLIR *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/arm/CodegenCommon.c b/vm/compiler/codegen/arm/CodegenCommon.c
new file mode 100644
index 0000000..d8854ba
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenCommon.c
@@ -0,0 +1,384 @@
+/*
+ * 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[256];
+
+static void setMemRefType(ArmLIR *lir, bool isLoad, int memType)
+{
+    u8 *maskPtr;
+    u8 mask;
+    assert( EncodingMap[lir->opCode].flags & (IS_LOAD | IS_STORE));
+    if (isLoad) {
+        maskPtr = &lir->useMask;
+        mask = ENCODE_MEM_USE;
+    } else {
+        maskPtr = &lir->defMask;
+        mask = ENCODE_MEM_DEF;
+    }
+    /* Clear out the memref flags */
+    *maskPtr &= ~mask;
+    /* ..and then add back the one we need */
+    switch(memType) {
+        case kLiteral:
+            assert(isLoad);
+            *maskPtr |= (ENCODE_LITERAL | ENCODE_LITPOOL_REF);
+            break;
+        case kDalvikReg:
+            *maskPtr |= (ENCODE_DALVIK_REG | ENCODE_FRAME_REF);
+            break;
+        case kHeapRef:
+            *maskPtr |= ENCODE_HEAP_REF;
+            break;
+        default:
+            LOGE("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(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 and mark the corresponding bit(s).
+ */
+static inline void setupRegMask(u8 *mask, 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;
+    *mask |= seed << shift;
+}
+
+/*
+ * 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);
+    }
+
+    if (flags & IS_BRANCH) {
+        lir->defMask |= ENCODE_REG_PC;
+        lir->useMask |= ENCODE_REG_PC;
+    }
+
+    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;
+    }
+
+    /* Set up the mask for resources that are used */
+    if (flags & IS_BRANCH) {
+        lir->useMask |= ENCODE_REG_PC;
+    }
+
+    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;
+    }
+}
+
+/*
+ * The following are building blocks to construct low-level IRs with 0 - 4
+ * operands.
+ */
+static ArmLIR *newLIR0(CompilationUnit *cUnit, ArmOpCode opCode)
+{
+    ArmLIR *insn = 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 = 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 = 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 = dvmCompilerNew(sizeof(ArmLIR), true);
+    if (!(EncodingMap[opCode].flags & IS_TERTIARY_OP)) {
+        LOGE("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 = 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(CompilationUnit *cUnit, int value,
+                                   unsigned int delta)
+{
+    LIR *dataTarget = cUnit->wordList;
+    while (dataTarget) {
+        if (((unsigned) (value - ((ArmLIR *) dataTarget)->operands[0])) <=
+            delta)
+            return (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, int value, bool inPlace)
+{
+    /* Add the constant to the literal pool */
+    if (!inPlace) {
+        ArmLIR *newValue = dvmCompilerNew(sizeof(ArmLIR), true);
+        newValue->operands[0] = value;
+        newValue->generic.next = cUnit->wordList;
+        cUnit->wordList = (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;
+}
+
+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 */
+extern 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 = 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, pcrLabel);
+    }
+    /* Branch to the PC reconstruction code */
+    branch->generic.target = (LIR *) pcrLabel;
+    return pcrLabel;
+}
diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c
new file mode 100644
index 0000000..011679b
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenDriver.c
@@ -0,0 +1,4482 @@
+/*
+ * 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);
+    opRegImm(cUnit, kOpCmp, valReg, 0); /* storing null? */
+    ArmLIR *branchOver = opCondBranch(cUnit, kArmCondEq);
+    loadWordDisp(cUnit, rGLUE, offsetof(InterpState, 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*) 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, rlr, (int)funct);
+    loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+    loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+    opReg(cUnit, kOpBlx, rlr);
+    dvmCompilerClobberCallRegs(cUnit);
+    rlResult = dvmCompilerGetReturnWide(cUnit);
+    storeValueWide(cUnit, rlDest, rlResult);
+    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 = dvmCompilerNew(sizeof(ArmLIR), true);
+     insn->opCode = opCode;
+     insn->operands[0] = dest;
+     insn->operands[1] = src1;
+     setupResourceMasks(insn);
+     dvmCompilerInsertLIRBefore(currentLIR, (LIR *) insn);
+}
+
+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->branchInsertSV) {
+            /* 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]);
+        }
+    }
+}
+#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);
+    }
+
+    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);
+    }
+    HEAP_ACCESS_SHADOW(true);
+    storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
+    HEAP_ACCESS_SHADOW(false);
+    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(ArrayObject, length);
+    int dataOffset = offsetof(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(ArrayObject, length);
+    int dataOffset = offsetof(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(ArrayObject, length);
+    int dataOffset = offsetof(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 */
+    opRegImm(cUnit, kOpCmp, r0, 0);
+    ArmLIR *branchOver = opCondBranch(cUnit, kArmCondEq);
+
+    /* 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;
+    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;
+            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;
+            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:
+            LOGE("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, rlSrc1, r0, r1);
+        LOAD_FUNC_ADDR(cUnit, rlr, (int) callTgt);
+        loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+        opReg(cUnit, kOpBlx, rlr);
+        dvmCompilerClobberCallRegs(cUnit);
+        if (retReg == r0)
+            rlResult = dvmCompilerGetReturnWide(cUnit);
+        else
+            rlResult = dvmCompilerGetReturnWideAlt(cUnit);
+        storeValueWide(cUnit, rlDest, rlResult);
+    }
+    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;
+    void *callTgt;
+    RegLocation rlResult;
+    bool shiftOp = 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:
+            callOut = true;
+            checkZero = true;
+            callTgt = __aeabi_idiv;
+            retReg = r0;
+            break;
+        /* NOTE: returns in r1 */
+        case OP_REM_INT:
+        case OP_REM_INT_2ADDR:
+            callOut = true;
+            checkZero = true;
+            callTgt = __aeabi_idivmod;
+            retReg = r1;
+            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:
+            LOGE("Invalid word arith op: 0x%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);
+            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);
+    } 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, 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 = 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, 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, rFP,
+                    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     : &rFP[vC]
+     * r7: &newFP[0]
+     */
+    opRegRegImm(cUnit, kOpAdd, r4PC, rFP, 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.
+     */
+    loadMultiple(cUnit, r4PC, regMask);
+
+    opRegRegImm(cUnit, kOpSub, r7, rFP,
+                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 r5 (rFP) is just for stack alignment purposes.
+         */
+        opImm(cUnit, kOpPush, (1 << r0 | 1 << rFP));
+        /* 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, rFP, 4);
+            genConditionalBranch(cUnit, kArmCondNe, loopLabel);
+        }
+    }
+
+    /* Save the last batch of loaded values */
+    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 << rFP));
+
+    /* 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 */
+    dvmCompilerLockTemp(cUnit, r1);
+    ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0);
+    /* r4PC = dalvikCallsite */
+    loadConstant(cUnit, r4PC,
+                 (int) (cUnit->method->insns + mir->offset));
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+    /*
+     * r0 = calleeMethod (loaded upon calling genInvokeSingletonCommon)
+     * r1 = &ChainingCell
+     * r4PC = callsiteDPC
+     */
+    if (dvmIsNativeMethod(calleeMethod)) {
+        genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NATIVE);
+#if defined(WITH_JIT_TUNING)
+        gDvmJit.invokeNative++;
+#endif
+    } else {
+        genDispatchToHandler(cUnit, 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, [r6, #96] --+ 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);
+
+    /* "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, rpc, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    /* r2 = &predictedChainingCell */
+    ArmLIR *predictedChainingCell = opRegRegImm(cUnit, kOpAdd, r2, rpc, 0);
+    predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+    genDispatchToHandler(cUnit, 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 = 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, 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 */
+    opRegImm(cUnit, kOpCmp, r1, 0);
+
+    ArmLIR *bypassRechaining = opCondBranch(cUnit, kArmCondGt);
+
+    loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
+                 jitToInterpEntries.dvmJitToPatchPredictedChain), r7);
+
+    genRegCopy(cUnit, r1, rGLUE);
+
+    /*
+     * 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, rpc, 0);
+    addrRetChain->generic.target = (LIR *) retChainingCell;
+
+    bypassRechaining->generic.target = (LIR *) addrRetChain;
+    /*
+     * r0 = calleeMethod,
+     * r1 = &ChainingCell,
+     * r4PC = callsiteDPC,
+     */
+    genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.invokePolymorphic++;
+#endif
+    /* Handle exceptions using the interpreter */
+    genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/* 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, rGLUE, offsetof(InterpState,
+                 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 = dexGetInstrFlags(gDvm.instrFlags, mir->dalvikInsn.opCode);
+    int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
+                       kInstrCanThrow;
+
+    //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(InterpState,
+                             jitToInterpEntries.dvmJitToInterpSingleStep);
+    loadWordDisp(cUnit, rGLUE, 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(WITH_DEADLOCK_PREDICTION) || defined(WITH_MONITOR_TRACKING) || \
+    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);
+    loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0);
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    if (isEnter) {
+        /* Get dPC of next insn */
+        loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+                 dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_ENTER)));
+#if defined(WITH_DEADLOCK_PREDICTION)
+        genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER_DEBUG);
+#else
+        genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+#endif
+    } else {
+        LOAD_FUNC_ADDR(cUnit, r2, (int)dvmUnlockObject);
+        /* Do the call */
+        opReg(cUnit, kOpBlx, r2);
+        opRegImm(cUnit, kOpCmp, r0, 0); /* Did we throw? */
+        ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+        loadConstant(cUnit, r0,
+                     (int) (cUnit->method->insns + mir->offset +
+                     dexGetInstrWidthAbs(gDvm.instrWidth, 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
+
+/*
+ * 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)
+{
+    /* 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)) {
+        LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode);
+        return true;
+    }
+    switch (dalvikOpCode) {
+        case OP_RETURN_VOID:
+            genReturnCommon(cUnit,mir);
+            break;
+        case OP_UNUSED_73:
+        case OP_UNUSED_79:
+        case OP_UNUSED_7A:
+        case OP_UNUSED_F1:
+        case OP_UNUSED_FF:
+            LOGE("Codegen: got unused opcode 0x%x\n",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) {
+                LOGE("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) {
+                LOGE("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_VOLATILE:
+        case OP_SGET_OBJECT_VOLATILE:
+        case OP_SGET_OBJECT:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_CHAR:
+        case OP_SGET_BYTE:
+        case OP_SGET_SHORT:
+        case OP_SGET: {
+            int valOffset = offsetof(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) {
+                LOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            isVolatile = (mir->dalvikInsn.opCode == OP_SGET_VOLATILE) ||
+                         (mir->dalvikInsn.opCode == OP_SGET_OBJECT_VOLATILE) ||
+                         dvmIsVolatileField(fieldPtr);
+
+            rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+            loadConstant(cUnit, tReg,  (int) fieldPtr + valOffset);
+
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit);
+            }
+            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(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) {
+                LOGE("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_OBJECT:
+        case OP_SPUT_OBJECT_VOLATILE:
+        case OP_SPUT_VOLATILE:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_SHORT:
+        case OP_SPUT: {
+            int valOffset = offsetof(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]);
+
+            isVolatile = (mir->dalvikInsn.opCode == OP_SPUT_VOLATILE) ||
+                         (mir->dalvikInsn.opCode == OP_SPUT_OBJECT_VOLATILE) ||
+                         dvmIsVolatileField(fieldPtr);
+
+            isSputObject = (mir->dalvikInsn.opCode == OP_SPUT_OBJECT) ||
+                           (mir->dalvikInsn.opCode == OP_SPUT_OBJECT_VOLATILE);
+
+            if (fieldPtr == NULL) {
+                LOGE("Unexpected null static field");
+                dvmAbort();
+            }
+
+            rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+            rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
+            loadConstant(cUnit, tReg,  (int) fieldPtr);
+            if (isSputObject) {
+                objHead = dvmCompilerAllocTemp(cUnit);
+                loadWordDisp(cUnit, tReg, offsetof(Field, clazz), objHead);
+            }
+            HEAP_ACCESS_SHADOW(true);
+            storeWordDisp(cUnit, tReg, valOffset ,rlSrc.lowReg);
+            dvmCompilerFreeTemp(cUnit, tReg);
+            HEAP_ACCESS_SHADOW(false);
+            if (isVolatile) {
+                dvmCompilerGenMemBarrier(cUnit);
+            }
+            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(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) {
+                LOGE("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 = (void*)
+              (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+            if (classPtr == NULL) {
+                LOGE("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 */
+            opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+            ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+            /*
+             * 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) {
+                 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);
+            opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0);   /* Null? */
+            ArmLIR *branch1 = opCondBranch(cUnit, kArmCondEq);
+            /*
+             *  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 offset = offsetof(InterpState, self);
+            int exOffset = offsetof(Thread, exception);
+            int selfReg = dvmCompilerAllocTemp(cUnit);
+            int resetReg = dvmCompilerAllocTemp(cUnit);
+            RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+            rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+            loadWordDisp(cUnit, rGLUE, offset, selfReg);
+            loadConstant(cUnit, resetReg, 0);
+            loadWordDisp(cUnit, selfReg, exOffset, rlResult.lowReg);
+            storeWordDisp(cUnit, selfReg, 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:
+#if defined(WITH_DEADLOCK_PREDICTION) || defined(WITH_MONITOR_TRACKING)
+            genMonitorPortable(cUnit, mir);
+#else
+            genMonitor(cUnit, mir);
+#endif
+            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(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;
+    RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+    opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0);
+
+//TUNING: break this out to allow use of Thumb2 CB[N]Z
+    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 = 0;
+            LOGE("Unexpected opcode (%d) for Fmt21t\n", 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 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 = 0;      /* Make gcc happy */
+    int shiftOp = false;
+    bool isDiv = 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;
+            }
+            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 {
+        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:
+        case OP_IGET_VOLATILE:
+        case OP_IGET_WIDE:
+        case OP_IGET_OBJECT:
+        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_VOLATILE:
+        case OP_IPUT_WIDE:
+        case OP_IPUT_OBJECT:
+        case OP_IPUT_OBJECT_VOLATILE:
+        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) {
+                LOGE("Unexpected null instance field");
+                dvmAbort();
+            }
+            isVolatile = dvmIsVolatileField(fieldPtr);
+            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) {
+                LOGE("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 */
+            opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+            ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+            /*
+             * 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) {
+                LOGD("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 );
+//TUNING: compare to 0 primative to allow use of CB[N]Z
+            opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+            /* When taken r0 has NULL which can be used for store directly */
+            ArmLIR *branch1 = opCondBranch(cUnit, kArmCondEq);
+            /* 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:
+            isVolatile = true;
+            // NOTE: intentional fallthrough
+        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:
+        case OP_IPUT_SHORT:
+        case OP_IPUT_CHAR:
+        case OP_IPUT_BYTE:
+        case OP_IPUT_BOOLEAN:
+            genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile);
+            break;
+        case OP_IPUT_VOLATILE:
+        case OP_IPUT_OBJECT_VOLATILE:
+            isVolatile = true;
+            // NOTE: intentional fallthrough
+        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;
+    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 = 0;
+            LOGE("Unexpected opcode (%d) for Fmt22t\n", 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 s8 findPackedSwitchIndex(const u2* switchData, int testVal, int pc)
+{
+    int size;
+    int firstKey;
+    const int *entries;
+    int index;
+    int jumpIndex;
+    int caseDPCOffset = 0;
+    /* In Thumb mode pc is 4 ahead of the "mov r2, pc" instruction */
+    int 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 (((s8) caseDPCOffset) << 32) | (u8) chainingPC;
+}
+
+/* See comments for findPackedSwitchIndex */
+static s8 findSparseSwitchIndex(const u2* switchData, int testVal, int pc)
+{
+    int size;
+    const int *keys;
+    const int *entries;
+    int 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 (((s8) 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 */
+            opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+            ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+            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, rpc);
+            opReg(cUnit, kOpBlx, r4PC);
+            dvmCompilerClobberCallRegs(cUnit);
+            /* pc <- computed goto target */
+            opRegReg(cUnit, kOpMov, rpc, 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);
+
+            /* 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 */);
+
+            /* 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, [r6, #108] --+
+         * 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, rpc, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+            /* r2 = &predictedChainingCell */
+            ArmLIR *predictedChainingCell =
+                opRegRegImm(cUnit, kOpAdd, r2, rpc, 0);
+            predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+            genDispatchToHandler(cUnit, 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 = 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, 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 */
+            opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+            ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+            /*
+             * 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 */
+            opRegImm(cUnit, kOpCmp, r1, 0);
+
+            ArmLIR *bypassRechaining = opCondBranch(cUnit, kArmCondGt);
+
+            loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
+                         jitToInterpEntries.dvmJitToPatchPredictedChain), r7);
+
+            genRegCopy(cUnit, r1, rGLUE);
+            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, rpc, 0);
+            addrRetChain->generic.target = (LIR *) retChainingCell;
+
+            bypassRechaining->generic.target = (LIR *) addrRetChain;
+
+            /*
+             * r0 = this, r1 = calleeMethod,
+             * r1 = &ChainingCell,
+             * r4PC = callsiteDPC,
+             */
+            genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+            gDvmJit.invokePolymorphic++;
+#endif
+            /* Handle exceptions using the interpreter */
+            genTrap(cUnit, mir->offset, pcrLabel);
+            break;
+        }
+        /* NOP */
+        case OP_INVOKE_DIRECT_EMPTY: {
+            return false;
+        }
+        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 *retChainingCell = &labelList[bb->fallThrough->id];
+    ArmLIR *predChainingCell = &labelList[bb->taken->id];
+    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;
+
+            /*
+             * 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);
+
+            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 false;
+#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 true;
+#endif
+}
+
+static bool genInlinedFastIndexOf(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+    return false;
+#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 true;
+#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(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;
+}
+
+/*
+ * 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;
+    switch( mir->dalvikInsn.opCode) {
+        case OP_EXECUTE_INLINE_RANGE:
+        case OP_EXECUTE_INLINE: {
+            unsigned int i;
+            const InlineOperation* inLineTable = dvmGetInlineOpsTable();
+            int offset = offsetof(InterpState, retval);
+            int operation = dInsn->vB;
+            switch (operation) {
+                case INLINE_EMPTYINLINEMETHOD:
+                    return false;  /* Nop */
+                case INLINE_STRING_LENGTH:
+                    return genInlinedStringLength(cUnit, mir);
+                case INLINE_STRING_IS_EMPTY:
+                    return genInlinedStringIsEmpty(cUnit, mir);
+                case INLINE_MATH_ABS_INT:
+                    return genInlinedAbsInt(cUnit, mir);
+                case INLINE_MATH_ABS_LONG:
+                    return genInlinedAbsLong(cUnit, mir);
+                case INLINE_MATH_MIN_INT:
+                    return genInlinedMinMaxInt(cUnit, mir, true);
+                case INLINE_MATH_MAX_INT:
+                    return genInlinedMinMaxInt(cUnit, mir, false);
+                case INLINE_STRING_CHARAT:
+                    return genInlinedStringCharAt(cUnit, mir);
+                case INLINE_MATH_SQRT:
+                    if (genInlineSqrt(cUnit, mir))
+                        return false;
+                    else
+                        break;   /* Handle with C routine */
+                case INLINE_MATH_ABS_FLOAT:
+                    if (genInlinedAbsFloat(cUnit, mir))
+                        return false;
+                    else
+                        break;
+                case INLINE_MATH_ABS_DOUBLE:
+                    if (genInlinedAbsDouble(cUnit, mir))
+                        return false;
+                    else
+                        break;
+                case INLINE_STRING_COMPARETO:
+                    if (genInlinedCompareTo(cUnit, mir))
+                        return false;
+                    else
+                        break;
+                case INLINE_STRING_FASTINDEXOF_II:
+                    if (genInlinedFastIndexOf(cUnit, mir))
+                        return false;
+                    else
+                        break;
+                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);
+                case INLINE_STRING_EQUALS:
+                case INLINE_MATH_COS:
+                case INLINE_MATH_SIN:
+                case INLINE_FLOAT_TO_INT_BITS:
+                case INLINE_DOUBLE_TO_LONG_BITS:
+                    break;   /* Handle with C routine */
+                default:
+                    dvmCompilerAbort(cUnit);
+            }
+            dvmCompilerFlushAllRegs(cUnit);   /* Everything to home location */
+            dvmCompilerClobberCallRegs(cUnit);
+            dvmCompilerClobber(cUnit, r4PC);
+            dvmCompilerClobber(cUnit, r7);
+            opRegRegImm(cUnit, kOpAdd, r4PC, rGLUE, offset);
+            opImm(cUnit, kOpPush, (1<<r4PC) | (1<<r7));
+            LOAD_FUNC_ADDR(cUnit, r4PC, (int)inLineTable[operation].func);
+            genExportPC(cUnit, mir);
+            for (i=0; i < dInsn->vA; i++) {
+                loadValueDirect(cUnit, dvmCompilerGetSrc(cUnit, mir, i), i);
+            }
+            opReg(cUnit, kOpBlx, r4PC);
+            opRegImm(cUnit, kOpAdd, r13, 8);
+            opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+            ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+            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;
+        }
+        default:
+            return true;
+    }
+    return false;
+}
+
+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, rGLUE,
+            offsetof(InterpState,
+                     jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
+}
+
+/*
+ * 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, rGLUE,
+            offsetof(InterpState,
+                     jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
+}
+
+#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
+/* 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, rGLUE,
+        offsetof(InterpState,
+                 jitToInterpEntries.dvmJitToInterpBackwardBranch) >> 2);
+#else
+    newLIR3(cUnit, kThumbLdrRRI5, r0, rGLUE,
+        offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+#endif
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
+}
+
+#endif
+/* 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, rGLUE,
+            offsetof(InterpState,
+                     jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2);
+    newLIR1(cUnit, kThumbBlxR, r0);
+    addWordData(cUnit, (int) (callee->insns), true);
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokePredictedChainingCell(CompilationUnit *cUnit)
+{
+
+    /* Should not be executed in the initial state */
+    addWordData(cUnit, PREDICTED_CHAIN_BX_PAIR_INIT, true);
+    /* To be filled: class */
+    addWordData(cUnit, PREDICTED_CHAIN_CLAZZ_INIT, true);
+    /* To be filled: method */
+    addWordData(cUnit, PREDICTED_CHAIN_METHOD_INIT, true);
+    /*
+     * Rechain count. The initial value of 0 here will trigger chaining upon
+     * the first invocation of this callsite.
+     */
+    addWordData(cUnit, PREDICTED_CHAIN_COUNTER_INIT, true);
+}
+
+/* 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;
+    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 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(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(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);
+    loadConstant(cUnit, regPredictedClass, (int) callsiteInfo->clazz);
+    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 = dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1,
+                               false);
+    strcpy(msg, extendedMIROpNames[opOffset]);
+    newLIR1(cUnit, kArmPseudoExtended, (int) msg);
+
+    switch (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 = 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, pcrLabel);
+
+    /*
+     * Next, create two branches - one branch over to the loop body and the
+     * other branch to the PCR cell to punt.
+     */
+    ArmLIR *branchToBody = dvmCompilerNew(sizeof(ArmLIR), true);
+    branchToBody->opCode = kThumbBUncond;
+    branchToBody->generic.target = (LIR *) bodyLabel;
+    setupResourceMasks(branchToBody);
+    cUnit->loopAnalysis->branchToBody = (LIR *) branchToBody;
+
+    ArmLIR *branchToPCR = 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;
+    OpCode op = decInsn->opCode;
+
+    /*
+     * All opcodes that can throw exceptions and use the
+     * TEMPLATE_THROW_EXCEPTION_COMMON template should be excluded in the trace
+     * under self-verification mode.
+     */
+    return (op == OP_MONITOR_ENTER || op == OP_MONITOR_EXIT ||
+            op == OP_NEW_INSTANCE || op == OP_NEW_ARRAY ||
+            op == OP_CHECK_CAST || op == OP_MOVE_EXCEPTION ||
+            op == OP_FILL_ARRAY_DATA || op == OP_EXECUTE_INLINE ||
+            op == OP_EXECUTE_INLINE_RANGE);
+}
+#endif
+
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit)
+{
+    /* Used to hold the labels of each block */
+    ArmLIR *labelList =
+        dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
+    GrowableList chainingListByType[kChainingCellGap];
+    int i;
+
+    /*
+     * Initialize various types chaining lists.
+     */
+    for (i = 0; i < kChainingCellGap; i++) {
+        dvmInitGrowableList(&chainingListByType[i], 2);
+    }
+
+    BasicBlock **blockList = cUnit->blockList;
+
+    if (cUnit->executionCount) {
+        /*
+         * Reserve 6 bytes at the beginning of the trace
+         *        +----------------------------+
+         *        | execution count (4 bytes)  |
+         *        +----------------------------+
+         *        | chain cell offset (2 bytes)|
+         *        +----------------------------+
+         * ...and then code to increment the execution
+         * count:
+         *       mov   r0, pc       @ move adr of "mov r0,pc" + 4 to r0
+         *       sub   r0, #10      @ back up to addr of executionCount
+         *       ldr   r1, [r0]
+         *       add   r1, #1
+         *       str   r1, [r0]
+         */
+        newLIR1(cUnit, kArm16BitData, 0);
+        newLIR1(cUnit, kArm16BitData, 0);
+        cUnit->chainCellOffsetLIR =
+            (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+        cUnit->headerSize = 6;
+        /* Thumb instruction used directly here to ensure correct size */
+        newLIR2(cUnit, kThumbMovRR_H2L, r0, rpc);
+        newLIR2(cUnit, kThumbSubRI8, r0, 10);
+        newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
+        newLIR2(cUnit, kThumbAddRI8, r1, 1);
+        newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
+    } else {
+         /* Just reserve 2 bytes for the chain cell offset */
+        cUnit->chainCellOffsetLIR =
+            (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+        cUnit->headerSize = 2;
+    }
+
+    /* Handle the content in each basic block */
+    for (i = 0; i < cUnit->numBlocks; i++) {
+        blockList[i]->visited = true;
+        MIR *mir;
+
+        labelList[i].operands[0] = blockList[i]->startOffset;
+
+        if (blockList[i]->blockType >= kChainingCellGap) {
+            if (blockList[i]->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 (blockList[i]->blockType == kTraceEntryBlock) {
+            labelList[i].opCode = kArmPseudoEntryBlock;
+            if (blockList[i]->firstMIRInsn == NULL) {
+                continue;
+            } else {
+              setupLoopEntryBlock(cUnit, blockList[i],
+                                  &labelList[blockList[i]->fallThrough->id]);
+            }
+        } else if (blockList[i]->blockType == kTraceExitBlock) {
+            labelList[i].opCode = kArmPseudoExitBlock;
+            goto gen_fallthrough;
+        } else if (blockList[i]->blockType == kDalvikByteCode) {
+            labelList[i].opCode = kArmPseudoNormalBlockLabel;
+            /* Reset the register state */
+            dvmCompilerResetRegPool(cUnit);
+            dvmCompilerClobberAllRegs(cUnit);
+            dvmCompilerResetNullCheck(cUnit);
+        } else {
+            switch (blockList[i]->blockType) {
+                case kChainingCellNormal:
+                    labelList[i].opCode = kArmPseudoChainingCellNormal;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellNormal], (void *) i);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    labelList[i].opCode =
+                        kArmPseudoChainingCellInvokeSingleton;
+                    labelList[i].operands[0] =
+                        (int) blockList[i]->containingMethod;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokeSingleton],
+                        (void *) i);
+                    break;
+                case kChainingCellInvokePredicted:
+                    labelList[i].opCode =
+                        kArmPseudoChainingCellInvokePredicted;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellInvokePredicted],
+                        (void *) i);
+                    break;
+                case kChainingCellHot:
+                    labelList[i].opCode =
+                        kArmPseudoChainingCellHot;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellHot],
+                        (void *) i);
+                    break;
+                case kPCReconstruction:
+                    /* Make sure exception handling block is next */
+                    labelList[i].opCode =
+                        kArmPseudoPCReconstructionBlockLabel;
+                    assert (i == cUnit->numBlocks - 2);
+                    handlePCReconstruction(cUnit, &labelList[i+1]);
+                    break;
+                case kExceptionHandling:
+                    labelList[i].opCode = kArmPseudoEHBlockLabel;
+                    if (cUnit->pcReconstructionList.numUsed) {
+                        loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
+                                     jitToInterpEntries.dvmJitToInterpPunt),
+                                     r1);
+                        opReg(cUnit, kOpBlx, r1);
+                    }
+                    break;
+#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
+                case kChainingCellBackwardBranch:
+                    labelList[i].opCode =
+                        kArmPseudoChainingCellBackwardBranch;
+                    /* handle the codegen later */
+                    dvmInsertGrowableList(
+                        &chainingListByType[kChainingCellBackwardBranch],
+                        (void *) i);
+                    break;
+#endif
+                default:
+                    break;
+            }
+            continue;
+        }
+
+        ArmLIR *headLIR = NULL;
+
+        for (mir = blockList[i]->firstMIRInsn; mir; mir = mir->next) {
+
+            dvmCompilerResetRegPool(cUnit);
+            if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
+                dvmCompilerClobberAllRegs(cUnit);
+            }
+
+            if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
+                dvmCompilerResetDefTracking(cUnit);
+            }
+
+            if (mir->dalvikInsn.opCode >= kMirOpFirst) {
+                handleExtendedMIR(cUnit, mir);
+                continue;
+            }
+
+
+            OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+            InstructionFormat dalvikFormat =
+                dexGetInstrFormat(gDvm.instrFormat, dalvikOpCode);
+            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 =
+                newLIR2(cUnit, kArmPseudoDalvikByteCodeBoundary,
+                        mir->offset,
+                        (int) dvmCompilerGetDalvikDisassembly(&mir->dalvikInsn,
+                                                              note));
+            if (mir->ssaRep) {
+                char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+                newLIR1(cUnit, kArmPseudoSSARep, (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, blockList[i], 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, blockList[i],
+                                                  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, blockList[i],
+                                                  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, blockList[i],
+                                                      labelList);
+                        break;
+                    case kFmt3rms:
+                    case kFmt35ms:
+                        notHandled = handleFmt35ms_3rms(cUnit, mir,blockList[i],
+                                                        labelList);
+                        break;
+                    case kFmt3inline:
+                    case kFmt3rinline:
+                        notHandled = handleExecuteInline(cUnit, mir);
+                        break;
+                    case kFmt51l:
+                        notHandled = handleFmt51l(cUnit, mir);
+                        break;
+                    default:
+                        notHandled = true;
+                        break;
+                }
+            }
+            if (notHandled) {
+                LOGE("%#06x: Opcode 0x%x (%s) / Fmt %d not handled\n",
+                     mir->offset,
+                     dalvikOpCode, dexGetOpcodeName(dalvikOpCode),
+                     dalvikFormat);
+                dvmCompilerAbort(cUnit);
+                break;
+            }
+        }
+
+        if (blockList[i]->blockType == kTraceEntryBlock) {
+            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);
+        }
+
+gen_fallthrough:
+        /*
+         * Check if the block is terminated due to trace length constraint -
+         * insert an unconditional branch to the chaining cell.
+         */
+        if (blockList[i]->needFallThroughBranch) {
+            genUnconditionalBranch(cUnit,
+                                   &labelList[blockList[i]->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];
+
+            /* Align this chaining cell first */
+            newLIR0(cUnit, kArmPseudoPseudoAlign4);
+
+            /* Insert the pseudo chaining instruction */
+            dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+            switch (blockList[blockId]->blockType) {
+                case kChainingCellNormal:
+                    handleNormalChainingCell(cUnit,
+                      blockList[blockId]->startOffset);
+                    break;
+                case kChainingCellInvokeSingleton:
+                    handleInvokeSingletonChainingCell(cUnit,
+                        blockList[blockId]->containingMethod);
+                    break;
+                case kChainingCellInvokePredicted:
+                    handleInvokePredictedChainingCell(cUnit);
+                    break;
+                case kChainingCellHot:
+                    handleHotChainingCell(cUnit,
+                        blockList[blockId]->startOffset);
+                    break;
+#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
+                case kChainingCellBackwardBranch:
+                    handleBackwardBranchChainingCell(cUnit,
+                        blockList[blockId]->startOffset);
+                    break;
+#endif
+                default:
+                    LOGE("Bad blocktype %d", blockList[blockId]->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, rGLUE, offsetof(InterpState,
+                     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 */
+bool dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+    bool res;
+
+    if (gDvmJit.codeCacheFull) {
+        return false;
+    }
+
+    switch (work->kind) {
+        case kWorkOrderTrace:
+            /* Start compilation with maximally allowed trace length */
+            res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result,
+                                  work->bailPtr, 0 /* no hints */);
+            break;
+        case kWorkOrderTraceDebug: {
+            bool oldPrintMe = gDvmJit.printMe;
+            gDvmJit.printMe = true;
+            /* Start compilation with maximally allowed trace length */
+            res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result,
+                                  work->bailPtr, 0 /* no hints */);
+            gDvmJit.printMe = oldPrintMe;
+            break;
+        }
+        default:
+            res = false;
+            LOGE("Jit: unknown work order type");
+            assert(0);  // Bail if debug build, discard otherwise
+    }
+    return res;
+}
+
+/* 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)) {
+        LOGD("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) {
+            LOGE("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]);
+}
+
+/* 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/CodegenFactory.c b/vm/compiler/codegen/arm/CodegenFactory.c
new file mode 100644
index 0000000..157bd1f
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenFactory.c
@@ -0,0 +1,333 @@
+/*
+ * 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.
+ */
+
+
+/* Load a word at base + displacement.  Displacement must be word multiple */
+static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+                            int rDest)
+{
+    return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord,
+                        INVALID_SREG);
+}
+
+static ArmLIR *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, rGLUE, offsetof(InterpState, 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, rGLUE, offsetof(InterpState, 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, rGLUE, offsetof(InterpState, 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, rGLUE, offsetof(InterpState, 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, rGLUE, offsetof(InterpState, 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, rGLUE, offsetof(InterpState, 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 ArmLIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg,
+                                int dOffset, ArmLIR *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 ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
+                              ArmConditionCode cond,
+                              int reg1, int reg2, int dOffset,
+                              ArmLIR *pcrLabel)
+{
+    ArmLIR *res;
+    res = opRegReg(cUnit, kOpCmp, reg1, reg2);
+    ArmLIR *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 ArmLIR *genZeroCheck(CompilationUnit *cUnit, int mReg,
+                                int dOffset, ArmLIR *pcrLabel)
+{
+    return genRegImmCheck(cUnit, kArmCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+/* Perform bound check on two registers */
+static ArmLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
+                                  int rBound, int dOffset, ArmLIR *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/FP/Thumb2VFP.c b/vm/compiler/codegen/arm/FP/Thumb2VFP.c
new file mode 100644
index 0000000..b5bcf99
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/Thumb2VFP.c
@@ -0,0 +1,261 @@
+/*
+ * 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, 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, op, S2D(rlResult.lowReg, rlResult.highReg),
+            S2D(rlSrc1.lowReg, rlSrc1.highReg),
+            S2D(rlSrc2.lowReg, rlSrc2.highReg));
+    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:
+        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, 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, 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)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 true;
+}
+
+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
+    genIT(cUnit, kArmCondEq, "");
+    loadConstant(cUnit, rlResult.lowReg, 0);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
diff --git a/vm/compiler/codegen/arm/FP/ThumbPortableFP.c b/vm/compiler/codegen/arm/FP/ThumbPortableFP.c
new file mode 100644
index 0000000..957b4d4
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/ThumbPortableFP.c
@@ -0,0 +1,89 @@
+/*
+ * 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 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);
+
+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 false;   /* punt to C handler */
+}
+
+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.c b/vm/compiler/codegen/arm/FP/ThumbVFP.c
new file mode 100644
index 0000000..16d3ff7
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/ThumbVFP.c
@@ -0,0 +1,261 @@
+/*
+ * 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);
+
+/*
+ * 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) {
+             dvmCompilerFlushRegWideForV5TEVFP(cUnit, rlSrc.lowReg,
+                                               rlSrc.highReg);
+         } else {
+             dvmCompilerFlushRegForV5TEVFP(cUnit, rlSrc.lowReg);
+         }
+     }
+     dvmCompilerClobber(cUnit, rDest);
+     dvmCompilerLockTemp(cUnit, rDest);
+     opRegRegImm(cUnit, kOpAdd, rDest, rFP,
+                 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
+ * 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)
+{
+    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 template;
+    switch (opCode) {
+        case OP_INT_TO_FLOAT:
+            longSrc = false;
+            longDest = false;
+            template = TEMPLATE_INT_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_INT:
+            longSrc = false;
+            longDest = false;
+            template = TEMPLATE_FLOAT_TO_INT_VFP;
+            break;
+        case OP_DOUBLE_TO_FLOAT:
+            longSrc = true;
+            longDest = false;
+            template = TEMPLATE_DOUBLE_TO_FLOAT_VFP;
+            break;
+        case OP_FLOAT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            template = TEMPLATE_FLOAT_TO_DOUBLE_VFP;
+            break;
+        case OP_INT_TO_DOUBLE:
+            longSrc = false;
+            longDest = true;
+            template = TEMPLATE_INT_TO_DOUBLE_VFP;
+            break;
+        case OP_DOUBLE_TO_INT:
+            longSrc = true;
+            longDest = false;
+            template = 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, template);
+    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 template;
+    RegLocation rlResult = dvmCompilerGetReturn(cUnit);
+    bool wide = true;
+
+    switch(mir->dalvikInsn.opCode) {
+        case OP_CMPL_FLOAT:
+            template = TEMPLATE_CMPL_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPG_FLOAT:
+            template = TEMPLATE_CMPG_FLOAT_VFP;
+            wide = false;
+            break;
+        case OP_CMPL_DOUBLE:
+            template = TEMPLATE_CMPL_DOUBLE_VFP;
+            break;
+        case OP_CMPG_DOUBLE:
+            template = TEMPLATE_CMPG_DOUBLE_VFP;
+            break;
+        default:
+            return true;
+    }
+    loadValueAddressDirect(cUnit, rlSrc1, r0);
+    loadValueAddressDirect(cUnit, rlSrc2, r1);
+    genDispatchToHandler(cUnit, template);
+    storeValue(cUnit, rlDest, rlResult);
+    return false;
+}
diff --git a/vm/compiler/codegen/arm/GlobalOptimizations.c b/vm/compiler/codegen/arm/GlobalOptimizations.c
new file mode 100644
index 0000000..1cfa32b
--- /dev/null
+++ b/vm/compiler/codegen/arm/GlobalOptimizations.c
@@ -0,0 +1,61 @@
+/*
+ * 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->isNop = true;
+                    break;
+                }
+
+                /*
+                 * Found real useful stuff between the branch and the target
+                 */
+                if (!isPseudoOpCode(nextLIR->opCode))
+                    break;
+            }
+        }
+    }
+}
+
+void dvmCompilerApplyGlobalOptimizations(CompilationUnit *cUnit)
+{
+    applyRedundantBranchElimination(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/LocalOptimizations.c b/vm/compiler/codegen/arm/LocalOptimizations.c
new file mode 100644
index 0000000..724fdb7
--- /dev/null
+++ b/vm/compiler/codegen/arm/LocalOptimizations.c
@@ -0,0 +1,512 @@
+/*
+ * 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)
+
+ArmLIR* dvmCompilerGenCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
+/* Is this a Dalvik register access? */
+static inline bool isDalvikLoad(ArmLIR *lir)
+{
+    return (lir->useMask != ENCODE_ALL) && (lir->useMask & ENCODE_DALVIK_REG);
+}
+
+/* Is this a load from the literal pool? */
+static inline bool isLiteralLoad(ArmLIR *lir)
+{
+    return (lir->useMask != ENCODE_ALL) && (lir->useMask & ENCODE_LITERAL);
+}
+
+static inline bool isDalvikStore(ArmLIR *lir)
+{
+    return (lir->defMask != ENCODE_ALL) && (lir->defMask & ENCODE_DALVIK_REG);
+}
+
+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)
+{
+    LOGD("************ %s ************", optimization);
+    dvmDumpLIRInsn((LIR *) thisLIR, 0);
+    dvmDumpLIRInsn((LIR *) checkLIR, 0);
+}
+#endif
+
+/*
+ * Perform a pass of top-down walk to
+ * 1) Eliminate redundant loads and stores
+ * 2) Sink stores to latest possible slot
+ */
+static void applyLoadStoreElimination(CompilationUnit *cUnit,
+                                      ArmLIR *headLIR,
+                                      ArmLIR *tailLIR)
+{
+    ArmLIR *thisLIR;
+
+    cUnit->optRound++;
+    for (thisLIR = headLIR;
+         thisLIR != tailLIR;
+         thisLIR = NEXT_LIR(thisLIR)) {
+        /* Skip newly added instructions */
+        if (thisLIR->age >= cUnit->optRound) {
+            continue;
+        }
+        if (isDalvikStore(thisLIR)) {
+            int nativeRegId = thisLIR->operands[0];
+            ArmLIR *checkLIR;
+            int sinkDistance = 0;
+            /*
+             * Add r15 (pc) to the mask to prevent this instruction
+             * from sinking past branch instructions. Unset the Dalvik register
+             * bit when checking with native resource constraints.
+             */
+            u8 stopMask = (ENCODE_REG_PC | thisLIR->useMask) &
+                          ~ENCODE_DALVIK_REG;
+
+            for (checkLIR = NEXT_LIR(thisLIR);
+                 checkLIR != tailLIR;
+                 checkLIR = NEXT_LIR(checkLIR)) {
+
+                /* Check if a Dalvik register load is redundant */
+                if (isDalvikLoad(checkLIR) &&
+                    (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
+                    (REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId))) {
+                    /* Insert a move to replace the load */
+                    if (checkLIR->operands[0] != nativeRegId) {
+                        ArmLIR *moveLIR;
+                        moveLIR = dvmCompilerRegCopyNoInsert(
+                                    cUnit, checkLIR->operands[0], nativeRegId);
+                        /*
+                         * Insert the converted checkLIR instruction after the
+                         * the original checkLIR since the optimization is
+                         * scannng in the top-down order and the new instruction
+                         * will need to be checked.
+                         */
+                        dvmCompilerInsertLIRAfter((LIR *) checkLIR,
+                                                  (LIR *) moveLIR);
+                    }
+                    checkLIR->isNop = true;
+                    continue;
+
+                /*
+                 * Found a true output dependency - nuke the previous store.
+                 * The register type doesn't matter here.
+                 */
+                } else if (isDalvikStore(checkLIR) &&
+                           (checkLIR->aliasInfo == thisLIR->aliasInfo)) {
+                    thisLIR->isNop = true;
+                    break;
+                /* Find out the latest slot that the store can be sunk into */
+                } else {
+                    /* Last instruction reached */
+                    bool stopHere = (NEXT_LIR(checkLIR) == tailLIR);
+
+                    /* Store data is clobbered */
+                    stopHere |= ((stopMask & checkLIR->defMask) != 0);
+
+                    /* Store data partially clobbers the Dalvik register */
+                    if (stopHere == false &&
+                        ((checkLIR->useMask | checkLIR->defMask) &
+                         ENCODE_DALVIK_REG)) {
+                        stopHere = isDalvikRegisterClobbered(thisLIR, checkLIR);
+                    }
+
+                    /* Found a new place to put the store - move it here */
+                    if (stopHere == true) {
+                        DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+                                                        "SINK STORE"));
+                        /* The store can be sunk for at least one cycle */
+                        if (sinkDistance != 0) {
+                            ArmLIR *newStoreLIR =
+                                dvmCompilerNew(sizeof(ArmLIR), true);
+                            *newStoreLIR = *thisLIR;
+                            newStoreLIR->age = cUnit->optRound;
+                            /*
+                             * Stop point found - insert *before* the checkLIR
+                             * since the instruction list is scanned in the
+                             * top-down order.
+                             */
+                            dvmCompilerInsertLIRBefore((LIR *) checkLIR,
+                                                       (LIR *) newStoreLIR);
+                            thisLIR->isNop = true;
+                        }
+                        break;
+                    }
+
+                    /*
+                     * Saw a real instruction that the store can be sunk after
+                     */
+                    if (!isPseudoOpCode(checkLIR->opCode)) {
+                        sinkDistance++;
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void applyLoadHoisting(CompilationUnit *cUnit,
+                              ArmLIR *headLIR,
+                              ArmLIR *tailLIR)
+{
+    ArmLIR *thisLIR;
+    /*
+     * Don't want to hoist in front of first load following a barrier (or
+     * first instruction of the block.
+     */
+    bool firstLoad = true;
+    int maxHoist = dvmCompilerTargetOptHint(kMaxHoistDistance);
+
+    cUnit->optRound++;
+    for (thisLIR = headLIR;
+         thisLIR != tailLIR;
+         thisLIR = NEXT_LIR(thisLIR)) {
+        /* Skip newly added instructions */
+        if (thisLIR->age >= cUnit->optRound ||
+            thisLIR->isNop == true) {
+            continue;
+        }
+
+        if (firstLoad && (EncodingMap[thisLIR->opCode].flags & IS_LOAD)) {
+            /*
+             * Ensure nothing will be hoisted in front of this load because
+             * it's result will likely be needed soon.
+             */
+            thisLIR->defMask |= ENCODE_MEM_USE;
+            firstLoad = false;
+        }
+
+        firstLoad |= (thisLIR->defMask == ENCODE_ALL);
+
+        if (isDalvikLoad(thisLIR)) {
+            int dRegId = DECODE_ALIAS_INFO_REG(thisLIR->aliasInfo);
+            int nativeRegId = thisLIR->operands[0];
+            ArmLIR *checkLIR;
+            int hoistDistance = 0;
+            u8 stopUseMask = (ENCODE_REG_PC | thisLIR->useMask);
+            u8 stopDefMask = thisLIR->defMask;
+            u8 checkResult;
+
+            /* First check if the load can be completely elinimated */
+            for (checkLIR = PREV_LIR(thisLIR);
+                 checkLIR != headLIR;
+                 checkLIR = PREV_LIR(checkLIR)) {
+
+                if (checkLIR->isNop) continue;
+
+                /*
+                 * Check if the Dalvik register is previously accessed
+                 * with exactly the same type.
+                 */
+                if ((isDalvikLoad(checkLIR) || isDalvikStore(checkLIR)) &&
+                    (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
+                    (checkLIR->operands[0] == nativeRegId)) {
+                    /*
+                     * If it is previously accessed but with a different type,
+                     * the search will terminate later at the point checking
+                     * for partially overlapping stores.
+                     */
+                    thisLIR->isNop = true;
+                    break;
+                }
+
+                /*
+                 * No earlier use/def can reach this load if:
+                 * 1) Head instruction is reached
+                 */
+                if (checkLIR == headLIR) {
+                    break;
+                }
+
+                checkResult = (stopUseMask | stopDefMask) & checkLIR->defMask;
+
+                /*
+                 * If both instructions are verified Dalvik accesses, clear the
+                 * may- and must-alias bits to detect true resource
+                 * dependencies.
+                 */
+                if (checkResult & ENCODE_DALVIK_REG) {
+                    checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF);
+                }
+
+                /*
+                 * 2) load target register is clobbered
+                 * 3) A branch is seen (stopUseMask has the PC bit set).
+                 */
+                if (checkResult) {
+                    break;
+                }
+
+                /* Store data partially clobbers the Dalvik register */
+                if (isDalvikStore(checkLIR) &&
+                    isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+                    break;
+                }
+            }
+
+            /* The load has been eliminated */
+            if (thisLIR->isNop) continue;
+
+            /*
+             * The load cannot be eliminated. See if it can be hoisted to an
+             * earlier spot.
+             */
+            for (checkLIR = PREV_LIR(thisLIR);
+                 /* empty by intention */;
+                 checkLIR = PREV_LIR(checkLIR)) {
+
+                if (checkLIR->isNop) continue;
+
+                /*
+                 * Check if the "thisLIR" load is redundant
+                 * NOTE: At one point, we also triggered if the checkLIR
+                 * instruction was a load.  However, that tended to insert
+                 * a load/use dependency because the full scheduler is
+                 * not yet complete.  When it is, we chould also trigger
+                 * on loads.
+                 */
+                if (isDalvikStore(checkLIR) &&
+                    (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
+                    (REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId))) {
+                    /* Insert a move to replace the load */
+                    if (checkLIR->operands[0] != nativeRegId) {
+                        ArmLIR *moveLIR;
+                        moveLIR = dvmCompilerRegCopyNoInsert(
+                                    cUnit, nativeRegId, checkLIR->operands[0]);
+                        /*
+                         * Convert *thisLIR* load into a move
+                         */
+                        dvmCompilerInsertLIRAfter((LIR *) checkLIR,
+                                                  (LIR *) moveLIR);
+                    }
+                    thisLIR->isNop = true;
+                    break;
+
+                /* Find out if the load can be yanked past the checkLIR */
+                } else {
+                    /* Last instruction reached */
+                    bool stopHere = (checkLIR == headLIR);
+
+                    /* Base address is clobbered by checkLIR */
+                    checkResult = stopUseMask & checkLIR->defMask;
+                    if (checkResult & ENCODE_DALVIK_REG) {
+                        checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF);
+                    }
+                    stopHere |= (checkResult != 0);
+
+                    /* Load target clobbers use/def in checkLIR */
+                    checkResult = stopDefMask &
+                                  (checkLIR->useMask | checkLIR->defMask);
+                    if (checkResult & ENCODE_DALVIK_REG) {
+                        checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF);
+                    }
+                    stopHere |= (checkResult != 0);
+
+                    /* Store data partially clobbers the Dalvik register */
+                    if (stopHere == false &&
+                        (checkLIR->defMask & ENCODE_DALVIK_REG)) {
+                        stopHere = isDalvikRegisterClobbered(thisLIR, checkLIR);
+                    }
+
+                    /*
+                     * Stop at an earlier Dalvik load if the offset of checkLIR
+                     * is not less than thisLIR
+                     *
+                     * Experiments show that doing
+                     *
+                     * ldr     r1, [r5, #16]
+                     * ldr     r0, [r5, #20]
+                     *
+                     * is much faster than
+                     *
+                     * ldr     r0, [r5, #20]
+                     * ldr     r1, [r5, #16]
+                     */
+                    if (isDalvikLoad(checkLIR)) {
+                        int dRegId2 =
+                            DECODE_ALIAS_INFO_REG(checkLIR->aliasInfo);
+                        if (dRegId2 <= dRegId) {
+                            stopHere = true;
+                        }
+                    }
+
+                    /* Don't go too far */
+                    stopHere |= (hoistDistance >= maxHoist);
+
+                    /* Found a new place to put the load - move it here */
+                    if (stopHere == true) {
+                        DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+                                                        "HOIST LOAD"));
+                        /* The load can be hoisted for at least one cycle */
+                        if (hoistDistance != 0) {
+                            ArmLIR *newLoadLIR =
+                                dvmCompilerNew(sizeof(ArmLIR), true);
+                            *newLoadLIR = *thisLIR;
+                            newLoadLIR->age = cUnit->optRound;
+                            /*
+                             * Stop point found - insert *after* the checkLIR
+                             * since the instruction list is scanned in the
+                             * bottom-up order.
+                             */
+                            dvmCompilerInsertLIRAfter((LIR *) checkLIR,
+                                                      (LIR *) newLoadLIR);
+                            thisLIR->isNop = true;
+                        }
+                        break;
+                    }
+
+                    /*
+                     * Saw a real instruction that hosting the load is
+                     * beneficial
+                     */
+                    if (!isPseudoOpCode(checkLIR->opCode)) {
+                        hoistDistance++;
+                    }
+                }
+            }
+        } else if (isLiteralLoad(thisLIR)) {
+            int litVal = thisLIR->aliasInfo;
+            int nativeRegId = thisLIR->operands[0];
+            ArmLIR *checkLIR;
+            int hoistDistance = 0;
+            u8 stopUseMask = (ENCODE_REG_PC | thisLIR->useMask) &
+                             ~ENCODE_LITPOOL_REF;
+            u8 stopDefMask = thisLIR->defMask & ~ENCODE_LITPOOL_REF;
+
+            /* First check if the load can be completely elinimated */
+            for (checkLIR = PREV_LIR(thisLIR);
+                 checkLIR != headLIR;
+                 checkLIR = PREV_LIR(checkLIR)) {
+
+                if (checkLIR->isNop) continue;
+
+                /* Reloading same literal into same tgt reg? Eliminate if so */
+                if (isLiteralLoad(checkLIR) &&
+                    (checkLIR->aliasInfo == litVal) &&
+                    (checkLIR->operands[0] == nativeRegId)) {
+                    thisLIR->isNop = true;
+                    break;
+                }
+
+                /*
+                 * No earlier use/def can reach this load if:
+                 * 1) Head instruction is reached
+                 * 2) load target register is clobbered
+                 * 3) A branch is seen (stopUseMask has the PC bit set).
+                 */
+                if ((checkLIR == headLIR) ||
+                    (stopUseMask | stopDefMask) & checkLIR->defMask) {
+                    break;
+                }
+            }
+
+            /* The load has been eliminated */
+            if (thisLIR->isNop) continue;
+
+            /*
+             * The load cannot be eliminated. See if it can be hoisted to an
+             * earlier spot.
+             */
+            for (checkLIR = PREV_LIR(thisLIR);
+                 /* empty by intention */;
+                 checkLIR = PREV_LIR(checkLIR)) {
+
+                if (checkLIR->isNop) continue;
+
+                /*
+                 * TUNING: once a full scheduler exists, check here
+                 * for conversion of a redundant load into a copy similar
+                 * to the way redundant loads are handled above.
+                 */
+
+                /* Find out if the load can be yanked past the checkLIR */
+
+                /* Last instruction reached */
+                bool stopHere = (checkLIR == headLIR);
+
+                /* Base address is clobbered by checkLIR */
+                stopHere |= ((stopUseMask & checkLIR->defMask) != 0);
+
+                /* Load target clobbers use/def in checkLIR */
+                stopHere |= ((stopDefMask &
+                             (checkLIR->useMask | checkLIR->defMask)) != 0);
+
+                /* Avoid re-ordering literal pool loads */
+                stopHere |= isLiteralLoad(checkLIR);
+
+                /* Don't go too far */
+                stopHere |= (hoistDistance >= maxHoist);
+
+                /* Found a new place to put the load - move it here */
+                if (stopHere == true) {
+                    DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+                                                    "HOIST LOAD"));
+                    /* The store can be hoisted for at least one cycle */
+                    if (hoistDistance != 0) {
+                        ArmLIR *newLoadLIR =
+                            dvmCompilerNew(sizeof(ArmLIR), true);
+                        *newLoadLIR = *thisLIR;
+                        newLoadLIR->age = cUnit->optRound;
+                        /*
+                         * Insertion is guaranteed to succeed since checkLIR
+                         * is never the first LIR on the list
+                         */
+                        dvmCompilerInsertLIRAfter((LIR *) checkLIR,
+                                                  (LIR *) newLoadLIR);
+                        thisLIR->isNop = true;
+                    }
+                    break;
+                }
+
+                /*
+                 * Saw a real instruction that hosting the load is
+                 * beneficial
+                 */
+                if (!isPseudoOpCode(checkLIR->opCode)) {
+                    hoistDistance++;
+                }
+            }
+        }
+    }
+}
+
+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..a49eef8
--- /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.c"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.c"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.c"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
+
+--
+
+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/Ralloc.h b/vm/compiler/codegen/arm/Ralloc.h
new file mode 100644
index 0000000..cc3e605
--- /dev/null
+++ b/vm/compiler/codegen/arm/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/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 dvmCompilerResetDefTracking(CompilationUnit *cUnit);
diff --git a/vm/compiler/codegen/arm/RallocUtil.c b/vm/compiler/codegen/arm/RallocUtil.c
new file mode 100644
index 0000000..e917995
--- /dev/null
+++ b/vm/compiler/codegen/arm/RallocUtil.c
@@ -0,0 +1,1010 @@
+/*
+ * 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 "ArmLIR.h"
+#include "Codegen.h"
+#include "Ralloc.h"
+
+/*
+ * Register usage for 16-bit Thumb systems:
+ *     r0-r3: Temp/argument
+ *     lr(r14):      Temp for translations, return address for handlers
+ *     rGLUE(r6):    Pointer to InterpState
+ *     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
+ *     rGLUE(r6):    Pointer to InterpState
+ *     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
+ *
+ */
+
+#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;
+    LOGE("================================================");
+    for (i=0; i < numRegs; i++ ){
+        LOGE("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);
+    }
+    LOGE("================================================");
+}
+
+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];
+        }
+    }
+    LOGE("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) {
+        LOGE("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;
+    }
+    LOGE("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:
+            LOGE("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;
+        }
+    }
+    LOGE("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;
+        }
+    }
+    LOGE("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, 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, rlr);
+}
+
+/* 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 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)->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) {
+                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, 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;
+}
+
+/* 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/arm/Thumb/Factory.c b/vm/compiler/codegen/arm/Thumb/Factory.c
new file mode 100644
index 0000000..85f612e
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Factory.c
@@ -0,0 +1,879 @@
+/*
+ * 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, value, 255);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, value, false);
+    }
+    ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opCode = kThumbLdrPcRel;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = tDest;
+    setupResourceMasks(loadPcRel);
+    /*
+     * Special case for literal loads with a link register target.
+     * Self-cosim mode will insert calls prior to heap references
+     * after optimization, and those will destroy r14.  The easy
+     * workaround is to treat literal loads into r14 as heap references
+     * to prevent them from being hoisted.  Use of r14 in this manner
+     * is currently rare.  Revist if that changes.
+     */
+    if (rDest != rlr)
+        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);
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+    ArmOpCode opCode = kThumbBkpt;
+    switch (op) {
+        case kOpUncondBr:
+            opCode = kThumbBUncond;
+            break;
+        default:
+            LOGE("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:
+            LOGE("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:
+            LOGE("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 == 13) && (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 == 13) && (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:
+            LOGE("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 == 13) && (value <= 1020)) { /* sp */
+                assert((value & 0x3) == 0);
+                shortForm = true;
+                opCode = kThumbAddSpRel;
+                value >>= 2;
+            } else if ((rSrc1 == 15) && (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:
+            LOGE("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:
+            LOGE("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:
+            LOGE("Jit: bad case in loadBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR3(cUnit, opCode, rDest, rBase, rNewIndex);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->branchInsertSV = 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:
+            LOGE("Jit: bad case in storeBaseIndexed");
+            dvmCompilerAbort(cUnit);
+    }
+    res = newLIR3(cUnit, opCode, rSrc, rBase, rNewIndex);
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        res->branchInsertSV = 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->branchInsertSV = 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->branchInsertSV = 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 == rpc) &&
+                (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opCode = kThumbLdrPcRel;
+            } else if (LOWREG(rDest) && (rBase == r13) &&
+                      (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:
+            LOGE("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 == rFP)
+                annotateDalvikRegAccess(load, displacement >> 2,
+                                        true /* isLoad */);
+            if (rTmp != rDest)
+                dvmCompilerFreeTemp(cUnit, rTmp);
+        }
+    }
+    if (rBase == rFP) {
+        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->branchInsertSV = true;
+    if (load2 != NULL && cUnit->heapMemOp)
+        load2->branchInsertSV = true;
+#endif
+    return res;
+}
+
+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:
+            LOGE("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 == rFP) {
+        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->branchInsertSV = true;
+    if (store2 != NULL && cUnit->heapMemOp)
+        store2->branchInsertSV = 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 = 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->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 inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
+                                     ArmConditionCode cond, int reg,
+                                     int checkValue, int dOffset,
+                                     ArmLIR *pcrLabel)
+{
+    int tReg;
+    ArmLIR *res;
+    if ((checkValue & 0xff) != checkValue) {
+        tReg = dvmCompilerAllocTemp(cUnit);
+        loadConstant(cUnit, tReg, checkValue);
+        res = genRegRegCheck(cUnit, cond, reg, tReg, dOffset, pcrLabel);
+        dvmCompilerFreeTemp(cUnit, tReg);
+        return res;
+    }
+    newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
+    ArmLIR *branch = newLIR2(cUnit, kThumbBCond, 0, cond);
+    return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
diff --git a/vm/compiler/codegen/arm/Thumb/Gen.c b/vm/compiler/codegen/arm/Thumb/Gen.c
new file mode 100644
index 0000000..37cc18d
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Gen.c
@@ -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 codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ *        Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * 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 = dvmCompilerNew(sizeof(*pool), true);
+    cUnit->regPool = pool;
+    pool->numCoreTemps = numTemps;
+    pool->coreTemps =
+            dvmCompilerNew(numTemps * sizeof(*pool->coreTemps), true);
+    pool->numFPTemps = 0;
+    pool->FPTemps = NULL;
+    pool->numCoreRegs = 0;
+    pool->coreRegs = NULL;
+    pool->numFPRegs = 0;
+    pool->FPRegs = NULL;
+    dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+    dvmCompilerInitPool(pool->FPTemps, NULL, 0);
+    dvmCompilerInitPool(pool->coreRegs, NULL, 0);
+    dvmCompilerInitPool(pool->FPRegs, 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, rFP);
+    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(InterpState, 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, rGLUE, offset, reg0);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reg0);
+    return true;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(InterpState, 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, rGLUE, offset, reglo);
+    newLIR2(cUnit, kThumbAndRR, reghi, signMask);
+    dvmCompilerFreeTemp(cUnit, signMask);
+    storeWordDisp(cUnit, rGLUE, offset + 4, reghi);
+    //TUNING: rewrite this to not clobber
+    dvmCompilerClobber(cUnit, reghi);
+    return true;
+}
+
+/* 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(InterpState, 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, rGLUE, 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);
+}
diff --git a/vm/compiler/codegen/arm/Thumb/Ralloc.c b/vm/compiler/codegen/arm/Thumb/Ralloc.c
new file mode 100644
index 0000000..6769972
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Ralloc.c
@@ -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.c b/vm/compiler/codegen/arm/Thumb2/Factory.c
new file mode 100644
index 0000000..2bf2940
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Factory.c
@@ -0,0 +1,1210 @@
+/*
+ * 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 (encodedImm >= 0) {
+        return newLIR2(cUnit, kThumb2Vmovs_IMM8, rDest, encodedImm);
+    }
+    ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, value, false);
+    }
+    ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opCode = kThumb2Vldrs;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    loadPcRel->operands[1] = rpc;
+    setupResourceMasks(loadPcRel);
+    // Self-cosim workaround.
+    if (rDest != rlr)
+        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, value, 0);
+    if (dataTarget == NULL) {
+        dataTarget = addWordData(cUnit, value, false);
+    }
+    ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+    loadPcRel->opCode = kThumb2LdrPcRel12;
+    loadPcRel->generic.target = (LIR *) dataTarget;
+    loadPcRel->operands[0] = rDest;
+    setupResourceMasks(loadPcRel);
+    /*
+     * Special case for literal loads with a link register target.
+     * Self-cosim mode will insert calls prior to heap references
+     * after optimization, and those will destroy r14.  The easy
+     * workaround is to treat literal loads into r14 as heap references
+     * to prevent them from being hoisted.  Use of r14 in this manner
+     * is currently rare.  Revisit if that changes.
+     */
+    if (rDest != rlr)
+        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);
+}
+
+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:
+            opCode = ((value & 0xff00) != 0) ? kThumb2Push : kThumbPush;
+            break;
+        case kOpPop:
+            opCode = ((value & 0xff00) != 0) ? kThumb2Pop : kThumbPop;
+            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 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 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 *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 == 13) &&
+                (value <= 1020) && ((value & 0x3)==0)) {
+                return newLIR3(cUnit, kThumbAddSpRel, rDest, rSrc1,
+                               value >> 2);
+            } else if (LOWREG(rDest) && (rSrc1 == rpc) &&
+                       (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 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 == 13) && (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 == 13) && (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;
+    if (FPREG(rDestLo) && (encodedImm >= 0)) {
+        res = newLIR2(cUnit, kThumb2Vmovd_IMM8, S2D(rDestLo, rDestHi),
+                      encodedImm);
+    } 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->branchInsertSV = 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->branchInsertSV = 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->branchInsertSV = 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->branchInsertSV = 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 == rpc) &&
+                (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                encodedDisp >>= 2;
+                opCode = kThumbLdrPcRel;
+            } else if (LOWREG(rDest) && (rBase == r13) &&
+                      (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 == rFP) {
+        annotateDalvikRegAccess(load, displacement >> 2, true /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        load->branchInsertSV = true;
+#endif
+    return res;
+}
+
+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 == rFP) {
+        annotateDalvikRegAccess(store, displacement >> 2, false /* isLoad */);
+    }
+#if defined(WITH_SELF_VERIFICATION)
+    if (cUnit->heapMemOp)
+        store->branchInsertSV = 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->branchInsertSV = 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->branchInsertSV = 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);
+}
+
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
+                              ArmConditionCode cond, int reg,
+                              int checkValue, int dOffset,
+                              ArmLIR *pcrLabel)
+{
+    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 genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+static ArmLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+    ArmLIR* res = dvmCompilerNew(sizeof(ArmLIR), true);
+    res->operands[0] = rDest;
+    res->operands[1] = rSrc;
+    if (rDest == rSrc) {
+        res->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 = 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->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);
+            }
+        }
+    }
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Gen.c b/vm/compiler/codegen/arm/Thumb2/Gen.c
new file mode 100644
index 0000000..3632388
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Gen.c
@@ -0,0 +1,415 @@
+/*
+ * 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 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 = dvmCompilerNew(sizeof(*pool), true);
+    cUnit->regPool = pool;
+    pool->numCoreTemps = numTemps;
+    pool->coreTemps =
+            dvmCompilerNew(numTemps * sizeof(*cUnit->regPool->coreTemps), true);
+    pool->numFPTemps = numFPTemps;
+    pool->FPTemps =
+            dvmCompilerNew(numFPTemps * sizeof(*cUnit->regPool->FPTemps), true);
+    pool->numCoreRegs = 0;
+    pool->coreRegs = NULL;
+    pool->numFPRegs = 0;
+    pool->FPRegs = NULL;
+    dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+    dvmCompilerInitPool(pool->FPTemps, fpTemps, pool->numFPTemps);
+    dvmCompilerInitPool(pool->coreRegs, NULL, 0);
+    dvmCompilerInitPool(pool->FPRegs, NULL, 0);
+    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,
+                     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:
+            LOGE("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, rFP,
+            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
+    loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0); // Get self
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    loadWordDisp(cUnit, r0, 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);
+    branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
+
+    hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+    hopTarget->defMask = ENCODE_ALL;
+    hopBranch->generic.target = (LIR *)hopTarget;
+
+    // Clear the lock
+    ArmLIR *inst = newLIR0(cUnit, kThumb2Clrex);
+    // ...and make it a scheduling barrier
+    inst->defMask = ENCODE_ALL;
+
+    // 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 +
+                 dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_ENTER)));
+    // Export PC (part 2)
+    newLIR3(cUnit, kThumb2StrRRI8Predec, r3, rFP,
+            sizeof(StackSaveArea) -
+            offsetof(StackSaveArea, xtra.currentPc));
+    /* Call template, and don't return */
+    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
+    loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0); // Get self
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    loadWordDisp(cUnit, r1, offsetof(Object, lock), r2); // Get object->lock
+    loadWordDisp(cUnit, r0, 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);
+    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);
+    // Export PC (part 2)
+    newLIR3(cUnit, kThumb2StrRRI8Predec, r3, rFP,
+            sizeof(StackSaveArea) -
+            offsetof(StackSaveArea, xtra.currentPc));
+    opReg(cUnit, kOpBlx, r7);
+    opRegImm(cUnit, kOpCmp, r0, 0); /* Did we throw? */
+    ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+    loadConstant(cUnit, r0,
+                 (int) (cUnit->method->insns + mir->offset +
+                 dexGetInstrWidthAbs(gDvm.instrWidth, 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 true;
+}
+
+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 true;
+}
+
+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);
+    }
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Ralloc.c b/vm/compiler/codegen/arm/Thumb2/Ralloc.c
new file mode 100644
index 0000000..6adfd62
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Ralloc.c
@@ -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.c b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c
new file mode 100644
index 0000000..6511eac
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    int i = 0;
+    extern void dvmCompilerTemplateStart(void);
+
+    /*
+     * 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;
+    gDvmJit.threshold = 200;
+    gDvmJit.codeCacheSize = 512*1024;
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking mode */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(offsetof(ClassObject, vtable) < 128 &&
+           (offsetof(ClassObject, vtable) & 0x3) == 0);
+    assert(offsetof(ArrayObject, length) < 128 &&
+           (offsetof(ArrayObject, length) & 0x3) == 0);
+    assert(offsetof(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(InterpState, jitToInterpEntries) +
+            sizeof(struct JitToInterpEntries)) <= 128);
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 2;
+            break;
+        default:
+            LOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#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..9f862e8
--- /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,
+typedef enum {
+#include "../../../template/armv5te-vfp/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/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.c b/vm/compiler/codegen/arm/armv5te-vfp/Codegen.c
new file mode 100644
index 0000000..eda243f
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/Codegen.c
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/arm/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Thumb-specific factory utilities */
+#include "../Thumb/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb-specific codegen routines */
+#include "../Thumb/Gen.c"
+/* Thumb+VFP codegen routines */
+#include "../FP/ThumbVFP.c"
+
+/* Thumb-specific register allocation */
+#include "../Thumb/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.c b/vm/compiler/codegen/arm/armv5te/ArchVariant.c
new file mode 100644
index 0000000..814f410
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.c
@@ -0,0 +1,101 @@
+/*
+ * 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.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;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    int i = 0;
+    extern void dvmCompilerTemplateStart(void);
+
+    /*
+     * 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;
+    gDvmJit.threshold = 200;
+    gDvmJit.codeCacheSize = 512*1024;
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking mode */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(offsetof(ClassObject, vtable) < 128 &&
+           (offsetof(ClassObject, vtable) & 0x3) == 0);
+    assert(offsetof(ArrayObject, length) < 128 &&
+           (offsetof(ArrayObject, length) & 0x3) == 0);
+    assert(offsetof(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(InterpState, jitToInterpEntries) +
+            sizeof(struct JitToInterpEntries)) <= 128);
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 2;
+            break;
+        default:
+            LOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#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..6420df7
--- /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,
+typedef enum {
+#include "../../../template/armv5te/TemplateOpList.h"
+/*
+ * For example,
+ *     TEMPLATE_CMP_LONG,
+ *     TEMPLATE_RETURN,
+ *     ...
+ */
+    TEMPLATE_LAST_MARK,
+} TemplateOpCode;
+#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.c b/vm/compiler/codegen/arm/armv5te/Codegen.c
new file mode 100644
index 0000000..f953390
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/Codegen.c
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/arm/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Architectural independent building blocks */
+#include "../Thumb/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb-specific codegen routines */
+#include "../Thumb/Gen.c"
+/* Thumb+Portable FP codegen routines */
+#include "../FP/ThumbPortableFP.c"
+
+/* Thumb-specific register allocation */
+#include "../Thumb/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c
new file mode 100644
index 0000000..f1727c6
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    int i = 0;
+    extern void dvmCompilerTemplateStart(void);
+
+    /*
+     * 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 << 12; // 4096
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    gDvmJit.threshold = 40;
+    gDvmJit.codeCacheSize = 1024*1024;
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(offsetof(ClassObject, vtable) < 128 &&
+           (offsetof(ClassObject, vtable) & 0x3) == 0);
+    assert(offsetof(ArrayObject, length) < 128 &&
+           (offsetof(ArrayObject, length) & 0x3) == 0);
+    assert(offsetof(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(InterpState, jitToInterpEntries) +
+            sizeof(struct JitToInterpEntries)) <= 128);
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 7;
+            break;
+        default:
+            LOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+    ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, kSY);  // Full system DMB
+    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..9f862e8
--- /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/armv5te-vfp/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.c b/vm/compiler/codegen/arm/armv7-a-neon/Codegen.c
new file mode 100644
index 0000000..1edc25b
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/Codegen.c
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/arm/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.c"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.c"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.c b/vm/compiler/codegen/arm/armv7-a/ArchVariant.c
new file mode 100644
index 0000000..f1727c6
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.c
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+    /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+    int i = 0;
+    extern void dvmCompilerTemplateStart(void);
+
+    /*
+     * 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 << 12; // 4096
+    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+    gDvmJit.threshold = 40;
+    gDvmJit.codeCacheSize = 1024*1024;
+
+#if defined(WITH_SELF_VERIFICATION)
+    /* Force into blocking */
+    gDvmJit.blockingMode = true;
+    gDvm.nativeDebuggerActive = true;
+#endif
+
+    /* Codegen-specific assumptions */
+    assert(offsetof(ClassObject, vtable) < 128 &&
+           (offsetof(ClassObject, vtable) & 0x3) == 0);
+    assert(offsetof(ArrayObject, length) < 128 &&
+           (offsetof(ArrayObject, length) & 0x3) == 0);
+    assert(offsetof(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(InterpState, jitToInterpEntries) +
+            sizeof(struct JitToInterpEntries)) <= 128);
+    return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+    int res;
+    switch (key) {
+        case kMaxHoistDistance:
+            res = 7;
+            break;
+        default:
+            LOGE("Unknown target optimization hint key: %d",key);
+            res = 0;
+    }
+    return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+    ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, kSY);  // Full system DMB
+    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..9f862e8
--- /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_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/armv5te-vfp/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/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.c b/vm/compiler/codegen/arm/armv7-a/Codegen.c
new file mode 100644
index 0000000..1514d55
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/Codegen.c
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/arm/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.c"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.c"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
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..21e23a9
--- /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
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    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..ec80139
--- /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 rGLUE to the 1st element of the coreRegs save array.
+     */
+    add     r0, r0, rGLUE               @ 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..1bd02c8
--- /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 rGLUE 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, rGLUE               @ 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..d991bed
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TemplateOpList.h
@@ -0,0 +1,59 @@
+/*
+ * 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)
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..880e875
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/platform.S
@@ -0,0 +1,16 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
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..15988f6
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S
@@ -0,0 +1,36 @@
+%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_PC_LR ".L__aeabi_cdcmple"       @ PIC way of "bl __aeabi_cdcmple"
+    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_PC_LR ".L__aeabi_cdcmple"       @ r0<- Z set if eq, C clear if <
+    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..eb1c7e1
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S
@@ -0,0 +1,54 @@
+%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_PC_LR ".L__aeabi_cfcmple"       @ cmp <=: C clear if <, Z set if eq
+    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_PC_LR ".L__aeabi_cfcmple"       @ r0<- Z set if eq, C clear if <
+    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..2b0c730
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S
@@ -0,0 +1,23 @@
+    /*
+     * 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
+     *    rGLUE - pointer to interpState
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+    ldrne   r1,[lr, #3]
+    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..a137d22
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S
@@ -0,0 +1,45 @@
+    /*
+     * 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, rPC = dalvikCallsite
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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)]
+    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                      @ suspendCount != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    bx      lr                              @ return to the callee-chaining cell
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..2557863
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
@@ -0,0 +1,70 @@
+    @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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
+    ldr     r8, [r8]                    @ r3<- suspendCount (int)
+    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)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ suspendCount != 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, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    mov     r2, #0
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    blx     r8                          @ off to the native code
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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_NO_OPT.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
new file mode 100644
index 0000000..5be6978
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
@@ -0,0 +1,54 @@
+    /*
+     * 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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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                      @ suspendCount != 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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    mov     pc, r10                         @ dvmJitToInterpTraceSelectNoChain
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..c3085b9
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
@@ -0,0 +1,50 @@
+    /*
+     * 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, [rGLUE, #offGlue_icRechainCount]   @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+    ldreq   r10, [r7, #0]
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    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, [rGLUE, #offGlue_icRechainCount]   @ write back to InterpState
+    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_MEM_OP_DECODE.S b/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..ecd4eaa
--- /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
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    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..8e7f728
--- /dev/null
+++ b/vm/compiler/template/armv5te/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:
+     *    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)
+    @ refresh Jit's on/off status
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r0, [r0]
+    ldr     r2, .LdvmJitToInterpNoChain
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    @ 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..5cf26e7
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S
@@ -0,0 +1,32 @@
+    /*
+     * 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)
+    @ refresh Jit's on/off status & test for exception
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r1, [rGLUE, #offGlue_self]
+    ldr     r0, [r0]
+    ldr     r1, [r1, #offThread_exception]
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    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..8a9b115
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S
@@ -0,0 +1,28 @@
+    /*
+     * 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)
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    mov     r0,r9
+    mov     r1,r10
+    bx      lr
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..e3719db
--- /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 rGLUE to the 1st element of the coreRegs save array.
+     */
+    add     r0, r0, rGLUE               @ 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..b7ab971
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S
@@ -0,0 +1,50 @@
+    /*
+     * 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.
+     */
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    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
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    ldr     r8, [r8]                    @ r8<- suspendCount
+
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rGLUE, #offGlue_methodClassDex]
+    cmp     r8, #0                      @ check the suspendCount
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [r3, #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:
+    stmia   rGLUE, {rPC, rFP}           @ SAVE_PC_FP_TO_GLUE()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r1, #0                      @ changeInterp = false
+    mov     r0, rGLUE                   @ Expecting rGLUE in r0
+    blx     r2                          @ exit the interpreter
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..df2d1e6
--- /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 rGLUE 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, rGLUE               @ 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..bdfdf28
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S
@@ -0,0 +1,112 @@
+    /*
+     * 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    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+    ldr    r0, [r0, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    r0: object pointer
+     *    r1: char to match
+     *    r2: starting offset
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r0, #16
+     add   r0, r0, 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..e81383c
--- /dev/null
+++ b/vm/compiler/template/armv5te/TemplateOpList.h
@@ -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.
+ */
+
+/*
+ * 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)
diff --git a/vm/compiler/template/armv5te/footer.S b/vm/compiler/template/armv5te/footer.S
new file mode 100644
index 0000000..73fc3d7
--- /dev/null
+++ b/vm/compiler/template/armv5te/footer.S
@@ -0,0 +1,107 @@
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    mov     r2, #0
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+    @ Refresh Jit's on/off status
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable]
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    ldr     r3, [r3]    @ r1 <- pointer to Jit profile table
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+    str     r3, [rGLUE, #offGlue_pJitProfTable]  @ cache current JitProfTable
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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
+    mov     pc, 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
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    mov     r2, #0
+    str     r2, [r3, #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
+    mov     pc, 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
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#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..e6b3362
--- /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  rGLUE     MterpGlue 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 rGLUE   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..880e875
--- /dev/null
+++ b/vm/compiler/template/armv5te/platform.S
@@ -0,0 +1,16 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
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..d991bed
--- /dev/null
+++ b/vm/compiler/template/armv7-a-neon/TemplateOpList.h
@@ -0,0 +1,59 @@
+/*
+ * 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)
diff --git a/vm/compiler/template/armv7-a/TemplateOpList.h b/vm/compiler/template/armv7-a/TemplateOpList.h
new file mode 100644
index 0000000..d991bed
--- /dev/null
+++ b/vm/compiler/template/armv7-a/TemplateOpList.h
@@ -0,0 +1,59 @@
+/*
+ * 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)
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..1b02261
--- /dev/null
+++ b/vm/compiler/template/config-armv5te-vfp
@@ -0,0 +1,62 @@
+
+# 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-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..be7af31
--- /dev/null
+++ b/vm/compiler/template/config-armv7-a
@@ -0,0 +1,61 @@
+
+# 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 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-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..be7af31
--- /dev/null
+++ b/vm/compiler/template/config-armv7-a-neon
@@ -0,0 +1,61 @@
+
+# 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 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-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/gen-template.py b/vm/compiler/template/gen-template.py
new file mode 100755
index 0000000..8a1ba0c
--- /dev/null
+++ b/vm/compiler/template/gen-template.py
@@ -0,0 +1,422 @@
+#!/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 256 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 256 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("    .text\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/out/CompilerTemplateAsm-armv5te-vfp.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
new file mode 100644
index 0000000..60664fa
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
@@ -0,0 +1,1544 @@
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   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
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .text
+
+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.
+     */
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    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
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    ldr     r8, [r8]                    @ r8<- suspendCount
+
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rGLUE, #offGlue_methodClassDex]
+    cmp     r8, #0                      @ check the suspendCount
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [r3, #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:
+    stmia   rGLUE, {rPC, rFP}           @ SAVE_PC_FP_TO_GLUE()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r1, #0                      @ changeInterp = false
+    mov     r0, rGLUE                   @ Expecting rGLUE 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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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                      @ suspendCount != 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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    mov     pc, 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, rPC = dalvikCallsite
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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)]
+    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                      @ suspendCount != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    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, [rGLUE, #offGlue_icRechainCount]   @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+    ldreq   r10, [r7, #0]
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    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, [rGLUE, #offGlue_icRechainCount]   @ write back to InterpState
+    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
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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
+    ldr     r8, [r8]                    @ r3<- suspendCount (int)
+    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)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ suspendCount != 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, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    mov     r2, #0
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    blx     r8                          @ off to the native code
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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)
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    mov     r0,r9
+    mov     r1,r10
+    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
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    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    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+    ldr    r0, [r0, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    r0: object pointer
+     *    r1: char to match
+     *    r2: starting offset
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r0, #16
+     add   r0, r0, 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
+     *    rGLUE - pointer to interpState
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+    ldrne   r1,[lr, #3]
+    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)
+    @ refresh Jit's on/off status
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r0, [r0]
+    ldr     r2, .LdvmJitToInterpNoChain
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    @ 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)
+    @ refresh Jit's on/off status & test for exception
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r1, [rGLUE, #offGlue_self]
+    ldr     r0, [r0]
+    ldr     r1, [r1, #offThread_exception]
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    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
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    mov     r2, #0
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+    @ Refresh Jit's on/off status
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable]
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    ldr     r3, [r3]    @ r1 <- pointer to Jit profile table
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+    str     r3, [rGLUE, #offGlue_pJitProfTable]  @ cache current JitProfTable
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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
+    mov     pc, 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
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    mov     r2, #0
+    str     r2, [r3, #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
+    mov     pc, 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
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#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..ccdbcca
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
@@ -0,0 +1,1267 @@
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   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
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .text
+
+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.
+     */
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    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
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    ldr     r8, [r8]                    @ r8<- suspendCount
+
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rGLUE, #offGlue_methodClassDex]
+    cmp     r8, #0                      @ check the suspendCount
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [r3, #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:
+    stmia   rGLUE, {rPC, rFP}           @ SAVE_PC_FP_TO_GLUE()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r1, #0                      @ changeInterp = false
+    mov     r0, rGLUE                   @ Expecting rGLUE 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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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                      @ suspendCount != 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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    mov     pc, 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, rPC = dalvikCallsite
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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)]
+    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                      @ suspendCount != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    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, [rGLUE, #offGlue_icRechainCount]   @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+    ldreq   r10, [r7, #0]
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    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, [rGLUE, #offGlue_icRechainCount]   @ write back to InterpState
+    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
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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
+    ldr     r8, [r8]                    @ r3<- suspendCount (int)
+    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)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ suspendCount != 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, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    mov     r2, #0
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    blx     r8                          @ off to the native code
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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_PC_LR ".L__aeabi_cdcmple"       @ PIC way of "bl __aeabi_cdcmple"
+    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_PC_LR ".L__aeabi_cdcmple"       @ r0<- Z set if eq, C clear if <
+    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_PC_LR ".L__aeabi_cdcmple"       @ PIC way of "bl __aeabi_cdcmple"
+    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_PC_LR ".L__aeabi_cdcmple"       @ r0<- Z set if eq, C clear if <
+    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_PC_LR ".L__aeabi_cfcmple"       @ cmp <=: C clear if <, Z set if eq
+    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_PC_LR ".L__aeabi_cfcmple"       @ r0<- Z set if eq, C clear if <
+    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_PC_LR ".L__aeabi_cfcmple"       @ cmp <=: C clear if <, Z set if eq
+    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_PC_LR ".L__aeabi_cfcmple"       @ r0<- Z set if eq, C clear if <
+    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)
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    mov     r0,r9
+    mov     r1,r10
+    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
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    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    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+    ldr    r0, [r0, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    r0: object pointer
+     *    r1: char to match
+     *    r2: starting offset
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r0, #16
+     add   r0, r0, 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
+     *    rGLUE - pointer to interpState
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+    ldrne   r1,[lr, #3]
+    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)
+    @ refresh Jit's on/off status
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r0, [r0]
+    ldr     r2, .LdvmJitToInterpNoChain
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    @ 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)
+    @ refresh Jit's on/off status & test for exception
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r1, [rGLUE, #offGlue_self]
+    ldr     r0, [r0]
+    ldr     r1, [r1, #offThread_exception]
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    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
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    mov     r2, #0
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+    @ Refresh Jit's on/off status
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable]
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    ldr     r3, [r3]    @ r1 <- pointer to Jit profile table
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+    str     r3, [rGLUE, #offGlue_pJitProfTable]  @ cache current JitProfTable
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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
+    mov     pc, 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
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    mov     r2, #0
+    str     r2, [r3, #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
+    mov     pc, 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
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#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..e520056
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S
@@ -0,0 +1,1544 @@
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   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
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .text
+
+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.
+     */
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    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
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    ldr     r8, [r8]                    @ r8<- suspendCount
+
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rGLUE, #offGlue_methodClassDex]
+    cmp     r8, #0                      @ check the suspendCount
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [r3, #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:
+    stmia   rGLUE, {rPC, rFP}           @ SAVE_PC_FP_TO_GLUE()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r1, #0                      @ changeInterp = false
+    mov     r0, rGLUE                   @ Expecting rGLUE 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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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                      @ suspendCount != 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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    mov     pc, 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, rPC = dalvikCallsite
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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)]
+    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                      @ suspendCount != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    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, [rGLUE, #offGlue_icRechainCount]   @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+    ldreq   r10, [r7, #0]
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    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, [rGLUE, #offGlue_icRechainCount]   @ write back to InterpState
+    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
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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
+    ldr     r8, [r8]                    @ r3<- suspendCount (int)
+    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)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ suspendCount != 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, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    mov     r2, #0
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    blx     r8                          @ off to the native code
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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)
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    mov     r0,r9
+    mov     r1,r10
+    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
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    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    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+    ldr    r0, [r0, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    r0: object pointer
+     *    r1: char to match
+     *    r2: starting offset
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r0, #16
+     add   r0, r0, 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
+     *    rGLUE - pointer to interpState
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+    ldrne   r1,[lr, #3]
+    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)
+    @ refresh Jit's on/off status
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r0, [r0]
+    ldr     r2, .LdvmJitToInterpNoChain
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    @ 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)
+    @ refresh Jit's on/off status & test for exception
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r1, [rGLUE, #offGlue_self]
+    ldr     r0, [r0]
+    ldr     r1, [r1, #offThread_exception]
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    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
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    mov     r2, #0
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+    @ Refresh Jit's on/off status
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable]
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    ldr     r3, [r3]    @ r1 <- pointer to Jit profile table
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+    str     r3, [rGLUE, #offGlue_pJitProfTable]  @ cache current JitProfTable
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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
+    mov     pc, 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
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    mov     r2, #0
+    str     r2, [r3, #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
+    mov     pc, 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
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#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..87a0691
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
@@ -0,0 +1,1544 @@
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   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
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+
+    .global dvmCompilerTemplateStart
+    .type   dvmCompilerTemplateStart, %function
+    .text
+
+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.
+     */
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    ldr     rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+    ldr     r9,  [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+    mov     r9, #0                      @ disable chaining
+#endif
+    ldr     r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    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
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    ldr     r8, [r8]                    @ r8<- suspendCount
+
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+    add     rPC, rPC, #6                @ publish new rPC (advance 6 bytes)
+    str     r0, [rGLUE, #offGlue_methodClassDex]
+    cmp     r8, #0                      @ check the suspendCount
+    movne   r9, #0                      @ clear the chaining cell address
+    str     r9, [r3, #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:
+    stmia   rGLUE, {rPC, rFP}           @ SAVE_PC_FP_TO_GLUE()
+    ldr     r2, .LdvmMterpStdBail       @ defined in footer.S
+    mov     r1, #0                      @ changeInterp = false
+    mov     r0, rGLUE                   @ Expecting rGLUE 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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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                      @ suspendCount != 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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+    mov     r0, #kInlineCacheMiss
+#endif
+    mov     pc, 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, rPC = dalvikCallsite
+    @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldrh    r2, [r0, #offMethod_outsSize]  @ r2<- methodToCall->outsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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)
+    ldr     r8, [r8]                    @ r8<- suspendCount (int)
+    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)]
+    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                      @ suspendCount != 0
+    bxne    r12                         @ bail to the interpreter
+
+    ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+    mov     rFP, r1                         @ fp = newFp
+    str     rFP, [r2, #offThread_curFrame]  @ self->curFrame = newFp
+
+    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, [rGLUE, #offGlue_icRechainCount]   @ r1 <- shared rechainCount
+    cmp     r3, r8          @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+    ldr     r7, .LdvmICHitCount
+    ldreq   r10, [r7, #0]
+    add     r10, r10, #1
+    streq   r10, [r7, #0]
+#endif
+    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, [rGLUE, #offGlue_icRechainCount]   @ write back to InterpState
+    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
+    ldrh    r7, [r0, #offMethod_registersSize]  @ r7<- methodToCall->regsSize
+    ldr     r9, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    ldr     r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+    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
+    ldr     r8, [r8]                    @ r3<- suspendCount (int)
+    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)]
+    ldr     rPC, [r0, #offMethod_insns]     @ rPC<- methodToCall->insns
+
+
+    @ set up newSaveArea
+    str     rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+    str     r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    str     r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+    cmp     r8, #0                      @ suspendCount != 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, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    mov     r2, #0
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    blx     r8                          @ off to the native code
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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)
+    add     r10, r2, r10                @  r10<- r10 + low(ZxW + (YxX))
+    mov     r0,r9
+    mov     r1,r10
+    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
+    mov     r0, lr                      @ arg0 <- link register
+    mov     r1, sp                      @ arg1 <- stack pointer
+    ldr     r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+    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    r7, [r0, #STRING_FIELDOFF_OFFSET]
+    ldr    r8, [r0, #STRING_FIELDOFF_COUNT]
+    ldr    r0, [r0, #STRING_FIELDOFF_VALUE]
+
+    /*
+     * At this point, we have:
+     *    r0: object pointer
+     *    r1: char to match
+     *    r2: starting offset
+     *    r7: offset
+     *    r8: string length
+     */
+
+     /* Build pointer to start of string data */
+     add   r0, #16
+     add   r0, r0, 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
+     *    rGLUE - pointer to interpState
+     *    rFP - Dalvik frame pointer
+     */
+    cmp     lr, #0
+    ldrne   r1,[lr, #3]
+    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)
+    @ refresh Jit's on/off status
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r0, [r0]
+    ldr     r2, .LdvmJitToInterpNoChain
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    @ 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)
+    @ refresh Jit's on/off status & test for exception
+    ldr     r0, [rGLUE, #offGlue_ppJitProfTable]
+    ldr     r1, [rGLUE, #offGlue_self]
+    ldr     r0, [r0]
+    ldr     r1, [r1, #offThread_exception]
+    str     r0, [rGLUE, #offGlue_pJitProfTable]
+    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
+
+    .size   dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+    .text
+    .align  2
+.LinvokeNative:
+    @ Prep for the native call
+    @ r1 = newFP, r0 = methodToCall
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    mov     r2, #0
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+                                        @ newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+    SAVEAREA_FROM_FP(r10, r1)           @ r10<- new stack save area
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFP
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+    @ Refresh Jit's on/off status
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable]
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+    ldr     r3, [r3]    @ r1 <- pointer to Jit profile table
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+    ldr     r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+    str     r3, [rGLUE, #offGlue_pJitProfTable]  @ cache current JitProfTable
+
+    @ r0 = dalvikCallsitePC
+    bne     .LhandleException           @ no, handle exception
+
+    str     r2, [r9, #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
+    mov     pc, 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
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    mov     r2, #0
+    str     r2, [r3, #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
+    mov     pc, 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
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+    .word   gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+    .word   dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+    .word   __aeabi_cdcmple
+.L__aeabi_cfcmple:
+    .word   __aeabi_cfcmple
+
+    .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/rebuild.sh b/vm/compiler/template/rebuild.sh
new file mode 100755
index 0000000..8c47dd7
--- /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 armv5te armv5te-vfp armv7-a armv7-a-neon; do TARGET_ARCH_EXT=$arch make -f Makefile-template; done
diff --git a/vm/hprof/Hprof.c b/vm/hprof/Hprof.c
new file mode 100644
index 0000000..bcad8b1
--- /dev/null
+++ b/vm/hprof/Hprof.c
@@ -0,0 +1,185 @@
+/*
+ * 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 <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();
+#if WITH_HPROF_STACK
+    hprofStartup_StackFrame();
+    hprofStartup_Stack();
+#endif
+
+    hprof_context_t *ctx = malloc(sizeof(*ctx));
+    if (ctx == NULL) {
+        LOGE("hprof: can't allocate context.\n");
+        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 = malloc(sizeof(*headCtx));
+    if (headCtx == NULL) {
+        LOGE("hprof: can't allocate context.\n");
+        hprofFreeContext(tailCtx);
+        return false;
+    }
+    hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true,
+        tailCtx->directToDdms);
+
+    LOGI("hprof: dumping heap strings to \"%s\".\n", 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
+
+#if WITH_HPROF_STACK
+    hprofDumpStackFrames(headCtx);
+    hprofDumpStacks(headCtx);
+#endif
+
+    hprofFlushCurrentRecord(headCtx);
+
+    hprofShutdown_Class();
+    hprofShutdown_String();
+#if WITH_HPROF_STACK
+    hprofShutdown_Stack();
+    hprofShutdown_StackFrame();
+#endif
+
+    /* 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) {
+                LOGE("dup(%d) failed: %s\n", headCtx->fd, strerror(errno));
+                /* continue to fail-handler below */
+            }
+        } else {
+            outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT, 0644);
+            if (outFd < 0) {
+                LOGE("can't open %s: %s\n", 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" */
+    LOGI("hprof: heap dump completed (%dKB)\n",
+        (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);
+}
diff --git a/vm/hprof/Hprof.h b/vm/hprof/Hprof.h
new file mode 100644
index 0000000..18f4102
--- /dev/null
+++ b/vm/hprof/Hprof.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.
+ */
+#ifndef _DALVIK_HPROF_HPROF
+#define _DALVIK_HPROF_HPROF
+
+#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;
+#if WITH_HPROF_STACK
+typedef hprof_id hprof_stack_frame_id;
+#endif
+
+typedef 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,
+} hprof_basic_type;
+
+typedef 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,
+} hprof_tag_t;
+
+/* Values for the first byte of
+ * HEAP_DUMP and HEAP_DUMP_SEGMENT
+ * records:
+ */
+typedef 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,
+    HPROF_ROOT_DEBUGGER = 0x8b,
+    HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,
+    HPROF_ROOT_VM_INTERNAL = 0x8d,
+    HPROF_ROOT_JNI_MONITOR = 0x8e,
+    HPROF_UNREACHABLE = 0x90,  /* deprecated */
+    HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
+} hprof_heap_tag_t;
+
+/* 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
+ */
+typedef struct hprof_record_t {
+    unsigned char *body;
+    u4 time;
+    u4 length;
+    size_t allocLen;
+    u1 tag;
+    bool dirty;
+} hprof_record_t;
+
+typedef enum {
+    HPROF_HEAP_DEFAULT = 0,
+    HPROF_HEAP_ZYGOTE = 'Z',
+    HPROF_HEAP_APP = 'A'
+} HprofHeapId;
+
+typedef 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;
+} hprof_context_t;
+
+
+/*
+ * HprofString.c functions
+ */
+
+hprof_string_id hprofLookupStringId(const char *str);
+
+int hprofDumpStrings(hprof_context_t *ctx);
+
+int hprofStartup_String(void);
+int hprofShutdown_String(void);
+
+
+/*
+ * HprofClass.c functions
+ */
+
+hprof_class_object_id hprofLookupClassId(const ClassObject *clazz);
+
+int hprofDumpClasses(hprof_context_t *ctx);
+
+int hprofStartup_Class(void);
+int hprofShutdown_Class(void);
+
+
+/*
+ * HprofHeap.c 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);
+int hprofMarkRootObject(hprof_context_t *ctx,
+                        const Object *obj, jobject jniObj);
+
+int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj);
+
+/*
+ * HprofOutput.c 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))
+
+#if WITH_HPROF_STACK
+
+/*
+ * HprofStack.c functions
+ */
+
+void hprofFillInStackTrace(void *objectPtr);
+
+int hprofDumpStacks(hprof_context_t *ctx);
+
+int hprofStartup_Stack(void);
+int hprofShutdown_Stack(void);
+
+/*
+ * HprofStackFrame.c functions
+ */
+
+int hprofDumpStackFrames(hprof_context_t *ctx);
+
+int hprofStartup_StackFrame(void);
+int hprofShutdown_StackFrame(void);
+
+#endif
+
+/*
+ * Hprof.c functions
+ */
+
+hprof_context_t* hprofStartup(const char *outputFileName, int fd,
+    bool directToDdms);
+bool hprofShutdown(hprof_context_t *ctx);
+void hprofFreeContext(hprof_context_t *ctx);
+
+/*
+ * Heap.c functions
+ *
+ * The contents of the hprof directory have no knowledge of
+ * the heap implementation; these functions require heap knowledge,
+ * so they are implemented in Heap.c.
+ */
+int hprofDumpHeap(const char* fileName, int fd, bool directToDdms);
+void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber);
+
+#endif  // _DALVIK_HPROF_HPROF
diff --git a/vm/hprof/HprofClass.c b/vm/hprof/HprofClass.c
new file mode 100644
index 0000000..f76f159
--- /dev/null
+++ b/vm/hprof/HprofClass.c
@@ -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.
+ */
+/*
+ * 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)
+{
+    hprof_string_id classNameId;
+    char *dotName = dvmDescriptorToDot(descriptor);
+
+    /* Hprof suggests that array class names be converted from, e.g.,
+     * "[[[I" to "int[][][]" and "[Lorg.blort.Spaz;" to
+     * "org.blort.Spaz[]".
+     */
+    if (dotName[0] == '[') {
+        const char *c;
+        char *newName;
+        char *nc;
+        size_t dim;
+        size_t newLen;
+
+        c = dotName;
+        dim = 0;
+        while (*c == '[') {
+            dim++;
+            c++;
+        }
+        if (*c == 'L') {
+            c++;
+        } else {
+            /* It's a primitive type;  we should use a pretty name.
+             * Add semicolons to make all strings have the format
+             * of object class names.
+             */
+            switch (*c) {
+            case 'Z': c = "boolean;";    break;
+            case 'C': c = "char;";       break;
+            case 'F': c = "float;";      break;
+            case 'D': c = "double;";     break;
+            case 'B': c = "byte;";       break;
+            case 'S': c = "short;";      break;
+            case 'I': c = "int;";        break;
+            case 'J': c = "long;";       break;
+            default: assert(false); c = "UNKNOWN;"; break;
+            }
+        }
+
+        /* We have a string of the form "name;" and
+         * we want to replace the semicolon with as many
+         * "[]" pairs as is in dim.
+         */
+        newLen = strlen(c)-1 + dim*2;
+        newName = malloc(newLen + 1);
+        if (newName == NULL) {
+            return -1;
+        }
+        strcpy(newName, c);
+        newName[newLen] = '\0';
+
+        /* Point nc to the semicolon.
+         */
+        nc = newName + newLen - dim*2;
+        assert(*nc == ';');
+
+        while (dim--) {
+            *nc++ = '[';
+            *nc++ = ']';
+        }
+        assert(*nc == '\0');
+
+        classNameId = hprofLookupStringId(newName);
+        free(newName);
+    } else {
+        classNameId = hprofLookupStringId(dotName);
+    }
+
+    free(dotName);
+    return classNameId;
+}
+
+
+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.c b/vm/hprof/HprofHeap.c
new file mode 100644
index 0000000..75a1d2b
--- /dev/null
+++ b/vm/hprof/HprofHeap.c
@@ -0,0 +1,494 @@
+/*
+ * 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"
+
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.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.
+ */
+int
+hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
+{
+    hprof_record_t *rec = &ctx->curRec;
+    int err;
+    hprof_heap_tag_t heapTag = ctx->gcScanState;
+
+    if (heapTag == 0) {
+        return 0;
+    }
+
+    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:
+        err = 0;
+        break;
+    }
+
+    ctx->objectsInSegment++;
+
+    return err;
+}
+
+static int
+stackTraceSerialNumber(const void *obj)
+
+{
+#if WITH_HPROF_STACK
+    DvmHeapChunk *chunk = ptr2chunk(obj);
+    return chunk->stackTraceSerialNumber;
+#else
+    return HPROF_NULL_STACK_TRACE;
+#endif
+}
+
+int
+hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
+{
+    const ClassObject *clazz;
+    hprof_record_t *rec = &ctx->curRec;
+    HprofHeapId desiredHeap;
+
+    desiredHeap =
+            dvmHeapSourceGetPtrFlag(obj, HS_ALLOCATED_IN_ZYGOTE) ?
+            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 {
+        hprof_class_object_id clazzId;
+
+        clazzId = hprofLookupClassId(clazz);
+
+        if (clazz == gDvm.classJavaLangClass) {
+            const ClassObject *thisClass = (const ClassObject *)obj;
+            int i, sFieldCount, iFieldCount;
+            /* obj is a ClassObject.
+             */
+            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 (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 (i = 0; i < sFieldCount; i++) {
+                    hprof_basic_type t;
+                    size_t size;
+                    const StaticField *f = &thisClass->sfields[i];
+
+                    t = signatureToBasicTypeAndSize(f->field.signature, &size);
+                    hprofAddIdToRecord(rec, hprofLookupStringId(f->field.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)
+             */
+            iFieldCount = thisClass->ifieldCount;
+            hprofAddU2ToRecord(rec, (u2)iFieldCount);
+            for (i = 0; i < iFieldCount; i++) {
+                const InstField *f = &thisClass->ifields[i];
+                hprof_basic_type t;
+
+                t = signatureToBasicTypeAndSize(f->field.signature, NULL);
+                hprofAddIdToRecord(rec, hprofLookupStringId(f->field.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 *)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 *)aobj->contents,
+                            length);
+                } else if (size == 4) {
+                    hprofAddU4ListToRecord(rec, (const u4 *)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 i, ifieldCount;
+
+                ifieldCount = sclass->ifieldCount;
+                for (i = 0; i < ifieldCount; i++) {
+                    const InstField *f = &sclass->ifields[i];
+                    hprof_basic_type t;
+                    size_t size;
+
+                    t = signatureToBasicTypeAndSize(f->field.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.c b/vm/hprof/HprofOutput.c
new file mode 100644
index 0000000..25512b2
--- /dev/null
+++ b/vm/hprof/HprofOutput.c
@@ -0,0 +1,339 @@
+/*
+ * 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".
+ */
+void
+hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
+    bool writeHeader, bool directToDdms)
+{
+    memset(ctx, 0, sizeof (*ctx));
+
+    /*
+     * 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 */
+        LOGE("hprof: open_memstream failed: %s\n", strerror(errno));
+        dvmAbort();
+    }
+
+    ctx->directToDdms = directToDdms;
+    ctx->fileName = fileName;
+    ctx->memFp = fp;
+    ctx->fd = fd;
+
+    ctx->curRec.allocLen = 128;
+    ctx->curRec.body = 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 = 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)
+{
+    unsigned char *insert;
+    size_t i;
+    int err;
+
+    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
+    insert = rec->body + rec->length;
+    for (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)
+{
+    unsigned char *insert;
+    size_t i;
+    int err;
+
+    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
+    insert = rec->body + rec->length;
+    for (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)
+{
+    unsigned char *insert;
+    size_t i;
+    int err;
+
+    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
+    insert = rec->body + rec->length;
+    for (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/HprofStack.c b/vm/hprof/HprofStack.c
new file mode 100644
index 0000000..04641ef
--- /dev/null
+++ b/vm/hprof/HprofStack.c
@@ -0,0 +1,266 @@
+/*
+ * 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 "Hprof.h"
+#include "HprofStack.h"
+#include "alloc/HeapInternal.h"
+
+static HashTable *gStackTraceHashTable = NULL;
+static int gSerialNumber = 0;
+
+/* Number of stack frames to cache */
+#define STACK_DEPTH 8
+
+typedef struct {
+    int serialNumber;
+    int threadSerialNumber;
+    int frameIds[STACK_DEPTH];
+} StackTrace;
+
+typedef struct {
+    StackTrace trace;
+    u1 live;
+} StackTraceEntry;
+
+static u4 computeStackTraceHash(const StackTraceEntry *stackTraceEntry);
+
+int
+hprofStartup_Stack()
+{
+    HashIter iter;
+
+    /* This will be called when a GC begins. */
+    for (dvmHashIterBegin(gStackTraceHashTable, &iter);
+         !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter)) {
+        StackTraceEntry *stackTraceEntry;
+
+        /* Clear the 'live' bit at the start of the GC pass. */
+        stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter);
+        stackTraceEntry->live = 0;
+    }
+
+    return 0;
+}
+
+int
+hprofShutdown_Stack()
+{
+    HashIter iter;
+
+    /* This will be called when a GC has completed. */
+    for (dvmHashIterBegin(gStackTraceHashTable, &iter);
+         !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter)) {
+        StackTraceEntry *stackTraceEntry;
+
+        /*
+         * If the 'live' bit is 0, the trace is not in use by any current
+         * heap object and may be destroyed.
+         */
+        stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter);
+        if (!stackTraceEntry->live) {
+            dvmHashTableRemove(gStackTraceHashTable,
+                    computeStackTraceHash(stackTraceEntry), stackTraceEntry);
+            free(stackTraceEntry);
+        }
+    }
+
+    return 0;
+}
+
+static u4
+computeStackTraceHash(const StackTraceEntry *stackTraceEntry)
+{
+    u4 hash = 0;
+    const char *cp = (const char *) &stackTraceEntry->trace;
+    int i;
+
+    for (i = 0; i < (int) sizeof(StackTrace); i++) {
+        hash = hash * 31 + cp[i];
+    }
+
+    return hash;
+}
+
+/* Only compare the 'trace' portion of the StackTraceEntry. */
+static int
+stackCmp(const void *tableItem, const void *looseItem)
+{
+    return memcmp(&((StackTraceEntry *) tableItem)->trace,
+            &((StackTraceEntry *) looseItem)->trace, sizeof(StackTrace));
+}
+
+static StackTraceEntry *
+stackDup(const StackTraceEntry *stackTrace)
+{
+    StackTraceEntry *newStackTrace = malloc(sizeof(StackTraceEntry));
+    memcpy(newStackTrace, stackTrace, sizeof(StackTraceEntry));
+    return newStackTrace;
+}
+
+static u4
+hprofLookupStackSerialNumber(const StackTraceEntry *stackTrace)
+{
+    StackTraceEntry *val;
+    u4 hashValue;
+    int serial;
+
+    /*
+     * Create the hash table on first contact.  We can't do this in
+     * hprofStartupStack, because we have to compute stack trace
+     * serial numbers and place them into object headers before the
+     * rest of hprof is triggered by a GC event.
+     */
+    if (gStackTraceHashTable == NULL) {
+        gStackTraceHashTable = dvmHashTableCreate(512, free);
+    }
+    dvmHashTableLock(gStackTraceHashTable);
+
+    hashValue = computeStackTraceHash(stackTrace);
+    val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)stackTrace,
+            (HashCompareFunc)stackCmp, false);
+    if (val == NULL) {
+        StackTraceEntry *newStackTrace;
+
+        newStackTrace = stackDup(stackTrace);
+        newStackTrace->trace.serialNumber = ++gSerialNumber;
+        val = dvmHashTableLookup(gStackTraceHashTable, hashValue,
+                (void *)newStackTrace, (HashCompareFunc)stackCmp, true);
+        assert(val != NULL);
+    }
+
+    /* Mark the trace as live (in use by an object in the current heap). */
+    val->live = 1;
+
+    /* Grab the serial number before unlocking the table. */
+    serial = val->trace.serialNumber;
+
+    dvmHashTableUnlock(gStackTraceHashTable);
+
+    return serial;
+}
+
+int
+hprofDumpStacks(hprof_context_t *ctx)
+{
+    HashIter iter;
+    hprof_record_t *rec = &ctx->curRec;
+
+    dvmHashTableLock(gStackTraceHashTable);
+
+    for (dvmHashIterBegin(gStackTraceHashTable, &iter);
+         !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter))
+    {
+        const StackTraceEntry *stackTraceEntry;
+        int count;
+        int i;
+
+        hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
+
+        stackTraceEntry = (const StackTraceEntry *) dvmHashIterData(&iter);
+        assert(stackTraceEntry != NULL);
+
+        /* STACK TRACE format:
+         *
+         * u4:     serial number for this stack
+         * u4:     serial number for the running thread
+         * u4:     number of frames
+         * [ID]*:  ID for the stack frame
+         */
+        hprofAddU4ToRecord(rec, stackTraceEntry->trace.serialNumber);
+        hprofAddU4ToRecord(rec, stackTraceEntry->trace.threadSerialNumber);
+
+        count = 0;
+        while ((count < STACK_DEPTH) &&
+               (stackTraceEntry->trace.frameIds[count] != 0)) {
+            count++;
+        }
+        hprofAddU4ToRecord(rec, count);
+        for (i = 0; i < count; i++) {
+            hprofAddU4ToRecord(rec, stackTraceEntry->trace.frameIds[i]);
+        }
+    }
+
+    dvmHashTableUnlock(gStackTraceHashTable);
+
+    return 0;
+}
+
+void
+hprofFillInStackTrace(void *objectPtr)
+
+{
+    DvmHeapChunk *chunk;
+    StackTraceEntry stackTraceEntry;
+    Thread* self;
+    void* fp;
+    int i;
+
+    if (objectPtr == NULL) {
+        return;
+    }
+    self = dvmThreadSelf();
+    if (self == NULL) {
+        return;
+    }
+    fp = self->curFrame;
+
+    /* Serial number to be filled in later. */
+    stackTraceEntry.trace.serialNumber = -1;
+
+    /*
+     * TODO - The HAT tool doesn't care about thread data, so we can defer
+     * actually emitting thread records and assigning thread serial numbers.
+     */
+    stackTraceEntry.trace.threadSerialNumber = (int) self;
+
+    memset(&stackTraceEntry.trace.frameIds, 0,
+            sizeof(stackTraceEntry.trace.frameIds));
+
+    i = 0;
+    while ((fp != NULL) && (i < STACK_DEPTH)) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+        StackFrameEntry frame;
+
+        if (!dvmIsBreakFrame(fp)) {
+            frame.frame.method = method;
+            if (dvmIsNativeMethod(method)) {
+                frame.frame.pc = 0; /* no saved PC for native methods */
+            } else {
+                assert(saveArea->xtra.currentPc >= method->insns &&
+                        saveArea->xtra.currentPc <
+                        method->insns + dvmGetMethodInsnsSize(method));
+                frame.frame.pc = (int) (saveArea->xtra.currentPc -
+                        method->insns);
+            }
+
+            // Canonicalize the frame and cache it in the hprof context
+            stackTraceEntry.trace.frameIds[i++] =
+                hprofLookupStackFrameId(&frame);
+        }
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+
+    /* Store the stack trace serial number in the object header */
+    chunk = ptr2chunk(objectPtr);
+    chunk->stackTraceSerialNumber =
+            hprofLookupStackSerialNumber(&stackTraceEntry);
+}
diff --git a/vm/hprof/HprofStack.h b/vm/hprof/HprofStack.h
new file mode 100644
index 0000000..1f16c1e
--- /dev/null
+++ b/vm/hprof/HprofStack.h
@@ -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.
+ */
+#ifndef _DALVIK_HPROF_STACK
+#define _DALVIK_HPROF_STACK
+
+#include "../alloc/HeapInternal.h"
+
+typedef struct {
+    const Method *method;
+    int pc;
+} StackFrame;
+
+typedef struct {
+    StackFrame frame;
+    unsigned char live;
+} StackFrameEntry;
+
+int hprofStartupStack();
+int hprofShutdown_Stack();
+int hprofDumpStacks(hprof_context_t *ctx);
+void hprofFillInStackTrace(void *objectPtr);
+
+int hprofStartup_StackFrame();
+int hprofShutdown_StackFrame();
+hprof_stack_frame_id hprofLookupStackFrameId(const StackFrameEntry
+    *stackFrameEntry);
+int hprofDumpStackFrames(hprof_context_t *ctx);
+
+#endif /* _DALVIK_HPROF_STACK */
diff --git a/vm/hprof/HprofStackFrame.c b/vm/hprof/HprofStackFrame.c
new file mode 100644
index 0000000..f9c789e
--- /dev/null
+++ b/vm/hprof/HprofStackFrame.c
@@ -0,0 +1,242 @@
+/*
+ * 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 "Hprof.h"
+#include "HprofStack.h"
+
+#include "alloc/HeapInternal.h"
+
+static HashTable *gStackFrameHashTable;
+
+static u4 computeStackFrameHash(const StackFrameEntry *stackFrameEntry);
+
+int
+hprofStartup_StackFrame()
+{
+    HashIter iter;
+
+    /* Cache the string "<unknown>" for use when the source file is
+     * unknown.
+     */
+    hprofLookupStringId("<unknown>");
+
+    /* This will be called when a GC begins. */
+    for (dvmHashIterBegin(gStackFrameHashTable, &iter);
+         !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter)) {
+        StackFrameEntry *stackFrameEntry;
+        const Method *method;
+
+        /* Clear the 'live' bit at the start of the GC pass. */
+        stackFrameEntry = (StackFrameEntry *) dvmHashIterData(&iter);
+        stackFrameEntry->live = 0;
+
+        method = stackFrameEntry->frame.method;
+        if (method == NULL) {
+            continue;
+        }
+
+        /* Make sure the method name, descriptor, and source file are in
+         * the string table, and that the method class is in the class
+         * table. This is needed because strings and classes will be dumped
+         * before stack frames.
+         */
+
+        if (method->name) {
+            hprofLookupStringId(method->name);
+        }
+
+        DexStringCache cache;
+        const char* descriptor;
+
+        dexStringCacheInit(&cache);
+        descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache);
+        hprofLookupStringId(descriptor);
+        dexStringCacheRelease(&cache);
+
+        const char* sourceFile = dvmGetMethodSourceFile(method);
+        if (sourceFile) {
+            hprofLookupStringId(sourceFile);
+        }
+
+        if (method->clazz) {
+            hprofLookupClassId(method->clazz);
+        }
+    }
+
+    return 0;
+}
+
+int
+hprofShutdown_StackFrame()
+{
+    HashIter iter;
+
+    /* This will be called when a GC has completed. */
+    for (dvmHashIterBegin(gStackFrameHashTable, &iter);
+         !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter)) {
+        const StackFrameEntry *stackFrameEntry;
+
+        /*
+         * If the 'live' bit is 0, the frame is not in use by any current
+         * heap object and may be destroyed.
+         */
+        stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter);
+        if (!stackFrameEntry->live) {
+            dvmHashTableRemove(gStackFrameHashTable,
+                    computeStackFrameHash(stackFrameEntry),
+                    (void*) stackFrameEntry);
+            free((void*) stackFrameEntry);
+        }
+    }
+
+    return 0;
+}
+
+/* Only hash the 'frame' portion of the StackFrameEntry. */
+static u4
+computeStackFrameHash(const StackFrameEntry *stackFrameEntry)
+{
+    u4 hash = 0;
+    const char *cp = (char *) &stackFrameEntry->frame;
+    int i;
+
+    for (i = 0; i < (int) sizeof(StackFrame); i++) {
+        hash = 31 * hash + cp[i];
+    }
+    return hash;
+}
+
+/* Only compare the 'frame' portion of the StackFrameEntry. */
+static int
+stackFrameCmp(const void *tableItem, const void *looseItem)
+{
+    return memcmp(&((StackFrameEntry *)tableItem)->frame,
+            &((StackFrameEntry *) looseItem)->frame, sizeof(StackFrame));
+}
+
+static StackFrameEntry *
+stackFrameDup(const StackFrameEntry *stackFrameEntry)
+{
+    StackFrameEntry *newStackFrameEntry = malloc(sizeof(StackFrameEntry));
+    memcpy(newStackFrameEntry, stackFrameEntry, sizeof(StackFrameEntry));
+    return newStackFrameEntry;
+}
+
+hprof_stack_frame_id
+hprofLookupStackFrameId(const StackFrameEntry *stackFrameEntry)
+{
+    StackFrameEntry *val;
+    u4 hashValue;
+
+    /*
+     * Create the hash table on first contact.  We can't do this in
+     * hprofStartupStackFrame, because we have to compute stack trace
+     * serial numbers and place them into object headers before the
+     * rest of hprof is triggered by a GC event.
+     */
+    if (gStackFrameHashTable == NULL) {
+        gStackFrameHashTable = dvmHashTableCreate(512, free);
+    }
+    dvmHashTableLock(gStackFrameHashTable);
+
+    hashValue = computeStackFrameHash(stackFrameEntry);
+    val = dvmHashTableLookup(gStackFrameHashTable, hashValue,
+        (void *)stackFrameEntry, (HashCompareFunc)stackFrameCmp, false);
+    if (val == NULL) {
+        const StackFrameEntry *newStackFrameEntry;
+
+        newStackFrameEntry = stackFrameDup(stackFrameEntry);
+        val = dvmHashTableLookup(gStackFrameHashTable, hashValue,
+            (void *)newStackFrameEntry, (HashCompareFunc)stackFrameCmp, true);
+        assert(val != NULL);
+    }
+
+    /* Mark the frame as live (in use by an object in the current heap). */
+    val->live = 1;
+
+    dvmHashTableUnlock(gStackFrameHashTable);
+
+    return (hprof_stack_frame_id) val;
+}
+
+int
+hprofDumpStackFrames(hprof_context_t *ctx)
+{
+    HashIter iter;
+    hprof_record_t *rec = &ctx->curRec;
+
+    dvmHashTableLock(gStackFrameHashTable);
+
+    for (dvmHashIterBegin(gStackFrameHashTable, &iter);
+         !dvmHashIterDone(&iter);
+         dvmHashIterNext(&iter))
+    {
+        const StackFrameEntry *stackFrameEntry;
+        const Method *method;
+        int pc;
+        const char *sourceFile;
+        ClassObject *clazz;
+        int lineNum;
+
+        hprofStartNewRecord(ctx, HPROF_TAG_STACK_FRAME, HPROF_TIME);
+
+        stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter);
+        assert(stackFrameEntry != NULL);
+
+        method = stackFrameEntry->frame.method;
+        pc = stackFrameEntry->frame.pc;
+        sourceFile = dvmGetMethodSourceFile(method);
+        if (sourceFile == NULL) {
+            sourceFile = "<unknown>";
+            lineNum = 0;
+        } else {
+            lineNum = dvmLineNumFromPC(method, pc);
+        }
+        clazz = (ClassObject *) hprofLookupClassId(method->clazz);
+
+        /* STACK FRAME format:
+         *
+         * ID:     ID for this stack frame
+         * ID:     ID for the method name
+         * ID:     ID for the method descriptor
+         * ID:     ID for the source file name
+         * u4:     class serial number
+         * u4:     line number, 0 = no line information
+         *
+         * We use the address of the stack frame as its ID.
+         */
+
+        DexStringCache cache;
+        const char* descriptor;
+
+        dexStringCacheInit(&cache);
+        descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache);
+
+        hprofAddIdToRecord(rec, (u4) stackFrameEntry);
+        hprofAddIdToRecord(rec, hprofLookupStringId(method->name));
+        hprofAddIdToRecord(rec, hprofLookupStringId(descriptor));
+        hprofAddIdToRecord(rec, hprofLookupStringId(sourceFile));
+        hprofAddU4ToRecord(rec, (u4) clazz->serialNumber);
+        hprofAddU4ToRecord(rec, (u4) lineNum);
+
+        dexStringCacheRelease(&cache);
+    }
+
+    dvmHashTableUnlock(gStackFrameHashTable);
+    return 0;
+}
diff --git a/vm/hprof/HprofString.c b/vm/hprof/HprofString.c
new file mode 100644
index 0000000..3f697f5
--- /dev/null
+++ b/vm/hprof/HprofString.c
@@ -0,0 +1,118 @@
+/*
+ * 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.c b/vm/interp/Interp.c
new file mode 100644
index 0000000..9f44a13
--- /dev/null
+++ b/vm/interp/Interp.c
@@ -0,0 +1,1360 @@
+/*
+ * 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"
+
+
+/*
+ * ===========================================================================
+ *      Debugger support
+ * ===========================================================================
+ */
+
+// fwd
+static BreakpointSet* dvmBreakpointSetAlloc(void);
+static void dvmBreakpointSetFree(BreakpointSet* pSet);
+
+/*
+ * Initialize global breakpoint structures.
+ */
+bool dvmBreakpointStartup(void)
+{
+    gDvm.breakpointSet = dvmBreakpointSetAlloc();
+    return (gDvm.breakpointSet != NULL);
+}
+
+/*
+ * Free resources.
+ */
+void dvmBreakpointShutdown(void)
+{
+    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.
+ */
+typedef struct {
+    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 */
+} Breakpoint;
+
+/*
+ * 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(void)
+{
+    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 ((curVal & 0xff) == 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;
+
+            LOGV("+++ increasing breakpoint set size to %d\n", newSize);
+
+            /* pSet->breakpoints will be NULL on first entry */
+            newVec = 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)) {
+            LOGV("Class %s verified, adding breakpoint at %p\n",
+                method->clazz->descriptor, addr);
+            if (instructionIsMagicNop(addr)) {
+                LOGV("Refusing to set breakpoint on %04x at %s.%s + 0x%x\n",
+                    *addr, method->clazz->descriptor, method->name,
+                    instrOffset);
+            } else {
+                ANDROID_MEMBAR_FULL();
+                dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
+                    OP_BREAKPOINT);
+            }
+        } else {
+            LOGV("Class %s NOT verified, deferring breakpoint at %p\n",
+                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) {
+            LOGE("Unable to restore breakpoint opcode (%s.%s +0x%x)\n",
+                method->clazz->descriptor, method->name, instrOffset);
+            dvmAbort();
+        } else {
+            LOGW("Breakpoint was already restored? (%s.%s +0x%x)\n",
+                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.
+             */
+            LOGV("Flushing breakpoint at %p for %s\n",
+                pBreak->addr, clazz->descriptor);
+            if (instructionIsMagicNop(pBreak->addr)) {
+                LOGV("Refusing to flush breakpoint on %04x at %s.%s + 0x%x\n",
+                    *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(void)
+{
+    /* quick sanity check */
+    BreakpointSet* pSet = gDvm.breakpointSet;
+    dvmBreakpointSetLock(pSet);
+    if (dvmBreakpointSetCount(pSet) != 0) {
+        LOGW("WARNING: %d leftover breakpoints\n", 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) {
+            LOGE("GLITCH: can't find breakpoint, opcode is still set\n");
+            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) {
+        LOGW("WARNING: single-step active for %p; adding %p\n",
+            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 = size;
+    pCtrl->depth = 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;
+    void* fp;
+    void* prevFp = NULL;
+
+    for (fp = thread->curFrame; fp != NULL; fp = saveArea->prevFrame) {
+        const Method* method;
+
+        saveArea = SAVEAREA_FROM_FP(fp);
+        method = saveArea->method;
+
+        if (!dvmIsBreakFrame(fp) && !dvmIsNativeMethod(method))
+            break;
+        prevFp = fp;
+    }
+    if (fp == NULL) {
+        LOGW("Unexpected: step req in native-only threadid=%d\n",
+            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.
+         */
+        LOGV("##### init step while in native method\n");
+        fp = prevFp;
+        assert(!dvmIsBreakFrame(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->curFrame);
+    pCtrl->active = true;
+
+    LOGV("##### step init: thread=%p meth=%p '%s' line=%d frameDepth=%d depth=%s size=%s\n",
+        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;
+}
+
+
+/*
+ * 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);
+
+        LOGE("TRACK: unreleased internal reference (prev=%d total=%d)\n",
+            debugTrackedRefStart, count);
+        desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        LOGE("       current method is %s.%s %s\n", method->clazz->descriptor,
+            method->name, desc);
+        free(desc);
+        top = self->internalLocalRefTable.table + debugTrackedRefStart;
+        while (top < self->internalLocalRefTable.nextEntry) {
+            LOGE("  %p (%s)\n",
+                 *top,
+                 ((*top)->clazz != NULL) ? (*top)->clazz->descriptor : "");
+            top++;
+        }
+        dvmDumpThread(self, false);
+
+        dvmAbort();
+    }
+    //LOGI("TRACK OK\n");
+}
+#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;
+
+    LOG(LOG_VERBOSE, LOG_TAG"i", "Registers (fp=%p):\n", framePtr);
+    for (i = method->registersSize-1; i >= 0; i--) {
+        if (i >= localCount) {
+            LOG(LOG_VERBOSE, LOG_TAG"i", "  v%-2d in%-2d : 0x%08x\n",
+                i, i-localCount, framePtr[i]);
+        } else {
+            if (inOnly) {
+                LOG(LOG_VERBOSE, LOG_TAG"i", "  [...]\n");
+                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
+            LOG(LOG_VERBOSE, LOG_TAG"i", "  v%-2d      : 0x%08x %s\n",
+                i, framePtr[i], name);
+        }
+    }
+}
+#endif
+
+
+/*
+ * ===========================================================================
+ *      Entry point and general support functions
+ * ===========================================================================
+ */
+
+/*
+ * 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
+
+/*
+ * 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;
+    u2 size;
+    s4 firstKey;
+    const s4* entries;
+
+    /*
+     * 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 */
+        dvmThrowException("Ljava/lang/InternalError;",
+            "bad packed switch magic");
+        return kInstrLen;
+    }
+
+    size = *switchData++;
+    assert(size > 0);
+
+    firstKey = *switchData++;
+    firstKey |= (*switchData++) << 16;
+
+    if (testVal < firstKey || testVal >= firstKey + size) {
+        LOGVV("Value %d not found in switch (%d-%d)\n",
+            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.
+     */
+    entries = (const s4*) switchData;
+    assert(((u4)entries & 0x3) == 0);
+
+    assert(testVal - firstKey >= 0 && testVal - firstKey < size);
+    LOGVV("Value %d found in slot %d (goto 0x%02x)\n",
+        testVal, testVal - firstKey,
+        s4FromSwitchData(&entries[testVal - firstKey]));
+    return s4FromSwitchData(&entries[testVal - firstKey]);
+}
+
+/*
+ * 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 */
+        dvmThrowException("Ljava/lang/InternalError;",
+            "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)\n",
+                testVal, mid, s4FromSwitchData(&entries[mid]));
+            return s4FromSwitchData(&entries[mid]);
+        }
+    }
+
+    LOGVV("Value %d not found in switch\n", 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:
+        LOGE("Unexpected width %d in copySwappedArrayData\n", 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", 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) {
+        dvmThrowException("Ljava/lang/InternalError;", "bad array data magic");
+        return false;
+    }
+
+    width = arrayData[1];
+    size = arrayData[2] | (((u4)arrayData[3]) << 16);
+
+    if (size > arrayObj->length) {
+        dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", NULL);
+        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) {
+            LOGV("+ unknown method\n");
+            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 */
+        dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+            "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)) {
+        dvmThrowException("Ljava/lang/AbstractMethodError;",
+            "interface method not implemented");
+        return NULL;
+    }
+#else
+    assert(!dvmIsAbstractMethod(methodToCall) ||
+        methodToCall->nativeFunc != NULL);
+#endif
+
+    LOGVV("+++ interface=%s.%s concrete=%s.%s\n",
+        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 char* classNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    static const int kBufLen = 256;
+    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);
+    char* dotClassName = dvmDescriptorToDot(className);
+    if (flags == 0)
+        return dotClassName;
+
+    char* result = (char*) malloc(kBufLen);
+
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+        snprintf(result, kBufLen, "tried to access class %s from class %s",
+            dotClassName, dotFromName);
+        free(dotFromName);
+    } else {
+        assert(false);      // should've been caught above
+        result[0] = '\0';
+    }
+
+    free(dotClassName);
+    return result;
+}
+static char* fieldNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    static const int kBufLen = 256;
+    const DvmDex* pDvmDex = method->clazz->pDvmDex;
+    const DexFieldId* pFieldId;
+    const char* className;
+    const char* fieldName;
+
+    if (refType != VERIFY_ERROR_REF_FIELD) {
+        LOGW("Expected ref type %d, got %d\n", VERIFY_ERROR_REF_FIELD, refType);
+        return NULL;    /* no message */
+    }
+
+    pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
+    className = dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->classIdx);
+    fieldName = dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx);
+
+    char* dotName = dvmDescriptorToDot(className);
+    char* result = (char*) malloc(kBufLen);
+
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+        snprintf(result, kBufLen, "tried to access field %s.%s from class %s",
+            dotName, fieldName, dotFromName);
+        free(dotFromName);
+    } else {
+        snprintf(result, kBufLen, "%s.%s", dotName, fieldName);
+    }
+
+    free(dotName);
+    return result;
+}
+static char* methodNameFromIndex(const Method* method, int ref,
+    VerifyErrorRefType refType, int flags)
+{
+    static const int kBufLen = 384;
+    const DvmDex* pDvmDex = method->clazz->pDvmDex;
+    const DexMethodId* pMethodId;
+    const char* className;
+    const char* methodName;
+
+    if (refType != VERIFY_ERROR_REF_METHOD) {
+        LOGW("Expected ref type %d, got %d\n", VERIFY_ERROR_REF_METHOD,refType);
+        return NULL;    /* no message */
+    }
+
+    pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
+    className = dexStringByTypeIdx(pDvmDex->pDexFile, pMethodId->classIdx);
+    methodName = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+
+    char* dotName = dvmDescriptorToDot(className);
+    char* result = (char*) malloc(kBufLen);
+
+    if ((flags & kThrowShow_accessFromClass) != 0) {
+        char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+        snprintf(result, kBufLen,
+            "tried to access method %s.%s:%s from class %s",
+            dotName, methodName, desc, dotFromName);
+        free(dotFromName);
+        free(desc);
+    } else {
+        snprintf(result, kBufLen, "%s.%s", dotName, methodName);
+    }
+
+    free(dotName);
+    return result;
+}
+
+/*
+ * 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)
+{
+    const int typeMask = 0xff << kVerifyErrorRefTypeShift;
+    VerifyError errorKind = kind & ~typeMask;
+    VerifyErrorRefType refType = kind >> kVerifyErrorRefTypeShift;
+    const char* exceptionName = "Ljava/lang/VerifyError;";
+    char* msg = NULL;
+
+    switch ((VerifyError) errorKind) {
+    case VERIFY_ERROR_NO_CLASS:
+        exceptionName = "Ljava/lang/NoClassDefFoundError;";
+        msg = classNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_NO_FIELD:
+        exceptionName = "Ljava/lang/NoSuchFieldError;";
+        msg = fieldNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_NO_METHOD:
+        exceptionName = "Ljava/lang/NoSuchMethodError;";
+        msg = methodNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_ACCESS_CLASS:
+        exceptionName = "Ljava/lang/IllegalAccessError;";
+        msg = classNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_ACCESS_FIELD:
+        exceptionName = "Ljava/lang/IllegalAccessError;";
+        msg = fieldNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_ACCESS_METHOD:
+        exceptionName = "Ljava/lang/IllegalAccessError;";
+        msg = methodNameFromIndex(method, ref, refType,
+            kThrowShow_accessFromClass);
+        break;
+    case VERIFY_ERROR_CLASS_CHANGE:
+        exceptionName = "Ljava/lang/IncompatibleClassChangeError;";
+        msg = classNameFromIndex(method, ref, refType, 0);
+        break;
+    case VERIFY_ERROR_INSTANTIATION:
+        exceptionName = "Ljava/lang/InstantiationError;";
+        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 = strdup("weird - no error specified");
+        break;
+
+    /* no default clause -- want warning if enum updated */
+    }
+
+    dvmThrowException(exceptionName, msg);
+    free(msg);
+}
+
+/*
+ * Main interpreter loop entry point.  Select "standard" or "debug"
+ * interpreter and switch between them as required.
+ *
+ * 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)
+{
+    InterpState interpState;
+    bool change;
+#if defined(WITH_JIT)
+    /* Target-specific save/restore */
+    extern void dvmJitCalleeSave(double *saveArea);
+    extern void dvmJitCalleeRestore(double *saveArea);
+    /* Interpreter entry points from compiled code */
+    extern void dvmJitToInterpNormal();
+    extern void dvmJitToInterpNoChain();
+    extern void dvmJitToInterpPunt();
+    extern void dvmJitToInterpSingleStep();
+    extern void dvmJitToInterpTraceSelectNoChain();
+    extern void dvmJitToInterpTraceSelect();
+    extern void dvmJitToPatchPredictedChain();
+#if defined(WITH_SELF_VERIFICATION)
+    extern void dvmJitToInterpBackwardBranch();
+#endif
+
+    /*
+     * 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,
+        dvmJitToInterpTraceSelectNoChain,
+        dvmJitToInterpTraceSelect,
+        dvmJitToPatchPredictedChain,
+#if defined(WITH_SELF_VERIFICATION)
+        dvmJitToInterpBackwardBranch,
+#endif
+    };
+
+    /*
+     * 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
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+    interpState.debugTrackedRefStart =
+        dvmReferenceTableEntries(&self->internalLocalRefTable);
+#endif
+    interpState.debugIsMethodEntry = true;
+#if defined(WITH_JIT)
+    dvmJitCalleeSave(interpState.calleeSave);
+    /* Initialize the state to kJitNot */
+    interpState.jitState = kJitNot;
+
+    /* Setup the Jit-to-interpreter entry points */
+    interpState.jitToInterpEntries = jitToInterpEntries;
+
+    /*
+     * Initialize the threshold filter [don't bother to zero out the
+     * actual table.  We're looking for matches, and an occasional
+     * false positive is acceptible.
+     */
+    interpState.lastThreshFilter = 0;
+
+    interpState.icRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+#endif
+
+    /*
+     * Initialize working state.
+     *
+     * No need to initialize "retval".
+     */
+    interpState.method = method;
+    interpState.fp = (u4*) self->curFrame;
+    interpState.pc = method->insns;
+    interpState.entryPoint = kInterpEntryInstr;
+
+    if (dvmDebuggerOrProfilerActive())
+        interpState.nextMode = INTERP_DBG;
+    else
+        interpState.nextMode = INTERP_STD;
+
+    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)
+    {
+        LOGE("ERROR: tried to execute code in unprepared class '%s' (%d)\n",
+            method->clazz->descriptor, method->clazz->status);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    typedef bool (*Interpreter)(Thread*, InterpState*);
+    Interpreter stdInterp;
+    if (gDvm.executionMode == kExecutionModeInterpFast)
+        stdInterp = dvmMterpStd;
+#if defined(WITH_JIT)
+    else if (gDvm.executionMode == kExecutionModeJit)
+/* If profiling overhead can be kept low enough, we can use a profiling
+ * mterp fast for both Jit and "fast" modes.  If overhead is too high,
+ * create a specialized profiling interpreter.
+ */
+        stdInterp = dvmMterpStd;
+#endif
+    else
+        stdInterp = dvmInterpretStd;
+
+    change = true;
+    while (change) {
+        switch (interpState.nextMode) {
+        case INTERP_STD:
+            LOGVV("threadid=%d: interp STD\n", self->threadId);
+            change = (*stdInterp)(self, &interpState);
+            break;
+        case INTERP_DBG:
+            LOGVV("threadid=%d: interp DBG\n", self->threadId);
+            change = dvmInterpretDbg(self, &interpState);
+            break;
+        default:
+            dvmAbort();
+        }
+    }
+
+    *pResult = interpState.retval;
+#if defined(WITH_JIT)
+    dvmJitCalleeRestore(interpState.calleeSave);
+#endif
+}
diff --git a/vm/interp/Interp.h b/vm/interp/Interp.h
new file mode 100644
index 0000000..76d7d29
--- /dev/null
+++ b/vm/interp/Interp.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.
+ */
+
+/*
+ * Dalvik interpreter public definitions.
+ */
+#ifndef _DALVIK_INTERP_INTERP
+#define _DALVIK_INTERP_INTERP
+
+/*
+ * 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.
+ */
+void dvmThrowVerificationError(const Method* method, int kind, int ref);
+
+/*
+ * One-time initialization and shutdown.
+ */
+bool dvmBreakpointStartup(void);
+void dvmBreakpointShutdown(void);
+
+/*
+ * 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.
+ */
+u1 dvmGetOriginalOpCode(const u2* addr);
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".
+ */
+void dvmFlushBreakpoints(ClassObject* clazz);
+
+#endif /*_DALVIK_INTERP_INTERP*/
diff --git a/vm/interp/InterpDefs.h b/vm/interp/InterpDefs.h
new file mode 100644
index 0000000..4ccdee3
--- /dev/null
+++ b/vm/interp/InterpDefs.h
@@ -0,0 +1,277 @@
+/*
+ * 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
+#define _DALVIK_INTERP_DEFS
+
+
+/*
+ * Specify the starting point when switching between interpreters.
+ */
+typedef enum InterpEntry {
+    kInterpEntryInstr = 0,      // continue to next instruction
+    kInterpEntryReturn = 1,     // jump to method return
+    kInterpEntryThrow = 2,      // jump to exception throw
+#if defined(WITH_JIT)
+    kInterpEntryResume = 3,     // Resume after single-step
+#endif
+} InterpEntry;
+
+#if defined(WITH_JIT)
+/*
+ * There are six entry points from the compiled code to the interpreter:
+ * 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.
+ * 2) dvmJitToInterpInvokeNoChain: similar to 1) but don't chain. This is
+ *    for handling 1-to-many mappings like virtual method call and
+ *    packed switch.
+ * 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: if there is a single exit from a translation that
+ *    has already gone hot enough to be translated, we should assume that
+ *    the exit point should also be translated (this is a common case for
+ *    invokes).  This trace exit will first check for a chaining
+ *    opportunity, and if none is available will switch to the debug
+ *    interpreter immediately for trace selection (as if threshold had
+ *    just been reached).
+ * 6) dvmJitToPredictedChain: patch the chaining cell for a virtual call site
+ *    to a predicted callee.
+ * 7) dvmJitToBackwardBranch: (WITH_SELF_VERIFICATION ONLY) special case of 1)
+ *    and 5). This is used instead if the ending branch of the trace jumps back
+ *    into the same basic block.
+ */
+struct JitToInterpEntries {
+    void *dvmJitToInterpNormal;
+    void *dvmJitToInterpNoChain;
+    void *dvmJitToInterpPunt;
+    void *dvmJitToInterpSingleStep;
+    void *dvmJitToInterpTraceSelectNoChain;
+    void *dvmJitToInterpTraceSelect;
+    void *dvmJitToPatchPredictedChain;
+#if defined(WITH_SELF_VERIFICATION)
+    void *dvmJitToInterpBackwardBranch;
+#endif
+};
+
+/*
+ * 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
+
+/* 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 4
+#endif
+
+/*
+ * Interpreter context, used when switching from one interpreter to
+ * another.  We also tuck "mterp" state in here.
+ */
+typedef struct InterpState {
+    /*
+     * To make some mterp state updates easier, "pc" and "fp" MUST come
+     * first and MUST appear in this order.
+     */
+    const u2*   pc;                     // program counter
+    u4*         fp;                     // frame pointer
+
+    JValue      retval;                 // return value -- "out" only
+    const Method* method;               // method being executed
+
+
+    /* ----------------------------------------------------------------------
+     * Mterp-only state
+     */
+    DvmDex*         methodClassDex;
+    Thread*         self;
+
+    /* housekeeping */
+    void*           bailPtr;
+
+    /*
+     * These are available globally, from gDvm, or from another glue field
+     * (self/method).  They're copied in here for speed.
+     */
+    /* copy of self->interpStackEnd */
+    const u1*       interpStackEnd;
+    /* points at self->suspendCount */
+    volatile int*   pSelfSuspendCount;
+    /* Biased base of GC's card table */
+    u1*             cardTable;
+    /* points at gDvm.debuggerActive, or NULL if debugger not enabled */
+    volatile u1*    pDebuggerActive;
+    /* points at gDvm.activeProfilers */
+    volatile int*   pActiveProfilers;
+    /* ----------------------------------------------------------------------
+     */
+
+    /*
+     * Interpreter switching.
+     */
+    InterpEntry entryPoint;             // what to do when we start
+    int         nextMode;               // INTERP_STD, INTERP_DBG
+
+#if defined(WITH_JIT)
+    /*
+     * Local copies of field from gDvm placed here for fast access
+     */
+    unsigned char*     pJitProfTable;
+    JitState           jitState;
+    const void*        jitResumeNPC;    // Native PC of compiled code
+    const u2*          jitResumeDPC;    // Dalvik PC corresponding to NPC
+    int                jitThreshold;
+    /*
+     * ppJitProfTable holds the address of gDvmJit.pJitProfTable, which
+     * doubles as an on/off switch for the Jit.  Because a change in
+     * the value of gDvmJit.pJitProfTable isn't reflected in the cached
+     * copy above (pJitProfTable), we need to periodically refresh it.
+     * ppJitProfTable is used for that purpose.
+     */
+    unsigned char**    ppJitProfTable; // Used to refresh pJitProfTable
+    int                icRechainCount; // Count down to next rechain request
+#endif
+
+    bool        debugIsMethodEntry;     // used for method entry event triggers
+#if defined(WITH_TRACKREF_CHECKS)
+    int         debugTrackedRefStart;   // tracked refs from prior invocations
+#endif
+
+#if defined(WITH_JIT)
+    struct JitToInterpEntries jitToInterpEntries;
+
+    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
+    int lastThreshFilter;
+    const u2* lastPC;         // Stage the PC first for the threaded interpreter
+    intptr_t threshFilter[JIT_TRACE_THRESH_FILTER_SIZE];
+    JitTraceRun trace[MAX_JIT_RUN_LEN];
+    double calleeSave[JIT_CALLEE_SAVE_DOUBLE_COUNT];
+#endif
+
+} InterpState;
+
+/*
+ * These are generated from InterpCore.h.
+ */
+extern bool dvmInterpretDbg(Thread* self, InterpState* interpState);
+extern bool dvmInterpretStd(Thread* self, InterpState* interpState);
+#define INTERP_STD 0
+#define INTERP_DBG 1
+
+/*
+ * "mterp" interpreter.
+ */
+extern bool dvmMterpStd(Thread* self, InterpState* interpState);
+
+/*
+ * 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.
+ */
+s4 dvmInterpHandlePackedSwitch(const u2* switchData, s4 testVal);
+s4 dvmInterpHandleSparseSwitch(const u2* switchData, s4 testVal);
+
+/*
+ * Process fill-array-data.
+ */
+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.  Used when
+ * selecting which interpreter to start or switch to.
+ */
+static inline bool dvmDebuggerOrProfilerActive(void)
+{
+    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
+        || gDvm.activeProfilers != 0
+        || gDvm.debuggerActive;
+}
+
+/*
+ * 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);
+}
+
+/*
+ * The fast and debug interpreter may be doing ping-pong without making forward
+ * progress if the same trace building request sent upon entering the fast
+ * interpreter is rejected immediately by the debug interpreter. Use the
+ * following function to poll the rejection reasons and stay in the debug
+ * interpreter until they are cleared. This will guarantee forward progress
+ * in the extreme corner cases (eg set compiler threashold to 1).
+ */
+static inline bool dvmJitStayInPortableInterpreter()
+{
+    return dvmJitHideTranslation() ||
+           (gDvmJit.compilerQueueLength >= gDvmJit.compilerHighWater);
+}
+#endif
+
+#endif /*_DALVIK_INTERP_DEFS*/
diff --git a/vm/interp/Jit.c b/vm/interp/Jit.c
new file mode 100644
index 0000000..c800fe5
--- /dev/null
+++ b/vm/interp/Jit.c
@@ -0,0 +1,1352 @@
+/*
+ * 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/OpCodeNames.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, InterpState, and registers to shadow space.
+ * Return a pointer to the shadow space for JIT to use.
+ */
+void* dvmSelfVerificationSaveState(const u2* pc, const void* fp,
+                                   InterpState* interpState, int targetTrace)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    unsigned preBytes = interpState->method->outsSize*4 + sizeof(StackSaveArea);
+    unsigned postBytes = interpState->method->registersSize*4;
+
+    //LOGD("### selfVerificationSaveState(%d) pc: 0x%x fp: 0x%x",
+    //    self->threadId, (int)pc, (int)fp);
+
+    if (shadowSpace->selfVerificationState != kSVSIdle) {
+        LOGD("~~~ Save: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, shadowSpace->selfVerificationState);
+        LOGD("********** SHADOW STATE DUMP **********");
+        LOGD("PC: 0x%x FP: 0x%x", (int)pc, (int)fp);
+    }
+    shadowSpace->selfVerificationState = kSVSStart;
+
+    if (interpState->entryPoint == kInterpEntryResume) {
+        interpState->entryPoint = kInterpEntryInstr;
+#if 0
+        /* Tracking the success rate of resume after single-stepping */
+        if (interpState->jitResumeDPC == pc) {
+            LOGD("SV single step resumed at %p", pc);
+        }
+        else {
+            LOGD("real %p DPC %p NPC %p", pc, interpState->jitResumeDPC,
+                 interpState->jitResumeNPC);
+        }
+#endif
+    }
+
+    // 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->glue = interpState;
+    /*
+     * Store the original method here in case the trace ends with a
+     * return/invoke, the last method.
+     */
+    shadowSpace->method = interpState->method;
+    shadowSpace->shadowFP = shadowSpace->registerSpace +
+                            shadowSpace->registerSpaceSize - postBytes/4;
+
+    // Create a copy of the InterpState
+    memcpy(&(shadowSpace->interpState), interpState, sizeof(InterpState));
+    shadowSpace->interpState.fp = shadowSpace->shadowFP;
+    shadowSpace->interpState.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, const void* fp,
+                                      SelfVerificationState exitState)
+{
+    Thread *self = dvmThreadSelf();
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    // Official InterpState structure
+    InterpState *realGlue = shadowSpace->glue;
+    shadowSpace->endPC = pc;
+    shadowSpace->endShadowFP = fp;
+    shadowSpace->jitExitState = exitState;
+
+    //LOGD("### selfVerificationRestoreState(%d) pc: 0x%x fp: 0x%x endPC: 0x%x",
+    //    self->threadId, (int)shadowSpace->startPC, (int)shadowSpace->fp,
+    //    (int)pc);
+
+    if (shadowSpace->selfVerificationState != kSVSStart) {
+        LOGD("~~~ Restore: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, shadowSpace->selfVerificationState);
+        LOGD("********** SHADOW STATE DUMP **********");
+        LOGD("Dalvik PC: 0x%x endPC: 0x%x", (int)shadowSpace->startPC,
+            (int)shadowSpace->endPC);
+        LOGD("Interp FP: 0x%x", (int)shadowSpace->fp);
+        LOGD("Shadow FP: 0x%x endFP: 0x%x", (int)shadowSpace->shadowFP,
+            (int)shadowSpace->endShadowFP);
+    }
+
+    // Move the resume [ND]PC from the shadow space to the real space so that
+    // the debug interpreter can return to the translation
+    if (exitState == kSVSSingleStep) {
+        realGlue->jitResumeNPC = shadowSpace->interpState.jitResumeNPC;
+        realGlue->jitResumeDPC = shadowSpace->interpState.jitResumeDPC;
+    } else {
+        realGlue->jitResumeNPC = NULL;
+        realGlue->jitResumeDPC = NULL;
+    }
+
+    // Special case when punting after a single instruction
+    if (exitState == kSVSPunt && pc == shadowSpace->startPC) {
+        shadowSpace->selfVerificationState = kSVSIdle;
+    } else if (exitState == kSVSBackwardBranch && pc < shadowSpace->startPC) {
+        /*
+         * Consider a trace with a backward branch:
+         *   1: ..
+         *   2: ..
+         *   3: ..
+         *   4: ..
+         *   5: Goto {1 or 2 or 3 or 4}
+         *
+         * If there instruction 5 goes to 1 and there is no single-step
+         * instruction in the loop, pc is equal to shadowSpace->startPC and
+         * we will honor the backward branch condition.
+         *
+         * If the single-step instruction is outside the loop, then after
+         * resuming in the trace the startPC will be less than pc so we will
+         * also honor the backward branch condition.
+         *
+         * If the single-step is inside the loop, we won't hit the same endPC
+         * twice when the interpreter is re-executing the trace so we want to
+         * cancel the backward branch condition. In this case it can be
+         * detected as the endPC (ie pc) will be less than startPC.
+         */
+        shadowSpace->selfVerificationState = kSVSNormal;
+    } else {
+        shadowSpace->selfVerificationState = exitState;
+    }
+
+    return shadowSpace;
+}
+
+/* Print contents of virtual registers */
+static void selfVerificationPrintRegisters(int* addr, int* addrRef,
+                                           int numWords)
+{
+    int i;
+    for (i = 0; i < numWords; i++) {
+        LOGD("(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->curFrame);
+    int frameBytes = (int) shadowSpace->registerSpace +
+                     shadowSpace->registerSpaceSize*4 -
+                     (int) shadowSpace->shadowFP;
+    int localRegs = 0;
+    int frameBytes2 = 0;
+    if (self->curFrame < shadowSpace->fp) {
+        localRegs = (stackSave->method->registersSize -
+                     stackSave->method->insSize)*4;
+        frameBytes2 = (int) shadowSpace->fp - (int) self->curFrame - localRegs;
+    }
+    LOGD("********** SHADOW STATE DUMP **********");
+    LOGD("CurrentPC: 0x%x, Offset: 0x%04x", (int)pc,
+        (int)(pc - stackSave->method->insns));
+    LOGD("Class: %s", shadowSpace->method->clazz->descriptor);
+    LOGD("Method: %s", shadowSpace->method->name);
+    LOGD("Dalvik PC: 0x%x endPC: 0x%x", (int)shadowSpace->startPC,
+        (int)shadowSpace->endPC);
+    LOGD("Interp FP: 0x%x endFP: 0x%x", (int)shadowSpace->fp,
+        (int)self->curFrame);
+    LOGD("Shadow FP: 0x%x endFP: 0x%x", (int)shadowSpace->shadowFP,
+        (int)shadowSpace->endShadowFP);
+    LOGD("Frame1 Bytes: %d Frame2 Local: %d Bytes: %d", frameBytes,
+        localRegs, frameBytes2);
+    LOGD("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->curFrame);
+    int i, addr, offset;
+    DecodedInstruction *decInsn;
+
+    LOGD("********** 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 */
+        LOGD("0x%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);
+}
+
+/* Manage self verification while in the debug interpreter */
+static bool selfVerificationDebugInterp(const u2* pc, Thread* self,
+                                        InterpState *interpState)
+{
+    ShadowSpace *shadowSpace = self->shadowSpace;
+    SelfVerificationState state = shadowSpace->selfVerificationState;
+
+    DecodedInstruction decInsn;
+    dexDecodeInstruction(gDvm.instrFormat, pc, &decInsn);
+
+    //LOGD("### DbgIntp(%d): PC: 0x%x endPC: 0x%x state: %d len: %d %s",
+    //    self->threadId, (int)pc, (int)shadowSpace->endPC, state,
+    //    shadowSpace->traceLength, dexGetOpcodeName(decInsn.opCode));
+
+    if (state == kSVSIdle || state == kSVSStart) {
+        LOGD("~~~ DbgIntrp: INCORRECT PREVIOUS STATE(%d): %d",
+            self->threadId, state);
+        selfVerificationDumpState(pc, self);
+        selfVerificationDumpTrace(pc, self);
+    }
+
+    /*
+     * Skip endPC once when trace has a backward branch. If the SV state is
+     * single step, keep it that way.
+     */
+    if ((state == kSVSBackwardBranch && pc == shadowSpace->endPC) ||
+        (state != kSVSBackwardBranch && state != kSVSSingleStep)) {
+        shadowSpace->selfVerificationState = kSVSDebugInterp;
+    }
+
+    /* Check that the current pc is the end of the trace */
+    if ((state == kSVSDebugInterp || state == kSVSSingleStep) &&
+        pc == shadowSpace->endPC) {
+
+        shadowSpace->selfVerificationState = kSVSIdle;
+
+        /* Check register space */
+        int frameBytes = (int) shadowSpace->registerSpace +
+                         shadowSpace->registerSpaceSize*4 -
+                         (int) shadowSpace->shadowFP;
+        if (memcmp(shadowSpace->fp, shadowSpace->shadowFP, frameBytes)) {
+            LOGD("~~~ DbgIntp(%d): REGISTERS DIVERGENCE!", self->threadId);
+            selfVerificationDumpState(pc, self);
+            selfVerificationDumpTrace(pc, self);
+            LOGD("*** Interp Registers: addr: 0x%x bytes: %d",
+                (int)shadowSpace->fp, frameBytes);
+            selfVerificationPrintRegisters((int*)shadowSpace->fp,
+                                           (int*)shadowSpace->shadowFP,
+                                           frameBytes/4);
+            LOGD("*** Shadow Registers: addr: 0x%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 (self->curFrame < shadowSpace->fp) {
+            StackSaveArea* stackSave = SAVEAREA_FROM_FP(self->curFrame);
+            int localRegs = (stackSave->method->registersSize -
+                             stackSave->method->insSize)*4;
+            int frameBytes2 = (int) shadowSpace->fp -
+                              (int) self->curFrame - localRegs;
+            if (memcmp(((char*)self->curFrame)+localRegs,
+                ((char*)shadowSpace->endShadowFP)+localRegs, frameBytes2)) {
+                LOGD("~~~ DbgIntp(%d): REGISTERS (FRAME2) DIVERGENCE!",
+                    self->threadId);
+                selfVerificationDumpState(pc, self);
+                selfVerificationDumpTrace(pc, self);
+                LOGD("*** Interp Registers: addr: 0x%x l: %d bytes: %d",
+                    (int)self->curFrame, localRegs, frameBytes2);
+                selfVerificationPrintRegisters((int*)self->curFrame,
+                                               (int*)shadowSpace->endShadowFP,
+                                               (frameBytes2+localRegs)/4);
+                LOGD("*** Shadow Registers: addr: 0x%x l: %d bytes: %d",
+                    (int)shadowSpace->endShadowFP, localRegs, frameBytes2);
+                selfVerificationPrintRegisters((int*)shadowSpace->endShadowFP,
+                                               (int*)self->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) {
+                LOGD("~~~ DbgIntp(%d): MEMORY DIVERGENCE!", self->threadId);
+                LOGD("Addr: 0x%x Intrp Data: 0x%x Jit Data: 0x%x",
+                    heapSpacePtr->addr, memData, heapSpacePtr->data);
+                selfVerificationDumpState(pc, self);
+                selfVerificationDumpTrace(pc, self);
+                memDiff = true;
+            }
+        }
+        if (memDiff) selfVerificationSpinLoop(shadowSpace);
+
+        /*
+         * Switch to JIT single step mode to stay in the debug interpreter for
+         * one more instruction
+         */
+        if (state == kSVSSingleStep) {
+            interpState->jitState = kJitSingleStepEnd;
+        }
+        return true;
+
+    /* If end not been reached, make sure max length not exceeded */
+    } else if (shadowSpace->traceLength >= JIT_MAX_TRACE_LEN) {
+        LOGD("~~~ DbgIntp(%d): CONTROL DIVERGENCE!", self->threadId);
+        LOGD("startPC: 0x%x endPC: 0x%x currPC: 0x%x",
+            (int)shadowSpace->startPC, (int)shadowSpace->endPC, (int)pc);
+        selfVerificationDumpState(pc, self);
+        selfVerificationDumpTrace(pc, self);
+        selfVerificationSpinLoop(shadowSpace);
+
+        return true;
+    }
+    /* Log the instruction address and decoded instruction for debug */
+    shadowSpace->trace[shadowSpace->traceLength].addr = (int)pc;
+    shadowSpace->trace[shadowSpace->traceLength].decInsn = decInsn;
+    shadowSpace->traceLength++;
+
+    return false;
+}
+#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 InterpState 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;
+}
+
+#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++;
+        }
+        LOGD("JIT: table size is %d, entries used is %d",
+             gDvmJit.jitTableSize,  gDvmJit.jitTableEntriesUsed);
+        LOGD("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)
+        LOGD("JIT: Code cache patches: %d", gDvmJit.codeCachePatches);
+
+        LOGD("JIT: Lookups: %d hits, %d misses; %d normal, %d punt",
+             gDvmJit.addrLookupsFound, gDvmJit.addrLookupsNotFound,
+             gDvmJit.normalExit, gDvmJit.puntExit);
+
+        LOGD("JIT: ICHits: %d", gDvmICHitCount);
+
+        LOGD("JIT: noChainExit: %d IC miss, %d interp callsite, "
+             "%d switch overflow",
+             gDvmJit.noChainExit[kInlineCacheMiss],
+             gDvmJit.noChainExit[kCallsiteInterpreted],
+             gDvmJit.noChainExit[kSwitchOverflow]);
+
+        LOGD("JIT: ICPatch: %d init, %d rejected, %d lock-free, %d queued, "
+             "%d dropped",
+             gDvmJit.icPatchInit, gDvmJit.icPatchRejected,
+             gDvmJit.icPatchLockFree, gDvmJit.icPatchQueued,
+             gDvmJit.icPatchDropped);
+
+        LOGD("JIT: Invoke: %d mono, %d poly, %d native, %d return",
+             gDvmJit.invokeMonomorphic, gDvmJit.invokePolymorphic,
+             gDvmJit.invokeNative, gDvmJit.returnOp);
+        LOGD("JIT: Inline: %d mgetter, %d msetter, %d pgetter, %d psetter",
+             gDvmJit.invokeMonoGetterInlined, gDvmJit.invokeMonoSetterInlined,
+             gDvmJit.invokePolyGetterInlined, gDvmJit.invokePolySetterInlined);
+        LOGD("JIT: Total compilation time: %llu ms", gDvmJit.jitTime / 1000);
+        LOGD("JIT: Avg unit compilation time: %llu us",
+             gDvmJit.jitTime / gDvmJit.numCompilations);
+#endif
+
+        LOGD("JIT: %d Translation chains, %d interp stubs",
+             gDvmJit.translationChains, stubs);
+        if (gDvmJit.profile) {
+            dvmCompilerSortAndPrintTraceProfiles();
+        }
+    }
+}
+
+
+void setTraceConstruction(JitEntry *slot, bool value)
+{
+
+    JitEntryInfoUnion oldValue;
+    JitEntryInfoUnion newValue;
+    do {
+        oldValue = slot->u;
+        newValue = oldValue;
+        newValue.info.traceConstruction = value;
+    } while (android_atomic_release_cas(oldValue.infoWord, newValue.infoWord,
+            &slot->u.infoWord) != 0);
+}
+
+void resetTracehead(InterpState* interpState, JitEntry *slot)
+{
+    slot->codeAddress = dvmCompilerGetInterpretTemplate();
+    setTraceConstruction(slot, false);
+}
+
+/* Clean up any pending trace builds */
+void dvmJitAbortTraceSelect(InterpState* interpState)
+{
+    if (interpState->jitState == kJitTSelect)
+        interpState->jitState = kJitDone;
+}
+
+/*
+ * Find an entry in the JitTable, creating if necessary.
+ * Returns null if table is full.
+ */
+static JitEntry *lookupAndAdd(const u2* dPC, bool callerLocked)
+{
+    u4 chainEndMarker = gDvmJit.jitTableSize;
+    u4 idx = dvmJitHash(dPC);
+
+    /* Walk the bucket chain to find an exact match for our PC */
+    while ((gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) &&
+           (gDvmJit.pJitEntryTable[idx].dPC != dPC)) {
+        idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+    }
+
+    if (gDvmJit.pJitEntryTable[idx].dPC != dPC) {
+        /*
+         * 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) {
+                    /* 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) {
+            /*
+             * Initialize codeAddress and allocate the slot.  Must
+             * happen in this order (since dPC is set, the entry is live.
+             */
+            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];
+}
+
+/*
+ * 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(InterpState* interpState,
+                                  const ClassObject* thisClass,
+                                  const Method* calleeMethod,
+                                  const DecodedInstruction* insn)
+{
+    int currTraceRun = ++interpState->currTraceRun;
+    interpState->trace[currTraceRun].meta = (void *) thisClass;
+    currTraceRun = ++interpState->currTraceRun;
+    interpState->trace[currTraceRun].meta = (void *) calleeMethod;
+}
+
+/*
+ * 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,
+                             InterpState *interpState)
+{
+    DecodedInstruction nextDecInsn;
+    const u2 *moveResultPC = lastPC + len;
+
+    dexDecodeInstruction(gDvm.instrFormat, 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 = ++interpState->currTraceRun;
+    interpState->currRunHead = moveResultPC;
+    interpState->trace[currTraceRun].frag.startOffset = offset + len;
+    interpState->trace[currTraceRun].frag.numInsts = 1;
+    interpState->trace[currTraceRun].frag.runEnd = false;
+    interpState->trace[currTraceRun].frag.hint = kJitHintNone;
+    interpState->trace[currTraceRun].frag.isCode = true;
+    interpState->totalTraceLen++;
+
+    interpState->currRunLen = dexGetInstrOrTableWidthAbs(gDvm.instrWidth,
+                                                         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.
+ */
+int dvmCheckJit(const u2* pc, Thread* self, InterpState* interpState,
+                const ClassObject* thisClass, const Method* curMethod)
+{
+    int flags, len;
+    int switchInterp = false;
+    bool debugOrProfile = dvmDebuggerOrProfilerActive();
+    /* Stay in the dbg interpreter for the next instruction */
+    bool stayOneMoreInst = false;
+
+    /*
+     * Bug 2710533 - dalvik crash when disconnecting debugger
+     *
+     * Reset the entry point to the default value. If needed it will be set to a
+     * specific value in the corresponding case statement (eg kJitSingleStepEnd)
+     */
+    interpState->entryPoint = kInterpEntryInstr;
+
+    /* Prepare to handle last PC and stage the current PC */
+    const u2 *lastPC = interpState->lastPC;
+    interpState->lastPC = pc;
+
+    switch (interpState->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(gDvm.instrFormat, lastPC, &decInsn);
+
+            /*
+             * Treat {PACKED,SPARSE}_SWITCH as trace-ending instructions due
+             * to the amount of space it takes to generate the chaining
+             * cells.
+             */
+            if (interpState->totalTraceLen != 0 &&
+                (decInsn.opCode == OP_PACKED_SWITCH ||
+                 decInsn.opCode == OP_SPARSE_SWITCH)) {
+                interpState->jitState = kJitTSelectEnd;
+                break;
+            }
+
+
+#if defined(SHOW_TRACE)
+            LOGD("TraceGen: adding %s", dexGetOpcodeName(decInsn.opCode));
+#endif
+            flags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
+            len = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, lastPC);
+            offset = lastPC - interpState->method->insns;
+            assert((unsigned) offset <
+                   dvmGetMethodInsnsSize(interpState->method));
+            if (lastPC != interpState->currRunHead + interpState->currRunLen) {
+                int currTraceRun;
+                /* We need to start a new trace run */
+                currTraceRun = ++interpState->currTraceRun;
+                interpState->currRunLen = 0;
+                interpState->currRunHead = (u2*)lastPC;
+                interpState->trace[currTraceRun].frag.startOffset = offset;
+                interpState->trace[currTraceRun].frag.numInsts = 0;
+                interpState->trace[currTraceRun].frag.runEnd = false;
+                interpState->trace[currTraceRun].frag.hint = kJitHintNone;
+                interpState->trace[currTraceRun].frag.isCode = true;
+            }
+            interpState->trace[interpState->currTraceRun].frag.numInsts++;
+            interpState->totalTraceLen++;
+            interpState->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 buildier */
+            if (interpState->currTraceRun ==
+                (MAX_JIT_RUN_LEN - 1 - needReservedRun)) {
+                interpState->jitState = kJitTSelectEnd;
+            }
+
+            if (  ((flags & kInstrUnconditional) == 0) &&
+                  /* don't end trace on INVOKE_DIRECT_EMPTY  */
+                  (decInsn.opCode != OP_INVOKE_DIRECT_EMPTY) &&
+                  ((flags & (kInstrCanBranch |
+                             kInstrCanSwitch |
+                             kInstrCanReturn |
+                             kInstrInvoke)) != 0)) {
+                    interpState->jitState = kJitTSelectEnd;
+#if defined(SHOW_TRACE)
+                LOGD("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(interpState, thisClass, curMethod,
+                                          &decInsn);
+                    insertMoveResult(lastPC, len, offset, interpState);
+                }
+            }
+            /* Break on throw or self-loop */
+            if ((decInsn.opCode == OP_THROW) || (lastPC == pc)){
+                interpState->jitState = kJitTSelectEnd;
+            }
+            if (interpState->totalTraceLen >= JIT_MAX_TRACE_LEN) {
+                interpState->jitState = kJitTSelectEnd;
+            }
+             /* Abandon the trace request if debugger/profiler is attached */
+            if (debugOrProfile) {
+                interpState->jitState = kJitDone;
+                break;
+            }
+            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:
+            {
+                /* Bad trace */
+                if (interpState->totalTraceLen == 0) {
+                    /* Bad trace - mark as untranslatable */
+                    interpState->jitState = kJitDone;
+                    switchInterp = true;
+                    break;
+                }
+
+                int lastTraceDesc = interpState->currTraceRun;
+
+                /* Extend a new empty desc if the last slot is meta info */
+                if (!interpState->trace[lastTraceDesc].frag.isCode) {
+                    lastTraceDesc = ++interpState->currTraceRun;
+                    interpState->trace[lastTraceDesc].frag.startOffset = 0;
+                    interpState->trace[lastTraceDesc].frag.numInsts = 0;
+                    interpState->trace[lastTraceDesc].frag.hint = kJitHintNone;
+                    interpState->trace[lastTraceDesc].frag.isCode = true;
+                }
+
+                /* Mark the end of the trace runs */
+                interpState->trace[lastTraceDesc].frag.runEnd = true;
+
+                JitTraceDescription* desc =
+                   (JitTraceDescription*)malloc(sizeof(JitTraceDescription) +
+                     sizeof(JitTraceRun) * (interpState->currTraceRun+1));
+
+                if (desc == NULL) {
+                    LOGE("Out of memory in trace selection");
+                    dvmJitStopTranslationRequests();
+                    interpState->jitState = kJitDone;
+                    switchInterp = true;
+                    break;
+                }
+
+                desc->method = interpState->method;
+                memcpy((char*)&(desc->trace[0]),
+                    (char*)&(interpState->trace[0]),
+                    sizeof(JitTraceRun) * (interpState->currTraceRun+1));
+#if defined(SHOW_TRACE)
+                LOGD("TraceGen:  trace done, adding to queue");
+#endif
+                if (dvmCompilerWorkEnqueue(
+                       interpState->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);
+                }
+                /*
+                 * Reset "trace in progress" flag whether or not we
+                 * successfully entered a work order.
+                 */
+                JitEntry *jitEntry =
+                    lookupAndAdd(interpState->currTraceHead, false);
+                if (jitEntry) {
+                    setTraceConstruction(jitEntry, false);
+                }
+                interpState->jitState = kJitDone;
+                switchInterp = true;
+            }
+            break;
+        case kJitSingleStep:
+            interpState->jitState = kJitSingleStepEnd;
+            break;
+        case kJitSingleStepEnd:
+            /*
+             * Clear the inJitCodeCache flag and abandon the resume attempt if
+             * we cannot switch back to the translation due to corner-case
+             * conditions. If the flag is not cleared and the code cache is full
+             * we will be stuck in the debug interpreter as the code cache
+             * cannot be reset.
+             */
+            if (dvmJitStayInPortableInterpreter()) {
+                interpState->entryPoint = kInterpEntryInstr;
+                self->inJitCodeCache = 0;
+            } else {
+                interpState->entryPoint = kInterpEntryResume;
+            }
+            interpState->jitState = kJitDone;
+            switchInterp = true;
+            break;
+        case kJitDone:
+            switchInterp = true;
+            break;
+#if defined(WITH_SELF_VERIFICATION)
+        case kJitSelfVerification:
+            if (selfVerificationDebugInterp(pc, self, interpState)) {
+                /*
+                 * If the next state is not single-step end, we can switch
+                 * interpreter now.
+                 */
+                if (interpState->jitState != kJitSingleStepEnd) {
+                    interpState->jitState = kJitDone;
+                    switchInterp = true;
+                }
+            }
+            break;
+#endif
+        case kJitNot:
+            switchInterp = !debugOrProfile;
+            break;
+        default:
+            LOGE("Unexpected JIT state: %d entry point: %d",
+                 interpState->jitState, interpState->entryPoint);
+            dvmAbort();
+            break;
+    }
+    /*
+     * Final check to see if we can really switch the interpreter. Make sure
+     * the jitState is kJitDone or kJitNot when switchInterp is set to true.
+     */
+     assert(switchInterp == false || interpState->jitState == kJitDone ||
+            interpState->jitState == kJitNot);
+     return switchInterp && !debugOrProfile && !stayOneMoreInst &&
+            !dvmJitStayInPortableInterpreter();
+}
+
+JitEntry *dvmFindJitEntry(const u2* pc)
+{
+    int idx = dvmJitHash(pc);
+
+    /* Expect a high hit rate on 1st shot */
+    if (gDvmJit.pJitEntryTable[idx].dPC == pc)
+        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)
+                return &gDvmJit.pJitEntryTable[idx];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * If a translated code address exists for the davik byte code
+ * pointer return it.  This routine needs to be fast.
+ */
+void* dvmJitGetCodeAddr(const u2* dPC)
+{
+    int idx = dvmJitHash(dPC);
+    const u2* npc = gDvmJit.pJitEntryTable[idx].dPC;
+    if (npc != NULL) {
+        bool hideTranslation = dvmJitHideTranslation();
+
+        if (npc == dPC) {
+#if defined(WITH_JIT_TUNING)
+            gDvmJit.addrLookupsFound++;
+#endif
+            return hideTranslation ?
+                NULL : gDvmJit.pJitEntryTable[idx].codeAddress;
+        } 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) {
+#if defined(WITH_JIT_TUNING)
+                    gDvmJit.addrLookupsFound++;
+#endif
+                    return hideTranslation ?
+                        NULL : gDvmJit.pJitEntryTable[idx].codeAddress;
+                }
+            }
+        }
+    }
+#if defined(WITH_JIT_TUNING)
+    gDvmJit.addrLookupsNotFound++;
+#endif
+    return NULL;
+}
+
+/*
+ * 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.  This routine should only be called by the compiler
+ * thread.
+ */
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set) {
+    JitEntryInfoUnion oldValue;
+    JitEntryInfoUnion newValue;
+    JitEntry *jitEntry = lookupAndAdd(dPC, false);
+    assert(jitEntry);
+    /* Note: order of update is important */
+    do {
+        oldValue = jitEntry->u;
+        newValue = oldValue;
+        newValue.info.instructionSet = set;
+    } while (android_atomic_release_cas(
+             oldValue.infoWord, newValue.infoWord,
+             &jitEntry->u.infoWord) != 0);
+    jitEntry->codeAddress = nPC;
+}
+
+/*
+ * Determine if valid trace-bulding request is active.  Return true
+ * if we need to abort and switch back to the fast interpreter, false
+ * otherwise.
+ */
+bool dvmJitCheckTraceRequest(Thread* self, InterpState* interpState)
+{
+    bool switchInterp = false;         /* Assume success */
+    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)interpState->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)interpState->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)interpState->method <<
+                   (JIT_TRACE_THRESH_FILTER_PC_BITS - 2);
+    u4 pcKey = ((u4)interpState->pc >> 1) &
+               ((1 << JIT_TRACE_THRESH_FILTER_PC_BITS) - 1);
+    intptr_t filterKey = (intptr_t)(methodKey | pcKey);
+    bool debugOrProfile = dvmDebuggerOrProfilerActive();
+
+    /* Check if the JIT request can be handled now */
+    if (gDvmJit.pJitEntryTable != NULL && debugOrProfile == false) {
+        /* Bypass the filter for hot trace requests or during stress mode */
+        if (interpState->jitState == kJitTSelectRequest &&
+            gDvmJit.threshold > 6) {
+            /* Two-level filtering scheme */
+            for (i=0; i< JIT_TRACE_THRESH_FILTER_SIZE; i++) {
+                if (filterKey == interpState->threshFilter[i]) {
+                    interpState->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;
+                interpState->threshFilter[i] = filterKey;
+                interpState->jitState = kJitDone;
+            }
+        }
+
+        /* If the compiler is backlogged, cancel any JIT actions */
+        if (gDvmJit.compilerQueueLength >= gDvmJit.compilerHighWater) {
+            interpState->jitState = kJitDone;
+        }
+
+        /*
+         * Check for additional reasons that might force the trace select
+         * request to be dropped
+         */
+        if (interpState->jitState == kJitTSelectRequest ||
+            interpState->jitState == kJitTSelectRequestHot) {
+            JitEntry *slot = lookupAndAdd(interpState->pc, false);
+            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.
+                 */
+                interpState->jitState = kJitDone;
+                LOGD("JIT: JitTable full, disabling profiling");
+                dvmJitStopTranslationRequests();
+            } else if (slot->u.info.traceConstruction) {
+                /*
+                 * Trace request already in progress, but most likely it
+                 * aborted without cleaning up.  Assume the worst and
+                 * mark trace head as untranslatable.  If we're wrong,
+                 * the compiler thread will correct the entry when the
+                 * translation is completed.  The downside here is that
+                 * some existing translation may chain to the interpret-only
+                 * template instead of the real translation during this
+                 * window.  Performance, but not correctness, issue.
+                 */
+                interpState->jitState = kJitDone;
+                resetTracehead(interpState, slot);
+            } else if (slot->codeAddress) {
+                 /* Nothing to do here - just return */
+                interpState->jitState = kJitDone;
+            } else {
+                /*
+                 * Mark request.  Note, we are not guaranteed exclusivity
+                 * here.  A window exists for another thread to be
+                 * attempting to build this same trace.  Rather than
+                 * bear the cost of locking, we'll just allow that to
+                 * happen.  The compiler thread, if it chooses, can
+                 * discard redundant requests.
+                 */
+                setTraceConstruction(slot, true);
+            }
+        }
+
+        switch (interpState->jitState) {
+            case kJitTSelectRequest:
+            case kJitTSelectRequestHot:
+                interpState->jitState = kJitTSelect;
+                interpState->currTraceHead = interpState->pc;
+                interpState->currTraceRun = 0;
+                interpState->totalTraceLen = 0;
+                interpState->currRunHead = interpState->pc;
+                interpState->currRunLen = 0;
+                interpState->trace[0].frag.startOffset =
+                     interpState->pc - interpState->method->insns;
+                interpState->trace[0].frag.numInsts = 0;
+                interpState->trace[0].frag.runEnd = false;
+                interpState->trace[0].frag.hint = kJitHintNone;
+                interpState->trace[0].frag.isCode = true;
+                interpState->lastPC = 0;
+                break;
+            /*
+             * For JIT's perspective there is no need to stay in the debug
+             * interpreter unless debugger/profiler is attached.
+             */
+            case kJitDone:
+                switchInterp = true;
+                break;
+            default:
+                LOGE("Unexpected JIT state: %d entry point: %d",
+                     interpState->jitState, interpState->entryPoint);
+                dvmAbort();
+        }
+    } else {
+        /*
+         * Cannot build trace this time - ready to leave the dbg interpreter
+         */
+        interpState->jitState = kJitDone;
+        switchInterp = true;
+    }
+
+    /*
+     * Final check to see if we can really switch the interpreter. Make sure
+     * the jitState is kJitDone when switchInterp is set to true.
+     */
+    assert(switchInterp == false || interpState->jitState == kJitDone);
+    return switchInterp && !debugOrProfile &&
+           !dvmJitStayInPortableInterpreter();
+}
+
+/*
+ * 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;
+    u4 newMask;
+    unsigned int oldSize;
+    unsigned int i;
+
+    assert(gDvmJit.pJitEntryTable != NULL);
+    assert(size && !(size & (size - 1)));   /* Is power of 2? */
+
+    LOGI("Jit: resizing JitTable from %d to %d", gDvmJit.jitTableSize, size);
+
+    newMask = size - 1;
+
+    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) {
+        LOGD("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*/ );
+            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(void)
+{
+    JitEntry *jitEntry = gDvmJit.pJitEntryTable;
+    unsigned int size = gDvmJit.jitTableSize;
+    unsigned int i;
+
+    dvmLockMutex(&gDvmJit.tableLock);
+    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);
+}
+
+/*
+ * 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;
+}
+
+#endif /* WITH_JIT */
diff --git a/vm/interp/Jit.h b/vm/interp/Jit.h
new file mode 100644
index 0000000..6101f54
--- /dev/null
+++ b/vm/interp/Jit.h
@@ -0,0 +1,126 @@
+/*
+ * 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
+#define _DALVIK_INTERP_JIT
+
+#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 */
+
+typedef struct ShadowHeap {
+    int addr;
+    int data;
+} ShadowHeap;
+
+typedef struct InstructionTrace {
+    int addr;
+    DecodedInstruction decInsn;
+} InstructionTrace;
+
+typedef struct ShadowSpace {
+    const u2* startPC;          /* starting pc of jitted region */
+    const void* fp;             /* starting fp of jitted region */
+    void* glue;                 /* starting glue of jitted region */
+    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 */
+    InterpState interpState;    /* copy of interpState */
+    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 */
+    const Method* method;       /* starting method of jitted region */
+} ShadowSpace;
+
+/*
+ * Self verification functions.
+ */
+void* dvmSelfVerificationShadowSpaceAlloc(Thread* self);
+void dvmSelfVerificationShadowSpaceFree(Thread* self);
+void* dvmSelfVerificationSaveState(const u2* pc, const void* fp,
+                                   InterpState* interpState,
+                                   int targetTrace);
+void* dvmSelfVerificationRestoreState(const u2* pc, const void* fp,
+                                      SelfVerificationState exitPoint);
+#endif
+
+/*
+ * 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 );
+}
+
+/*
+ * 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.
+ */
+
+typedef struct JitEntryInfo {
+    unsigned int           traceConstruction:1;   /* build underway? */
+    unsigned int           isMethodEntry:1;
+    unsigned int           inlineCandidate:1;
+    unsigned int           profileEnabled:1;
+    JitInstructionSetType  instructionSet:4;
+    unsigned int           unused:8;
+    u2                     chain;                 /* Index of next in chain */
+} JitEntryInfo;
+
+typedef union JitEntryInfoUnion {
+    JitEntryInfo info;
+    volatile int infoWord;
+} JitEntryInfoUnion;
+
+typedef struct JitEntry {
+    JitEntryInfoUnion   u;
+    const u2*           dPC;            /* Dalvik code address */
+    void*               codeAddress;    /* Code address of native translation */
+} JitEntry;
+
+int dvmCheckJit(const u2* pc, Thread* self, InterpState* interpState,
+                const ClassObject *callsiteClass, const Method* curMethod);
+void* dvmJitGetCodeAddr(const u2* dPC);
+bool dvmJitCheckTraceRequest(Thread* self, InterpState* interpState);
+void dvmJitStopTranslationRequests(void);
+void dvmJitStats(void);
+bool dvmJitResizeJitTable(unsigned int size);
+void dvmJitResetTable(void);
+struct JitEntry *dvmFindJitEntry(const u2* pc);
+s8 dvmJitd2l(double d);
+s8 dvmJitf2l(float f);
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set);
+void dvmJitAbortTraceSelect(InterpState* interpState);
+
+#endif /*_DALVIK_INTERP_JIT*/
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.c b/vm/interp/Stack.c
new file mode 100644
index 0000000..695aa44
--- /dev/null
+++ b/vm/interp/Stack.c
@@ -0,0 +1,1381 @@
+/*
+ * 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>
+
+/*
+ * 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->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->curFrame != NULL)
+        stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame);
+    else
+        stackPtr = self->interpStackStart;
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space */
+        LOGW("Stack overflow on call to interp "
+             "(req=%d top=%p cur=%p size=%d %s.%s)\n",
+            stackReq, self->interpStackStart, self->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 = FP_FROM_SAVEAREA(self->curFrame);
+    saveBlock->prevSave = breakSaveBlock;
+#endif
+
+    breakSaveBlock->prevFrame = self->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)\n",
+        self->curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->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->curFrame != NULL)
+        stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame);
+    else
+        stackPtr = self->interpStackStart;
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space */
+        LOGW("Stack overflow on call to native "
+             "(req=%d top=%p cur=%p size=%d '%s')\n",
+            stackReq, self->interpStackStart, self->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->curFrame == NULL)
+        breakSaveBlock->prevSave = NULL;
+    else
+        breakSaveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame);
+    saveBlock->prevSave = breakSaveBlock;
+#endif
+
+    breakSaveBlock->prevFrame = self->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
+#ifdef USE_INDIRECT_REF
+    saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+    saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+    saveBlock->method = method;
+
+    LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)\n",
+        self->curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->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->curFrame != NULL);
+    stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame);
+
+    if (stackPtr - stackReq < self->interpStackEnd) {
+        /* not enough space; let JNI throw the exception */
+        LOGW("Stack overflow on PushLocal "
+             "(req=%d top=%p cur=%p size=%d '%s')\n",
+            stackReq, self->interpStackStart, self->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 = FP_FROM_SAVEAREA(self->curFrame);
+#endif
+
+    saveBlock->prevFrame = self->curFrame;
+    saveBlock->savedPc = NULL;                  // not required
+#ifdef USE_INDIRECT_REF
+    saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+    saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+    saveBlock->method = method;
+
+    LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)\n",
+        self->curFrame, FP_FROM_SAVEAREA(saveBlock),
+        (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+    self->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->curFrame);
+
+    assert(!dvmIsBreakFrame(self->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(saveBlock->prevFrame) ||
+               !dvmIsNativeMethod(
+                       SAVEAREA_FROM_FP(saveBlock->prevFrame)->method));
+        return false;
+    }
+
+    LOGVV("POP JNI local frame: removing %s, now %s\n",
+        saveBlock->method->name,
+        SAVEAREA_FROM_FP(saveBlock->prevFrame)->method->name);
+    dvmPopJniLocals(self, saveBlock);
+    self->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->curFrame == NULL)
+        return false;
+
+    saveBlock = SAVEAREA_FROM_FP(self->curFrame);
+    assert(!dvmIsBreakFrame(self->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\n",
+                saveBlock->method->clazz->descriptor,
+                saveBlock->method->name,
+                (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ?
+                "" : " (JNI local)");
+            assert(saveBlock->xtra.localRefCookie != 0);
+            //assert(saveBlock->xtra.localRefCookie >= self->jniLocalRefTable.table &&
+            //    saveBlock->xtra.localRefCookie <=self->jniLocalRefTable.nextEntry);
+
+            dvmPopJniLocals(self, saveBlock);
+        }
+
+        saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame);
+    }
+    if (saveBlock->method != NULL) {
+        LOGE("PopFrame missed the break\n");
+        assert(false);
+        dvmAbort();     // stack trashed -- nowhere to go in this thread
+    }
+
+    LOGVV("POP frame: cur=%p new=%p\n",
+        self->curFrame, saveBlock->prevFrame);
+
+    self->curFrame = saveBlock->prevFrame;
+    return true;
+}
+
+/*
+ * Common code for dvmCallMethodV/A and dvmInvokeMethod.
+ *
+ * Pushes a call frame on, advancing self->curFrame.
+ */
+static ClassObject* callPrep(Thread* self, const Method* method, Object* obj,
+    bool checkAccess)
+{
+    ClassObject* clazz;
+
+#ifndef NDEBUG
+    if (self->status != THREAD_RUNNING) {
+        LOGW("threadid=%d: status=%d on call to %s.%s -\n",
+            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\n", self->threadId,
+            clazz->descriptor, method->name, desc);
+        free(desc);
+    }
+
+    if (checkAccess) {
+        /* needed for java.lang.reflect.Method.invoke */
+        if (!dvmCheckMethodAccess(dvmGetCaller2Class(self->curFrame),
+                method))
+        {
+            /* note this throws IAException, not IAError */
+            dvmThrowException("Ljava/lang/IllegalAccessException;",
+                "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->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->curFrame) + (method->registersSize - method->insSize);
+
+    //LOGD("  FP is %p, INs live at >= %p\n", self->curFrame, ins);
+
+    /* put "this" pointer into in0 if appropriate */
+    if (!dvmIsStaticMethod(method)) {
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+        assert(obj != NULL && dvmIsValidObject(obj));
+#endif
+        *ins++ = (u4) obj;
+        verifyCount++;
+    }
+
+    JNIEnv* env = self->jniEnv;
+    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* argObj = va_arg(args, void*);
+                assert(obj == NULL || dvmIsValidObject(obj));
+                if (fromJni)
+                    *ins++ = (u4) dvmDecodeIndirectRef(env, 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) {
+        LOGE("Got vfycount=%d insSize=%d for %s.%s\n", 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)(self->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->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++;
+    }
+
+    JNIEnv* env = self->jniEnv;
+    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(env, 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:
+            LOGE("Invalid char %c in short signature of %s.%s\n",
+                *(desc-1), clazz->descriptor, method->name);
+            assert(false);
+            goto bail;
+        }
+
+        verifyCount++;
+        args++;
+    }
+
+#ifndef NDEBUG
+    if (verifyCount != method->insSize) {
+        LOGE("Got vfycount=%d insSize=%d for %s.%s\n", 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)(self->curFrame, pResult, method, self);
+        TRACE_METHOD_EXIT(self, method);
+    } else {
+        dvmInterpret(self, method, pResult);
+    }
+
+bail:
+    dvmPopFrame(self);
+}
+
+/*
+ * 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) {
+        LOGI("invoke: expected %d args, received %d args\n",
+            params->length, argListLength);
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "wrong number of arguments");
+        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->curFrame) + (method->registersSize - method->insSize);
+    verifyCount = 0;
+
+    //LOGD("  FP is %p, INs live at >= %p\n", self->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;
+    ClassObject** types;
+    int i;
+
+    args = (DataObject**) argList->contents;
+    types = (ClassObject**) params->contents;
+    for (i = 0; i < argListLength; i++) {
+        int width;
+
+        width = dvmConvertArgument(*args++, *types++, ins);
+        if (width < 0) {
+            if (*(args-1) != NULL) {
+                LOGV("invoke: type mismatch on arg %d ('%s' '%s')\n",
+                    i, (*(args-1))->obj.clazz->descriptor,
+                    (*(types-1))->descriptor);
+            }
+            dvmPopFrame(self);      // throw wants to pull PC out of stack
+            needPop = false;
+            dvmThrowException("Ljava/lang/IllegalArgumentException;",
+                "argument type mismatch");
+            goto bail;
+        }
+
+        ins += width;
+        verifyCount += width;
+    }
+
+    if (verifyCount != method->insSize) {
+        LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount,
+            method->insSize, clazz->descriptor, method->name);
+        assert(false);
+        goto bail;
+    }
+    //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)(self->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*)dvmWrapPrimitive(retval, returnType);
+            dvmReleaseTrackedAlloc(retObj, NULL);
+        }
+    }
+
+bail:
+    if (needPop) {
+        dvmPopFrame(self);
+    }
+    return retObj;
+}
+
+typedef struct LineNumFromPcContext {
+    u4 address;
+    u4 lineNum;
+} LineNumFromPcContext;
+
+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(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(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(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(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;
+}
+
+/*
+ * Create a flat array of methods that comprise the current interpreter
+ * stack trace.  Pass in the current frame ptr.
+ *
+ * Allocates a new array and fills it with method pointers.  Break frames
+ * are skipped, but reflection invocations are not.  The caller must free
+ * "*pArray".
+ *
+ * The current frame will be in element 0.
+ *
+ * Returns "true" on success, "false" on failure (e.g. malloc failed).
+ */
+bool dvmCreateStackTraceArray(const void* fp, const Method*** pArray,
+    int* pLength)
+{
+    const Method** array;
+    int idx, depth;
+
+    depth = dvmComputeExactFrameDepth(fp);
+    array = (const Method**) malloc(depth * sizeof(Method*));
+    if (array == NULL)
+        return false;
+
+    for (idx = 0; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
+        if (!dvmIsBreakFrame(fp))
+            array[idx++] = SAVEAREA_FROM_FP(fp)->method;
+    }
+    assert(idx == depth);
+
+    *pArray = array;
+    *pLength = depth;
+    return true;
+}
+
+/*
+ * 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.
+         */
+        LOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting\n",
+            self->threadId);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    /* open it up to the full range */
+    LOGI("threadid=%d: stack overflow on call to %s.%s:%s\n",
+        self->threadId,
+        method->clazz->descriptor, method->name, method->shorty);
+    StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->curFrame);
+    LOGI("  method requires %d+%d+%d=%d bytes, fp is %p (%d left)\n",
+        method->registersSize * 4, sizeof(StackSaveArea), method->outsSize * 4,
+        (method->registersSize + method->outsSize) * 4 + sizeof(StackSaveArea),
+        saveArea, (u1*) saveArea - self->interpStackEnd);
+    LOGI("  expanding stack end (%p to %p)\n", 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) {
+        LOGW("Stack overflow while throwing exception\n");
+        dvmClearException(self);
+    }
+    dvmThrowChainedExceptionByClass(gDvm.classJavaLangStackOverflowError,
+        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.classJavaLangStackOverflowError) {
+        /* exception caused during SOE, not the SOE itself */
+        return;
+    }
+
+    newStackEnd = (self->interpStackStart - self->interpStackSize)
+        + STACK_OVERFLOW_RESERVE;
+    if ((u1*)self->curFrame <= newStackEnd) {
+        LOGE("Can't shrink stack: curFrame is in reserved area (%p %p)\n",
+            self->interpStackEnd, self->curFrame);
+        dvmDumpThread(self, false);
+        dvmAbort();
+    }
+
+    self->interpStackEnd = newStackEnd;
+    self->stackOverflowed = false;
+
+    LOGI("Shrank stack (to %p, curFrame is %p)\n", self->interpStackEnd,
+        self->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.
+ *
+ * We assume the thread list lock is currently 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->curFrame;
+
+    if (framePtr == NULL || dvmIsBreakFrame(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))) {
+        LOGD("ExtrMon: method %p not valid\n", method);
+        return false;
+    }
+
+    /* check currentPc */
+    u4 insnsSize = dvmGetMethodInsnsSize(method);
+    if (currentPc < method->insns ||
+        currentPc >= method->insns + insnsSize)
+    {
+        LOGD("ExtrMon: insns %p not valid (%p - %p)\n",
+            currentPc, method->insns, method->insns + insnsSize);
+        return false;
+    }
+
+    /* check the instruction */
+    if ((*currentPc & 0xff) != OP_MONITOR_ENTER) {
+        LOGD("ExtrMon: insn at %p is not monitor-enter (0x%02x)\n",
+            currentPc, *currentPc & 0xff);
+        return false;
+    }
+
+    /* get and check the register index */
+    unsigned int reg = *currentPc >> 8;
+    if (reg >= method->registersSize) {
+        LOGD("ExtrMon: invalid register %d (max %d)\n",
+            reg, method->registersSize);
+        return false;
+    }
+
+    /* get and check the object in that register */
+    u4* fp = (u4*) framePtr;
+    Object* obj = (Object*) fp[reg];
+    if (!dvmIsValidObject(obj)) {
+        LOGD("ExtrMon: invalid object %p at %p[%d]\n", 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;
+}
+
+/*
+ * 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;
+
+    /*
+     * The "currentPc" is updated whenever we execute an instruction that
+     * might throw an exception.  Show it here.
+     */
+    if (framePtr != NULL && !dvmIsBreakFrame(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(framePtr)) {
+            //dvmPrintDebugMessage(target, "  (break frame)\n");
+        } else {
+            int relPc;
+
+            if (currentPc != NULL)
+                relPc = currentPc - saveArea->method->insns;
+            else
+                relPc = -1;
+
+            char* className = dvmDescriptorToDot(method->clazz->descriptor);
+            if (dvmIsNativeMethod(method))
+                dvmPrintDebugMessage(target,
+                    "  at %s.%s(Native Method)\n", className, method->name);
+            else {
+                dvmPrintDebugMessage(target,
+                    "  at %s.%s(%s:%s%d)\n",
+                    className, method->name, dvmGetMethodSourceFile(method),
+                    (relPc >= 0 && first) ? "~" : "",
+                    relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc));
+            }
+            free(className);
+
+            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) {
+                        className = dvmDescriptorToDot(obj->clazz->descriptor);
+                        dvmPrintDebugMessage(target,
+                            "  - waiting on <%p> (a %s)\n", obj, className);
+                        free(className);
+                    }
+                } else if (thread->status == THREAD_MONITOR) {
+                    Object* obj;
+                    Thread* owner;
+                    if (extractMonitorEnterObject(thread, &obj, &owner)) {
+                        className = dvmDescriptorToDot(obj->clazz->descriptor);
+                        if (owner != NULL) {
+                            char* threadName = dvmGetThreadName(owner);
+                            dvmPrintDebugMessage(target,
+                                "  - waiting to lock <%p> (a %s) held by threadid=%d (%s)\n",
+                                obj, className, owner->threadId, threadName);
+                            free(threadName);
+                        } else {
+                            dvmPrintDebugMessage(target,
+                                "  - waiting to lock <%p> (a %s) held by ???\n",
+                                obj, className);
+                        }
+                        free(className);
+                    }
+                }
+            }
+        }
+
+        /*
+         * 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) {
+            LOGW("Warning: loop in stack trace at frame %d (%p -> %p)\n",
+                checkCount, framePtr, saveArea->prevFrame);
+            break;
+        }
+        framePtr = saveArea->prevFrame;
+
+        checkCount++;
+        if (checkCount > 300) {
+            dvmPrintDebugMessage(target,
+                "  ***** printed %d frames, not showing any more\n",
+                checkCount);
+            break;
+        }
+    }
+    dvmPrintDebugMessage(target, "\n");
+}
+
+
+/*
+ * Dump the stack for the specified thread.
+ */
+void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread)
+{
+    dumpFrames(target, thread->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->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->curFrame - origStack;
+    memcpy(stackCopy, origStack, origSize);
+
+    /*
+     * Run through the stack and rewrite the "prev" pointers.
+     */
+    //LOGI("DR: fpOff=%d (from %p %p)\n",fpOffset, origStack, thread->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 = stackCopy + prevOffset;
+        fp = saveArea->prevFrame;
+    }
+
+    /*
+     * We still need to pass the Thread for some monitor wait stuff.
+     */
+    dumpFrames(target, stackCopy + fpOffset, thread);
+    free(stackCopy);
+}
diff --git a/vm/interp/Stack.h b/vm/interp/Stack.h
new file mode 100644
index 0000000..3f76cb1
--- /dev/null
+++ b/vm/interp/Stack.h
@@ -0,0 +1,286 @@
+/*
+ * 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
+#define _DALVIK_INTERP_STACK
+
+#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;
+typedef struct StackSaveArea 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 */
+    void*       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 */
+#ifdef USE_INDIRECT_REF
+        u4          localRefCookie;
+#else
+        Object**    localRefCookie;
+#endif
+
+        /* 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) ((void*) ((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.
+ */
+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);
+
+/*
+ * Allocate and fill an array of method pointers representing the current
+ * stack trace (element 0 is current frame).
+ */
+bool dvmCreateStackTraceArray(const void* fp, const Method*** pArray,
+    int* pLength);
+
+/*
+ * Common handling for stack overflow.
+ */
+void dvmHandleStackOverflow(Thread* self, const Method* method);
+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);
+
+#endif /*_DALVIK_INTERP_STACK*/
diff --git a/vm/jdwp/ExpandBuf.c b/vm/jdwp/ExpandBuf.c
new file mode 100644
index 0000000..ade239c
--- /dev/null
+++ b/vm/jdwp/ExpandBuf.c
@@ -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(void)
+{
+    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 = realloc(pBuf->storage, pBuf->maxLen);
+    if (newPtr == NULL) {
+        LOGE("realloc(%d) failed\n", 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..8bdc8a7
--- /dev/null
+++ b/vm/jdwp/ExpandBuf.h
@@ -0,0 +1,57 @@
+/*
+ * 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
+#define _DALVIK_JDWP_EXPANDBUF
+
+#include "Common.h"     // need u1/u2/u4/u8 types
+
+struct ExpandBuf;   /* private */
+typedef struct ExpandBuf ExpandBuf;
+
+/* 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*/
diff --git a/vm/jdwp/Jdwp.h b/vm/jdwp/Jdwp.h
new file mode 100644
index 0000000..7313579
--- /dev/null
+++ b/vm/jdwp/Jdwp.h
@@ -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.
+ */
+/*
+ * 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
+#define _DALVIK_JDWP_JDWP
+
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/ExpandBuf.h"
+#include "Common.h"
+#include "Bits.h"
+#include <pthread.h>
+
+struct JdwpState;       /* opaque */
+typedef struct JdwpState JdwpState;
+
+/*
+ * 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".
+ */
+typedef 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 */
+} JdwpLocation;
+//#define kJDWPLocationSize   (25)
+
+/*
+ * How we talk to the debugger.
+ */
+typedef enum JdwpTransportType {
+    kJdwpTransportUnknown = 0,
+    kJdwpTransportSocket,       /* transport=dt_socket */
+    kJdwpTransportAndroidAdb,   /* transport=dt_android_adb */
+} JdwpTransportType;
+
+/*
+ * Holds collection of JDWP initialization parameters.
+ */
+typedef struct JdwpStartupParams {
+    JdwpTransportType transport;
+    bool        server;
+    bool        suspend;
+    char        host[64];
+    short       port;
+    /* more will be here someday */
+} JdwpStartupParams;
+
+/*
+ * 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*/
diff --git a/vm/jdwp/JdwpAdb.c b/vm/jdwp/JdwpAdb.c
new file mode 100644
index 0000000..c3a1a72
--- /dev/null
+++ b/vm/jdwp/JdwpAdb.c
@@ -0,0 +1,761 @@
+/*
+ * 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>
+
+/* the JDWP <-> ADB transport protocol is explained in details
+ * in //device/tools/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 {
+    int                 controlSock;
+    int                 clientSock;
+    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;
+};
+
+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;
+    }
+
+    free(netState);
+}
+
+
+static JdwpNetState*
+adbStateAlloc(void)
+{
+    JdwpNetState*   netState = calloc(sizeof(*netState),1);
+
+    netState->controlSock = -1;
+    netState->clientSock  = -1;
+
+    netState->controlAddr.controlAddrUn.sun_family = AF_UNIX;
+    netState->controlAddrLen =
+            sizeof(netState->controlAddr.controlAddrUn.sun_family) +
+            kJdwpControlNameLen;
+
+    memcpy(netState->controlAddr.controlAddrUn.sun_path,
+           kJdwpControlName, kJdwpControlNameLen);
+
+    netState->wakeFds[0] = -1;
+    netState->wakeFds[1] = -1;
+
+    return 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;
+
+    LOGV("ADB transport startup\n");
+
+    state->netState = netState = adbStateAlloc();
+    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*)CMSG_DATA(cmsg))[0] = -1;
+
+    do {
+        ret = recvmsg(netState->controlSock, &msg, 0);
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret <= 0) {
+        if (ret < 0) {
+            LOGW("receiving file descriptor from ADB failed (socket %d): %s\n",
+                 netState->controlSock, strerror(errno));
+        } else {
+            LOGD("adbd disconnected\n");
+        }
+        close(netState->controlSock);
+        netState->controlSock = -1;
+        return -1;
+    }
+
+    return ((int*)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) {
+            LOGE("Could not create ADB control socket:%s\n",
+                 strerror(errno));
+            return false;
+        }
+
+        if (pipe(netState->wakeFds) < 0) {
+            LOGE("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) {
+                /* 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) {
+                    LOGV("PID sent as '%.*s' to ADB\n", 4, buff);
+                    break;
+                }
+
+                LOGE("Weird, can't send JDWP process pid to ADB: %s\n",
+                     strerror(errno));
+                return false;
+            }
+            LOGV("Can't connect to ADB control socket:%s\n",
+                 strerror(errno));
+
+            usleep( sleep_ms*1000 );
+
+            sleep_ms += (sleep_ms >> 1);
+            if (sleep_ms > sleep_max_ms)
+                sleep_ms = sleep_max_ms;
+        }
+    }
+
+    LOGV("trying to receive file descriptor from ADB\n");
+    /* 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) {
+            LOGE("adb connection max retries exceeded\n");
+            return false;
+        }
+        goto retry;
+    } else {
+        LOGV("received file descriptor %d from ADB\n", 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;
+
+    LOGV("+++ closed JDWP <-> ADB connection\n");
+
+    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) {
+        LOGV("+++ writing to wakePipe\n");
+        (void) 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) {
+            int cc;
+
+            /*
+             * TODO: we currently assume the write() will complete in one
+             * go, which may not be safe for a network socket.  We may need
+             * to mutex this against sendRequest().
+             */
+            cc = write(netState->clientSock, expandBufGetBuffer(pReply),
+                    expandBufGetLength(pReply));
+            if (cc != (int) expandBufGetLength(pReply)) {
+                LOGE("Failed sending reply to debugger: %s\n", strerror(errno));
+                expandBufFree(pReply);
+                return false;
+            }
+        } else {
+            LOGW("No reply created for set=%d cmd=%d\n", cmdSet, cmd);
+        }
+        expandBufFree(pReply);
+    } else {
+        LOGV("reply?!\n");
+        assert(false);
+    }
+
+    LOGV("----------\n");
+
+    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 {
+                LOGI("NOTE: entering select w/o wakepipe\n");
+            }
+
+            if (maxfd < 0) {
+                LOGV("+++ all fds are closed\n");
+                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;
+                LOGE("select failed: %s\n", strerror(errno));
+                goto fail;
+            }
+
+            if (netState->wakeFds[0] >= 0 &&
+                FD_ISSET(netState->wakeFds[0], &readfds))
+            {
+                LOGD("Got wake-up signal, bailing out of select\n");
+                goto fail;
+            }
+            if (netState->controlSock >= 0 &&
+                FD_ISSET(netState->controlSock, &readfds))
+            {
+                int  sock = receiveClientFd(netState);
+                if (sock >= 0) {
+                    LOGI("Ignoring second debugger -- accepting and dropping\n");
+                    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;
+                    LOGD("+++ EINTR hit\n");
+                    return true;
+                } else if (readCount == 0) {
+                    /* EOF hit -- far end went away */
+                    LOGV("+++ peer disconnected\n");
+                    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)
+        {
+            LOGE("ERROR: bad handshake '%.14s'\n", netState->inputBuffer);
+            goto fail;
+        }
+
+        errno = 0;
+        cc = write(netState->clientSock, netState->inputBuffer,
+                kMagicHandshakeLen);
+        if (cc != kMagicHandshakeLen) {
+            LOGE("Failed writing handshake bytes: %s (%d of %d)\n",
+                strerror(errno), cc, (int) kMagicHandshakeLen);
+            goto fail;
+        }
+
+        consumeBytes(netState, kMagicHandshakeLen);
+        netState->awaitingHandshake = false;
+        LOGV("+++ handshake complete\n");
+        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;
+    int cc;
+
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        LOGV("NOT sending request -- no debugger is attached\n");
+        return false;
+    }
+
+    /*
+     * TODO: we currently assume the write() will complete in one
+     * go, which may not be safe for a network socket.  We may need
+     * to mutex this against handlePacket().
+     */
+    errno = 0;
+    cc = write(netState->clientSock, expandBufGetBuffer(pReq),
+            expandBufGetLength(pReq));
+    if (cc != (int) expandBufGetLength(pReq)) {
+        LOGE("Failed sending req to debugger: %s (%d of %d)\n",
+            strerror(errno), 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 */
+        LOGV("NOT sending request -- no debugger is attached\n");
+        return false;
+    }
+
+    size_t expected = 0;
+    int i;
+    for (i = 0; i < iovcnt; i++)
+        expected += iov[i].iov_len;
+
+    /*
+     * TODO: we currently assume the writev() will complete in one
+     * go, which may not be safe for a network socket.  We may need
+     * to mutex this against handlePacket().
+     */
+    ssize_t actual;
+    actual = writev(netState->clientSock, iov, iovcnt);
+    if ((size_t)actual != expected) {
+        LOGE("Failed sending b-req to debugger: %s (%d of %zu)\n",
+            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(void)
+{
+    return &socketTransport;
+}
diff --git a/vm/jdwp/JdwpConstants.c b/vm/jdwp/JdwpConstants.c
new file mode 100644
index 0000000..898fe2c
--- /dev/null
+++ b/vm/jdwp/JdwpConstants.c
@@ -0,0 +1,239 @@
+/*
+ * 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(enum 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(enum 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 StepDepth.
+ */
+const char* dvmJdwpStepDepthStr(enum 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(enum 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(enum 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(enum JdwpSuspendStatus status)
+{
+    switch (status) {
+    case 0:                         return "Not SUSPENDED";
+    case SUSPEND_STATUS_SUSPENDED:  return "SUSPENDED";
+    default:                        return "?UNKNOWN?";
+    }
+}
+
+/*
+ * Return a string for the ThreadStatus.
+ */
+const char* dvmJdwpThreadStatusStr(enum 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..922dbcd
--- /dev/null
+++ b/vm/jdwp/JdwpConstants.h
@@ -0,0 +1,229 @@
+/*
+ * 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
+#define _DALVIK_JDWP_JDWPCONSTANTS
+
+/*
+ * 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,
+};
+typedef enum JdwpError JdwpError;
+const char* dvmJdwpErrorStr(enum 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(enum 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,
+};
+
+/*
+ * 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(enum 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(enum 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(enum JdwpSuspendPolicy policy);
+
+/*
+ * SuspendStatus constants.
+ */
+enum JdwpSuspendStatus {
+    SUSPEND_STATUS_SUSPENDED = 1,
+};
+const char* dvmJdwpSuspendStatusStr(enum 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(enum 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*/
diff --git a/vm/jdwp/JdwpEvent.c b/vm/jdwp/JdwpEvent.c
new file mode 100644
index 0000000..69d2237
--- /dev/null
+++ b/vm/jdwp/JdwpEvent.c
@@ -0,0 +1,1292 @@
+/*
+ * 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.
+ */
+typedef 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 */
+} ModBasket;
+
+/*
+ * Get the next "request" serial number.  We use this when sending
+ * packets to the debugger.
+ */
+u4 dvmJdwpNextRequestSerial(JdwpState* state)
+{
+    u4 result;
+
+    dvmDbgLockMutex(&state->serialLock);
+    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)
+{
+    u4 result;
+
+    dvmDbgLockMutex(&state->serialLock);
+    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);
+}
+
+/*
+ * 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)
+{
+    JdwpError err = ERR_NONE;
+    int i;
+
+    lockEventMutex(state);
+
+    assert(state != NULL);
+    assert(pEvent != NULL);
+    assert(pEvent->prev == NULL);
+    assert(pEvent->next == NULL);
+
+    /*
+     * If one or more LocationOnly mods are used, register them with
+     * the interpreter.
+     */
+    for (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 */
+            dvmDbgWatchLocation(&pMod->locationOnly.loc);
+        }
+        if (pMod->modKind == MK_STEP) {
+            /* should only be for EK_SINGLE_STEP; should only be one */
+            dvmDbgConfigureStep(pMod->step.threadId, pMod->step.size,
+                pMod->step.depth);
+        }
+    }
+
+    /*
+     * Add to list.
+     */
+    if (state->eventList != NULL) {
+        pEvent->next = state->eventList;
+        state->eventList->prev = pEvent;
+    }
+    state->eventList = pEvent;
+    state->numEvents++;
+
+    unlockEventMutex(state);
+
+    return err;
+}
+
+/*
+ * 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)
+{
+    int i;
+
+    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 (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)
+{
+    JdwpEvent* pEvent;
+
+    lockEventMutex(state);
+
+    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;
+    }
+
+    //LOGD("Odd: no match when removing event reqId=0x%04x\n", requestId);
+
+done:
+    unlockEventMutex(state);
+}
+
+/*
+ * Remove all entries from the event list.
+ */
+void dvmJdwpUnregisterAll(JdwpState* state)
+{
+    JdwpEvent* pEvent;
+    JdwpEvent* pNextEvent;
+
+    lockEventMutex(state);
+
+    pEvent = state->eventList;
+    while (pEvent != NULL) {
+        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*)malloc(allocSize);
+    memset(newEvent, 0, allocSize);
+    return newEvent;
+}
+
+/*
+ * Free a JdwpEvent.
+ *
+ * Do not call this until the event has been removed from the list.
+ */
+void dvmJdwpEventFree(JdwpEvent* pEvent)
+{
+    int i;
+
+    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 (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;
+        int i;
+
+        for (i = 0; i < pEvent->modCount; i++) {
+            if (pEvent->mods[i].modKind == MK_COUNT &&
+                pEvent->mods[i].count.count == 0)
+            {
+                LOGV("##### Removing expired event\n");
+                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
+        LOGE(">>> comparing '%s' to '%s'\n",
+            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;
+    int i;
+
+    for (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.referenceTypeId))
+                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:
+            // TODO
+            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:
+            LOGE("unhandled mod kind %d\n", 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, enum JdwpEventKind eventKind,
+    ModBasket* basket, JdwpEvent** matchList, int* pMatchCount)
+{
+    JdwpEvent* pEvent;
+
+    /* start after the existing entries */
+    matchList += *pMatchCount;
+
+    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 enum JdwpSuspendPolicy scanSuspendPolicy(JdwpEvent** matchList,
+    int matchCount)
+{
+    enum 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,
+    enum 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) {
+        LOGI("NOTE: suspendByPolicy not suspending JDWP thread\n");
+        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\n");*/
+            break;
+        }
+
+        /* grab this before posting/suspending again */
+        dvmJdwpSetWaitForEventThread(state, dvmDbgGetThreadSelfId());
+
+        /* leave pReq->invokeNeeded raised so we can check reentrancy */
+        LOGV("invoking method...\n");
+        dvmDbgExecuteMethod(pReq);
+
+        pReq->err = ERR_NONE;
+
+        /* clear this before signaling */
+        pReq->invokeNeeded = false;
+
+        LOGV("invoke complete, signaling and self-suspending\n");
+        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) {
+        LOGV("event in progress (0x%llx), 0x%llx sleeping\n",
+            state->eventThreadId, threadId);
+        waited = true;
+        dvmDbgCondWait(&state->eventThreadCond, &state->eventThreadLock);
+    }
+
+    if (waited || threadId != 0)
+        LOGV("event token grabbed (0x%llx)\n", 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);
+    LOGV("cleared event token (0x%llx)\n", 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(void)
+{
+    ExpandBuf* pReq;
+
+    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)
+{
+    enum 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) {
+        LOGV("EVENT: %s\n", dvmJdwpEventKindStr(EK_VM_START));
+        LOGV("  suspendPolicy=%s\n", 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)
+{
+    enum JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    JdwpEvent** matchList;
+    int matchCount;
+    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) {
+        LOGV("Ignoring location event in JDWP thread\n");
+        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)) {
+        LOGV("Not checking breakpoints during invoke (%s)\n", basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    matchList = allocMatchList(state);
+    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) {
+        int i;
+
+        LOGV("EVENT: %s(%d total) %s.%s thread=%llx code=%llx)\n",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.className,
+            dvmDbgGetMethodName(pLoc->classId, pLoc->methodId),
+            basket.threadId, pLoc->idx);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        LOGV("  suspendPolicy=%s\n",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (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)
+{
+    enum JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    JdwpEvent** matchList;
+    int matchCount;
+
+    assert(threadId = dvmDbgGetThreadSelfId());
+
+    /*
+     * I don't think this can happen.
+     */
+    if (invokeInProgress(state)) {
+        LOGW("Not posting thread change during invoke\n");
+        return false;
+    }
+
+    memset(&basket, 0, sizeof(basket));
+    basket.threadId = threadId;
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    matchList = allocMatchList(state);
+    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) {
+        int i;
+
+        LOGV("EVENT: %s(%d total) thread=%llx)\n",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        LOGV("  suspendPolicy=%s\n",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (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)
+{
+    ExpandBuf* pReq;
+
+    LOGV("EVENT: %s\n", dvmJdwpEventKindStr(EK_VM_DEATH));
+
+    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)
+{
+    enum JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    JdwpEvent** matchList;
+    int matchCount;
+    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)) {
+        LOGV("Not posting exception hit during invoke (%s)\n",basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    matchList = allocMatchList(state);
+    matchCount = 0;
+
+    findMatchingEvents(state, EK_EXCEPTION, &basket, matchList, &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        int i;
+
+        LOGV("EVENT: %s(%d total) thread=%llx exceptId=%llx caught=%d)\n",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId, exceptionId, basket.caught);
+        LOGV("  throw: %d %llx %x %lld (%s.%s)\n", pThrowLoc->typeTag,
+            pThrowLoc->classId, pThrowLoc->methodId, pThrowLoc->idx,
+            dvmDbgGetClassDescriptor(pThrowLoc->classId),
+            dvmDbgGetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
+        if (pCatchLoc->classId == 0) {
+            LOGV("  catch: (not caught)\n");
+        } else {
+            LOGV("  catch: %d %llx %x %lld (%s.%s)\n", pCatchLoc->typeTag,
+                pCatchLoc->classId, pCatchLoc->methodId, pCatchLoc->idx,
+                dvmDbgGetClassDescriptor(pCatchLoc->classId),
+                dvmDbgGetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
+        }
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        LOGV("  suspendPolicy=%s\n",
+            dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (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)
+{
+    enum JdwpSuspendPolicy suspendPolicy = SP_NONE;
+    ModBasket basket;
+    JdwpEvent** matchList;
+    int matchCount;
+    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)) {
+        LOGV("Not posting class prep caused by invoke (%s)\n",basket.className);
+        free(nameAlloc);
+        return false;
+    }
+
+    /* don't allow the list to be updated while we scan it */
+    lockEventMutex(state);
+
+    matchList = allocMatchList(state);
+    matchCount = 0;
+
+    findMatchingEvents(state, EK_CLASS_PREPARE, &basket, matchList,
+        &matchCount);
+
+    ExpandBuf* pReq = NULL;
+    if (matchCount != 0) {
+        int i;
+
+        LOGV("EVENT: %s(%d total) thread=%llx)\n",
+            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+            basket.threadId);
+
+        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+        LOGV("  suspendPolicy=%s\n",
+            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.
+             */
+            LOGV("  NOTE: class prepare in debugger thread!\n");
+            basket.threadId = 0;
+            if (suspendPolicy == SP_EVENT_THREAD)
+                suspendPolicy = SP_ALL;
+        }
+
+        pReq = eventPrep();
+        expandBufAdd1(pReq, suspendPolicy);
+        expandBufAdd4BE(pReq, matchCount);
+
+        for (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)
+{
+    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;
+    int i;
+
+    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 (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);
+
+    dvmJdwpSendBufferedRequest(state, wrapiov, iovcnt+1);
+}
diff --git a/vm/jdwp/JdwpEvent.h b/vm/jdwp/JdwpEvent.h
new file mode 100644
index 0000000..1a6a2c7
--- /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
+#define _DALVIK_JDWP_JDWPEVENT
+
+#include "JdwpConstants.h"
+#include "ExpandBuf.h"
+
+/*
+ * Event modifiers.  A JdwpEvent may have zero or more of these.
+ */
+typedef 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   referenceTypeId;
+    } 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;
+} JdwpEventMod;
+
+/*
+ * One of these for every registered event.
+ *
+ * We over-allocate the struct to hold the modifiers.
+ */
+typedef struct JdwpEvent {
+    struct JdwpEvent*       prev;           /* linked list */
+    struct JdwpEvent*       next;
+
+    enum JdwpEventKind      eventKind;      /* what kind of event is this? */
+    enum 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 */
+} JdwpEvent;
+
+/*
+ * 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*/
diff --git a/vm/jdwp/JdwpHandler.c b/vm/jdwp/JdwpHandler.c
new file mode 100644
index 0000000..d2a657d
--- /dev/null
+++ b/vm/jdwp/JdwpHandler.c
@@ -0,0 +1,2223 @@
+/*
+ * 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>
+
+#if 0
+#include <time.h>
+#include <sys/time.h>
+static void showTime(const char* label)
+{
+    struct timeval tv;
+    int min, sec, msec;
+
+    gettimeofday(&tv, NULL);
+    min = (tv.tv_sec / 60) % 60;
+    sec = tv.tv_sec % 60;
+    msec = tv.tv_usec / 1000;
+
+    LOGI("%02d:%02d.%03d %s\n", min, sec, msec, label);
+}
+#endif
+
+/*
+ * 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)
+{
+    JdwpError err = ERR_NONE;
+    u8* argArray = NULL;
+    u4 numArgs;
+    u4 options;     /* enum InvokeOptions bit flags */
+    int i;
+
+    assert(!isConstructor || objectId != 0);
+
+    numArgs = read4BE(&buf);
+
+    LOGV("    --> threadId=%llx objectId=%llx\n", threadId, objectId);
+    LOGV("        classId=%llx methodId=%x %s.%s\n",
+        classId, methodId,
+        dvmDbgGetClassDescriptor(classId),
+        dvmDbgGetMethodName(classId, methodId));
+    LOGV("        %d args:\n", numArgs);
+
+    if (numArgs > 0)
+        argArray = (ObjectId*) malloc(sizeof(ObjectId) * numArgs);
+
+    for (i = 0; i < (int) numArgs; i++) {
+        u1 typeTag;
+        u8 value;
+        int width;
+
+        typeTag = read1(&buf);
+        width = dvmDbgGetTagWidth(typeTag);
+        value = jdwpReadValue(&buf, width);
+
+        LOGV("          '%c'(%d): 0x%llx\n", typeTag, width, value);
+        argArray[i] = value;
+    }
+
+    options = read4BE(&buf);
+    LOGV("        options=0x%04x%s%s\n", options,
+        (options & INVOKE_SINGLE_THREADED) ? " (SINGLE_THREADED)" : "",
+        (options & INVOKE_NONVIRTUAL) ? " (NONVIRTUAL)" : "");
+
+
+    u1 resultTag;
+    u8 resultValue;
+    ObjectId exceptObjId;
+
+    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);
+
+        LOGV("  --> returned '%c' 0x%llx (except=%08llx)\n",
+            resultTag, resultValue, exceptObjId);
+
+        /* show detailed debug output */
+        if (resultTag == JT_STRING && exceptObjId == 0) {
+            if (resultValue != 0) {
+                char* str = dvmDbgStringToUtf8(resultValue);
+                LOGV("      string '%s'\n", str);
+                free(str);
+            } else {
+                LOGV("      string (null)\n");
+            }
+        }
+    }
+
+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)
+{
+    char* classDescriptor = NULL;
+    u4 numClasses;
+    size_t strLen;
+    RefTypeId refTypeId;
+
+    classDescriptor = readNewUtf8String(&buf, &strLen);
+    LOGV("  Req for class by signature '%s'\n", 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".
+     */
+    if (!dvmDbgFindLoadedClassBySignature(classDescriptor, &refTypeId)) {
+        /* not currently loaded */
+        LOGV("    --> no match!\n");
+        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)
+{
+    u4 threadCount;
+    ObjectId* pThreadIds;
+    ObjectId* walker;
+    int i;
+
+    dvmDbgGetAllThreads(&pThreadIds, &threadCount);
+
+    expandBufAdd4BE(pReply, threadCount);
+
+    walker = pThreadIds;
+    for (i = 0; i < (int) 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)
+{
+    u4 groups;
+    ObjectId threadGroupId;
+
+    /*
+     * 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".
+     */
+    groups = 1;
+    expandBufAdd4BE(pReply, groups);
+    //threadGroupId = debugGetMainThreadGroup();
+    //expandBufAdd8BE(pReply, threadGroupId);
+    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;
+
+    exitCode = get4BE(buf);
+
+    LOGW("Debugger is telling the VM to exit with code=%d\n", 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)
+{
+    char* str;
+    size_t strLen;
+    ObjectId stringId;
+
+    str = readNewUtf8String(&buf, &strLen);
+
+    LOGV("  Req to create string '%s'\n", str);
+
+    stringId = dvmDbgCreateString(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, false);   /* 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] = "/";
+    u4 classPaths;
+    u4 bootClassPaths;
+    int i;
+
+    /*
+     * TODO: make this real.  Not important for remote debugging, but
+     * might be useful for local debugging.
+     */
+    classPaths = 1;
+    bootClassPaths = 0;
+
+    expandBufAddUtf8String(pReply, (const u1*) baseDir);
+    expandBufAdd4BE(pReply, classPaths);
+    for (i = 0; i < (int) classPaths; i++) {
+        expandBufAddUtf8String(pReply, (const u1*) ".");
+    }
+
+    expandBufAdd4BE(pReply, bootClassPaths);
+    for (i = 0; i < (int) 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)
+{
+    int i;
+
+    expandBufAdd1(pReply, false);   /* canWatchFieldModification */
+    expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
+    expandBufAdd1(pReply, false);   /* canGetBytecodes */
+    expandBufAdd1(pReply, false);   /* 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 (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;
+    int i;
+
+    dvmDbgGetClassList(&numClasses, &classRefBuf);
+
+    expandBufAdd4BE(pReply, numClasses);
+
+    for (i = 0; i < (int) numClasses; i++) {
+        static const u1 genericSignature[1] = "";
+        u1 refTypeTag;
+        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(signature);
+    }
+
+    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)
+{
+    char* signature;
+    RefTypeId refTypeId;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+
+    LOGV("  Req for signature of refTypeId=0x%llx\n", refTypeId);
+    signature = dvmDbgGetSignature(refTypeId);
+    expandBufAddUtf8String(pReply, (const u1*) signature);
+    free(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;
+    u4 modBits;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+    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;
+    u4 numFields;
+    int i;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+    numFields = read4BE(&buf);
+
+    expandBufAdd4BE(pReply, numFields);
+    for (i = 0; i < (int) numFields; i++) {
+        FieldId fieldId;
+        u1 fieldTag;
+        int width;
+        u1* ptr;
+
+        fieldId = dvmReadFieldId(&buf);
+        fieldTag = dvmDbgGetFieldTag(refTypeId, fieldId);
+        width = dvmDbgGetTagWidth(fieldTag);
+
+        expandBufAdd1(pReply, fieldTag);
+        ptr = expandBufAddSpace(pReply, width);
+        dvmDbgGetStaticFieldValue(refTypeId, fieldId, ptr, width);
+    }
+
+    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;
+    const char* fileName;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+
+    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;
+    u1 typeTag;
+    u4 status;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+
+    /* get status flags */
+    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;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+
+    LOGV("  Req for interfaces in %llx (%s)\n", 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;
+    ObjectId classObjId;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+    classObjId = dvmDbgGetClassObject(refTypeId);
+
+    LOGV("  RefTypeId %llx -> ObjectId %llx\n", 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] = "";
+    char* signature;
+    RefTypeId refTypeId;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+
+    LOGV("  Req for signature of refTypeId=0x%llx\n", refTypeId);
+    signature = dvmDbgGetSignature(refTypeId);
+    if (signature != NULL)
+        expandBufAddUtf8String(pReply, (const u1*) signature);
+    else
+        expandBufAddUtf8String(pReply, (const u1*) "Lunknown;");  /* native? */
+    expandBufAddUtf8String(pReply, genericSignature);
+    free(signature);
+
+    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;
+
+    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;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+    LOGV("  Req for fields in refTypeId=0x%llx\n", refTypeId);
+    {
+        char* tmp = dvmDbgGetSignature(refTypeId);
+        LOGV("  --> '%s'\n", tmp);
+        free(tmp);
+    }
+
+    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;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+
+    LOGV("  Req for methods in refTypeId=0x%llx\n", refTypeId);
+    {
+        char* tmp = dvmDbgGetSignature(refTypeId);
+        LOGV("  --> '%s'\n", tmp);
+        free(tmp);
+    }
+
+    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;
+    RefTypeId superClassId;
+
+    classId = dvmReadRefTypeId(&buf);
+
+    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;
+    u4 values;
+    int i;
+
+    classId = dvmReadRefTypeId(&buf);
+    values = read4BE(&buf);
+
+    LOGV("  Req to set %d values in classId=%llx\n", values, classId);
+
+    for (i = 0; i < (int) values; i++) {
+        FieldId fieldId;
+        u1 fieldTag;
+        u8 value;
+        int width;
+
+        fieldId = dvmReadFieldId(&buf);
+        fieldTag = dvmDbgGetStaticFieldTag(classId, fieldId);
+        width = dvmDbgGetTagWidth(fieldTag);
+        value = jdwpReadValue(&buf, width);
+
+        LOGV("    --> field=%x tag=%c -> %lld\n", 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;
+    ObjectId threadId;
+    MethodId methodId;
+
+    classId = dvmReadRefTypeId(&buf);
+    threadId = dvmReadObjectId(&buf);
+    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;
+    ObjectId threadId;
+    MethodId methodId;
+    ObjectId objectId;
+
+    classId = dvmReadRefTypeId(&buf);
+    threadId = dvmReadObjectId(&buf);
+    methodId = dvmReadMethodId(&buf);
+
+    LOGV("Creating instance of %s\n", dvmDbgGetClassDescriptor(classId));
+    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;
+    u4 length;
+    ObjectId objectId;
+
+    arrayTypeId = dvmReadRefTypeId(&buf);
+    length = read4BE(&buf);
+
+    LOGV("Creating array %s[%u]\n",
+        dvmDbgGetClassDescriptor(arrayTypeId), length);
+    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;
+    MethodId methodId;
+
+    refTypeId = dvmReadRefTypeId(&buf);
+    methodId = dvmReadMethodId(&buf);
+
+    LOGV("  Req for line table in %s.%s\n",
+        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;
+    MethodId methodId;
+
+    classId = dvmReadRefTypeId(&buf);
+    methodId = dvmReadMethodId(&buf);
+
+    LOGV("  Req for LocalVarTab in class=%s method=%s\n",
+        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;
+    u1 refTypeTag;
+    RefTypeId typeId;
+
+    objectId = dvmReadObjectId(&buf);
+    LOGV("  Req for type of objectId=0x%llx\n", objectId);
+
+    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;
+    u4 numFields;
+    int i;
+
+    objectId = dvmReadObjectId(&buf);
+    numFields = read4BE(&buf);
+
+    LOGV("  Req for %d fields from objectId=0x%llx\n", numFields, objectId);
+
+    expandBufAdd4BE(pReply, numFields);
+
+    for (i = 0; i < (int) numFields; i++) {
+        FieldId fieldId;
+        u1 fieldTag;
+        int width;
+        u1* ptr;
+
+        fieldId = dvmReadFieldId(&buf);
+
+        fieldTag = dvmDbgGetFieldTag(objectId, fieldId);
+        width = dvmDbgGetTagWidth(fieldTag);
+
+        LOGV("    --> fieldId %x --> tag '%c'(%d)\n",
+            fieldId, fieldTag, width);
+
+        expandBufAdd1(pReply, fieldTag);
+        ptr = expandBufAddSpace(pReply, width);
+        dvmDbgGetFieldValue(objectId, fieldId, ptr, width);
+    }
+
+    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;
+    u4 numFields;
+    int i;
+
+    objectId = dvmReadObjectId(&buf);
+    numFields = read4BE(&buf);
+
+    LOGV("  Req to set %d fields in objectId=0x%llx\n", numFields, objectId);
+
+    for (i = 0; i < (int) numFields; i++) {
+        FieldId fieldId;
+        u1 fieldTag;
+        int width;
+        u8 value;
+
+        fieldId = dvmReadFieldId(&buf);
+
+        fieldTag = dvmDbgGetFieldTag(objectId, fieldId);
+        width = dvmDbgGetTagWidth(fieldTag);
+        value = jdwpReadValue(&buf, width);
+
+        LOGV("    --> fieldId=%x tag='%c'(%d) value=%lld\n",
+            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;
+    ObjectId threadId;
+    RefTypeId classId;
+    MethodId methodId;
+
+    objectId = dvmReadObjectId(&buf);
+    threadId = dvmReadObjectId(&buf);
+    classId = dvmReadRefTypeId(&buf);
+    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);
+
+    LOGV("  Req IsCollected(0x%llx)\n", 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;
+    char* str;
+
+    stringObject = dvmReadObjectId(&buf);
+    str = dvmDbgStringToUtf8(stringObject);
+
+    LOGV("  Req for str %llx --> '%s'\n", 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;
+    char* name;
+
+    threadId = dvmReadObjectId(&buf);
+
+    LOGV("  Req for name of thread 0x%llx\n", threadId);
+    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;
+
+    threadId = dvmReadObjectId(&buf);
+
+    if (threadId == dvmDbgGetThreadSelfId()) {
+        LOGI("  Warning: ignoring request to suspend self\n");
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+    LOGV("  Req to suspend thread 0x%llx\n", 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;
+
+    threadId = dvmReadObjectId(&buf);
+
+    if (threadId == dvmDbgGetThreadSelfId()) {
+        LOGI("  Warning: ignoring request to resume self\n");
+        return ERR_NONE;
+    }
+    LOGV("  Req to resume thread 0x%llx\n", 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;
+    u4 threadStatus;
+    u4 suspendStatus;
+
+    threadId = dvmReadObjectId(&buf);
+
+    LOGV("  Req for status of thread 0x%llx\n", threadId);
+
+    if (!dvmDbgGetThreadStatus(threadId, &threadStatus, &suspendStatus))
+        return ERR_INVALID_THREAD;
+
+    LOGV("    --> %s, %s\n", dvmJdwpThreadStatusStr(threadStatus),
+        dvmJdwpSuspendStatusStr(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;
+    ObjectId threadGroupId;
+
+    threadId = dvmReadObjectId(&buf);
+
+    /* currently not handling these */
+    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;
+    u4 startFrame, length, frames;
+    int i, frameCount;
+
+    threadId = dvmReadObjectId(&buf);
+    startFrame = read4BE(&buf);
+    length = read4BE(&buf);
+
+    if (!dvmDbgThreadExists(threadId))
+        return ERR_INVALID_THREAD;
+    if (!dvmDbgIsSuspended(threadId)) {
+        LOGV("  Rejecting req for frames in running thread '%s' (%llx)\n",
+            dvmDbgGetThreadName(threadId), threadId);
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+
+    frameCount = dvmDbgGetThreadFrameCount(threadId);
+
+    LOGV("  Request for frames: threadId=%llx start=%d length=%d [count=%d]\n",
+        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);
+
+    frames = length;
+    expandBufAdd4BE(pReply, frames);
+    for (i = startFrame; i < (int) (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}\n",
+            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;
+    int frameCount;
+
+    threadId = dvmReadObjectId(&buf);
+
+    if (!dvmDbgThreadExists(threadId))
+        return ERR_INVALID_THREAD;
+    if (!dvmDbgIsSuspended(threadId)) {
+        LOGV("  Rejecting req for frames in running thread '%s' (%llx)\n",
+            dvmDbgGetThreadName(threadId), threadId);
+        return ERR_THREAD_NOT_SUSPENDED;
+    }
+
+    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;
+    u4 suspendCount;
+
+    threadId = dvmReadObjectId(&buf);
+
+    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;
+    char* name = NULL;
+
+    threadGroupId = dvmReadObjectId(&buf);
+    LOGV("  Req for name of threadGroupId=0x%llx\n", threadGroupId);
+
+    name = dvmDbgGetThreadGroupName(threadGroupId);
+    if (name != NULL)
+        expandBufAddUtf8String(pReply, (u1*) name);
+    else {
+        expandBufAddUtf8String(pReply, (u1*) "BAD-GROUP-ID");
+        LOGW("bad thread group ID\n");
+    }
+
+    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;
+    ObjectId parentGroup;
+
+    groupId = dvmReadObjectId(&buf);
+
+    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;
+    u4 threadCount;
+    ObjectId* pThreadIds;
+    ObjectId* walker;
+    int i;
+
+    threadGroupId = dvmReadObjectId(&buf);
+    LOGV("  Req for threads in threadGroupId=0x%llx\n", threadGroupId);
+
+    dvmDbgGetThreadGroupThreads(threadGroupId, &pThreadIds, &threadCount);
+
+    expandBufAdd4BE(pReply, threadCount);
+
+    walker = pThreadIds;
+    for (i = 0; i < (int) 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;
+    u4 arrayLength;
+
+    arrayId = dvmReadObjectId(&buf);
+    LOGV("  Req for length of array 0x%llx\n", arrayId);
+
+    arrayLength = dvmDbgGetArrayLength(arrayId);
+
+    LOGV("    --> %d\n", 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;
+    u4 firstIndex;
+    u4 length;
+    u1 tag;
+
+    arrayId = dvmReadObjectId(&buf);
+    firstIndex = read4BE(&buf);
+    length = read4BE(&buf);
+
+    tag = dvmDbgGetArrayElementTag(arrayId);
+    LOGV("  Req for array values 0x%llx first=%d len=%d (elem tag=%c)\n",
+        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;
+    u4 firstIndex;
+    u4 values;
+
+    arrayId = dvmReadObjectId(&buf);
+    firstIndex = read4BE(&buf);
+    values = read4BE(&buf);
+
+    LOGV("  Req to set array values 0x%llx first=%d count=%d\n",
+        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)
+{
+    JdwpEvent* pEvent;
+    JdwpError err;
+    const u1* origBuf = buf;
+    /*int origDataLen = dataLen;*/
+    u1 eventKind;
+    u1 suspendPolicy;
+    u4 modifierCount;
+    u4 requestId;
+    int idx;
+
+    eventKind = read1(&buf);
+    suspendPolicy = read1(&buf);
+    modifierCount = read4BE(&buf);
+
+    LOGVV("  Set(kind=%s(%u) suspend=%s(%u) mods=%u)\n",
+        dvmJdwpEventKindStr(eventKind), eventKind,
+        dvmJdwpSuspendPolicyStr(suspendPolicy), suspendPolicy,
+        modifierCount);
+
+    assert(modifierCount < 256);    /* reasonableness check */
+
+    pEvent = dvmJdwpEventAlloc(modifierCount);
+    pEvent->eventKind = eventKind;
+    pEvent->suspendPolicy = suspendPolicy;
+    pEvent->modCount = modifierCount;
+
+    /*
+     * Read modifiers.  Ordering may be significant (see explanation of Count
+     * mods in JDWP doc).
+     */
+    for (idx = 0; idx < (int) modifierCount; idx++) {
+        u1 modKind;
+
+        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\n", 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\n", 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\n", threadId);
+                pEvent->mods[idx].threadOnly.threadId = threadId;
+            }
+            break;
+        case MK_CLASS_ONLY:     /* for ClassPrepare, MethodEntry */
+            {
+                RefTypeId clazzId = dvmReadRefTypeId(&buf);
+                LOGVV("    ClassOnly: %llx (%s)\n",
+                    clazzId, dvmDbgGetClassDescriptor(clazzId));
+                pEvent->mods[idx].classOnly.referenceTypeId = clazzId;
+            }
+            break;
+        case MK_CLASS_MATCH:    /* restrict events to matching classes */
+            {
+                char* pattern;
+                size_t strLen;
+
+                pattern = readNewUtf8String(&buf, &strLen);
+                LOGVV("    ClassMatch: '%s'\n", 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'\n", 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\n",
+                    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\n",
+                    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\n", 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\n",
+                    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\n", instance);
+                pEvent->mods[idx].instanceOnly.objectId = instance;
+            }
+            break;
+        default:
+            LOGW("GLITCH: unsupported modKind=%d\n", 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) {
+        LOGW("GLITCH: dataLen is %d, we have consumed %d\n", dataLen,
+            (int) (buf - origBuf));
+    }
+
+    /*
+     * We reply with an integer "requestID".
+     */
+    requestId = dvmJdwpNextEventSerial(state);
+    expandBufAdd4BE(pReply, requestId);
+
+    pEvent->requestId = requestId;
+
+    LOGV("    --> event requestId=0x%x\n", requestId);
+
+    /* add it to the list */
+    err = dvmJdwpRegisterEvent(state, pEvent);
+    if (err != ERR_NONE) {
+        /* registration failed, probably because event is bogus */
+        dvmJdwpEventFree(pEvent);
+        LOGW("WARNING: event request rejected\n");
+    }
+    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;
+    u4 requestId;
+
+    eventKind = read1(&buf);
+    requestId = read4BE(&buf);
+
+    LOGV("  Req to clear eventKind=%d requestId=0x%08x\n", 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;
+    FrameId frameId;
+    u4 slots;
+    int i;
+
+    threadId = dvmReadObjectId(&buf);
+    frameId = dvmReadFrameId(&buf);
+    slots = read4BE(&buf);
+
+    LOGV("  Req for %d slots in threadId=%llx frameId=%llx\n",
+        slots, threadId, frameId);
+
+    expandBufAdd4BE(pReply, slots);     /* "int values" */
+    for (i = 0; i < (int) slots; i++) {
+        u4 slot;
+        u1 reqSigByte;
+        int width;
+        u1* ptr;
+
+        slot = read4BE(&buf);
+        reqSigByte = read1(&buf);
+
+        LOGV("    --> slot %d '%c'\n", slot, reqSigByte);
+
+        width = dvmDbgGetTagWidth(reqSigByte);
+        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;
+    FrameId frameId;
+    u4 slots;
+    int i;
+
+    threadId = dvmReadObjectId(&buf);
+    frameId = dvmReadFrameId(&buf);
+    slots = read4BE(&buf);
+
+    LOGV("  Req to set %d slots in threadId=%llx frameId=%llx\n",
+        slots, threadId, frameId);
+
+    for (i = 0; i < (int) slots; i++) {
+        u4 slot;
+        u1 sigByte;
+        u8 value;
+        int width;
+
+        slot = read4BE(&buf);
+        sigByte = read1(&buf);
+        width = dvmDbgGetTagWidth(sigByte);
+        value = jdwpReadValue(&buf, width);
+
+        LOGV("    --> slot %d '%c' %llx\n", 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;
+    FrameId frameId;
+    u1 objectTag;
+    ObjectId objectId;
+    char* typeName;
+
+    threadId = dvmReadObjectId(&buf);
+    frameId = dvmReadFrameId(&buf);
+
+    if (!dvmDbgGetThisObject(threadId, frameId, &objectId))
+        return ERR_INVALID_FRAMEID;
+
+    if (objectId == 0) {
+        typeName = strdup("null");
+        objectTag = 0;
+    } else {
+        typeName = dvmDbgGetObjectTypeName(objectId);
+        objectTag = dvmDbgGetObjectTag(objectId, typeName);
+    }
+    LOGV("  Req for 'this' in thread=%llx frame=%llx --> %llx %s '%c'\n",
+        threadId, frameId, objectId, typeName, (char)objectTag);
+    free(typeName);
+
+    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;
+
+    classObjectId = dvmReadRefTypeId(&buf);
+
+    LOGV("  Req for refTypeId for class=%llx (%s)\n",
+        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;
+
+    LOGV("  Handling DDM packet (%.4s)\n", 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)) {
+        assert(replyLen > 0 && replyLen < 1*1024*1024);
+        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);
+
+typedef struct {
+    u1  cmdSet;
+    u1  cmd;
+    JdwpRequestHandler  func;
+    const char* descr;
+} JdwpHandlerMap;
+
+/*
+ * 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)
+        {
+            LOGV("REQ: %s (cmd=%d/%d dataLen=%d id=0x%06x)\n",
+                gHandlerMap[i].descr, pHeader->cmdSet, pHeader->cmd,
+                dataLen, pHeader->id);
+            result = (*gHandlerMap[i].func)(state, buf, dataLen, pReply);
+            break;
+        }
+    }
+    if (i == NELEM(gHandlerMap)) {
+        LOGE("REQ: UNSUPPORTED (cmd=%d/%d dataLen=%d id=0x%06x)\n",
+            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_LOG(LOG_VERBOSE, LOG_TAG) {
+        LOGV("reply: dataLen=%d err=%s(%d)%s\n", 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..3a7a98c
--- /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
+#define _DALVIK_JDWP_JDWPHANDLER
+
+#include "Common.h"
+#include "ExpandBuf.h"
+
+/*
+ * JDWP message header for a request.
+ */
+typedef struct JdwpReqHeader {
+    u4  length;
+    u4  id;
+    u1  cmdSet;
+    u1  cmd;
+} JdwpReqHeader;
+
+/*
+ * 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*/
diff --git a/vm/jdwp/JdwpMain.c b/vm/jdwp/JdwpMain.c
new file mode 100644
index 0000000..24e5c6c
--- /dev/null
+++ b/vm/jdwp/JdwpMain.c
@@ -0,0 +1,415 @@
+/*
+ * 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);
+
+
+/*
+ * 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:
+        // LOGD("prepping for JDWP over TCP\n");
+        state->transport = dvmJdwpSocketTransport();
+        break;
+    case kJdwpTransportAndroidAdb:
+        // LOGD("prepping for JDWP over ADB\n");
+        state->transport = dvmJdwpAndroidAdbTransport();
+        /* TODO */
+        break;
+    default:
+        LOGE("Unknown transport %d\n", 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)) {
+            LOGE("JDWP connection failed\n");
+            goto fail;
+        }
+
+        LOGI("JDWP connected\n");
+
+        /*
+         * 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) {
+        LOGW("WARNING: resetting state while event in progress\n");
+        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)
+            LOGD("JDWP shutting down net...\n");
+        dvmJdwpNetShutdown(state);
+
+        if (state->debugThreadStarted) {
+            state->run = false;
+            if (pthread_join(state->debugThreadHandle, &threadReturn) != 0) {
+                LOGW("JDWP thread join failed\n");
+            }
+        }
+
+        if (gDvm.verboseShutdown)
+            LOGD("JDWP freeing netstate...\n");
+        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;
+
+    LOGV("JDWP: thread running\n");
+
+    /*
+     * 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) {
+                LOGE("JDWP thread no longer in VMWAIT (now %d); resetting\n",
+                    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();
+
+    LOGV("JDWP: thread exiting\n");
+    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(void)
+{
+#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) {
+        LOGD("dvmJdwpLastDebuggerActivity: no active debugger\n");
+        return -1;
+    }
+
+    s8 last = dvmQuasiAtomicRead64(&state->lastActivityWhen);
+
+    /* initializing or in the middle of something? */
+    if (last == 0) {
+        LOGV("+++ last=busy\n");
+        return 0;
+    }
+
+    /* now get the current time */
+    s8 now = dvmJdwpGetNowMsec();
+    assert(now > last);
+
+    LOGV("+++ debugger interval=%lld\n", now - last);
+    return now - last;
+}
diff --git a/vm/jdwp/JdwpPriv.h b/vm/jdwp/JdwpPriv.h
new file mode 100644
index 0000000..bc249f1
--- /dev/null
+++ b/vm/jdwp/JdwpPriv.h
@@ -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.
+ */
+/*
+ * JDWP internal interfaces.
+ */
+#ifndef _DALVIK_JDWP_JDWPPRIV
+#define _DALVIK_JDWP_JDWPPRIV
+
+#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;
+typedef struct JdwpNetState JdwpNetState;
+
+struct JdwpState;
+
+/*
+ * Transport functions.
+ */
+typedef 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);
+} JdwpTransport;
+
+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;
+};
+
+
+/* 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*/
diff --git a/vm/jdwp/JdwpSocket.c b/vm/jdwp/JdwpSocket.c
new file mode 100644
index 0000000..0c39202
--- /dev/null
+++ b/vm/jdwp/JdwpSocket.c
@@ -0,0 +1,924 @@
+/*
+ * 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 {
+    short   listenPort;
+    int     listenSock;         /* listen for connection from debugger */
+    int     clientSock;         /* active connection to 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;
+};
+
+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) {
+            LOGE("JDWP net startup failed (req port=%d)\n", pParams->port);
+            return false;
+        }
+    } else {
+        port = pParams->port;   // used in a debug msg later
+        state->netState = netStartup(-1);
+    }
+
+    if (pParams->suspend)
+        LOGI("JDWP will wait for debugger on port %d\n", port);
+    else
+        LOGD("JDWP will %s on port %d\n",
+            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)
+{
+    JdwpNetState* netState;
+    int one = 1;
+
+    netState = (JdwpNetState*) malloc(sizeof(*netState));
+    memset(netState, 0, sizeof(*netState));
+    netState->listenSock = -1;
+    netState->clientSock = -1;
+    netState->wakePipe[0] = -1;
+    netState->wakePipe[1] = -1;
+
+    if (port < 0)
+        return netState;
+
+    assert(port != 0);
+
+    netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (netState->listenSock < 0) {
+        LOGE("Socket create failed: %s\n", strerror(errno));
+        goto fail;
+    }
+
+    /* allow immediate re-use */
+    if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
+            sizeof(one)) < 0)
+    {
+        LOGE("setsockopt(SO_REUSEADDR) failed: %s\n", 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) {
+        LOGV("attempt to bind to port %u failed: %s\n", port, strerror(errno));
+        goto fail;
+    }
+
+    netState->listenPort = port;
+    LOGVV("+++ bound to port %d\n", netState->listenPort);
+
+    if (listen(netState->listenSock, 5) != 0) {
+        LOGE("Listen failed: %s\n", 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) {
+        LOGV("+++ writing to wakePipe\n");
+        (void) 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;
+    }
+
+    free(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;
+
+    LOGE("WEIRD: odd behavior in select (count=%d)\n", 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\n", strerror(errno));
+            else
+                LOGE("accept failed: %s\n", strerror(errno));
+            return false;
+        }
+    } while (sock < 0);
+
+    netState->remoteAddr = addr.addrInet.sin_addr;
+    netState->remotePort = ntohs(addr.addrInet.sin_port);
+    LOGV("+++ accepted connection from %s:%u\n",
+        inet_ntoa(netState->remoteAddr), netState->remotePort);
+
+    netState->clientSock = sock;
+    netState->awaitingHandshake = true;
+    netState->inputCount = 0;
+
+    LOGV("Setting TCP_NODELAY on accepted socket\n");
+    setNoDelay(netState->clientSock);
+
+    if (pipe(netState->wakePipe) < 0) {
+        LOGE("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) {
+        LOGW("gethostbyname_r('%s') failed: %s\n",
+            state->params.host, strerror(errno));
+        return false;
+    }
+
+#else
+    h_errno = 0;
+    pEntry = gethostbyname(state->params.host);
+    if (pEntry == NULL) {
+        LOGW("gethostbyname('%s') failed: %s\n",
+            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);
+
+    LOGI("Connecting out to '%s' %d\n",
+        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) {
+        LOGE("Unable to create socket: %s\n", strerror(errno));
+        return false;
+    }
+
+    /*
+     * Try to connect.
+     */
+    if (connect(netState->clientSock, &addr.addrPlain, sizeof(addr)) != 0) {
+        LOGE("Unable to connect to %s:%d: %s\n",
+            inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port),
+            strerror(errno));
+        close(netState->clientSock);
+        netState->clientSock = -1;
+        return false;
+    }
+
+    LOGI("Connection established to %s (%s:%d)\n",
+        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) {
+        LOGE("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;
+
+    LOGV("+++ closed connection to %s:%u\n",
+        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);
+
+    LOGV("--- %s: dataLen=%u id=0x%08x flags=0x%02x cmd=%d/%d\n",
+        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) {
+            int cc;
+
+            /*
+             * TODO: we currently assume the write() will complete in one
+             * go, which may not be safe for a network socket.  We may need
+             * to mutex this against sendRequest().
+             */
+            cc = write(netState->clientSock, expandBufGetBuffer(pReply),
+                    expandBufGetLength(pReply));
+            if (cc != (int) expandBufGetLength(pReply)) {
+                LOGE("Failed sending reply to debugger: %s\n", strerror(errno));
+                expandBufFree(pReply);
+                return false;
+            }
+        } else {
+            LOGW("No reply created for set=%d cmd=%d\n", cmdSet, cmd);
+        }
+        expandBufFree(pReply);
+    } else {
+        LOGV("reply?!\n");
+        assert(false);
+    }
+
+    LOGV("----------\n");
+
+    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) {
+                LOGV("+++ all fds are closed\n");
+                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 {
+                LOGI("NOTE: entering select w/o wakepipe\n");
+            }
+
+            /*
+             * 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;
+                LOGE("select failed: %s\n", strerror(errno));
+                goto fail;
+            }
+
+            if (netState->wakePipe[0] >= 0 &&
+                FD_ISSET(netState->wakePipe[0], &readfds))
+            {
+                if (netState->listenSock >= 0)
+                    LOGE("Exit wake set, but not exiting?\n");
+                else
+                    LOGD("Got wake-up signal, bailing out of select\n");
+                goto fail;
+            }
+            if (netState->listenSock >= 0 &&
+                FD_ISSET(netState->listenSock, &readfds))
+            {
+                LOGI("Ignoring second debugger -- accepting and dropping\n");
+                union {
+                    struct sockaddr_in   addrInet;
+                    struct sockaddr      addrPlain;
+                } addr;
+                socklen_t addrlen;
+                int tmpSock;
+                tmpSock = accept(netState->listenSock, &addr.addrPlain,
+                                &addrlen);
+                if (tmpSock < 0)
+                    LOGI("Weird -- accept failed\n");
+                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;
+                    LOGD("+++ EINTR hit\n");
+                    return true;
+                } else if (readCount == 0) {
+                    /* EOF hit -- far end went away */
+                    LOGD("+++ peer disconnected\n");
+                    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)
+        {
+            LOGE("ERROR: bad handshake '%.14s'\n", netState->inputBuffer);
+            goto fail;
+        }
+
+        errno = 0;
+        cc = write(netState->clientSock, netState->inputBuffer,
+                kMagicHandshakeLen);
+        if (cc != kMagicHandshakeLen) {
+            LOGE("Failed writing handshake bytes: %s (%d of %d)\n",
+                strerror(errno), cc, (int) kMagicHandshakeLen);
+            goto fail;
+        }
+
+        consumeBytes(netState, kMagicHandshakeLen);
+        netState->awaitingHandshake = false;
+        LOGV("+++ handshake complete\n");
+        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;
+    int cc;
+
+    /*dumpPacket(expandBufGetBuffer(pReq));*/
+    if (netState->clientSock < 0) {
+        /* can happen with some DDMS events */
+        LOGV("NOT sending request -- no debugger is attached\n");
+        return false;
+    }
+
+    /*
+     * TODO: we currently assume the write() will complete in one
+     * go, which may not be safe for a network socket.  We may need
+     * to mutex this against handlePacket().
+     */
+    errno = 0;
+    cc = write(netState->clientSock, expandBufGetBuffer(pReq),
+            expandBufGetLength(pReq));
+    if (cc != (int) expandBufGetLength(pReq)) {
+        LOGE("Failed sending req to debugger: %s (%d of %d)\n",
+            strerror(errno), 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 */
+        LOGV("NOT sending request -- no debugger is attached\n");
+        return false;
+    }
+
+    size_t expected = 0;
+    int i;
+    for (i = 0; i < iovcnt; i++)
+        expected += iov[i].iov_len;
+
+    /*
+     * TODO: we currently assume the writev() will complete in one
+     * go, which may not be safe for a network socket.  We may need
+     * to mutex this against handlePacket().
+     */
+    ssize_t actual;
+    actual = writev(netState->clientSock, iov, iovcnt);
+    if ((size_t)actual != expected) {
+        LOGE("Failed sending b-req to debugger: %s (%d of %zu)\n",
+            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(void)
+{
+    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.c b/vm/mterp/Mterp.c
new file mode 100644
index 0000000..dbf5003
--- /dev/null
+++ b/vm/mterp/Mterp.c
@@ -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.
+ */
+
+/*
+ * Mterp entry point and support functions.
+ */
+#include "mterp/Mterp.h"
+
+#include <stddef.h>
+
+
+/*
+ * Verify some constants used by the mterp interpreter.
+ */
+bool dvmCheckAsmConstants(void)
+{
+    bool failed = false;
+
+#ifndef DVM_NO_ASM_INTERP
+
+    extern char dvmAsmInstructionStart[];
+    extern char dvmAsmInstructionEnd[];
+
+#define ASM_DEF_VERIFY
+#include "mterp/common/asm-constants.h"
+
+    if (failed) {
+        LOGE("Please correct the values in mterp/common/asm-constants.h\n");
+        dvmAbort();
+    }
+
+    /*
+     * If an instruction overflows the 64-byte handler size limit, it will
+     * push everything up and alter the total size.  Check it here.
+     */
+    const int width = 64;
+    int interpSize = dvmAsmInstructionEnd - dvmAsmInstructionStart;
+    if (interpSize != 0 && interpSize != 256*width) {
+        LOGE("ERROR: unexpected asm interp size %d\n", interpSize);
+        LOGE("(did an instruction handler exceed %d bytes?)\n", width);
+        dvmAbort();
+    }
+
+#endif // ndef DVM_NO_ASM_INTERP
+
+    return !failed;
+}
+
+
+/*
+ * "Standard" mterp entry point.  This sets up a "glue" structure and then
+ * calls into the assembly interpreter implementation.
+ *
+ * (There is presently no "debug" entry point.)
+ */
+bool dvmMterpStd(Thread* self, InterpState* glue)
+{
+    int changeInterp;
+
+    /* configure mterp items */
+    glue->self = self;
+    glue->methodClassDex = glue->method->clazz->pDvmDex;
+
+    glue->interpStackEnd = self->interpStackEnd;
+    glue->pSelfSuspendCount = &self->suspendCount;
+    glue->cardTable = gDvm.biasedCardTableBase;
+#if defined(WITH_JIT)
+    glue->pJitProfTable = gDvmJit.pProfTable;
+    glue->ppJitProfTable = &gDvmJit.pProfTable;
+    glue->jitThreshold = gDvmJit.threshold;
+#endif
+    if (gDvm.jdwpConfigured) {
+        glue->pDebuggerActive = &gDvm.debuggerActive;
+    } else {
+        glue->pDebuggerActive = NULL;
+    }
+    glue->pActiveProfilers = &gDvm.activeProfilers;
+
+    IF_LOGVV() {
+        char* desc = dexProtoCopyMethodDescriptor(&glue->method->prototype);
+        LOGVV("mterp threadid=%d entry %d: %s.%s %s\n",
+            dvmThreadSelf()->threadId,
+            glue->entryPoint,
+            glue->method->clazz->descriptor,
+            glue->method->name,
+            desc);
+        free(desc);
+    }
+    //LOGI("glue is %p, pc=%p, fp=%p\n", glue, glue->pc, glue->fp);
+    //LOGI("first instruction is 0x%04x\n", glue->pc[0]);
+
+    changeInterp = dvmMterpStdRun(glue);
+
+#if defined(WITH_JIT)
+    if (glue->jitState != kJitSingleStep) {
+        glue->self->inJitCodeCache = NULL;
+    }
+#endif
+
+    if (!changeInterp) {
+        /* this is a "normal" exit; we're not coming back */
+#ifdef LOG_INSTR
+        LOGD("|-- Leaving interpreter loop");
+#endif
+        return false;
+    } else {
+        /* we're "standard", so switch to "debug" */
+        LOGVV("  mterp returned, changeInterp=%d\n", changeInterp);
+        glue->nextMode = INTERP_DBG;
+        return true;
+    }
+}
diff --git a/vm/mterp/Mterp.h b/vm/mterp/Mterp.h
new file mode 100644
index 0000000..8b3f7b4
--- /dev/null
+++ b/vm/mterp/Mterp.h
@@ -0,0 +1,55 @@
+/*
+ * 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
+#define _DALVIK_MTERP_MTERP
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#if defined(WITH_JIT)
+#include "interp/Jit.h"
+#endif
+
+/*
+ * Interpreter state, passed into C functions from assembly stubs.  The
+ * assembly code exports all registers into the "glue" structure before
+ * calling, then extracts them when the call returns.
+ */
+typedef InterpState MterpGlue;
+
+/*
+ * Call this during initialization to verify that the values in asm-constants.h
+ * are still correct.
+ */
+bool dvmCheckAsmConstants(void);
+
+/*
+ * Local entry and exit points.  The platform-specific implementation must
+ * provide these two.
+ *
+ * dvmMterpStdRun() returns the "changeInterp" argument from dvmMterpStdBail(),
+ * indicating whether we want to bail out of the interpreter or just switch
+ * between "standard" and "debug" mode.
+ *
+ * The "mterp" interpreter is always "standard".
+ */
+bool dvmMterpStdRun(MterpGlue* glue);
+void dvmMterpStdBail(MterpGlue* glue, bool changeInterp);
+
+#endif /*_DALVIK_MTERP_MTERP*/
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..80596ea
--- /dev/null
+++ b/vm/mterp/README.txt
@@ -0,0 +1,215 @@
+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.  One form of the portable
+interpreter includes support for profiling and debugging features, and
+is included even if we have a platform-optimized implementation.
+
+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).
+
+Two basic assumptions are made about the operation of the interpreter:
+
+ - The assembly version uses fixed-size areas for each instruction
+   (e.g. 64 bytes).  "Overflow" code is tacked on to the end.
+ - When a C implementation is desired, the assembly version packs all
+   local state into a "glue" struct, and passes that into the C function.
+   Updates to the state are pulled out of the "glue" 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-size <bytes>
+
+    Specify the size of the assembly region, in bytes.  On most platforms
+    this will need to be a power of 2.
+
+  import <filename>
+
+    The specified file is included immediately, in its entirety.  No
+    substitutions are performed.  ".c" 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.
+    Text substitution is performed on the opcode name.
+
+  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).
+
+  op-end
+
+    Indicates the end of the opcode list.  All 256 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" directives is 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 256 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.
+
+  %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.
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_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/armv4t/OP_AGET_WIDE.S b/vm/mterp/armv4t/OP_AGET_WIDE.S
new file mode 100644
index 0000000..dc5eee8
--- /dev/null
+++ b/vm/mterp/armv4t/OP_AGET_WIDE.S
@@ -0,0 +1,33 @@
+%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
+    add     r0, r0, #offArrayObject_contents
+    ldmia   r0, {r2-r3}                 @ 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/armv4t/OP_APUT_WIDE.S b/vm/mterp/armv4t/OP_APUT_WIDE.S
new file mode 100644
index 0000000..9a718f2
--- /dev/null
+++ b/vm/mterp/armv4t/OP_APUT_WIDE.S
@@ -0,0 +1,31 @@
+%verify "executed"
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     */
+    /* 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
+    add     r0, #offArrayObject_contents
+    stmia   r0, {r2-r3}                 @ vBB[vCC] <- r2/r3
+    GOTO_OPCODE(ip)                     @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_IGET_WIDE.S b/vm/mterp/armv4t/OP_IGET_WIDE.S
new file mode 100644
index 0000000..5940f56
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IGET_WIDE.S
@@ -0,0 +1,50 @@
+%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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    add     r9, r9, r3                  @ r9<- obj + field offset
+    ldmia   r9, {r0-r1}                 @ 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/armv4t/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv4t/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..b0022f5
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,17 @@
+%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(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
+    add     r9, r3, r1                  @ r9<- object + offset
+    ldmia   r9, {r0-r1}                 @ r0/r1<- obj.field (64 bits, aligned)
+    and     r2, r2, #15                 @ 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/armv4t/OP_IGET_WIDE_VOLATILE.S b/vm/mterp/armv4t/OP_IGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..cdd8708
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv4t/OP_IGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv4t/OP_IPUT_WIDE.S b/vm/mterp/armv4t/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..d0f7315
--- /dev/null
+++ b/vm/mterp/armv4t/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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    add     r2, r9, r3                  @ r2<- object + byte offset
+    .if $volatile
+    bl      dvmQuasiAtomicSwap64        @ stores r0/r1 into addr r2
+    .else
+    stmia   r2, {r0-r1}                 @ obj.field (64 bits, aligned)<- r0/r1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..b062127
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,17 @@
+%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
+    add     r2, r2, r3                  @ r2<- object + byte offset
+    stmia   r2, {r0-r1}                 @ 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/armv4t/OP_IPUT_WIDE_VOLATILE.S b/vm/mterp/armv4t/OP_IPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..6b297f0
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv4t/OP_IPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv4t/OP_SGET_WIDE.S b/vm/mterp/armv4t/OP_SGET_WIDE.S
new file mode 100644
index 0000000..91e195d
--- /dev/null
+++ b/vm/mterp/armv4t/OP_SGET_WIDE.S
@@ -0,0 +1,44 @@
+%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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    .if $volatile
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldmia   r0, {r0-r1}                 @ 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
+     *
+     * Returns StaticField pointer in r0.
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .L${opcode}_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
diff --git a/vm/mterp/armv4t/OP_SGET_WIDE_VOLATILE.S b/vm/mterp/armv4t/OP_SGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..5615ab6
--- /dev/null
+++ b/vm/mterp/armv4t/OP_SGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv4t/OP_SGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv4t/OP_SPUT_WIDE.S b/vm/mterp/armv4t/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..a67e2d8
--- /dev/null
+++ b/vm/mterp/armv4t/OP_SPUT_WIDE.S
@@ -0,0 +1,46 @@
+%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, [rGLUE, #offGlue_methodClassDex]    @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    .if $volatile
+    bl      dvmQuasiAtomicSwap64        @ stores r0/r1 into addr r2
+    .else
+    stmia   r2, {r0-r1}                 @ 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]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .L${opcode}_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
diff --git a/vm/mterp/armv4t/OP_SPUT_WIDE_VOLATILE.S b/vm/mterp/armv4t/OP_SPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..850e83b
--- /dev/null
+++ b/vm/mterp/armv4t/OP_SPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv4t/OP_SPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv4t/platform.S b/vm/mterp/armv4t/platform.S
new file mode 100644
index 0000000..eca940a
--- /dev/null
+++ b/vm/mterp/armv4t/platform.S
@@ -0,0 +1,44 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5.  Essentially a
+ * one-way branch.
+ *
+ * May modify IP.  Does not modify LR.
+ */
+.macro  LDR_PC source
+    ldr     ip, \source
+    bx      ip
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    ldr     ip, \source
+    mov     lr, pc
+    bx      ip
+.endm
+
+/*
+ * Macro for "LDMFD SP!,{...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro  LDMFD_PC regs
+    ldmfd   sp!, {\regs,lr}
+    bx      lr
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.endm
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..9e98784
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_OBJECT.S
@@ -0,0 +1,51 @@
+%verify "executed"
+    /*
+     * Store an object into an array.  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.
+     */
+    /* 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(r0, r3)                    @ r0<- 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, r0, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r0, 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     common_errArrayStore        @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rGLUE, #offGlue_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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GOTO_OPCODE(ip)                     @ jump to next instruction
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..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_BREAKPOINT.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_CHECK_CAST.S b/vm/mterp/armv5te/OP_CHECK_CAST.S
new file mode 100644
index 0000000..c711276
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CHECK_CAST.S
@@ -0,0 +1,72 @@
+%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, [rGLUE, #offGlue_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 class resolved from BBBB
+     *  r9 holds object
+     */
+.L${opcode}_fullcheck:
+    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 with the
+    @ class of the object that failed to be cast.
+    EXPORT_PC()                         @ about to throw
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- obj->clazz
+    ldr     r0, .LstrClassCastExceptionPtr
+    ldr     r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+    bl      dvmThrowExceptionWithClassMessage
+    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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrClassCastExceptionPtr:
+    .word   .LstrClassCastException
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..665e582
--- /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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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..2df3fda
--- /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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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..cf9b009
--- /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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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..f7ca704
--- /dev/null
+++ b/vm/mterp/armv5te/OP_EXECUTE_INLINE.S
@@ -0,0 +1,60 @@
+%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.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &glue->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(r9, 2)                        @ r9<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, r9, #0xf000             @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, r9, #0x0f00             @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, r9, #0x00f0             @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, r9, #0x000f             @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     r9, .L${opcode}_table       @ table of InlineOperation
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.L${opcode}_table:
+    .word   gDvmInlineOpsTable
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..cf8b151
--- /dev/null
+++ b/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1,55 @@
+%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 */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &glue->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
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.L${opcode}_table:
+    .word   gDvmInlineOpsTable
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..5bb2d43
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,108 @@
+%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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!$isrange)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
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..26f0c8f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO.S
@@ -0,0 +1,25 @@
+%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 */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r9, r0, asr #24             @ r9<- ssssssAA (sign-extended)
+    mov     r9, r9, lsl #1              @ r9<- byte offset
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv5te/OP_GOTO_16.S b/vm/mterp/armv5te/OP_GOTO_16.S
new file mode 100644
index 0000000..5a8edee
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO_16.S
@@ -0,0 +1,24 @@
+%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)
+    movs    r9, r0, asl #1              @ r9<- byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv5te/OP_GOTO_32.S b/vm/mterp/armv5te/OP_GOTO_32.S
new file mode 100644
index 0000000..17780b9
--- /dev/null
+++ b/vm/mterp/armv5te/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".  The ORRS
+     * instruction doesn't affect the V flag, so we need to clear it
+     * explicitly.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    cmp     ip, ip                      @ (clear V flag during stall)
+    orrs    r0, r0, r1, lsl #16         @ r0<- AAAAaaaa, check sign
+    mov     r9, r0, asl #1              @ r9<- byte offset
+    ble     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
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..b9cdee4
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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..95944de
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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..66f0df3
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method]    @ r0<- glue->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..14ba8f7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT.S
@@ -0,0 +1,47 @@
+%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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .L${opcode}_resolve         @ not resolved, do it now
+.L${opcode}_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethod${routine}   @ no, continue on
+    b       common_errNullObject        @ yes, throw exception
+%break
+
+    /*
+     * On entry:
+     *  r1 = reference (BBBB or CCCC)
+     *  r10 = "this" register
+     */
+.L${opcode}_resolve:
+    ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .L${opcode}_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
diff --git a/vm/mterp/armv5te/OP_INVOKE_DIRECT_EMPTY.S b/vm/mterp/armv5te/OP_INVOKE_DIRECT_EMPTY.S
new file mode 100644
index 0000000..3c6b192
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT_EMPTY.S
@@ -0,0 +1,7 @@
+%verify "executed"
+    /*
+     * invoke-direct-empty is a no-op in a "standard" interpreter.
+     */
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
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..7d52454
--- /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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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} @ jump to common handler
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_STATIC.S b/vm/mterp/armv5te/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..cb359e6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_STATIC.S
@@ -0,0 +1,24 @@
+%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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethod${routine} @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethod${routine} @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
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..6117947
--- /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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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
+     *  r9 = method->clazz
+     */
+.L${opcode}_continue:
+    ldr     r1, [r9, #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, r9                      @ 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..bd07d06
--- /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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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} @ continue on
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..d92c6a9
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethod${routine} @ continue on
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..bc34023
--- /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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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} @ continue on
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..53f4b4e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT.S
@@ -0,0 +1,47 @@
+%default { "store":"str", "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 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $barrier                            @ releasing store
+    $store  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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..079094e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT.S
@@ -0,0 +1,50 @@
+%default { "barrier":"@ 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $barrier                            @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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..7e7144a
--- /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, [rGLUE, #offGlue_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..b4d24e7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT_OBJECT.S" {"barrier":"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..ba3f615
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT.S" {"barrier":"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..8796cbb
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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      dvmQuasiAtomicSwap64        @ 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..36faabc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MONITOR_ENTER.S
@@ -0,0 +1,22 @@
+%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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC, MONITOR_TRACKING
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r1, [r0, #offThread_exception] @ check for exception
+    cmp     r1, #0
+    bne     common_exceptionThrown      @ exception raised, bail out
+#endif
+    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..5c1b3c7
--- /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
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->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..f9e4cff
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* move-exception vAA */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [r0, #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, [r0, #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..9de8401
--- /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, [rGLUE, #offGlue_retval]    @ r0<- glue->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..92f7443
--- /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, rGLUE, #offGlue_retval  @ r3<- &glue->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
+    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_MOVE_WIDE.S b/vm/mterp/armv5te/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..05151e1
--- /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
+    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/armv5te/OP_MOVE_WIDE_16.S b/vm/mterp/armv5te/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..172ef03
--- /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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    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..da93c45
--- /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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_method] @ r3<- glue->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..2687e55
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
@@ -0,0 +1,68 @@
+%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, [rGLUE, #offGlue_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
+    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?
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * 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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrInstantiationErrorPtr:
+    .word   .LstrInstantiationError
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..941b232
--- /dev/null
+++ b/vm/mterp/armv5te/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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
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..8838182
--- /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 "glue"
+     * 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, [rGLUE, #offGlue_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_WIDE.S b/vm/mterp/armv5te/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..33880de
--- /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 "glue"
+     * 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, rGLUE, #offGlue_retval  @ r3<- &glue->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..c803d27
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET.S
@@ -0,0 +1,39 @@
+%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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .L${opcode}_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
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..768b9da
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_WIDE.S
@@ -0,0 +1,44 @@
+%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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+     *
+     * Returns StaticField pointer in r0.
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .L${opcode}_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
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..e709b22
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT.S
@@ -0,0 +1,39 @@
+%default { "barrier":"@ 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    $barrier                            @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+%break
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .L${opcode}_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
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..fe9fa4c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_OBJECT.S
@@ -0,0 +1,38 @@
+%default { "barrier":"@ 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .L${opcode}_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .L${opcode}_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+%break
+.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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $barrier                            @ releasing store
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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
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..fe12b9e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT_OBJECT.S" {"barrier":"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..cfb2b27
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S" {"barrier":"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..330c72b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_WIDE.S
@@ -0,0 +1,46 @@
+%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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.L${opcode}_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .L${opcode}_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
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..dd0a0b8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_THROW.S
@@ -0,0 +1,15 @@
+%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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    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, [r0, #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..8bd4f35
--- /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, [rGLUE, #offGlue_method]    @ r0<- glue->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_F1.S b/vm/mterp/armv5te/OP_UNUSED_F1.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_F1.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/bincmp.S b/vm/mterp/armv5te/bincmp.S
new file mode 100644
index 0000000..58f9079
--- /dev/null
+++ b/vm/mterp/armv5te/bincmp.S
@@ -0,0 +1,31 @@
+%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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    b${revcmp}  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
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.c b/vm/mterp/armv5te/debug.c
new file mode 100644
index 0000000..9f893fe
--- /dev/null
+++ b/vm/mterp/armv5te/debug.c
@@ -0,0 +1,79 @@
+#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)
+{
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rGLUE     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 rGLUE=%08x rINST=%08x\n",
+        rPC, rFP, rGLUE, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+    //MterpGlue* glue = (MterpGlue*) rGLUE;
+    //const Method* method = glue->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..eddac53
--- /dev/null
+++ b/vm/mterp/armv5te/entry.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.
+ */
+/*
+ * 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  MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value.  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, #offGlue_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rGLUE, r0                   @ set rGLUE
+    ldr     r1, [r0, #offGlue_entryPoint]   @ enum is 4 bytes in aapcs-EABI
+    LOAD_PC_FP_FROM_GLUE()              @ load rPC and rFP from "glue"
+    adr     rIBASE, dvmAsmInstructionStart  @ set rIBASE
+    cmp     r1, #kInterpEntryInstr      @ usual case?
+    bne     .Lnot_instr                 @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    /* Entry is always a possible trace start */
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [r10, #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, [r10, #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
+
+.Lnot_instr:
+    cmp     r1, #kInterpEntryReturn     @ were we returning from a method?
+    beq     common_returnFromMethod
+
+.Lnot_return:
+    cmp     r1, #kInterpEntryThrow      @ were we throwing an exception?
+    beq     common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+    ldr     r10,[rGLUE, #offGlue_jitResumeNPC]
+    ldr     r2,[rGLUE, #offGlue_jitResumeDPC]
+    cmp     r1, #kInterpEntryResume     @ resuming after Jit single-step?
+    bne     .Lbad_arg
+    cmp     rPC,r2
+    bne     .LentryInstr                @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+    @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+    b       jitSVShadowRunStart         @ re-enter the translation after the
+                                        @ single-stepped instruction
+    @noreturn
+#endif
+    mov     r1, #kInterpEntryInstr
+    str     r1, [rGLUE, #offGlue_entryPoint]
+    bx      r10                         @ re-enter the translation
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+
+
+    .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  MterpGlue* glue
+ *  r1  bool changeInterp
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offGlue_bailPtr]      @ sp<- saved SP
+    mov     r0, r1                          @ return the changeInterp value
+    add     sp, sp, #4                      @ un-align 64
+    LDMFD_PC "r4-r10,fp"                    @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+    .word   .LstrBadEntryPoint
diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S
new file mode 100644
index 0000000..b69aef8
--- /dev/null
+++ b/vm/mterp/armv5te/footer.S
@@ -0,0 +1,1229 @@
+
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+/*
+ * 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:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    r0 <= PC
+ *    r1 <= PC of resume instruction
+ *    lr <= resume point in translation
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r1,#kInterpEntryInstr
+    @ enum is 4 byte in aapcs-EABI
+    str    r1, [rGLUE, #offGlue_entryPoint]
+    mov    rPC,r0
+    EXPORT_PC()
+
+    adrl   rIBASE, dvmAsmInstructionStart
+    mov    r2,#kJitSingleStep     @ Ask for single step and then revert
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r1,#1                  @ set changeInterp to bail to debug interp
+    b      common_gotoBail
+
+/*
+ * 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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr       @ Is there a translation?
+    str    r0, [r10, #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:
+    adrl   rIBASE, dvmAsmInstructionStart
+    GET_JIT_PROF_TABLE(r0)
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    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
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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.
+ * rGLUE & 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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_JIT_PROF_TABLE(r0)
+    @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.  On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+    cmp     r0,#0
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE_IFEQ(ip)       @ if not profiling, fallthrough otherwise */
+
+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 */
+
+/*
+ * 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).
+ */
+    GET_JIT_THRESHOLD(r1)
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    bl      dvmJitGetCodeAddr           @ r0<- dvmJitGetCodeAddr(rPC)
+    str     r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ r10 <- glue->self
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+    str     r2,[rGLUE,#offGlue_jitState]
+    mov     r2,#kInterpEntryInstr       @ normal entry reason
+    str     r2,[rGLUE,#offGlue_entryPoint]
+    mov     r1,#1                       @ set changeInterp
+    b       common_gotoBail
+
+#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, rGLUE: 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,rGLUE                    @ r2<- InterpState pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    add     rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    ldr    rPC,[r0,#offShadowSpace_startPC] @ restore PC
+    ldr    rFP,[r0,#offShadowSpace_fp]   @ restore FP
+    ldr    rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r2,#kInterpEntryInstr         @ normal entry reason
+    str    r2,[rGLUE,#offGlue_entryPoint]
+    mov    r1,#1                         @ set changeInterp
+    b      common_gotoBail
+
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ *  r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+    mov     r0, #kInterpEntryInstr
+    bl      common_periodicChecks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun.  If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly.  If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ *  r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ *  r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+    ldr     r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+    orrne   ip, ip, r1                  @ ip<- suspendCount | debuggerActive
+    orrs    ip, ip, r2                  @ ip<- suspend|debugger|profiler; set Z
+
+    bxeq    lr                          @ all zero, return
+
+    /*
+     * One or more interesting events have happened.  Figure out what.
+     *
+     * If debugging or profiling are compiled in, we need to disambiguate.
+     *
+     * r0 still holds the reentry type.
+     */
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+    cmp     ip, #0                      @ want suspend?
+    beq     1f                          @ no, must be debugger/profiler
+
+    stmfd   sp!, {r0, lr}               @ preserve r0 and lr
+#if defined(WITH_JIT)
+    /*
+     * Refresh the Jit's cached copy of profile table pointer.  This pointer
+     * doubles as the Jit's on/off switch.
+     */
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r3, [r3] @ r3 <- pJitProfTable
+    EXPORT_PC()                         @ need for precise GC
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    EXPORT_PC()                         @ need for precise GC
+#endif
+    bl      dvmCheckSuspendPending      @ do full check, suspend if necessary
+    ldmfd   sp!, {r0, lr}               @ restore r0 and lr
+
+    /*
+     * Reload the debugger/profiler enable flags.  We're checking to see
+     * if either of these got set while we were suspended.
+     *
+     * We can't really avoid the #ifdefs here, because the fields don't
+     * exist when the feature is disabled.
+     */
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+
+    orrs    r1, r1, r2
+    beq     2f
+
+1:  @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+    str     r0, [rGLUE, #offGlue_entryPoint]    @ store r0, need for debug/prof
+    add     rPC, rPC, r9                @ update rPC
+    mov     r1, #1                      @ "want switch" = true
+    b       common_gotoBail             @ side exit
+
+2:
+    bx      lr                          @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ *  r1 is "bool changeInterp", indicating if we want to switch to the
+ *     other interpreter or just bail all the way out
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    mov     r0, rGLUE                   @ r0<- glue ptr
+    b       dvmMterpStdBail             @ call(glue, changeInterp)
+
+    @add     r1, r1, #1                  @ using (boolean+1)
+    @add     r0, rGLUE, #offGlue_jmpBuf  @ r0<- &glue->jmpBuf
+    @bl      _longjmp                    @ does not return
+    @bl      common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+    @ 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
+
+    @ 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
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    ldrh    r3, [r0, #offMethod_outsSize]   @ r3<- methodToCall->outsSize
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    @ 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)
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, 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, r3=outSize, r9=regSize
+    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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    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]
+    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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [r2, #offThread_curFrame]   @ self->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, [r2, #offThread_curFrame]   @ self->curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+#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
+
+    @mov     lr, pc                      @ set return addr
+    @ldr     pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+    ldr     r3, [r3]                    @ r3 <- gDvmJit.pProfTable
+#endif
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+    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
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+#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, rGLUE                   @ A0<- glue
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    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:
+    mov     r0, #kInterpEntryReturn
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    cmp     r2, #0                      @ is this a break frame?
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    mov     r1, #0                      @ "want switch" = false
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame]  @ self->curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rGLUE, #offGlue_methodClassDex]
+    str     r10, [r3, #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, [rGLUE, #offGlue_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ 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:
+    mov     r0, #kInterpEntryThrow
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    ldr     r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+    ldr     r9, [r10, #offThread_exception] @ r9<- self->exception
+    mov     r1, r10                     @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [r10, #offThread_exception] @ self->exception = NULL
+
+    /* 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, [rGLUE, #offGlue_method] @ r1<- glue->method
+    mov     r0, r10                     @ 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, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, r10                     @ 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->curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rGLUE, #offGlue_method]    @ glue->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, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    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, [r10, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+    /* fix stack overflow if necessary */
+    ldrb    r1, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, r10                     @ 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, [rGLUE, #offGlue_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rGLUE, #offGlue_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+    ldr     r1, strLogTag
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [r10, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    mov     r1, #0                      @ "want switch" = false
+    b       common_gotoBail             @ bail out
+
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .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_GLUE()              @ pull rPC and rFP out of glue
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    ldr     r0, strArrayIndexException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+    EXPORT_PC()
+    ldr     r0, strArrayStoreException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strArithmeticException
+    ldr     r1, strDivideByZero
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    ldr     r0, strNegativeArraySizeException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    ldr     r0, strNoSuchMethodError
+    mov     r1, #0
+    bl      dvmThrowException
+    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()
+    ldr     r0, strNullPointerException
+    mov     r1, #0
+    bl      dvmThrowException
+    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
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+    /*
+     * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+    .align  2
+strArithmeticException:
+    .word   .LstrArithmeticException
+strArrayIndexException:
+    .word   .LstrArrayIndexException
+strArrayStoreException:
+    .word   .LstrArrayStoreException
+strDivideByZero:
+    .word   .LstrDivideByZero
+strNegativeArraySizeException:
+    .word   .LstrNegativeArraySizeException
+strNoSuchMethodError:
+    .word   .LstrNoSuchMethodError
+strNullPointerException:
+    .word   .LstrNullPointerException
+
+strLogTag:
+    .word   .LstrLogTag
+strExceptionNotCaughtLocally:
+    .word   .LstrExceptionNotCaughtLocally
+
+strNewline:
+    .word   .LstrNewline
+strSqueak:
+    .word   .LstrSqueak
+strPrintHex:
+    .word   .LstrPrintHex
+strPrintLong:
+    .word   .LstrPrintLong
+
+/*
+ * 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"
+.LstrArithmeticException:
+    .asciz  "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz  "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+    .asciz  "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+    .asciz  "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+    .asciz  "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+    .asciz  "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+    .asciz  "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+    .asciz  "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<0x%x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
diff --git a/vm/mterp/armv5te/header.S b/vm/mterp/armv5te/header.S
new file mode 100644
index 0000000..bd6b7ee
--- /dev/null
+++ b/vm/mterp/armv5te/header.S
@@ -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.
+ */
+
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE()     ldr     rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE()       str     rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE()     ldr     rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE()       str     rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE()  ldmia   rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE()    stmia   rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * 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 #2]!", 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_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]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg)    ldr     _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg)     ldr     _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * 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..9f59c8b
--- /dev/null
+++ b/vm/mterp/armv5te/platform.S
@@ -0,0 +1,41 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5.  Essentially a
+ * one-way branch.
+ *
+ * May modify IP.  Does not modify LR.
+ */
+.macro  LDR_PC source
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro  LDMFD_PC regs
+    ldmfd   sp!, {\regs,pc}
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.endm
diff --git a/vm/mterp/armv5te/stub.S b/vm/mterp/armv5te/stub.S
new file mode 100644
index 0000000..54f0778
--- /dev/null
+++ b/vm/mterp/armv5te/stub.S
@@ -0,0 +1,8 @@
+    /* (stub) */
+    SAVE_PC_FP_TO_GLUE()            @ only need to export these two
+    mov     r0, rGLUE               @ glue is first arg to function
+    bl      dvmMterp_${opcode}      @ call
+    LOAD_PC_FP_FROM_GLUE()          @ 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..d79e7c4
--- /dev/null
+++ b/vm/mterp/armv5te/zcmp.S
@@ -0,0 +1,31 @@
+%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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    b${revcmp}  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
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..f5a21eb
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    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/armv6t2/OP_IGET_QUICK.S b/vm/mterp/armv6t2/OP_IGET_QUICK.S
new file mode 100644
index 0000000..0ce2ebc
--- /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
+    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/armv6t2/OP_IGET_WIDE.S b/vm/mterp/armv6t2/OP_IGET_WIDE.S
new file mode 100644
index 0000000..92cd1a6
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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..b69443b
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    $store  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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..334e352
--- /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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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..002eeed
--- /dev/null
+++ b/vm/mterp/armv6t2/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     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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    b${revcmp}  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
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/platform.S b/vm/mterp/armv7-a/platform.S
new file mode 100644
index 0000000..6419da1
--- /dev/null
+++ b/vm/mterp/armv7-a/platform.S
@@ -0,0 +1,51 @@
+/*
+ * ===========================================================================
+ *  CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5.  Essentially a
+ * one-way branch.
+ *
+ * May modify IP.  Does not modify LR.
+ */
+.macro  LDR_PC source
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro  LDMFD_PC regs
+    ldmfd   sp!, {\regs,pc}
+.endm
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ * If the argument is nonzero, emit barrier; otherwise, emit nothing.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb
+#else
+    /* not SMP */
+#endif
+.endm
diff --git a/vm/mterp/c/OP_ADD_DOUBLE.c b/vm/mterp/c/OP_ADD_DOUBLE.c
new file mode 100644
index 0000000..571aeb8
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.c b/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..af952cb
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_ADD_FLOAT.c
new file mode 100644
index 0000000..dab7d33
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_FLOAT_2ADDR.c b/vm/mterp/c/OP_ADD_FLOAT_2ADDR.c
new file mode 100644
index 0000000..a068fd0
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_FLOAT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_ADD_INT.c
new file mode 100644
index 0000000..bfaa590
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT.c
@@ -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.c b/vm/mterp/c/OP_ADD_INT_2ADDR.c
new file mode 100644
index 0000000..dfd3289
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_ADD_INT_LIT16.c
new file mode 100644
index 0000000..442ab40
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_LIT16.c
@@ -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.c b/vm/mterp/c/OP_ADD_INT_LIT8.c
new file mode 100644
index 0000000..1455599
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_ADD_LONG.c
new file mode 100644
index 0000000..25d1f47
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_LONG.c
@@ -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.c b/vm/mterp/c/OP_ADD_LONG_2ADDR.c
new file mode 100644
index 0000000..4ae71bb
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_AGET.c
new file mode 100644
index 0000000..766beaf
--- /dev/null
+++ b/vm/mterp/c/OP_AGET.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_BOOLEAN.c b/vm/mterp/c/OP_AGET_BOOLEAN.c
new file mode 100644
index 0000000..d63bc10
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_BYTE.c b/vm/mterp/c/OP_AGET_BYTE.c
new file mode 100644
index 0000000..61ecc05
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_CHAR.c b/vm/mterp/c/OP_AGET_CHAR.c
new file mode 100644
index 0000000..55e16ef
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_OBJECT.c b/vm/mterp/c/OP_AGET_OBJECT.c
new file mode 100644
index 0000000..903637c
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_OBJECT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_SHORT.c b/vm/mterp/c/OP_AGET_SHORT.c
new file mode 100644
index 0000000..176b4a6
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_WIDE.c b/vm/mterp/c/OP_AGET_WIDE.c
new file mode 100644
index 0000000..e7974cb
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_WIDE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT.c b/vm/mterp/c/OP_AND_INT.c
new file mode 100644
index 0000000..3cf31cb
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT.c
@@ -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.c b/vm/mterp/c/OP_AND_INT_2ADDR.c
new file mode 100644
index 0000000..9f69292
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_AND_INT_LIT16.c
new file mode 100644
index 0000000..19f825b
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_LIT16.c
@@ -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.c b/vm/mterp/c/OP_AND_INT_LIT8.c
new file mode 100644
index 0000000..c0e1315
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_AND_LONG.c
new file mode 100644
index 0000000..1c638fb
--- /dev/null
+++ b/vm/mterp/c/OP_AND_LONG.c
@@ -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.c b/vm/mterp/c/OP_AND_LONG_2ADDR.c
new file mode 100644
index 0000000..23c464d
--- /dev/null
+++ b/vm/mterp/c/OP_AND_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_APUT.c
new file mode 100644
index 0000000..07d3e04
--- /dev/null
+++ b/vm/mterp/c/OP_APUT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_BOOLEAN.c b/vm/mterp/c/OP_APUT_BOOLEAN.c
new file mode 100644
index 0000000..fc69147
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_BYTE.c b/vm/mterp/c/OP_APUT_BYTE.c
new file mode 100644
index 0000000..45aeb0b
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_CHAR.c b/vm/mterp/c/OP_APUT_CHAR.c
new file mode 100644
index 0000000..1553c27
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_OBJECT.c b/vm/mterp/c/OP_APUT_OBJECT.c
new file mode 100644
index 0000000..07e48c6
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_OBJECT.c
@@ -0,0 +1,40 @@
+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) {
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+                NULL);
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->obj.clazz)) {
+                LOGV("Can't put a '%s'(%p) into array type='%s'(%p)\n",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->obj.clazz->descriptor, arrayObj);
+                //dvmDumpClass(obj->clazz);
+                //dvmDumpClass(arrayObj->obj.clazz);
+                dvmThrowException("Ljava/lang/ArrayStoreException;", NULL);
+                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.c b/vm/mterp/c/OP_APUT_SHORT.c
new file mode 100644
index 0000000..a72b5ea
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_WIDE.c b/vm/mterp/c/OP_APUT_WIDE.c
new file mode 100644
index 0000000..39c8cfa
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_WIDE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_ARRAY_LENGTH.c b/vm/mterp/c/OP_ARRAY_LENGTH.c
new file mode 100644
index 0000000..0d5a933
--- /dev/null
+++ b/vm/mterp/c/OP_ARRAY_LENGTH.c
@@ -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.c b/vm/mterp/c/OP_BREAKPOINT.c
new file mode 100644
index 0000000..91cd36c
--- /dev/null
+++ b/vm/mterp/c/OP_BREAKPOINT.c
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+    {
+        /*
+         * 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);
+        LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+            INST_REPLACE_OP(inst, originalOpCode));
+        inst = INST_REPLACE_OP(inst, originalOpCode);
+        FINISH_BKPT(originalOpCode);
+    }
+#else
+    LOGE("Breakpoint hit in non-debug interpreter\n");
+    dvmAbort();
+#endif
+OP_END
diff --git a/vm/mterp/c/OP_CHECK_CAST.c b/vm/mterp/c/OP_CHECK_CAST.c
new file mode 100644
index 0000000..9a7ecfb
--- /dev/null
+++ b/vm/mterp/c/OP_CHECK_CAST.c
@@ -0,0 +1,32 @@
+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)) {
+                dvmThrowExceptionWithClassMessage(
+                    "Ljava/lang/ClassCastException;", obj->clazz->descriptor);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CMPG_DOUBLE.c b/vm/mterp/c/OP_CMPG_DOUBLE.c
new file mode 100644
index 0000000..3f4082c
--- /dev/null
+++ b/vm/mterp/c/OP_CMPG_DOUBLE.c
@@ -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.c b/vm/mterp/c/OP_CMPG_FLOAT.c
new file mode 100644
index 0000000..0bba49e
--- /dev/null
+++ b/vm/mterp/c/OP_CMPG_FLOAT.c
@@ -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.c b/vm/mterp/c/OP_CMPL_DOUBLE.c
new file mode 100644
index 0000000..4da18b4
--- /dev/null
+++ b/vm/mterp/c/OP_CMPL_DOUBLE.c
@@ -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.c b/vm/mterp/c/OP_CMPL_FLOAT.c
new file mode 100644
index 0000000..7916193
--- /dev/null
+++ b/vm/mterp/c/OP_CMPL_FLOAT.c
@@ -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.c b/vm/mterp/c/OP_CMP_LONG.c
new file mode 100644
index 0000000..a0e412c
--- /dev/null
+++ b/vm/mterp/c/OP_CMP_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
diff --git a/vm/mterp/c/OP_CONST.c b/vm/mterp/c/OP_CONST.c
new file mode 100644
index 0000000..e281a51
--- /dev/null
+++ b/vm/mterp/c/OP_CONST.c
@@ -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.c b/vm/mterp/c/OP_CONST_16.c
new file mode 100644
index 0000000..f58f50c
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_16.c
@@ -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.c b/vm/mterp/c/OP_CONST_4.c
new file mode 100644
index 0000000..800ef9a
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_4.c
@@ -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.c b/vm/mterp/c/OP_CONST_CLASS.c
new file mode 100644
index 0000000..9c60a27
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_CLASS.c
@@ -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.c b/vm/mterp/c/OP_CONST_HIGH16.c
new file mode 100644
index 0000000..26b22f4
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_HIGH16.c
@@ -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.c b/vm/mterp/c/OP_CONST_STRING.c
new file mode 100644
index 0000000..748119a
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_STRING.c
@@ -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.c b/vm/mterp/c/OP_CONST_STRING_JUMBO.c
new file mode 100644
index 0000000..435b34c
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_STRING_JUMBO.c
@@ -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.c b/vm/mterp/c/OP_CONST_WIDE.c
new file mode 100644
index 0000000..ccb3955
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE.c
@@ -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.c b/vm/mterp/c/OP_CONST_WIDE_16.c
new file mode 100644
index 0000000..da69f37
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_16.c
@@ -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.c b/vm/mterp/c/OP_CONST_WIDE_32.c
new file mode 100644
index 0000000..ad4acbb
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_32.c
@@ -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.c b/vm/mterp/c/OP_CONST_WIDE_HIGH16.c
new file mode 100644
index 0000000..bcc0664
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_HIGH16.c
@@ -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.c b/vm/mterp/c/OP_DIV_DOUBLE.c
new file mode 100644
index 0000000..d6e4b55
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.c b/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..85a1523
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_DIV_FLOAT.c
new file mode 100644
index 0000000..2c5049b
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_FLOAT_2ADDR.c b/vm/mterp/c/OP_DIV_FLOAT_2ADDR.c
new file mode 100644
index 0000000..cd7b4d9
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_FLOAT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_DIV_INT.c
new file mode 100644
index 0000000..af6e8c6
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT.c
@@ -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.c b/vm/mterp/c/OP_DIV_INT_2ADDR.c
new file mode 100644
index 0000000..80c0da7
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_DIV_INT_LIT16.c
new file mode 100644
index 0000000..4e8d901
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_LIT16.c
@@ -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.c b/vm/mterp/c/OP_DIV_INT_LIT8.c
new file mode 100644
index 0000000..eec5389
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_DIV_LONG.c
new file mode 100644
index 0000000..557b56b
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_LONG.c
@@ -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.c b/vm/mterp/c/OP_DIV_LONG_2ADDR.c
new file mode 100644
index 0000000..00e0e6c
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_DOUBLE_TO_FLOAT.c
new file mode 100644
index 0000000..152e5fd
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_FLOAT.c
@@ -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.c b/vm/mterp/c/OP_DOUBLE_TO_INT.c
new file mode 100644
index 0000000..e210b92
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_INT.c
@@ -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.c b/vm/mterp/c/OP_DOUBLE_TO_LONG.c
new file mode 100644
index 0000000..44d548c
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_LONG.c
@@ -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.c b/vm/mterp/c/OP_EXECUTE_INLINE.c
new file mode 100644
index 0000000..bc10f1a
--- /dev/null
+++ b/vm/mterp/c/OP_EXECUTE_INLINE.c
@@ -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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.c b/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.c
new file mode 100644
index 0000000..a767106
--- /dev/null
+++ b/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.c
@@ -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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_FILLED_NEW_ARRAY.c b/vm/mterp/c/OP_FILLED_NEW_ARRAY.c
new file mode 100644
index 0000000..fad7dbb
--- /dev/null
+++ b/vm/mterp/c/OP_FILLED_NEW_ARRAY.c
@@ -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.c b/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.c
new file mode 100644
index 0000000..06c3a79
--- /dev/null
+++ b/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.c
@@ -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.c b/vm/mterp/c/OP_FILL_ARRAY_DATA.c
new file mode 100644
index 0000000..095b465
--- /dev/null
+++ b/vm/mterp/c/OP_FILL_ARRAY_DATA.c
@@ -0,0 +1,28 @@
+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 */
+            dvmThrowException("Ljava/lang/InternalError;",
+                              "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.c b/vm/mterp/c/OP_FLOAT_TO_DOUBLE.c
new file mode 100644
index 0000000..ea5e7a6
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_DOUBLE.c
@@ -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.c b/vm/mterp/c/OP_FLOAT_TO_INT.c
new file mode 100644
index 0000000..15522f8
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_INT.c
@@ -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.c b/vm/mterp/c/OP_FLOAT_TO_LONG.c
new file mode 100644
index 0000000..03bd30d
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_LONG.c
@@ -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.c b/vm/mterp/c/OP_GOTO.c
new file mode 100644
index 0000000..eed7b9f
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO.c
@@ -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(kInterpEntryInstr, (s1)vdst);
+    FINISH((s1)vdst);
+OP_END
diff --git a/vm/mterp/c/OP_GOTO_16.c b/vm/mterp/c/OP_GOTO_16.c
new file mode 100644
index 0000000..afdccb3
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO_16.c
@@ -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(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_GOTO_32.c b/vm/mterp/c/OP_GOTO_32.c
new file mode 100644
index 0000000..d1cee32
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO_32.c
@@ -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(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_IF_EQ.c b/vm/mterp/c/OP_IF_EQ.c
new file mode 100644
index 0000000..2c3b9b5
--- /dev/null
+++ b/vm/mterp/c/OP_IF_EQ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
diff --git a/vm/mterp/c/OP_IF_EQZ.c b/vm/mterp/c/OP_IF_EQZ.c
new file mode 100644
index 0000000..d2dd1aa
--- /dev/null
+++ b/vm/mterp/c/OP_IF_EQZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GE.c b/vm/mterp/c/OP_IF_GE.c
new file mode 100644
index 0000000..8aa85c4
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GEZ.c b/vm/mterp/c/OP_IF_GEZ.c
new file mode 100644
index 0000000..8c4b78a
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GEZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GT.c b/vm/mterp/c/OP_IF_GT.c
new file mode 100644
index 0000000..d35eb29
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GTZ.c b/vm/mterp/c/OP_IF_GTZ.c
new file mode 100644
index 0000000..63a0073
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GTZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LE.c b/vm/mterp/c/OP_IF_LE.c
new file mode 100644
index 0000000..f4b213a
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LEZ.c b/vm/mterp/c/OP_IF_LEZ.c
new file mode 100644
index 0000000..1d57a50
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LEZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LT.c b/vm/mterp/c/OP_IF_LT.c
new file mode 100644
index 0000000..0233892
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LTZ.c b/vm/mterp/c/OP_IF_LTZ.c
new file mode 100644
index 0000000..b4b9be2
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LTZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
diff --git a/vm/mterp/c/OP_IF_NE.c b/vm/mterp/c/OP_IF_NE.c
new file mode 100644
index 0000000..8da70a5
--- /dev/null
+++ b/vm/mterp/c/OP_IF_NE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_NEZ.c b/vm/mterp/c/OP_IF_NEZ.c
new file mode 100644
index 0000000..209e836
--- /dev/null
+++ b/vm/mterp/c/OP_IF_NEZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
diff --git a/vm/mterp/c/OP_IGET.c b/vm/mterp/c/OP_IGET.c
new file mode 100644
index 0000000..c6333e5
--- /dev/null
+++ b/vm/mterp/c/OP_IGET.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_BOOLEAN.c b/vm/mterp/c/OP_IGET_BOOLEAN.c
new file mode 100644
index 0000000..a5a47be
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_BYTE.c b/vm/mterp/c/OP_IGET_BYTE.c
new file mode 100644
index 0000000..647f311
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_CHAR.c b/vm/mterp/c/OP_IGET_CHAR.c
new file mode 100644
index 0000000..9a8adb0
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT.c b/vm/mterp/c/OP_IGET_OBJECT.c
new file mode 100644
index 0000000..03c9e50
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT.c
@@ -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.c b/vm/mterp/c/OP_IGET_OBJECT_QUICK.c
new file mode 100644
index 0000000..2ac3a54
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT_QUICK.c
@@ -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.c b/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.c
new file mode 100644
index 0000000..3577552
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.c
@@ -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.c b/vm/mterp/c/OP_IGET_QUICK.c
new file mode 100644
index 0000000..b5724cc
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_QUICK.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_SHORT.c b/vm/mterp/c/OP_IGET_SHORT.c
new file mode 100644
index 0000000..3e77789
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_VOLATILE.c b/vm/mterp/c/OP_IGET_VOLATILE.c
new file mode 100644
index 0000000..7a3be56
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE.c b/vm/mterp/c/OP_IGET_WIDE.c
new file mode 100644
index 0000000..cb1fcca
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE.c
@@ -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.c b/vm/mterp/c/OP_IGET_WIDE_QUICK.c
new file mode 100644
index 0000000..adb4fc1
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE_QUICK.c
@@ -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.c b/vm/mterp/c/OP_IGET_WIDE_VOLATILE.c
new file mode 100644
index 0000000..a080823
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE_VOLATILE.c
@@ -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.c b/vm/mterp/c/OP_INSTANCE_OF.c
new file mode 100644
index 0000000..8b8f9d3
--- /dev/null
+++ b/vm/mterp/c/OP_INSTANCE_OF.c
@@ -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.c b/vm/mterp/c/OP_INT_TO_BYTE.c
new file mode 100644
index 0000000..ea75747
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_BYTE.c
@@ -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.c b/vm/mterp/c/OP_INT_TO_CHAR.c
new file mode 100644
index 0000000..45ae0df
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_CHAR.c
@@ -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.c b/vm/mterp/c/OP_INT_TO_DOUBLE.c
new file mode 100644
index 0000000..624c702
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_DOUBLE.c
@@ -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.c b/vm/mterp/c/OP_INT_TO_FLOAT.c
new file mode 100644
index 0000000..fd15199
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_FLOAT.c
@@ -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.c b/vm/mterp/c/OP_INT_TO_LONG.c
new file mode 100644
index 0000000..8bc4223
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_LONG.c
@@ -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.c b/vm/mterp/c/OP_INT_TO_SHORT.c
new file mode 100644
index 0000000..0f06739
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_SHORT.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_DIRECT.c
new file mode 100644
index 0000000..58cfe5b
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT.c
@@ -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_EMPTY.c b/vm/mterp/c/OP_INVOKE_DIRECT_EMPTY.c
new file mode 100644
index 0000000..d649252
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT_EMPTY.c
@@ -0,0 +1,15 @@
+HANDLE_OPCODE(OP_INVOKE_DIRECT_EMPTY /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+#if INTERP_TYPE != INTERP_DBG
+    //LOGI("Ignoring empty\n");
+    FINISH(3);
+#else
+    if (!gDvm.debuggerActive) {
+        //LOGI("Skipping empty\n");
+        FINISH(3);      // don't want it to show up in profiler output
+    } else {
+        //LOGI("Running empty\n");
+        /* fall through to OP_INVOKE_DIRECT */
+        GOTO_invoke(invokeDirect, false);
+    }
+#endif
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.c b/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.c
new file mode 100644
index 0000000..9877bbe
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_INTERFACE.c
new file mode 100644
index 0000000..9c639d5
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_INTERFACE.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.c
new file mode 100644
index 0000000..6244c9e
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.c
@@ -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_STATIC.c b/vm/mterp/c/OP_INVOKE_STATIC.c
new file mode 100644
index 0000000..81f3d62
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_STATIC.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_STATIC_RANGE.c
new file mode 100644
index 0000000..3fc4c35
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_STATIC_RANGE.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_SUPER.c
new file mode 100644
index 0000000..e7baea4
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_SUPER_QUICK.c
new file mode 100644
index 0000000..b66e033
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_QUICK.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.c
new file mode 100644
index 0000000..879497b
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_SUPER_RANGE.c
new file mode 100644
index 0000000..724e3a0
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_RANGE.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_VIRTUAL.c
new file mode 100644
index 0000000..29a4560
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.c
new file mode 100644
index 0000000..244fed4
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c
new file mode 100644
index 0000000..9adb4ad
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c
@@ -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.c b/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.c
new file mode 100644
index 0000000..94671ae
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.c
@@ -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.c b/vm/mterp/c/OP_IPUT.c
new file mode 100644
index 0000000..9d503ef
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_BOOLEAN.c b/vm/mterp/c/OP_IPUT_BOOLEAN.c
new file mode 100644
index 0000000..7fe4929
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_BYTE.c b/vm/mterp/c/OP_IPUT_BYTE.c
new file mode 100644
index 0000000..8a49fb7
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_CHAR.c b/vm/mterp/c/OP_IPUT_CHAR.c
new file mode 100644
index 0000000..b2812c2
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT.c b/vm/mterp/c/OP_IPUT_OBJECT.c
new file mode 100644
index 0000000..dbfb5ab
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT.c
@@ -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.c b/vm/mterp/c/OP_IPUT_OBJECT_QUICK.c
new file mode 100644
index 0000000..8670188
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT_QUICK.c
@@ -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.c b/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.c
new file mode 100644
index 0000000..cce63c1
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.c
@@ -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.c b/vm/mterp/c/OP_IPUT_QUICK.c
new file mode 100644
index 0000000..483b9b1
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_QUICK.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_SHORT.c b/vm/mterp/c/OP_IPUT_SHORT.c
new file mode 100644
index 0000000..0a63ebc
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_VOLATILE.c b/vm/mterp/c/OP_IPUT_VOLATILE.c
new file mode 100644
index 0000000..814379e
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE.c b/vm/mterp/c/OP_IPUT_WIDE.c
new file mode 100644
index 0000000..bb4926c
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE.c
@@ -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.c b/vm/mterp/c/OP_IPUT_WIDE_QUICK.c
new file mode 100644
index 0000000..691630b
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE_QUICK.c
@@ -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.c b/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.c
new file mode 100644
index 0000000..d888b4a
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.c
@@ -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.c b/vm/mterp/c/OP_LONG_TO_DOUBLE.c
new file mode 100644
index 0000000..91b5eb2
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_DOUBLE.c
@@ -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.c b/vm/mterp/c/OP_LONG_TO_FLOAT.c
new file mode 100644
index 0000000..ff1f5fb
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_FLOAT.c
@@ -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.c b/vm/mterp/c/OP_LONG_TO_INT.c
new file mode 100644
index 0000000..87c9a2e
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_INT.c
@@ -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.c b/vm/mterp/c/OP_MONITOR_ENTER.c
new file mode 100644
index 0000000..c9d8999
--- /dev/null
+++ b/vm/mterp/c/OP_MONITOR_ENTER.c
@@ -0,0 +1,20 @@
+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\n", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC, also WITH_MONITOR_TRACKING */
+        dvmLockObject(self, obj);
+#ifdef WITH_DEADLOCK_PREDICTION
+        if (dvmCheckException(self))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MONITOR_EXIT.c b/vm/mterp/c/OP_MONITOR_EXIT.c
new file mode 100644
index 0000000..cb8f99b
--- /dev/null
+++ b/vm/mterp/c/OP_MONITOR_EXIT.c
@@ -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\n", 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.c b/vm/mterp/c/OP_MOVE.c
new file mode 100644
index 0000000..6666199
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE.c
@@ -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.c b/vm/mterp/c/OP_MOVE_16.c
new file mode 100644
index 0000000..53af5d5
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_16.c
@@ -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.c b/vm/mterp/c/OP_MOVE_EXCEPTION.c
new file mode 100644
index 0000000..86587ca
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_EXCEPTION.c
@@ -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.c b/vm/mterp/c/OP_MOVE_FROM16.c
new file mode 100644
index 0000000..59fc285
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_FROM16.c
@@ -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.c b/vm/mterp/c/OP_MOVE_OBJECT.c
new file mode 100644
index 0000000..6ace6d9
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT.c
@@ -0,0 +1 @@
+%include "c/OP_MOVE.c"
diff --git a/vm/mterp/c/OP_MOVE_OBJECT_16.c b/vm/mterp/c/OP_MOVE_OBJECT_16.c
new file mode 100644
index 0000000..7789ef4
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT_16.c
@@ -0,0 +1 @@
+%include "c/OP_MOVE_16.c"
diff --git a/vm/mterp/c/OP_MOVE_OBJECT_FROM16.c b/vm/mterp/c/OP_MOVE_OBJECT_FROM16.c
new file mode 100644
index 0000000..8caf995
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT_FROM16.c
@@ -0,0 +1 @@
+%include "c/OP_MOVE_FROM16.c"
diff --git a/vm/mterp/c/OP_MOVE_RESULT.c b/vm/mterp/c/OP_MOVE_RESULT.c
new file mode 100644
index 0000000..ddf535b
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT.c
@@ -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.c b/vm/mterp/c/OP_MOVE_RESULT_OBJECT.c
new file mode 100644
index 0000000..a8358b1
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT_OBJECT.c
@@ -0,0 +1 @@
+%include "c/OP_MOVE_RESULT.c"
diff --git a/vm/mterp/c/OP_MOVE_RESULT_WIDE.c b/vm/mterp/c/OP_MOVE_RESULT_WIDE.c
new file mode 100644
index 0000000..f6ec8d9
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT_WIDE.c
@@ -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.c b/vm/mterp/c/OP_MOVE_WIDE.c
new file mode 100644
index 0000000..9ee323d
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE.c
@@ -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.c b/vm/mterp/c/OP_MOVE_WIDE_16.c
new file mode 100644
index 0000000..e3d0e16
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE_16.c
@@ -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.c b/vm/mterp/c/OP_MOVE_WIDE_FROM16.c
new file mode 100644
index 0000000..cdbaa2e
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE_FROM16.c
@@ -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.c b/vm/mterp/c/OP_MUL_DOUBLE.c
new file mode 100644
index 0000000..3e65efa
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.c b/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..905b6a7
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_MUL_FLOAT.c
new file mode 100644
index 0000000..310495c
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_FLOAT_2ADDR.c b/vm/mterp/c/OP_MUL_FLOAT_2ADDR.c
new file mode 100644
index 0000000..03623ca
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_FLOAT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_MUL_INT.c
new file mode 100644
index 0000000..b723a29
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT.c
@@ -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.c b/vm/mterp/c/OP_MUL_INT_2ADDR.c
new file mode 100644
index 0000000..f7a179c
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_MUL_INT_LIT16.c
new file mode 100644
index 0000000..3a34dbc
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_LIT16.c
@@ -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.c b/vm/mterp/c/OP_MUL_INT_LIT8.c
new file mode 100644
index 0000000..2ca0036
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_MUL_LONG.c
new file mode 100644
index 0000000..768a2ad
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_LONG.c
@@ -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.c b/vm/mterp/c/OP_MUL_LONG_2ADDR.c
new file mode 100644
index 0000000..1469a22
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_NEG_DOUBLE.c
new file mode 100644
index 0000000..805082c
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_FLOAT.c b/vm/mterp/c/OP_NEG_FLOAT.c
new file mode 100644
index 0000000..00e14f5
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_INT.c b/vm/mterp/c/OP_NEG_INT.c
new file mode 100644
index 0000000..9b97bef
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_INT.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
diff --git a/vm/mterp/c/OP_NEG_LONG.c b/vm/mterp/c/OP_NEG_LONG.c
new file mode 100644
index 0000000..52d553a
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_NEW_ARRAY.c b/vm/mterp/c/OP_NEW_ARRAY.c
new file mode 100644
index 0000000..525c43b
--- /dev/null
+++ b/vm/mterp/c/OP_NEW_ARRAY.c
@@ -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) {
+            dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+            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.c b/vm/mterp/c/OP_NEW_INSTANCE.c
new file mode 100644
index 0000000..f7d4c64
--- /dev/null
+++ b/vm/mterp/c/OP_NEW_INSTANCE.c
@@ -0,0 +1,45 @@
+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();
+
+        /*
+         * 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 (!dvmDexGetResolvedClass(methodClassDex, ref)) {
+            /* Class initialization is still ongoing - abandon the trace */
+            ABORT_JIT_TSELECT();
+        }
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+        //        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.c b/vm/mterp/c/OP_NOP.c
new file mode 100644
index 0000000..d9fd744
--- /dev/null
+++ b/vm/mterp/c/OP_NOP.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_NOT_INT.c b/vm/mterp/c/OP_NOT_INT.c
new file mode 100644
index 0000000..e585f62
--- /dev/null
+++ b/vm/mterp/c/OP_NOT_INT.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
diff --git a/vm/mterp/c/OP_NOT_LONG.c b/vm/mterp/c/OP_NOT_LONG.c
new file mode 100644
index 0000000..4fb393a
--- /dev/null
+++ b/vm/mterp/c/OP_NOT_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT.c b/vm/mterp/c/OP_OR_INT.c
new file mode 100644
index 0000000..19e397b
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT.c
@@ -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.c b/vm/mterp/c/OP_OR_INT_2ADDR.c
new file mode 100644
index 0000000..5d5fde0
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_OR_INT_LIT16.c
new file mode 100644
index 0000000..5a682fa
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_LIT16.c
@@ -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.c b/vm/mterp/c/OP_OR_INT_LIT8.c
new file mode 100644
index 0000000..40b9837
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_OR_LONG.c
new file mode 100644
index 0000000..62f4dd3
--- /dev/null
+++ b/vm/mterp/c/OP_OR_LONG.c
@@ -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.c b/vm/mterp/c/OP_OR_LONG_2ADDR.c
new file mode 100644
index 0000000..03a7c65
--- /dev/null
+++ b/vm/mterp/c/OP_OR_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_PACKED_SWITCH.c
new file mode 100644
index 0000000..d0986dc
--- /dev/null
+++ b/vm/mterp/c/OP_PACKED_SWITCH.c
@@ -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, vsrc2);
+        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();
+            dvmThrowException("Ljava/lang/InternalError;", "bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)\n", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_REM_DOUBLE.c b/vm/mterp/c/OP_REM_DOUBLE.c
new file mode 100644
index 0000000..343e25e
--- /dev/null
+++ b/vm/mterp/c/OP_REM_DOUBLE.c
@@ -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.c b/vm/mterp/c/OP_REM_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..392eacf
--- /dev/null
+++ b/vm/mterp/c/OP_REM_DOUBLE_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_REM_FLOAT.c
new file mode 100644
index 0000000..9604b30
--- /dev/null
+++ b/vm/mterp/c/OP_REM_FLOAT.c
@@ -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.c b/vm/mterp/c/OP_REM_FLOAT_2ADDR.c
new file mode 100644
index 0000000..87bb31e
--- /dev/null
+++ b/vm/mterp/c/OP_REM_FLOAT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_REM_INT.c
new file mode 100644
index 0000000..0e3efe6
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT.c
@@ -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.c b/vm/mterp/c/OP_REM_INT_2ADDR.c
new file mode 100644
index 0000000..5801f35
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_REM_INT_LIT16.c
new file mode 100644
index 0000000..a4dabe8
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_LIT16.c
@@ -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.c b/vm/mterp/c/OP_REM_INT_LIT8.c
new file mode 100644
index 0000000..2bc88be
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_REM_LONG.c
new file mode 100644
index 0000000..fb2ba71
--- /dev/null
+++ b/vm/mterp/c/OP_REM_LONG.c
@@ -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.c b/vm/mterp/c/OP_REM_LONG_2ADDR.c
new file mode 100644
index 0000000..3049770
--- /dev/null
+++ b/vm/mterp/c/OP_REM_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_RETURN.c
new file mode 100644
index 0000000..89d3b3b
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN.c
@@ -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.c b/vm/mterp/c/OP_RETURN_OBJECT.c
new file mode 100644
index 0000000..87cca39
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_OBJECT.c
@@ -0,0 +1 @@
+%include "c/OP_RETURN.c"
diff --git a/vm/mterp/c/OP_RETURN_VOID.c b/vm/mterp/c/OP_RETURN_VOID.c
new file mode 100644
index 0000000..7431f60
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_VOID.c
@@ -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_WIDE.c b/vm/mterp/c/OP_RETURN_WIDE.c
new file mode 100644
index 0000000..a27bfd4
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_WIDE.c
@@ -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.c b/vm/mterp/c/OP_RSUB_INT.c
new file mode 100644
index 0000000..336ca55
--- /dev/null
+++ b/vm/mterp/c/OP_RSUB_INT.c
@@ -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.c b/vm/mterp/c/OP_RSUB_INT_LIT8.c
new file mode 100644
index 0000000..742854b
--- /dev/null
+++ b/vm/mterp/c/OP_RSUB_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_SGET.c
new file mode 100644
index 0000000..5297cd7
--- /dev/null
+++ b/vm/mterp/c/OP_SGET.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_BOOLEAN.c b/vm/mterp/c/OP_SGET_BOOLEAN.c
new file mode 100644
index 0000000..7c5d45e
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_BYTE.c b/vm/mterp/c/OP_SGET_BYTE.c
new file mode 100644
index 0000000..b37cab4
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_CHAR.c b/vm/mterp/c/OP_SGET_CHAR.c
new file mode 100644
index 0000000..7ede5ec
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_OBJECT.c b/vm/mterp/c/OP_SGET_OBJECT.c
new file mode 100644
index 0000000..9f3b63d
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_OBJECT.c
@@ -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.c b/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.c
new file mode 100644
index 0000000..0a9049f
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.c
@@ -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.c b/vm/mterp/c/OP_SGET_SHORT.c
new file mode 100644
index 0000000..cd1fe4c
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_VOLATILE.c b/vm/mterp/c/OP_SGET_VOLATILE.c
new file mode 100644
index 0000000..6713a54
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_WIDE.c b/vm/mterp/c/OP_SGET_WIDE.c
new file mode 100644
index 0000000..817c6e7
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_WIDE.c
@@ -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.c b/vm/mterp/c/OP_SGET_WIDE_VOLATILE.c
new file mode 100644
index 0000000..26a67bf
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_WIDE_VOLATILE.c
@@ -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.c b/vm/mterp/c/OP_SHL_INT.c
new file mode 100644
index 0000000..e32af49
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT.c
@@ -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.c b/vm/mterp/c/OP_SHL_INT_2ADDR.c
new file mode 100644
index 0000000..c5f5399
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_SHL_INT_LIT8.c
new file mode 100644
index 0000000..009d14e
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_SHL_LONG.c
new file mode 100644
index 0000000..f6b502a
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_LONG.c
@@ -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.c b/vm/mterp/c/OP_SHL_LONG_2ADDR.c
new file mode 100644
index 0000000..b8a9954
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_SHR_INT.c
new file mode 100644
index 0000000..3834824
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT.c
@@ -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.c b/vm/mterp/c/OP_SHR_INT_2ADDR.c
new file mode 100644
index 0000000..c76c178
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_SHR_INT_LIT8.c
new file mode 100644
index 0000000..e2657d7
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_SHR_LONG.c
new file mode 100644
index 0000000..357a666
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_LONG.c
@@ -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.c b/vm/mterp/c/OP_SHR_LONG_2ADDR.c
new file mode 100644
index 0000000..43e27ea
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_SPARSE_SWITCH.c
new file mode 100644
index 0000000..7f3648a
--- /dev/null
+++ b/vm/mterp/c/OP_SPARSE_SWITCH.c
@@ -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, vsrc2);
+        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();
+            dvmThrowException("Ljava/lang/InternalError;", "bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)\n", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
diff --git a/vm/mterp/c/OP_SPUT.c b/vm/mterp/c/OP_SPUT.c
new file mode 100644
index 0000000..286e64c
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_BOOLEAN.c b/vm/mterp/c/OP_SPUT_BOOLEAN.c
new file mode 100644
index 0000000..55ceb11
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_BYTE.c b/vm/mterp/c/OP_SPUT_BYTE.c
new file mode 100644
index 0000000..d242fe1
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_CHAR.c b/vm/mterp/c/OP_SPUT_CHAR.c
new file mode 100644
index 0000000..18a2f06
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_OBJECT.c b/vm/mterp/c/OP_SPUT_OBJECT.c
new file mode 100644
index 0000000..fb223d6
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_OBJECT.c
@@ -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.c b/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.c
new file mode 100644
index 0000000..38d6c0d
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.c
@@ -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.c b/vm/mterp/c/OP_SPUT_SHORT.c
new file mode 100644
index 0000000..c6cd8d6
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_VOLATILE.c b/vm/mterp/c/OP_SPUT_VOLATILE.c
new file mode 100644
index 0000000..7899d05
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_WIDE.c b/vm/mterp/c/OP_SPUT_WIDE.c
new file mode 100644
index 0000000..0c74651
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_WIDE.c
@@ -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.c b/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.c
new file mode 100644
index 0000000..bdf552c
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.c
@@ -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.c b/vm/mterp/c/OP_SUB_DOUBLE.c
new file mode 100644
index 0000000..64a112d
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.c b/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..5870400
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_SUB_FLOAT.c
new file mode 100644
index 0000000..96c5fbd
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_FLOAT_2ADDR.c b/vm/mterp/c/OP_SUB_FLOAT_2ADDR.c
new file mode 100644
index 0000000..802935c
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_FLOAT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_SUB_INT.c
new file mode 100644
index 0000000..2c1006d
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_INT.c
@@ -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.c b/vm/mterp/c/OP_SUB_INT_2ADDR.c
new file mode 100644
index 0000000..328ed33
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_SUB_LONG.c
new file mode 100644
index 0000000..bace11a
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_LONG.c
@@ -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.c b/vm/mterp/c/OP_SUB_LONG_2ADDR.c
new file mode 100644
index 0000000..f234dd4
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_THROW.c
new file mode 100644
index 0000000..0dcaced
--- /dev/null
+++ b/vm/mterp/c/OP_THROW.c
@@ -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\n");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
diff --git a/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c
new file mode 100644
index 0000000..85cc8fb
--- /dev/null
+++ b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c
@@ -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.c b/vm/mterp/c/OP_UNUSED_3E.c
new file mode 100644
index 0000000..9ecf8e3
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_3E.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_3F.c b/vm/mterp/c/OP_UNUSED_3F.c
new file mode 100644
index 0000000..9d1d68d
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_3F.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_40.c b/vm/mterp/c/OP_UNUSED_40.c
new file mode 100644
index 0000000..f73a59c
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_40.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_41.c b/vm/mterp/c/OP_UNUSED_41.c
new file mode 100644
index 0000000..38747e6
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_41.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_42.c b/vm/mterp/c/OP_UNUSED_42.c
new file mode 100644
index 0000000..154d293
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_42.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_43.c b/vm/mterp/c/OP_UNUSED_43.c
new file mode 100644
index 0000000..c7e702c
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_43.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_73.c b/vm/mterp/c/OP_UNUSED_73.c
new file mode 100644
index 0000000..85aa95f
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_73.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_79.c b/vm/mterp/c/OP_UNUSED_79.c
new file mode 100644
index 0000000..1fa86e9
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_79.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_7A.c b/vm/mterp/c/OP_UNUSED_7A.c
new file mode 100644
index 0000000..beab006
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_7A.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_F1.c b/vm/mterp/c/OP_UNUSED_F1.c
new file mode 100644
index 0000000..af26195
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_F1.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_F1)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_FF.c b/vm/mterp/c/OP_UNUSED_FF.c
new file mode 100644
index 0000000..f4743db
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_FF.c
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    LOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT.c b/vm/mterp/c/OP_USHR_INT.c
new file mode 100644
index 0000000..7596c94
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT.c
@@ -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.c b/vm/mterp/c/OP_USHR_INT_2ADDR.c
new file mode 100644
index 0000000..5fa2b94
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_USHR_INT_LIT8.c
new file mode 100644
index 0000000..0d325d7
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_USHR_LONG.c
new file mode 100644
index 0000000..9b7e757
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_LONG.c
@@ -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.c b/vm/mterp/c/OP_USHR_LONG_2ADDR.c
new file mode 100644
index 0000000..4ac0598
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_LONG_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_XOR_INT.c
new file mode 100644
index 0000000..d591909
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT.c
@@ -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.c b/vm/mterp/c/OP_XOR_INT_2ADDR.c
new file mode 100644
index 0000000..7d32879
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_2ADDR.c
@@ -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.c b/vm/mterp/c/OP_XOR_INT_LIT16.c
new file mode 100644
index 0000000..c204b79
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_LIT16.c
@@ -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.c b/vm/mterp/c/OP_XOR_INT_LIT8.c
new file mode 100644
index 0000000..f01773a
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_LIT8.c
@@ -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.c b/vm/mterp/c/OP_XOR_LONG.c
new file mode 100644
index 0000000..d3dbc4c
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_LONG.c
@@ -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.c b/vm/mterp/c/OP_XOR_LONG_2ADDR.c
new file mode 100644
index 0000000..e7a50f4
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/gotoTargets.c b/vm/mterp/c/gotoTargets.c
new file mode 100644
index 0000000..0db6fb7
--- /dev/null
+++ b/vm/mterp/c/gotoTargets.c
@@ -0,0 +1,992 @@
+/*
+ * 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)
+    {
+        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)) {
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "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'\n", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            LOGE("non-int primitives not implemented\n");
+            dvmThrowException("Ljava/lang/InternalError;",
+                "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*) 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 = newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+    {
+        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\n");
+                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) && (INTERP_TYPE == INTERP_DBG)
+        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.
+             */
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+                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\n");
+                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.
+             */
+            dvmThrowException("Ljava/lang/NoSuchMethodError;",
+                baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+            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;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisClass;
+#endif
+
+        /*
+         * 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 (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        EXPORT_PC();
+
+        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\n");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    EXPORT_PC();
+
+    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\n");
+            GOTO_exceptionThrown();
+        }
+
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+            /* Class initialization is still ongoing */
+            ABORT_JIT_TSELECT();
+        }
+    }
+    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();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisPtr->clazz;
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s\n",
+            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) {
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < 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)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s\n",
+            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(kInterpEntryReturn, 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
+#if (INTERP_TYPE == INTERP_DBG)
+        TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = saveArea->prevFrame;
+        assert(fp != NULL);
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+            /* Let the Jit know the return is terminating normally */
+            CHECK_JIT_VOID();
+#endif
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 {
+            //LOGE("Unknown invoke instr %02x at %d\n",
+            //    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;
+
+        /*
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+        // Something threw during trace selection - abort the current trace
+        ABORT_JIT_TSELECT();
+#endif
+        /*
+         * 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);
+
+        LOGV("Handling exception %s at %s:%d\n",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+        /*
+         * Tell the debugger about it.
+         *
+         * 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 (gDvm.debuggerActive) {
+            void* catchFrame;
+            catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                        exception, true, &catchFrame);
+            dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+                catchRelPc, exception);
+        }
+#endif
+
+        /*
+         * 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*)&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
+            LOGD("Exception %s from %s:%d not caught locally\n",
+                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;
+            LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 */
+                LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+            //    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)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+            debugIsMethodEntry = true;              // profiling, debugging
+#endif
+            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 */
+#ifdef USE_INDIRECT_REF
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+            self->curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+            {
+                ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                        methodToCall->name, methodToCall->shorty);
+            }
+
+#if defined(WITH_JIT)
+            /* Allow the Jit to end any pending trace building */
+            CHECK_JIT_VOID();
+#endif
+
+            /*
+             * 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 (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->curFrame = fp;
+
+            /*
+             * 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)) {
+                LOGV("Exception thrown by/below native code\n");
+                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 {
+                //LOGE("Unknown invoke instr %02x at %d\n",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
diff --git a/vm/mterp/c/header.c b/vm/mterp/c/header.c
new file mode 100644
index 0000000..aaf6dab
--- /dev/null
+++ b/vm/mterp/c/header.c
@@ -0,0 +1,405 @@
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
diff --git a/vm/mterp/c/opcommon.c b/vm/mterp/c/opcommon.c
new file mode 100644
index 0000000..43ee5bc
--- /dev/null
+++ b/vm/mterp/c/opcommon.c
@@ -0,0 +1,656 @@
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
diff --git a/vm/mterp/common/FindInterface.h b/vm/mterp/common/FindInterface.h
new file mode 100644
index 0000000..021ed65
--- /dev/null
+++ b/vm/mterp/common/FindInterface.h
@@ -0,0 +1,37 @@
+/*
+ * 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"
+
+/*
+ * 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)
+
+    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..aeed88b
--- /dev/null
+++ b/vm/mterp/common/asm-constants.h
@@ -0,0 +1,326 @@
+/*
+ * 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(_type, _field) != _offset) {                               \
+        LOGE("Bad asm offset %s (%d), should be %d\n",                      \
+            #_name, _offset, offsetof(_type, _field));                      \
+        failed = true;                                                      \
+    }
+# define MTERP_SIZEOF(_name, _type, _size)                                  \
+    if (sizeof(_type) != (_size)) {                                         \
+        LOGE("Bad asm sizeof %s (%d), should be %d\n",                      \
+            #_name, (_size), sizeof(_type));                                \
+        failed = true;                                                      \
+    }
+# define MTERP_CONSTANT(_name, _value)                                      \
+    if ((_name) != (_value)) {                                              \
+        LOGE("Bad asm constant %s (%d), should be %d\n",                    \
+            #_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__)
+# 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.
+ */
+
+/* globals (sanity check for LDR vs LDRB) */
+MTERP_SIZEOF(sizeofGlobal_debuggerActive, gDvm.debuggerActive, 1)
+MTERP_SIZEOF(sizeofGlobal_activeProfilers, gDvm.activeProfilers, 4)
+
+/* MterpGlue fields */
+MTERP_OFFSET(offGlue_pc,                MterpGlue, pc, 0)
+MTERP_OFFSET(offGlue_fp,                MterpGlue, fp, 4)
+MTERP_OFFSET(offGlue_retval,            MterpGlue, retval, 8)
+MTERP_OFFSET(offGlue_method,            MterpGlue, method, 16)
+MTERP_OFFSET(offGlue_methodClassDex,    MterpGlue, methodClassDex, 20)
+MTERP_OFFSET(offGlue_self,              MterpGlue, self, 24)
+MTERP_OFFSET(offGlue_bailPtr,           MterpGlue, bailPtr, 28)
+MTERP_OFFSET(offGlue_interpStackEnd,    MterpGlue, interpStackEnd, 32)
+MTERP_OFFSET(offGlue_pSelfSuspendCount, MterpGlue, pSelfSuspendCount, 36)
+MTERP_OFFSET(offGlue_cardTable,         MterpGlue, cardTable, 40)
+MTERP_OFFSET(offGlue_pDebuggerActive,   MterpGlue, pDebuggerActive, 44)
+MTERP_OFFSET(offGlue_pActiveProfilers,  MterpGlue, pActiveProfilers, 48)
+MTERP_OFFSET(offGlue_entryPoint,        MterpGlue, entryPoint, 52)
+#if defined(WITH_JIT)
+MTERP_OFFSET(offGlue_pJitProfTable,     MterpGlue, pJitProfTable, 60)
+MTERP_OFFSET(offGlue_jitState,          MterpGlue, jitState, 64)
+MTERP_OFFSET(offGlue_jitResumeNPC,      MterpGlue, jitResumeNPC, 68)
+MTERP_OFFSET(offGlue_jitResumeDPC,      MterpGlue, jitResumeDPC, 72)
+MTERP_OFFSET(offGlue_jitThreshold,      MterpGlue, jitThreshold, 76)
+MTERP_OFFSET(offGlue_ppJitProfTable,    MterpGlue, ppJitProfTable, 80)
+MTERP_OFFSET(offGlue_icRechainCount,    MterpGlue, icRechainCount, 84)
+#endif
+/* make sure all JValue union members are stored at the same offset */
+MTERP_OFFSET(offGlue_retval_z,          MterpGlue, retval.z, 8)
+MTERP_OFFSET(offGlue_retval_i,          MterpGlue, retval.i, 8)
+MTERP_OFFSET(offGlue_retval_j,          MterpGlue, retval.j, 8)
+MTERP_OFFSET(offGlue_retval_l,          MterpGlue, retval.l, 8)
+
+/* 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_glue,        ShadowSpace, glue, 8)
+MTERP_OFFSET(offShadowSpace_jitExitState,ShadowSpace, jitExitState, 12)
+MTERP_OFFSET(offShadowSpace_svState,     ShadowSpace, selfVerificationState, 16)
+MTERP_OFFSET(offShadowSpace_shadowFP,    ShadowSpace, shadowFP, 24)
+MTERP_OFFSET(offShadowSpace_interpState, ShadowSpace, interpState, 32)
+#endif
+
+/* InstField fields */
+#ifdef PROFILE_FIELD_ACCESS
+MTERP_OFFSET(offInstField_byteOffset,   InstField, byteOffset, 24)
+#else
+MTERP_OFFSET(offInstField_byteOffset,   InstField, byteOffset, 16)
+#endif
+
+/* Field fields */
+MTERP_OFFSET(offField_clazz,            Field, clazz, 0)
+
+/* StaticField fields */
+#ifdef PROFILE_FIELD_ACCESS
+MTERP_OFFSET(offStaticField_value,      StaticField, value, 24)
+#else
+MTERP_OFFSET(offStaticField_value,      StaticField, value, 16)
+#endif
+
+/* 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_stackOverflowed, Thread, stackOverflowed, 36)
+MTERP_OFFSET(offThread_curFrame,        Thread, curFrame, 40)
+MTERP_OFFSET(offThread_exception,       Thread, exception, 44)
+
+#if defined(WITH_JIT)
+MTERP_OFFSET(offThread_inJitCodeCache,  Thread, inJitCodeCache, 72)
+#if defined(WITH_SELF_VERIFICATION)
+MTERP_OFFSET(offThread_shadowSpace,     Thread, shadowSpace, 76)
+#ifdef USE_INDIRECT_REF
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.segmentState.all, 80)
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.nextEntry, 80)
+#endif
+#else
+#ifdef USE_INDIRECT_REF
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.segmentState.all, 76)
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.nextEntry, 76)
+#endif
+#endif
+#else
+#ifdef USE_INDIRECT_REF
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.segmentState.all, 72)
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+                                Thread, jniLocalRefTable.nextEntry, 72)
+#endif
+#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)
+
+/* InterpEntry enumeration */
+MTERP_SIZEOF(sizeofClassStatus,         InterpEntry, MTERP_SMALL_ENUM)
+MTERP_CONSTANT(kInterpEntryInstr,   0)
+MTERP_CONSTANT(kInterpEntryReturn,  1)
+MTERP_CONSTANT(kInterpEntryThrow,   2)
+#if defined(WITH_JIT)
+MTERP_CONSTANT(kInterpEntryResume,  3)
+#endif
+
+#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(kJitSingleStep,          6)
+MTERP_CONSTANT(kJitSingleStepEnd,       7)
+MTERP_CONSTANT(kJitDone,                8)
+
+#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)
+
+/* 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)
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/config-allstubs b/vm/mterp/config-allstubs
new file mode 100644
index 0000000..23796d8
--- /dev/null
+++ b/vm/mterp/config-allstubs
@@ -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.
+
+#
+# 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.
+#
+
+handler-size 64
+
+# C file header and basic definitions
+import c/header.c
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# common defs for the C opcodes
+import c/opcommon.c
+
+# 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.c
+
+# "helper" code
+import c/gotoTargets.c
+
+# finish
+import cstubs/enddefs.c
diff --git a/vm/mterp/config-armv4t b/vm/mterp/config-armv4t
new file mode 100644
index 0000000..ed39b42
--- /dev/null
+++ b/vm/mterp/config-armv4t
@@ -0,0 +1,68 @@
+# 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 ARMv4T architecture targets.  This is largely pulled
+# from the ARMv5TE sources, but we can't use certain instructions introduced
+# in ARMv5 (BLX, CLZ, LDC2, MCR2, MRC2, STC2) or ARMv5TE (PLD, LDRD, MCRR,
+# MRRC, QADD, QDADD, QDSUB, QSUB, SMLA, SMLAL, SMLAW, SMUL, SMULW, STRD).
+#
+
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# 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
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+    op OP_AGET_WIDE armv4t
+    op OP_APUT_WIDE armv4t
+    op OP_IGET_WIDE armv4t
+    op OP_IGET_WIDE_QUICK armv4t
+    op OP_IPUT_WIDE armv4t
+    op OP_IPUT_WIDE_QUICK armv4t
+    op OP_SGET_WIDE armv4t
+    op OP_SPUT_WIDE armv4t
+    op OP_IGET_WIDE_VOLATILE armv4t
+    op OP_IPUT_WIDE_VOLATILE armv4t
+    op OP_SGET_WIDE_VOLATILE armv4t
+    op OP_SPUT_WIDE_VOLATILE armv4t
+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
+import armv5te/debug.c
diff --git a/vm/mterp/config-armv5te b/vm/mterp/config-armv5te
new file mode 100644
index 0000000..2dceb04
--- /dev/null
+++ b/vm/mterp/config-armv5te
@@ -0,0 +1,54 @@
+# 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-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# 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
+
+# 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.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-armv5te-vfp b/vm/mterp/config-armv5te-vfp
new file mode 100644
index 0000000..ce0c521
--- /dev/null
+++ b/vm/mterp/config-armv5te-vfp
@@ -0,0 +1,104 @@
+# 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-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# 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
+
+# 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.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-armv7-a b/vm/mterp/config-armv7-a
new file mode 100644
index 0000000..e66640c
--- /dev/null
+++ b/vm/mterp/config-armv7-a
@@ -0,0 +1,166 @@
+# 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-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# 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 armv7-a/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.c
+
+# 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_2ADDR armv6t2
+    op OP_DIV_INT_LIT16 armv6t2
+    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_2ADDR armv6t2
+    op OP_REM_INT_LIT16 armv6t2
+    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.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-armv7-a-neon b/vm/mterp/config-armv7-a-neon
new file mode 100644
index 0000000..e66640c
--- /dev/null
+++ b/vm/mterp/config-armv7-a-neon
@@ -0,0 +1,166 @@
+# 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-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# 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 armv7-a/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.c
+
+# 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_2ADDR armv6t2
+    op OP_DIV_INT_LIT16 armv6t2
+    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_2ADDR armv6t2
+    op OP_REM_INT_LIT16 armv6t2
+    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.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-portdbg b/vm/mterp/config-portdbg
new file mode 100644
index 0000000..c6982d7
--- /dev/null
+++ b/vm/mterp/config-portdbg
@@ -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.
+
+#
+# Configuration for "portdbg" target, a/k/a the portable interpreter with
+# debugging enabled.
+#
+
+#handler-size 64
+
+# C file header and basic definitions
+import c/header.c
+
+# simple def to specify the "debug" interp
+import portable/portdbg.c
+
+# C pre-processor defines for stub C instructions
+import portable/stubdefs.c
+
+# common defs for the C opcodes
+import c/opcommon.c
+
+# debug-only code
+import portable/debug.c
+
+# entry point
+import portable/entry.c
+
+# opcode list; argument to op-start is default directory
+op-start c
+    # concatenate all C implementations
+op-end
+
+# "helper" code
+import c/gotoTargets.c
+
+# finish
+import portable/enddefs.c
diff --git a/vm/mterp/config-portstd b/vm/mterp/config-portstd
new file mode 100644
index 0000000..41ecb4f
--- /dev/null
+++ b/vm/mterp/config-portstd
@@ -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.
+
+#
+# Configuration for "portstd" target, a/k/a the portable interpreter.  This
+# differs from other "mterp" targets in that it doesn't use the usual
+# stub/glue mechanism, and defines different entry points.  We generate it
+# here because it's convenient.
+#
+
+#handler-size 64
+
+# C file header and basic definitions
+import c/header.c
+
+# simple def to specify the "standard" interp
+import portable/portstd.c
+
+# C pre-processor defines for stub C instructions
+import portable/stubdefs.c
+
+# common defs for the C opcodes
+import c/opcommon.c
+
+# entry point
+import portable/entry.c
+
+# opcode list; argument to op-start is default directory
+op-start c
+    # concatenate all C implementations
+op-end
+
+# "helper" code
+import c/gotoTargets.c
+
+# finish
+import portable/enddefs.c
diff --git a/vm/mterp/config-x86 b/vm/mterp/config-x86
new file mode 100644
index 0000000..627e06c
--- /dev/null
+++ b/vm/mterp/config-x86
@@ -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.
+
+#
+# Configuration for "desktop" targets.
+#
+
+handler-size 64
+
+# source for the instruction table stub
+asm-stub x86/stub.S
+
+# C file header and basic definitions
+import c/header.c
+import x86/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# common defs for the C opcodes
+import c/opcommon.c
+
+# 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-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.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import x86/footer.S
diff --git a/vm/mterp/config-x86-atom b/vm/mterp/config-x86-atom
new file mode 100644
index 0000000..e1c3f07
--- /dev/null
+++ b/vm/mterp/config-x86-atom
@@ -0,0 +1,300 @@
+# 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.
+
+# Specifies the size of the assembly region in bytes
+handler-size 64
+
+# source for the instruction table stub
+asm-stub x86-atom/stub.S
+
+# file header, macros and definitions
+import c/header.c
+import x86-atom/header.S
+
+# common defs for the C helper; include this before the instruction handlers
+import cstubs/stubdefs.c
+import c/opcommon.c
+
+# start of opcode list; command gives default directory location of instruction files
+op-start x86-atom
+
+#op OP_ADD_DOUBLE_2ADDR c
+#op OP_ADD_DOUBLE c
+#op OP_ADD_FLOAT_2ADDR c
+#op OP_ADD_FLOAT c
+#op OP_ADD_INT_2ADDR c
+#op OP_ADD_INT_LIT16 c
+#op OP_ADD_INT_LIT8 c
+#op OP_ADD_INT c
+#op OP_ADD_LONG_2ADDR c
+#op OP_ADD_LONG c
+#op OP_AGET_BOOLEAN c
+#op OP_AGET_BYTE c
+#op OP_AGET_CHAR c
+#op OP_AGET_OBJECT c
+#op OP_AGET c
+#op OP_AGET_SHORT c
+#op OP_AGET_WIDE c
+#op OP_AND_INT_2ADDR c
+#op OP_AND_INT_LIT16 c
+#op OP_AND_INT_LIT8 c
+#op OP_AND_INT c
+#op OP_AND_LONG_2ADDR c
+#op OP_AND_LONG c
+#op OP_APUT_BOOLEAN c
+#op OP_APUT_BYTE c
+#op OP_APUT_CHAR c
+#op OP_APUT_OBJECT c
+#op OP_APUT c
+#op OP_APUT_SHORT c
+#op OP_APUT_WIDE c
+#op OP_ARRAY_LENGTH c
+#op OP_CHECK_CAST c
+#op OP_CMPG_DOUBLE c
+#op OP_CMPG_FLOAT c
+#op OP_CMPL_DOUBLE c
+#op OP_CMPL_FLOAT c
+#op OP_CMP_LONG c
+#op OP_CONST_16 c
+#op OP_CONST_4 c
+#op OP_CONST_CLASS c
+#op OP_CONST_HIGH16 c
+#op OP_CONST c
+#op OP_CONST_STRING_JUMBO c
+#op OP_CONST_STRING c
+#op OP_CONST_WIDE_16 c
+#op OP_CONST_WIDE_32 c
+#op OP_CONST_WIDE_HIGH16 c
+#op OP_CONST_WIDE c
+#op OP_DIV_DOUBLE_2ADDR c
+#op OP_DIV_DOUBLE c
+#op OP_DIV_FLOAT_2ADDR c
+#op OP_DIV_FLOAT c
+#op OP_DIV_INT_2ADDR c
+#op OP_DIV_INT_LIT16 c
+#op OP_DIV_INT_LIT8 c
+#op OP_DIV_INT c
+#op OP_DIV_LONG_2ADDR c
+#op OP_DIV_LONG c
+#op OP_DOUBLE_TO_FLOAT c
+#op OP_DOUBLE_TO_INT c
+#op OP_DOUBLE_TO_LONG c
+#op OP_EXECUTE_INLINE c
+#op OP_FILL_ARRAY_DATA c
+#op OP_FILLED_NEW_ARRAY_RANGE c
+#op OP_FILLED_NEW_ARRAY c
+#op OP_FLOAT_TO_DOUBLE c
+#op OP_FLOAT_TO_INT c
+#op OP_FLOAT_TO_LONG c
+#op OP_GOTO_16 c
+#op OP_GOTO_32 c
+#op OP_GOTO c
+#op OP_IF_EQ c
+#op OP_IF_EQZ c
+#op OP_IF_GE c
+#op OP_IF_GEZ c
+#op OP_IF_GT c
+#op OP_IF_GTZ c
+#op OP_IF_LE c
+#op OP_IF_LEZ c
+#op OP_IF_LT c
+#op OP_IF_LTZ c
+#op OP_IF_NE c
+#op OP_IF_NEZ c
+#op OP_IGET_BOOLEAN c
+#op OP_IGET_BYTE c
+#op OP_IGET_CHAR c
+#op OP_IGET_OBJECT_QUICK c
+#op OP_IGET_OBJECT c
+#op OP_IGET_QUICK c
+#op OP_IGET c
+#op OP_IGET_SHORT c
+#op OP_IGET_WIDE_QUICK c
+#op OP_IGET_WIDE c
+#op OP_INSTANCE_OF c
+#op OP_INT_TO_BYTE c
+#op OP_INT_TO_CHAR c
+#op OP_INT_TO_DOUBLE c
+#op OP_INT_TO_FLOAT c
+#op OP_INT_TO_LONG c
+#op OP_INT_TO_SHORT c
+#op OP_INVOKE_DIRECT_EMPTY c
+#op OP_INVOKE_DIRECT_RANGE c
+#op OP_INVOKE_DIRECT c
+#op OP_INVOKE_INTERFACE_RANGE c
+#op OP_INVOKE_INTERFACE c
+#op OP_INVOKE_STATIC_RANGE c
+#op OP_INVOKE_STATIC c
+#op OP_INVOKE_SUPER_QUICK_RANGE c
+#op OP_INVOKE_SUPER_QUICK c
+#op OP_INVOKE_SUPER_RANGE c
+#op OP_INVOKE_SUPER c
+#op OP_INVOKE_VIRTUAL_QUICK_RANGE c
+#op OP_INVOKE_VIRTUAL_QUICK c
+#op OP_INVOKE_VIRTUAL_RANGE c
+#op OP_INVOKE_VIRTUAL c
+#op OP_IPUT_BOOLEAN c
+#op OP_IPUT_BYTE c
+#op OP_IPUT_CHAR c
+#op OP_IPUT_OBJECT_QUICK c
+#op OP_IPUT_OBJECT c
+#op OP_IPUT_QUICK c
+#op OP_IPUT c
+#op OP_IPUT_SHORT c
+#op OP_IPUT_WIDE_QUICK c
+#op OP_IPUT_WIDE c
+#op OP_LONG_TO_DOUBLE c
+#op OP_LONG_TO_FLOAT c
+#op OP_LONG_TO_INT c
+#op OP_MONITOR_ENTER c
+#op OP_MONITOR_EXIT c
+#op OP_MOVE_16 c
+#op OP_MOVE_EXCEPTION c
+#op OP_MOVE_FROM16 c
+#op OP_MOVE_OBJECT_16 c
+#op OP_MOVE_OBJECT_FROM16 c
+#op OP_MOVE_OBJECT c
+#op OP_MOVE_RESULT_OBJECT c
+#op OP_MOVE_RESULT c
+#op OP_MOVE_RESULT_WIDE c
+#op OP_MOVE c
+#op OP_MOVE_WIDE_16 c
+#op OP_MOVE_WIDE_FROM16 c
+#op OP_MOVE_WIDE c
+#op OP_MUL_DOUBLE_2ADDR c
+#op OP_MUL_DOUBLE c
+#op OP_MUL_FLOAT_2ADDR c
+#op OP_MUL_FLOAT c
+#op OP_MUL_INT_2ADDR c
+#op OP_MUL_INT_LIT16 c
+#op OP_MUL_INT_LIT8 c
+#op OP_MUL_INT c
+#op OP_MUL_LONG_2ADDR c
+#op OP_MUL_LONG c
+#op OP_NEG_DOUBLE c
+#op OP_NEG_FLOAT c
+#op OP_NEG_INT c
+#op OP_NEG_LONG c
+#op OP_NEW_ARRAY c
+#op OP_NEW_INSTANCE c
+#op OP_NOP c
+#op OP_NOT_INT c
+#op OP_NOT_LONG c
+#op OP_OR_INT_2ADDR c
+#op OP_OR_INT_LIT16 c
+#op OP_OR_INT_LIT8 c
+#op OP_OR_INT c
+#op OP_OR_LONG_2ADDR c
+#op OP_OR_LONG c
+#op OP_PACKED_SWITCH c
+#op OP_REM_DOUBLE_2ADDR c
+#op OP_REM_DOUBLE c
+#op OP_REM_FLOAT_2ADDR c
+#op OP_REM_FLOAT c
+#op OP_REM_INT_2ADDR c
+#op OP_REM_INT_LIT16 c
+#op OP_REM_INT_LIT8 c
+#op OP_REM_INT c
+#op OP_REM_LONG_2ADDR c
+#op OP_REM_LONG c
+#op OP_RETURN_OBJECT c
+#op OP_RETURN c
+#op OP_RETURN_VOID c
+#op OP_RETURN_WIDE c
+#op OP_RSUB_INT_LIT8 c
+#op OP_RSUB_INT c
+#op OP_SGET_BOOLEAN c
+#op OP_SGET_BYTE c
+#op OP_SGET_CHAR c
+#op OP_SGET_OBJECT c
+#op OP_SGET c
+#op OP_SGET_SHORT c
+#op OP_SGET_WIDE c
+#op OP_SHL_INT_2ADDR c
+#op OP_SHL_INT_LIT8 c
+#op OP_SHL_INT c
+#op OP_SHL_LONG_2ADDR c
+#op OP_SHL_LONG c
+#op OP_SHR_INT_2ADDR c
+#op OP_SHR_INT_LIT8 c
+#op OP_SHR_INT c
+#op OP_SHR_LONG_2ADDR c
+#op OP_SHR_LONG c
+#op OP_SPARSE_SWITCH c
+#op OP_SPUT_BOOLEAN c
+#op OP_SPUT_BYTE c
+#op OP_SPUT_CHAR c
+#op OP_SPUT_OBJECT c
+#op OP_SPUT c
+#op OP_SPUT_SHORT c
+#op OP_SPUT_WIDE c
+#op OP_SUB_DOUBLE_2ADDR c
+#op OP_SUB_DOUBLE c
+#op OP_SUB_FLOAT_2ADDR c
+#op OP_SUB_FLOAT c
+#op OP_SUB_INT_2ADDR c
+#op OP_SUB_INT c
+#op OP_SUB_LONG_2ADDR c
+#op OP_SUB_LONG c
+#op OP_THROW c
+#op OP_UNUSED_3E c
+#op OP_UNUSED_3F c
+#op OP_UNUSED_40 c
+#op OP_UNUSED_41 c
+#op OP_UNUSED_42 c
+#op OP_UNUSED_43 c
+#op OP_UNUSED_73 c
+#op OP_UNUSED_79 c
+#op OP_UNUSED_7A c
+#op OP_UNUSED_F1 c
+#op OP_UNUSED_FF c
+#op OP_USHR_INT_2ADDR c
+#op OP_USHR_INT_LIT8 c
+#op OP_USHR_INT c
+#op OP_USHR_LONG_2ADDR c
+#op OP_USHR_LONG c
+#op OP_XOR_INT_2ADDR c
+#op OP_XOR_INT_LIT16 c
+#op OP_XOR_INT_LIT8 c
+#op OP_XOR_INT c
+#op OP_XOR_LONG_2ADDR c
+#op OP_XOR_LONG c
+
+# TODO: provide native implementations
+op OP_BREAKPOINT c
+op OP_EXECUTE_INLINE_RANGE c
+op OP_IGET_VOLATILE c
+op OP_IPUT_VOLATILE c
+op OP_SGET_VOLATILE c
+op OP_SPUT_VOLATILE c
+op OP_IGET_OBJECT_VOLATILE c
+op OP_IPUT_OBJECT_VOLATILE c
+op OP_SGET_OBJECT_VOLATILE c
+op OP_SPUT_OBJECT_VOLATILE 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-end
+
+# arch-specific entry point to interpreter
+import x86-atom/entry.S
+
+# "helper" code for C; include this after the instruction handlers
+import c/gotoTargets.c
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import x86-atom/footer.S
diff --git a/vm/mterp/cstubs/enddefs.c b/vm/mterp/cstubs/enddefs.c
new file mode 100644
index 0000000..cac74bf
--- /dev/null
+++ b/vm/mterp/cstubs/enddefs.c
@@ -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.c b/vm/mterp/cstubs/entry.c
new file mode 100644
index 0000000..af31a3d
--- /dev/null
+++ b/vm/mterp/cstubs/entry.c
@@ -0,0 +1,80 @@
+/*
+ * Handler function table, one entry per opcode.
+ */
+#undef H
+#define H(_op) 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.
+ */
+bool dvmMterpStdRun(MterpGlue* glue)
+{
+    jmp_buf jmpBuf;
+    int changeInterp;
+
+    glue->bailPtr = &jmpBuf;
+
+    /*
+     * We want to return "changeInterp" as a boolean, but we can't return
+     * zero through longjmp, so we return (boolean+1).
+     */
+    changeInterp = setjmp(jmpBuf) -1;
+    if (changeInterp >= 0) {
+        Thread* threadSelf = dvmThreadSelf();
+        LOGVV("mterp threadid=%d returning %d\n",
+            threadSelf->threadId, changeInterp);
+        return changeInterp;
+    }
+
+    /*
+     * We may not be starting at a point where we're executing instructions.
+     * We need to pick up where the other interpreter left off.
+     *
+     * In some cases we need to call into a throw/return handler which
+     * will do some processing and then either return to us (updating "glue")
+     * or longjmp back out.
+     */
+    switch (glue->entryPoint) {
+    case kInterpEntryInstr:
+        /* just start at the start */
+        break;
+    case kInterpEntryReturn:
+        dvmMterp_returnFromMethod(glue);
+        break;
+    case kInterpEntryThrow:
+        dvmMterp_exceptionThrown(glue);
+        break;
+    default:
+        dvmAbort();
+    }
+
+    /* run until somebody longjmp()s out */
+    while (true) {
+        typedef void (*Handler)(MterpGlue* glue);
+
+        u2 inst = /*glue->*/pc[0];
+        Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff];
+        LOGVV("handler %p %s\n",
+            handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]);
+        (*handler)(glue);
+    }
+}
+
+/*
+ * C mterp exit point.  Call here to bail out of the interpreter.
+ */
+void dvmMterpStdBail(MterpGlue* glue, bool changeInterp)
+{
+    jmp_buf* pJmpBuf = glue->bailPtr;
+    longjmp(*pJmpBuf, ((int)changeInterp)+1);
+}
diff --git a/vm/mterp/cstubs/stubdefs.c b/vm/mterp/cstubs/stubdefs.c
new file mode 100644
index 0000000..bf870c6
--- /dev/null
+++ b/vm/mterp/cstubs/stubdefs.c
@@ -0,0 +1,124 @@
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
diff --git a/vm/mterp/gen-mterp.py b/vm/mterp/gen-mterp.py
new file mode 100755
index 0000000..e0d854d
--- /dev/null
+++ b/vm/mterp/gen-mterp.py
@@ -0,0 +1,479 @@
+#!/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/OpCode.h" # need opcode list
+
+verbose = False
+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(".c"):
+        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 .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])
+    if opcode_locations.has_key(tokens[1]):
+        print "Warning: op overrides earlier %s (%s -> %s)" \
+                % (tokens[1], opcode_locations[tokens[1]], tokens[2])
+    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 256 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) != 256:
+        print "ERROR: found %d opcodes in Interp.h (expected 256)" \
+                % len(opcodes)
+        raise SyntaxError, "bad opcode count"
+    return opcodes
+
+
+#
+# Load and emit opcodes for all 256 instructions.
+#
+def loadAndEmitOpcodes():
+    sister_list = []
+    assert len(opcodes) == 256
+    need_dummy_start = False
+
+    # point dvmAsmInstructionStart at the first handler or stub
+    asm_fp.write("\n    .global dvmAsmInstructionStart\n")
+    asm_fp.write("    .type   dvmAsmInstructionStart, %function\n")
+    asm_fp.write("dvmAsmInstructionStart = " + label_prefix + "_OP_NOP\n")
+    asm_fp.write("    .text\n\n")
+
+    for i in xrange(256):
+        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:
+        asm_fp.write("    .balign %d\n" % handler_size_bytes)
+        asm_fp.write(label_prefix + "_OP_NOP:   /* dummy */\n");
+
+    asm_fp.write("\n    .balign %d\n" % handler_size_bytes)
+    asm_fp.write("    .size   dvmAsmInstructionStart, .-dvmAsmInstructionStart\n")
+    asm_fp.write("    .global dvmAsmInstructionEnd\n")
+    asm_fp.write("dvmAsmInstructionEnd:\n")
+
+    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 a C fragment and emit it, then output an assembly stub.
+#
+def loadAndEmitC(location, opindex):
+    op = opcodes[opindex]
+    source = "%s/%s.c" % (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)
+    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 %d\n" % handler_size_bytes)
+    # 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(label_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)
+    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:
+    c_fp = open("%s/InterpC-%s.c" % (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] == "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
+    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/out/InterpAsm-allstubs.S b/vm/mterp/out/InterpAsm-allstubs.S
new file mode 100644
index 0000000..a6973ae
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-allstubs.S
@@ -0,0 +1,35 @@
+/*
+ * 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-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S
new file mode 100644
index 0000000..2afeb9b
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv4t.S
@@ -0,0 +1,11077 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv4t'.
+ *
+ * --> 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  rGLUE     MterpGlue 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 rGLUE   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE()     ldr     rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE()       str     rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE()     ldr     rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE()       str     rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE()  ldmia   rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE()    stmia   rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * 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 #2]!", 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_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]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg)    ldr     _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg)     ldr     _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * 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 "LDR PC,xxx", which is not allowed pre-ARMv5.  Essentially a
+ * one-way branch.
+ *
+ * May modify IP.  Does not modify LR.
+ */
+.macro  LDR_PC source
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro  LDMFD_PC regs
+    ldmfd   sp!, {\regs,pc}
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.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  MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value.  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, #offGlue_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rGLUE, r0                   @ set rGLUE
+    ldr     r1, [r0, #offGlue_entryPoint]   @ enum is 4 bytes in aapcs-EABI
+    LOAD_PC_FP_FROM_GLUE()              @ load rPC and rFP from "glue"
+    adr     rIBASE, dvmAsmInstructionStart  @ set rIBASE
+    cmp     r1, #kInterpEntryInstr      @ usual case?
+    bne     .Lnot_instr                 @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    /* Entry is always a possible trace start */
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [r10, #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, [r10, #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
+
+.Lnot_instr:
+    cmp     r1, #kInterpEntryReturn     @ were we returning from a method?
+    beq     common_returnFromMethod
+
+.Lnot_return:
+    cmp     r1, #kInterpEntryThrow      @ were we throwing an exception?
+    beq     common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+    ldr     r10,[rGLUE, #offGlue_jitResumeNPC]
+    ldr     r2,[rGLUE, #offGlue_jitResumeDPC]
+    cmp     r1, #kInterpEntryResume     @ resuming after Jit single-step?
+    bne     .Lbad_arg
+    cmp     rPC,r2
+    bne     .LentryInstr                @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+    @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+    b       jitSVShadowRunStart         @ re-enter the translation after the
+                                        @ single-stepped instruction
+    @noreturn
+#endif
+    mov     r1, #kInterpEntryInstr
+    str     r1, [rGLUE, #offGlue_entryPoint]
+    bx      r10                         @ re-enter the translation
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+
+
+    .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  MterpGlue* glue
+ *  r1  bool changeInterp
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offGlue_bailPtr]      @ sp<- saved SP
+    mov     r0, r1                          @ return the changeInterp value
+    add     sp, sp, #4                      @ un-align 64
+    LDMFD_PC "r4-r10,fp"                    @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+    .word   .LstrBadEntryPoint
+
+
+    .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
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    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, [rGLUE, #offGlue_retval]    @ r0<- glue->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, rGLUE, #offGlue_retval  @ r3<- &glue->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
+    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_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, [rGLUE, #offGlue_retval]    @ r0<- glue->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 */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [r0, #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, [r0, #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 "glue"
+     * 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, [rGLUE, #offGlue_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 "glue"
+     * 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, rGLUE, #offGlue_retval  @ r3<- &glue->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 "glue"
+     * 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC, MONITOR_TRACKING
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r1, [r0, #offThread_exception] @ check for exception
+    cmp     r1, #0
+    bne     common_exceptionThrown      @ exception raised, bail out
+#endif
+    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
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    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, [r0, #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 */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r9, r0, asr #24             @ r9<- ssssssAA (sign-extended)
+    mov     r9, r9, lsl #1              @ r9<- byte offset
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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)
+    movs    r9, r0, asl #1              @ r9<- byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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".  The ORRS
+     * instruction doesn't affect the V flag, so we need to clear it
+     * explicitly.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    cmp     ip, ip                      @ (clear V flag during stall)
+    orrs    r0, r0, r1, lsl #16         @ r0<- AAAAaaaa, check sign
+    mov     r9, r0, asl #1              @ r9<- byte offset
+    ble     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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: armv4t/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: armv4t/OP_APUT_WIDE.S */
+    /*
+     * Array put, 64 bits.  vBB[vCC] <- vAA.
+     */
+    /* 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.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     */
+    /* 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(r0, r3)                    @ r0<- 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, r0, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r0, 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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: armv4t/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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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: armv4t/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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: armv4t/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    .if 0
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldmia   r0, {r0-r1}                 @ 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv4t/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rGLUE, #offGlue_methodClassDex]    @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    .if 0
+    bl      dvmQuasiAtomicSwap64        @ stores r0/r1 into addr r2
+    .else
+    stmia   r2, {r0-r1}                 @ 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodNoRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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                            @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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: armv4t/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv4t/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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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: armv4t/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv4t/OP_IPUT_WIDE.S */
+    /* iput-wide vA, vB, field@CCCC */
+    mov     r0, rINST, lsr #12          @ r0<- B
+    ldr     r3, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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: armv4t/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv4t/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     */
+    /* sget-wide vAA, field@BBBB */
+    ldr     r2, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    add     r0, r0, #offStaticField_value @ r0<- pointer to data
+    .if 1
+    bl      dvmQuasiAtomicRead64        @ r0/r1<- contents of field
+    .else
+    ldmia   r0, {r0-r1}                 @ 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: armv4t/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv4t/OP_SPUT_WIDE.S */
+    /*
+     * 64-bit SPUT handler.
+     */
+    /* sput-wide vAA, field@BBBB */
+    ldr     r0, [rGLUE, #offGlue_methodClassDex]    @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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
+    add     r2, r2, #offStaticField_value @ r2<- pointer to data
+    .if 1
+    bl      dvmQuasiAtomicSwap64        @ stores r0/r1 into addr r2
+    .else
+    stmia   r2, {r0-r1}                 @ field<- vAA/vAA+1
+    .endif
+    GOTO_OPCODE(r10)                    @ jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method]    @ r0<- glue->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.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &glue->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 */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &glue->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_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+    /*
+     * invoke-direct-empty is a no-op in a "standard" interpreter.
+     */
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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: armv4t/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(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
+    add     r9, r3, r1                  @ r9<- object + offset
+    ldmia   r9, {r0-r1}                 @ r0/r1<- obj.field (64 bits, aligned)
+    and     r2, r2, #15                 @ 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
+
+/* ------------------------------ */
+    .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: armv4t/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
+    add     r2, r2, r3                  @ r2<- object + byte offset
+    stmia   r2, {r0-r1}                 @ 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, [rGLUE, #offGlue_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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+/* ------------------------------ */
+    .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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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 class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    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 with the
+    @ class of the object that failed to be cast.
+    EXPORT_PC()                         @ about to throw
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- obj->clazz
+    ldr     r0, .LstrClassCastExceptionPtr
+    ldr     r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+    bl      dvmThrowExceptionWithClassMessage
+    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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrClassCastExceptionPtr:
+    .word   .LstrClassCastException
+
+/* 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, [rGLUE, #offGlue_method]    @ r0<- glue->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?
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * 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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrInstantiationErrorPtr:
+    .word   .LstrInstantiationError
+
+/* 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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!0)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!1)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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
+    add     r0, r0, #offArrayObject_contents
+    ldmia   r0, {r2-r3}                 @ 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
+    add     r0, #offArrayObject_contents
+    stmia   r0, {r2-r3}                 @ vBB[vCC] <- r2/r3
+    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     common_errArrayStore        @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rGLUE, #offGlue_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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* 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
+    add     r9, r9, r3                  @ r9<- obj + field offset
+    ldmia   r9, {r0-r1}                 @ 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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    add     r2, r9, r3                  @ r2<- object + byte offset
+    .if 0
+    bl      dvmQuasiAtomicSwap64        @ stores r0/r1 into addr r2
+    .else
+    stmia   r2, {r0-r1}                 @ 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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.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, [rGLUE, #offGlue_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
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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 for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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
+    add     r9, r9, r3                  @ r9<- obj + field offset
+    ldmia   r9, {r0-r1}                 @ 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
+    add     r2, r9, r3                  @ r2<- object + byte offset
+    .if 1
+    bl      dvmQuasiAtomicSwap64        @ stores r0/r1 into addr r2
+    .else
+    stmia   r2, {r0-r1}                 @ 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
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r9, 2)                        @ r9<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, r9, #0xf000             @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, r9, #0x0f00             @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, r9, #0x00f0             @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, r9, #0x000f             @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_table       @ table of InlineOperation
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+/*
+ * 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:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    r0 <= PC
+ *    r1 <= PC of resume instruction
+ *    lr <= resume point in translation
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r1,#kInterpEntryInstr
+    @ enum is 4 byte in aapcs-EABI
+    str    r1, [rGLUE, #offGlue_entryPoint]
+    mov    rPC,r0
+    EXPORT_PC()
+
+    adrl   rIBASE, dvmAsmInstructionStart
+    mov    r2,#kJitSingleStep     @ Ask for single step and then revert
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r1,#1                  @ set changeInterp to bail to debug interp
+    b      common_gotoBail
+
+/*
+ * 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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr       @ Is there a translation?
+    str    r0, [r10, #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:
+    adrl   rIBASE, dvmAsmInstructionStart
+    GET_JIT_PROF_TABLE(r0)
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    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
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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.
+ * rGLUE & 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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_JIT_PROF_TABLE(r0)
+    @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.  On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+    cmp     r0,#0
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE_IFEQ(ip)       @ if not profiling, fallthrough otherwise */
+
+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 */
+
+/*
+ * 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).
+ */
+    GET_JIT_THRESHOLD(r1)
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    bl      dvmJitGetCodeAddr           @ r0<- dvmJitGetCodeAddr(rPC)
+    str     r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ r10 <- glue->self
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+    str     r2,[rGLUE,#offGlue_jitState]
+    mov     r2,#kInterpEntryInstr       @ normal entry reason
+    str     r2,[rGLUE,#offGlue_entryPoint]
+    mov     r1,#1                       @ set changeInterp
+    b       common_gotoBail
+
+#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, rGLUE: 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,rGLUE                    @ r2<- InterpState pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    add     rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    ldr    rPC,[r0,#offShadowSpace_startPC] @ restore PC
+    ldr    rFP,[r0,#offShadowSpace_fp]   @ restore FP
+    ldr    rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r2,#kInterpEntryInstr         @ normal entry reason
+    str    r2,[rGLUE,#offGlue_entryPoint]
+    mov    r1,#1                         @ set changeInterp
+    b      common_gotoBail
+
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ *  r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+    mov     r0, #kInterpEntryInstr
+    bl      common_periodicChecks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun.  If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly.  If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ *  r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ *  r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+    ldr     r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+    orrne   ip, ip, r1                  @ ip<- suspendCount | debuggerActive
+    orrs    ip, ip, r2                  @ ip<- suspend|debugger|profiler; set Z
+
+    bxeq    lr                          @ all zero, return
+
+    /*
+     * One or more interesting events have happened.  Figure out what.
+     *
+     * If debugging or profiling are compiled in, we need to disambiguate.
+     *
+     * r0 still holds the reentry type.
+     */
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+    cmp     ip, #0                      @ want suspend?
+    beq     1f                          @ no, must be debugger/profiler
+
+    stmfd   sp!, {r0, lr}               @ preserve r0 and lr
+#if defined(WITH_JIT)
+    /*
+     * Refresh the Jit's cached copy of profile table pointer.  This pointer
+     * doubles as the Jit's on/off switch.
+     */
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r3, [r3] @ r3 <- pJitProfTable
+    EXPORT_PC()                         @ need for precise GC
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    EXPORT_PC()                         @ need for precise GC
+#endif
+    bl      dvmCheckSuspendPending      @ do full check, suspend if necessary
+    ldmfd   sp!, {r0, lr}               @ restore r0 and lr
+
+    /*
+     * Reload the debugger/profiler enable flags.  We're checking to see
+     * if either of these got set while we were suspended.
+     *
+     * We can't really avoid the #ifdefs here, because the fields don't
+     * exist when the feature is disabled.
+     */
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+
+    orrs    r1, r1, r2
+    beq     2f
+
+1:  @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+    str     r0, [rGLUE, #offGlue_entryPoint]    @ store r0, need for debug/prof
+    add     rPC, rPC, r9                @ update rPC
+    mov     r1, #1                      @ "want switch" = true
+    b       common_gotoBail             @ side exit
+
+2:
+    bx      lr                          @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ *  r1 is "bool changeInterp", indicating if we want to switch to the
+ *     other interpreter or just bail all the way out
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    mov     r0, rGLUE                   @ r0<- glue ptr
+    b       dvmMterpStdBail             @ call(glue, changeInterp)
+
+    @add     r1, r1, #1                  @ using (boolean+1)
+    @add     r0, rGLUE, #offGlue_jmpBuf  @ r0<- &glue->jmpBuf
+    @bl      _longjmp                    @ does not return
+    @bl      common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+    @ 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
+
+    @ 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
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    ldrh    r3, [r0, #offMethod_outsSize]   @ r3<- methodToCall->outsSize
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    @ 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)
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, 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, r3=outSize, r9=regSize
+    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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    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]
+    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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [r2, #offThread_curFrame]   @ self->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, [r2, #offThread_curFrame]   @ self->curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+#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
+
+    @mov     lr, pc                      @ set return addr
+    @ldr     pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+    ldr     r3, [r3]                    @ r3 <- gDvmJit.pProfTable
+#endif
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+    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
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+#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, rGLUE                   @ A0<- glue
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    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:
+    mov     r0, #kInterpEntryReturn
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    cmp     r2, #0                      @ is this a break frame?
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    mov     r1, #0                      @ "want switch" = false
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame]  @ self->curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rGLUE, #offGlue_methodClassDex]
+    str     r10, [r3, #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, [rGLUE, #offGlue_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ 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:
+    mov     r0, #kInterpEntryThrow
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    ldr     r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+    ldr     r9, [r10, #offThread_exception] @ r9<- self->exception
+    mov     r1, r10                     @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [r10, #offThread_exception] @ self->exception = NULL
+
+    /* 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, [rGLUE, #offGlue_method] @ r1<- glue->method
+    mov     r0, r10                     @ 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, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, r10                     @ 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->curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rGLUE, #offGlue_method]    @ glue->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, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    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, [r10, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+    /* fix stack overflow if necessary */
+    ldrb    r1, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, r10                     @ 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, [rGLUE, #offGlue_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rGLUE, #offGlue_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+    ldr     r1, strLogTag
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [r10, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    mov     r1, #0                      @ "want switch" = false
+    b       common_gotoBail             @ bail out
+
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .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_GLUE()              @ pull rPC and rFP out of glue
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    ldr     r0, strArrayIndexException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+    EXPORT_PC()
+    ldr     r0, strArrayStoreException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strArithmeticException
+    ldr     r1, strDivideByZero
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    ldr     r0, strNegativeArraySizeException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    ldr     r0, strNoSuchMethodError
+    mov     r1, #0
+    bl      dvmThrowException
+    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()
+    ldr     r0, strNullPointerException
+    mov     r1, #0
+    bl      dvmThrowException
+    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
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+    /*
+     * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+    .align  2
+strArithmeticException:
+    .word   .LstrArithmeticException
+strArrayIndexException:
+    .word   .LstrArrayIndexException
+strArrayStoreException:
+    .word   .LstrArrayStoreException
+strDivideByZero:
+    .word   .LstrDivideByZero
+strNegativeArraySizeException:
+    .word   .LstrNegativeArraySizeException
+strNoSuchMethodError:
+    .word   .LstrNoSuchMethodError
+strNullPointerException:
+    .word   .LstrNullPointerException
+
+strLogTag:
+    .word   .LstrLogTag
+strExceptionNotCaughtLocally:
+    .word   .LstrExceptionNotCaughtLocally
+
+strNewline:
+    .word   .LstrNewline
+strSqueak:
+    .word   .LstrSqueak
+strPrintHex:
+    .word   .LstrPrintHex
+strPrintLong:
+    .word   .LstrPrintLong
+
+/*
+ * 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"
+.LstrArithmeticException:
+    .asciz  "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz  "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+    .asciz  "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+    .asciz  "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+    .asciz  "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+    .asciz  "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+    .asciz  "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+    .asciz  "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<0x%x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S
new file mode 100644
index 0000000..2637e59
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S
@@ -0,0 +1,10615 @@
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE()     ldr     rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE()       str     rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE()     ldr     rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE()       str     rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE()  ldmia   rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE()    stmia   rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * 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 #2]!", 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_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]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg)    ldr     _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg)     ldr     _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * 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 "LDR PC,xxx", which is not allowed pre-ARMv5.  Essentially a
+ * one-way branch.
+ *
+ * May modify IP.  Does not modify LR.
+ */
+.macro  LDR_PC source
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro  LDMFD_PC regs
+    ldmfd   sp!, {\regs,pc}
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.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  MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value.  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, #offGlue_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rGLUE, r0                   @ set rGLUE
+    ldr     r1, [r0, #offGlue_entryPoint]   @ enum is 4 bytes in aapcs-EABI
+    LOAD_PC_FP_FROM_GLUE()              @ load rPC and rFP from "glue"
+    adr     rIBASE, dvmAsmInstructionStart  @ set rIBASE
+    cmp     r1, #kInterpEntryInstr      @ usual case?
+    bne     .Lnot_instr                 @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    /* Entry is always a possible trace start */
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [r10, #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, [r10, #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
+
+.Lnot_instr:
+    cmp     r1, #kInterpEntryReturn     @ were we returning from a method?
+    beq     common_returnFromMethod
+
+.Lnot_return:
+    cmp     r1, #kInterpEntryThrow      @ were we throwing an exception?
+    beq     common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+    ldr     r10,[rGLUE, #offGlue_jitResumeNPC]
+    ldr     r2,[rGLUE, #offGlue_jitResumeDPC]
+    cmp     r1, #kInterpEntryResume     @ resuming after Jit single-step?
+    bne     .Lbad_arg
+    cmp     rPC,r2
+    bne     .LentryInstr                @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+    @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+    b       jitSVShadowRunStart         @ re-enter the translation after the
+                                        @ single-stepped instruction
+    @noreturn
+#endif
+    mov     r1, #kInterpEntryInstr
+    str     r1, [rGLUE, #offGlue_entryPoint]
+    bx      r10                         @ re-enter the translation
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+
+
+    .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  MterpGlue* glue
+ *  r1  bool changeInterp
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offGlue_bailPtr]      @ sp<- saved SP
+    mov     r0, r1                          @ return the changeInterp value
+    add     sp, sp, #4                      @ un-align 64
+    LDMFD_PC "r4-r10,fp"                    @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+    .word   .LstrBadEntryPoint
+
+
+    .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
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    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, [rGLUE, #offGlue_retval]    @ r0<- glue->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, rGLUE, #offGlue_retval  @ r3<- &glue->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
+    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_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, [rGLUE, #offGlue_retval]    @ r0<- glue->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 */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [r0, #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, [r0, #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 "glue"
+     * 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, [rGLUE, #offGlue_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 "glue"
+     * 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, rGLUE, #offGlue_retval  @ r3<- &glue->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 "glue"
+     * 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC, MONITOR_TRACKING
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r1, [r0, #offThread_exception] @ check for exception
+    cmp     r1, #0
+    bne     common_exceptionThrown      @ exception raised, bail out
+#endif
+    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
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    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, [r0, #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 */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r9, r0, asr #24             @ r9<- ssssssAA (sign-extended)
+    mov     r9, r9, lsl #1              @ r9<- byte offset
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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)
+    movs    r9, r0, asl #1              @ r9<- byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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".  The ORRS
+     * instruction doesn't affect the V flag, so we need to clear it
+     * explicitly.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    cmp     ip, ip                      @ (clear V flag during stall)
+    orrs    r0, r0, r1, lsl #16         @ r0<- AAAAaaaa, check sign
+    mov     r9, r0, asl #1              @ r9<- byte offset
+    ble     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     */
+    /* 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(r0, r3)                    @ r0<- 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, r0, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r0, 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodNoRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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                            @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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 */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method]    @ r0<- glue->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.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &glue->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 */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &glue->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_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+    /*
+     * invoke-direct-empty is a no-op in a "standard" interpreter.
+     */
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+/* ------------------------------ */
+    .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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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 class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    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 with the
+    @ class of the object that failed to be cast.
+    EXPORT_PC()                         @ about to throw
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- obj->clazz
+    ldr     r0, .LstrClassCastExceptionPtr
+    ldr     r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+    bl      dvmThrowExceptionWithClassMessage
+    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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrClassCastExceptionPtr:
+    .word   .LstrClassCastException
+
+/* 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, [rGLUE, #offGlue_method]    @ r0<- glue->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?
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * 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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrInstantiationErrorPtr:
+    .word   .LstrInstantiationError
+
+/* 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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!0)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!1)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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     common_errArrayStore        @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rGLUE, #offGlue_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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* 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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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      dvmQuasiAtomicSwap64        @ 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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.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, [rGLUE, #offGlue_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
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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 for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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      dvmQuasiAtomicSwap64        @ 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
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r9, 2)                        @ r9<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, r9, #0xf000             @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, r9, #0x0f00             @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, r9, #0x00f0             @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, r9, #0x000f             @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_table       @ table of InlineOperation
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+/*
+ * 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:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    r0 <= PC
+ *    r1 <= PC of resume instruction
+ *    lr <= resume point in translation
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r1,#kInterpEntryInstr
+    @ enum is 4 byte in aapcs-EABI
+    str    r1, [rGLUE, #offGlue_entryPoint]
+    mov    rPC,r0
+    EXPORT_PC()
+
+    adrl   rIBASE, dvmAsmInstructionStart
+    mov    r2,#kJitSingleStep     @ Ask for single step and then revert
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r1,#1                  @ set changeInterp to bail to debug interp
+    b      common_gotoBail
+
+/*
+ * 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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr       @ Is there a translation?
+    str    r0, [r10, #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:
+    adrl   rIBASE, dvmAsmInstructionStart
+    GET_JIT_PROF_TABLE(r0)
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    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
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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.
+ * rGLUE & 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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_JIT_PROF_TABLE(r0)
+    @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.  On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+    cmp     r0,#0
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE_IFEQ(ip)       @ if not profiling, fallthrough otherwise */
+
+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 */
+
+/*
+ * 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).
+ */
+    GET_JIT_THRESHOLD(r1)
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    bl      dvmJitGetCodeAddr           @ r0<- dvmJitGetCodeAddr(rPC)
+    str     r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ r10 <- glue->self
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+    str     r2,[rGLUE,#offGlue_jitState]
+    mov     r2,#kInterpEntryInstr       @ normal entry reason
+    str     r2,[rGLUE,#offGlue_entryPoint]
+    mov     r1,#1                       @ set changeInterp
+    b       common_gotoBail
+
+#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, rGLUE: 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,rGLUE                    @ r2<- InterpState pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    add     rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    ldr    rPC,[r0,#offShadowSpace_startPC] @ restore PC
+    ldr    rFP,[r0,#offShadowSpace_fp]   @ restore FP
+    ldr    rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r2,#kInterpEntryInstr         @ normal entry reason
+    str    r2,[rGLUE,#offGlue_entryPoint]
+    mov    r1,#1                         @ set changeInterp
+    b      common_gotoBail
+
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ *  r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+    mov     r0, #kInterpEntryInstr
+    bl      common_periodicChecks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun.  If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly.  If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ *  r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ *  r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+    ldr     r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+    orrne   ip, ip, r1                  @ ip<- suspendCount | debuggerActive
+    orrs    ip, ip, r2                  @ ip<- suspend|debugger|profiler; set Z
+
+    bxeq    lr                          @ all zero, return
+
+    /*
+     * One or more interesting events have happened.  Figure out what.
+     *
+     * If debugging or profiling are compiled in, we need to disambiguate.
+     *
+     * r0 still holds the reentry type.
+     */
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+    cmp     ip, #0                      @ want suspend?
+    beq     1f                          @ no, must be debugger/profiler
+
+    stmfd   sp!, {r0, lr}               @ preserve r0 and lr
+#if defined(WITH_JIT)
+    /*
+     * Refresh the Jit's cached copy of profile table pointer.  This pointer
+     * doubles as the Jit's on/off switch.
+     */
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r3, [r3] @ r3 <- pJitProfTable
+    EXPORT_PC()                         @ need for precise GC
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    EXPORT_PC()                         @ need for precise GC
+#endif
+    bl      dvmCheckSuspendPending      @ do full check, suspend if necessary
+    ldmfd   sp!, {r0, lr}               @ restore r0 and lr
+
+    /*
+     * Reload the debugger/profiler enable flags.  We're checking to see
+     * if either of these got set while we were suspended.
+     *
+     * We can't really avoid the #ifdefs here, because the fields don't
+     * exist when the feature is disabled.
+     */
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+
+    orrs    r1, r1, r2
+    beq     2f
+
+1:  @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+    str     r0, [rGLUE, #offGlue_entryPoint]    @ store r0, need for debug/prof
+    add     rPC, rPC, r9                @ update rPC
+    mov     r1, #1                      @ "want switch" = true
+    b       common_gotoBail             @ side exit
+
+2:
+    bx      lr                          @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ *  r1 is "bool changeInterp", indicating if we want to switch to the
+ *     other interpreter or just bail all the way out
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    mov     r0, rGLUE                   @ r0<- glue ptr
+    b       dvmMterpStdBail             @ call(glue, changeInterp)
+
+    @add     r1, r1, #1                  @ using (boolean+1)
+    @add     r0, rGLUE, #offGlue_jmpBuf  @ r0<- &glue->jmpBuf
+    @bl      _longjmp                    @ does not return
+    @bl      common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+    @ 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
+
+    @ 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
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    ldrh    r3, [r0, #offMethod_outsSize]   @ r3<- methodToCall->outsSize
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    @ 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)
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, 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, r3=outSize, r9=regSize
+    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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    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]
+    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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [r2, #offThread_curFrame]   @ self->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, [r2, #offThread_curFrame]   @ self->curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+#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
+
+    @mov     lr, pc                      @ set return addr
+    @ldr     pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+    ldr     r3, [r3]                    @ r3 <- gDvmJit.pProfTable
+#endif
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+    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
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+#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, rGLUE                   @ A0<- glue
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    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:
+    mov     r0, #kInterpEntryReturn
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    cmp     r2, #0                      @ is this a break frame?
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    mov     r1, #0                      @ "want switch" = false
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame]  @ self->curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rGLUE, #offGlue_methodClassDex]
+    str     r10, [r3, #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, [rGLUE, #offGlue_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ 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:
+    mov     r0, #kInterpEntryThrow
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    ldr     r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+    ldr     r9, [r10, #offThread_exception] @ r9<- self->exception
+    mov     r1, r10                     @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [r10, #offThread_exception] @ self->exception = NULL
+
+    /* 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, [rGLUE, #offGlue_method] @ r1<- glue->method
+    mov     r0, r10                     @ 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, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, r10                     @ 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->curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rGLUE, #offGlue_method]    @ glue->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, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    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, [r10, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+    /* fix stack overflow if necessary */
+    ldrb    r1, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, r10                     @ 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, [rGLUE, #offGlue_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rGLUE, #offGlue_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+    ldr     r1, strLogTag
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [r10, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    mov     r1, #0                      @ "want switch" = false
+    b       common_gotoBail             @ bail out
+
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .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_GLUE()              @ pull rPC and rFP out of glue
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    ldr     r0, strArrayIndexException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+    EXPORT_PC()
+    ldr     r0, strArrayStoreException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strArithmeticException
+    ldr     r1, strDivideByZero
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    ldr     r0, strNegativeArraySizeException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    ldr     r0, strNoSuchMethodError
+    mov     r1, #0
+    bl      dvmThrowException
+    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()
+    ldr     r0, strNullPointerException
+    mov     r1, #0
+    bl      dvmThrowException
+    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
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+    /*
+     * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+    .align  2
+strArithmeticException:
+    .word   .LstrArithmeticException
+strArrayIndexException:
+    .word   .LstrArrayIndexException
+strArrayStoreException:
+    .word   .LstrArrayStoreException
+strDivideByZero:
+    .word   .LstrDivideByZero
+strNegativeArraySizeException:
+    .word   .LstrNegativeArraySizeException
+strNoSuchMethodError:
+    .word   .LstrNoSuchMethodError
+strNullPointerException:
+    .word   .LstrNullPointerException
+
+strLogTag:
+    .word   .LstrLogTag
+strExceptionNotCaughtLocally:
+    .word   .LstrExceptionNotCaughtLocally
+
+strNewline:
+    .word   .LstrNewline
+strSqueak:
+    .word   .LstrSqueak
+strPrintHex:
+    .word   .LstrPrintHex
+strPrintLong:
+    .word   .LstrPrintLong
+
+/*
+ * 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"
+.LstrArithmeticException:
+    .asciz  "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz  "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+    .asciz  "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+    .asciz  "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+    .asciz  "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+    .asciz  "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+    .asciz  "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+    .asciz  "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<0x%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..e6abac5
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -0,0 +1,11073 @@
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE()     ldr     rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE()       str     rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE()     ldr     rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE()       str     rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE()  ldmia   rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE()    stmia   rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * 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 #2]!", 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_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]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg)    ldr     _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg)     ldr     _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * 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 "LDR PC,xxx", which is not allowed pre-ARMv5.  Essentially a
+ * one-way branch.
+ *
+ * May modify IP.  Does not modify LR.
+ */
+.macro  LDR_PC source
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro  LDMFD_PC regs
+    ldmfd   sp!, {\regs,pc}
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro  SMP_DMB
+.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  MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value.  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, #offGlue_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rGLUE, r0                   @ set rGLUE
+    ldr     r1, [r0, #offGlue_entryPoint]   @ enum is 4 bytes in aapcs-EABI
+    LOAD_PC_FP_FROM_GLUE()              @ load rPC and rFP from "glue"
+    adr     rIBASE, dvmAsmInstructionStart  @ set rIBASE
+    cmp     r1, #kInterpEntryInstr      @ usual case?
+    bne     .Lnot_instr                 @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    /* Entry is always a possible trace start */
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [r10, #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, [r10, #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
+
+.Lnot_instr:
+    cmp     r1, #kInterpEntryReturn     @ were we returning from a method?
+    beq     common_returnFromMethod
+
+.Lnot_return:
+    cmp     r1, #kInterpEntryThrow      @ were we throwing an exception?
+    beq     common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+    ldr     r10,[rGLUE, #offGlue_jitResumeNPC]
+    ldr     r2,[rGLUE, #offGlue_jitResumeDPC]
+    cmp     r1, #kInterpEntryResume     @ resuming after Jit single-step?
+    bne     .Lbad_arg
+    cmp     rPC,r2
+    bne     .LentryInstr                @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+    @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+    b       jitSVShadowRunStart         @ re-enter the translation after the
+                                        @ single-stepped instruction
+    @noreturn
+#endif
+    mov     r1, #kInterpEntryInstr
+    str     r1, [rGLUE, #offGlue_entryPoint]
+    bx      r10                         @ re-enter the translation
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+
+
+    .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  MterpGlue* glue
+ *  r1  bool changeInterp
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offGlue_bailPtr]      @ sp<- saved SP
+    mov     r0, r1                          @ return the changeInterp value
+    add     sp, sp, #4                      @ un-align 64
+    LDMFD_PC "r4-r10,fp"                    @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+    .word   .LstrBadEntryPoint
+
+
+    .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
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    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, [rGLUE, #offGlue_retval]    @ r0<- glue->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, rGLUE, #offGlue_retval  @ r3<- &glue->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
+    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_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, [rGLUE, #offGlue_retval]    @ r0<- glue->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 */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [r0, #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, [r0, #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 "glue"
+     * 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, [rGLUE, #offGlue_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 "glue"
+     * 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, rGLUE, #offGlue_retval  @ r3<- &glue->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 "glue"
+     * 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC, MONITOR_TRACKING
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r1, [r0, #offThread_exception] @ check for exception
+    cmp     r1, #0
+    bne     common_exceptionThrown      @ exception raised, bail out
+#endif
+    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
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    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, [r0, #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 */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r9, r0, asr #24             @ r9<- ssssssAA (sign-extended)
+    mov     r9, r9, lsl #1              @ r9<- byte offset
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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)
+    movs    r9, r0, asl #1              @ r9<- byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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".  The ORRS
+     * instruction doesn't affect the V flag, so we need to clear it
+     * explicitly.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    cmp     ip, ip                      @ (clear V flag during stall)
+    orrs    r0, r0, r1, lsl #16         @ r0<- AAAAaaaa, check sign
+    mov     r9, r0, asl #1              @ r9<- byte offset
+    ble     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     */
+    /* 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(r0, r3)                    @ r0<- 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, r0, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r0, 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodNoRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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                            @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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 */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method]    @ r0<- glue->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.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &glue->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 */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &glue->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_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+    /*
+     * invoke-direct-empty is a no-op in a "standard" interpreter.
+     */
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+/* ------------------------------ */
+    .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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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 class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    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 with the
+    @ class of the object that failed to be cast.
+    EXPORT_PC()                         @ about to throw
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- obj->clazz
+    ldr     r0, .LstrClassCastExceptionPtr
+    ldr     r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+    bl      dvmThrowExceptionWithClassMessage
+    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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrClassCastExceptionPtr:
+    .word   .LstrClassCastException
+
+/* 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, [rGLUE, #offGlue_method]    @ r0<- glue->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?
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * 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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrInstantiationErrorPtr:
+    .word   .LstrInstantiationError
+
+/* 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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!0)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!1)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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     common_errArrayStore        @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rGLUE, #offGlue_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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* 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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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      dvmQuasiAtomicSwap64        @ 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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.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, [rGLUE, #offGlue_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
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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 for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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      dvmQuasiAtomicSwap64        @ 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
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r9, 2)                        @ r9<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, r9, #0xf000             @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, r9, #0x0f00             @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, r9, #0x00f0             @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, r9, #0x000f             @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_table       @ table of InlineOperation
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+/*
+ * 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:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    r0 <= PC
+ *    r1 <= PC of resume instruction
+ *    lr <= resume point in translation
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r1,#kInterpEntryInstr
+    @ enum is 4 byte in aapcs-EABI
+    str    r1, [rGLUE, #offGlue_entryPoint]
+    mov    rPC,r0
+    EXPORT_PC()
+
+    adrl   rIBASE, dvmAsmInstructionStart
+    mov    r2,#kJitSingleStep     @ Ask for single step and then revert
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r1,#1                  @ set changeInterp to bail to debug interp
+    b      common_gotoBail
+
+/*
+ * 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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr       @ Is there a translation?
+    str    r0, [r10, #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:
+    adrl   rIBASE, dvmAsmInstructionStart
+    GET_JIT_PROF_TABLE(r0)
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    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
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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.
+ * rGLUE & 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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_JIT_PROF_TABLE(r0)
+    @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.  On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+    cmp     r0,#0
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE_IFEQ(ip)       @ if not profiling, fallthrough otherwise */
+
+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 */
+
+/*
+ * 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).
+ */
+    GET_JIT_THRESHOLD(r1)
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    bl      dvmJitGetCodeAddr           @ r0<- dvmJitGetCodeAddr(rPC)
+    str     r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ r10 <- glue->self
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+    str     r2,[rGLUE,#offGlue_jitState]
+    mov     r2,#kInterpEntryInstr       @ normal entry reason
+    str     r2,[rGLUE,#offGlue_entryPoint]
+    mov     r1,#1                       @ set changeInterp
+    b       common_gotoBail
+
+#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, rGLUE: 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,rGLUE                    @ r2<- InterpState pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    add     rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    ldr    rPC,[r0,#offShadowSpace_startPC] @ restore PC
+    ldr    rFP,[r0,#offShadowSpace_fp]   @ restore FP
+    ldr    rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r2,#kInterpEntryInstr         @ normal entry reason
+    str    r2,[rGLUE,#offGlue_entryPoint]
+    mov    r1,#1                         @ set changeInterp
+    b      common_gotoBail
+
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ *  r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+    mov     r0, #kInterpEntryInstr
+    bl      common_periodicChecks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun.  If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly.  If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ *  r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ *  r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+    ldr     r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+    orrne   ip, ip, r1                  @ ip<- suspendCount | debuggerActive
+    orrs    ip, ip, r2                  @ ip<- suspend|debugger|profiler; set Z
+
+    bxeq    lr                          @ all zero, return
+
+    /*
+     * One or more interesting events have happened.  Figure out what.
+     *
+     * If debugging or profiling are compiled in, we need to disambiguate.
+     *
+     * r0 still holds the reentry type.
+     */
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+    cmp     ip, #0                      @ want suspend?
+    beq     1f                          @ no, must be debugger/profiler
+
+    stmfd   sp!, {r0, lr}               @ preserve r0 and lr
+#if defined(WITH_JIT)
+    /*
+     * Refresh the Jit's cached copy of profile table pointer.  This pointer
+     * doubles as the Jit's on/off switch.
+     */
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r3, [r3] @ r3 <- pJitProfTable
+    EXPORT_PC()                         @ need for precise GC
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    EXPORT_PC()                         @ need for precise GC
+#endif
+    bl      dvmCheckSuspendPending      @ do full check, suspend if necessary
+    ldmfd   sp!, {r0, lr}               @ restore r0 and lr
+
+    /*
+     * Reload the debugger/profiler enable flags.  We're checking to see
+     * if either of these got set while we were suspended.
+     *
+     * We can't really avoid the #ifdefs here, because the fields don't
+     * exist when the feature is disabled.
+     */
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+
+    orrs    r1, r1, r2
+    beq     2f
+
+1:  @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+    str     r0, [rGLUE, #offGlue_entryPoint]    @ store r0, need for debug/prof
+    add     rPC, rPC, r9                @ update rPC
+    mov     r1, #1                      @ "want switch" = true
+    b       common_gotoBail             @ side exit
+
+2:
+    bx      lr                          @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ *  r1 is "bool changeInterp", indicating if we want to switch to the
+ *     other interpreter or just bail all the way out
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    mov     r0, rGLUE                   @ r0<- glue ptr
+    b       dvmMterpStdBail             @ call(glue, changeInterp)
+
+    @add     r1, r1, #1                  @ using (boolean+1)
+    @add     r0, rGLUE, #offGlue_jmpBuf  @ r0<- &glue->jmpBuf
+    @bl      _longjmp                    @ does not return
+    @bl      common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+    @ 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
+
+    @ 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
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    ldrh    r3, [r0, #offMethod_outsSize]   @ r3<- methodToCall->outsSize
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    @ 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)
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, 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, r3=outSize, r9=regSize
+    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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    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]
+    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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [r2, #offThread_curFrame]   @ self->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, [r2, #offThread_curFrame]   @ self->curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+#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
+
+    @mov     lr, pc                      @ set return addr
+    @ldr     pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+    ldr     r3, [r3]                    @ r3 <- gDvmJit.pProfTable
+#endif
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+    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
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+#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, rGLUE                   @ A0<- glue
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    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:
+    mov     r0, #kInterpEntryReturn
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    cmp     r2, #0                      @ is this a break frame?
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    mov     r1, #0                      @ "want switch" = false
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame]  @ self->curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rGLUE, #offGlue_methodClassDex]
+    str     r10, [r3, #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, [rGLUE, #offGlue_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ 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:
+    mov     r0, #kInterpEntryThrow
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    ldr     r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+    ldr     r9, [r10, #offThread_exception] @ r9<- self->exception
+    mov     r1, r10                     @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [r10, #offThread_exception] @ self->exception = NULL
+
+    /* 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, [rGLUE, #offGlue_method] @ r1<- glue->method
+    mov     r0, r10                     @ 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, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, r10                     @ 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->curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rGLUE, #offGlue_method]    @ glue->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, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    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, [r10, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+    /* fix stack overflow if necessary */
+    ldrb    r1, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, r10                     @ 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, [rGLUE, #offGlue_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rGLUE, #offGlue_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+    ldr     r1, strLogTag
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [r10, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    mov     r1, #0                      @ "want switch" = false
+    b       common_gotoBail             @ bail out
+
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .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_GLUE()              @ pull rPC and rFP out of glue
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    ldr     r0, strArrayIndexException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+    EXPORT_PC()
+    ldr     r0, strArrayStoreException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strArithmeticException
+    ldr     r1, strDivideByZero
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    ldr     r0, strNegativeArraySizeException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    ldr     r0, strNoSuchMethodError
+    mov     r1, #0
+    bl      dvmThrowException
+    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()
+    ldr     r0, strNullPointerException
+    mov     r1, #0
+    bl      dvmThrowException
+    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
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+    /*
+     * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+    .align  2
+strArithmeticException:
+    .word   .LstrArithmeticException
+strArrayIndexException:
+    .word   .LstrArrayIndexException
+strArrayStoreException:
+    .word   .LstrArrayStoreException
+strDivideByZero:
+    .word   .LstrDivideByZero
+strNegativeArraySizeException:
+    .word   .LstrNegativeArraySizeException
+strNoSuchMethodError:
+    .word   .LstrNoSuchMethodError
+strNullPointerException:
+    .word   .LstrNullPointerException
+
+strLogTag:
+    .word   .LstrLogTag
+strExceptionNotCaughtLocally:
+    .word   .LstrExceptionNotCaughtLocally
+
+strNewline:
+    .word   .LstrNewline
+strSqueak:
+    .word   .LstrSqueak
+strPrintHex:
+    .word   .LstrPrintHex
+strPrintLong:
+    .word   .LstrPrintLong
+
+/*
+ * 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"
+.LstrArithmeticException:
+    .asciz  "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz  "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+    .asciz  "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+    .asciz  "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+    .asciz  "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+    .asciz  "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+    .asciz  "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+    .asciz  "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<0x%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..3ffd35d
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv7-a-neon.S
@@ -0,0 +1,10549 @@
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE()     ldr     rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE()       str     rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE()     ldr     rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE()       str     rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE()  ldmia   rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE()    stmia   rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * 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 #2]!", 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_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]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg)    ldr     _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg)     ldr     _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * 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
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5.  Essentially a
+ * one-way branch.
+ *
+ * May modify IP.  Does not modify LR.
+ */
+.macro  LDR_PC source
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro  LDMFD_PC regs
+    ldmfd   sp!, {\regs,pc}
+.endm
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ * If the argument is nonzero, emit barrier; otherwise, emit nothing.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb
+#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  MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value.  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, #offGlue_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rGLUE, r0                   @ set rGLUE
+    ldr     r1, [r0, #offGlue_entryPoint]   @ enum is 4 bytes in aapcs-EABI
+    LOAD_PC_FP_FROM_GLUE()              @ load rPC and rFP from "glue"
+    adr     rIBASE, dvmAsmInstructionStart  @ set rIBASE
+    cmp     r1, #kInterpEntryInstr      @ usual case?
+    bne     .Lnot_instr                 @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    /* Entry is always a possible trace start */
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [r10, #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, [r10, #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
+
+.Lnot_instr:
+    cmp     r1, #kInterpEntryReturn     @ were we returning from a method?
+    beq     common_returnFromMethod
+
+.Lnot_return:
+    cmp     r1, #kInterpEntryThrow      @ were we throwing an exception?
+    beq     common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+    ldr     r10,[rGLUE, #offGlue_jitResumeNPC]
+    ldr     r2,[rGLUE, #offGlue_jitResumeDPC]
+    cmp     r1, #kInterpEntryResume     @ resuming after Jit single-step?
+    bne     .Lbad_arg
+    cmp     rPC,r2
+    bne     .LentryInstr                @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+    @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+    b       jitSVShadowRunStart         @ re-enter the translation after the
+                                        @ single-stepped instruction
+    @noreturn
+#endif
+    mov     r1, #kInterpEntryInstr
+    str     r1, [rGLUE, #offGlue_entryPoint]
+    bx      r10                         @ re-enter the translation
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+
+
+    .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  MterpGlue* glue
+ *  r1  bool changeInterp
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offGlue_bailPtr]      @ sp<- saved SP
+    mov     r0, r1                          @ return the changeInterp value
+    add     sp, sp, #4                      @ un-align 64
+    LDMFD_PC "r4-r10,fp"                    @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+    .word   .LstrBadEntryPoint
+
+
+    .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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    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, [rGLUE, #offGlue_retval]    @ r0<- glue->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, rGLUE, #offGlue_retval  @ r3<- &glue->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
+    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_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, [rGLUE, #offGlue_retval]    @ r0<- glue->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 */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [r0, #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, [r0, #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 "glue"
+     * 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, [rGLUE, #offGlue_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 "glue"
+     * 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, rGLUE, #offGlue_retval  @ r3<- &glue->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 "glue"
+     * 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC, MONITOR_TRACKING
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r1, [r0, #offThread_exception] @ check for exception
+    cmp     r1, #0
+    bne     common_exceptionThrown      @ exception raised, bail out
+#endif
+    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
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    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, [r0, #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 */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r9, r0, asr #24             @ r9<- ssssssAA (sign-extended)
+    mov     r9, r9, lsl #1              @ r9<- byte offset
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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)
+    movs    r9, r0, asl #1              @ r9<- byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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".  The ORRS
+     * instruction doesn't affect the V flag, so we need to clear it
+     * explicitly.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    cmp     ip, ip                      @ (clear V flag during stall)
+    orrs    r0, r0, r1, lsl #16         @ r0<- AAAAaaaa, check sign
+    mov     r9, r0, asl #1              @ r9<- byte offset
+    ble     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     */
+    /* 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(r0, r3)                    @ r0<- 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, r0, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r0, 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodNoRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+
+/* ------------------------------ */
+    .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: 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: 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: armv6t2/OP_DIV_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 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: armv6t2/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* 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 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: 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: armv6t2/OP_DIV_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 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: armv6t2/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* 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 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: 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: 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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                            @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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 */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method]    @ r0<- glue->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.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &glue->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 */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &glue->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_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+    /*
+     * invoke-direct-empty is a no-op in a "standard" interpreter.
+     */
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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
+    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: 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, [rGLUE, #offGlue_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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+/* ------------------------------ */
+    .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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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 class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    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 with the
+    @ class of the object that failed to be cast.
+    EXPORT_PC()                         @ about to throw
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- obj->clazz
+    ldr     r0, .LstrClassCastExceptionPtr
+    ldr     r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+    bl      dvmThrowExceptionWithClassMessage
+    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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrClassCastExceptionPtr:
+    .word   .LstrClassCastException
+
+/* 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, [rGLUE, #offGlue_method]    @ r0<- glue->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?
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * 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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrInstantiationErrorPtr:
+    .word   .LstrInstantiationError
+
+/* 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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!0)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!1)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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     common_errArrayStore        @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rGLUE, #offGlue_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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* 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
+    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
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.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, [rGLUE, #offGlue_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
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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 for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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      dvmQuasiAtomicSwap64        @ 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
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r9, 2)                        @ r9<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, r9, #0xf000             @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, r9, #0x0f00             @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, r9, #0x00f0             @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, r9, #0x000f             @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_table       @ table of InlineOperation
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+/*
+ * 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:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    r0 <= PC
+ *    r1 <= PC of resume instruction
+ *    lr <= resume point in translation
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r1,#kInterpEntryInstr
+    @ enum is 4 byte in aapcs-EABI
+    str    r1, [rGLUE, #offGlue_entryPoint]
+    mov    rPC,r0
+    EXPORT_PC()
+
+    adrl   rIBASE, dvmAsmInstructionStart
+    mov    r2,#kJitSingleStep     @ Ask for single step and then revert
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r1,#1                  @ set changeInterp to bail to debug interp
+    b      common_gotoBail
+
+/*
+ * 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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr       @ Is there a translation?
+    str    r0, [r10, #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:
+    adrl   rIBASE, dvmAsmInstructionStart
+    GET_JIT_PROF_TABLE(r0)
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    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
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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.
+ * rGLUE & 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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_JIT_PROF_TABLE(r0)
+    @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.  On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+    cmp     r0,#0
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE_IFEQ(ip)       @ if not profiling, fallthrough otherwise */
+
+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 */
+
+/*
+ * 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).
+ */
+    GET_JIT_THRESHOLD(r1)
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    bl      dvmJitGetCodeAddr           @ r0<- dvmJitGetCodeAddr(rPC)
+    str     r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ r10 <- glue->self
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+    str     r2,[rGLUE,#offGlue_jitState]
+    mov     r2,#kInterpEntryInstr       @ normal entry reason
+    str     r2,[rGLUE,#offGlue_entryPoint]
+    mov     r1,#1                       @ set changeInterp
+    b       common_gotoBail
+
+#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, rGLUE: 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,rGLUE                    @ r2<- InterpState pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    add     rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    ldr    rPC,[r0,#offShadowSpace_startPC] @ restore PC
+    ldr    rFP,[r0,#offShadowSpace_fp]   @ restore FP
+    ldr    rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r2,#kInterpEntryInstr         @ normal entry reason
+    str    r2,[rGLUE,#offGlue_entryPoint]
+    mov    r1,#1                         @ set changeInterp
+    b      common_gotoBail
+
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ *  r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+    mov     r0, #kInterpEntryInstr
+    bl      common_periodicChecks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun.  If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly.  If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ *  r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ *  r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+    ldr     r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+    orrne   ip, ip, r1                  @ ip<- suspendCount | debuggerActive
+    orrs    ip, ip, r2                  @ ip<- suspend|debugger|profiler; set Z
+
+    bxeq    lr                          @ all zero, return
+
+    /*
+     * One or more interesting events have happened.  Figure out what.
+     *
+     * If debugging or profiling are compiled in, we need to disambiguate.
+     *
+     * r0 still holds the reentry type.
+     */
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+    cmp     ip, #0                      @ want suspend?
+    beq     1f                          @ no, must be debugger/profiler
+
+    stmfd   sp!, {r0, lr}               @ preserve r0 and lr
+#if defined(WITH_JIT)
+    /*
+     * Refresh the Jit's cached copy of profile table pointer.  This pointer
+     * doubles as the Jit's on/off switch.
+     */
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r3, [r3] @ r3 <- pJitProfTable
+    EXPORT_PC()                         @ need for precise GC
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    EXPORT_PC()                         @ need for precise GC
+#endif
+    bl      dvmCheckSuspendPending      @ do full check, suspend if necessary
+    ldmfd   sp!, {r0, lr}               @ restore r0 and lr
+
+    /*
+     * Reload the debugger/profiler enable flags.  We're checking to see
+     * if either of these got set while we were suspended.
+     *
+     * We can't really avoid the #ifdefs here, because the fields don't
+     * exist when the feature is disabled.
+     */
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+
+    orrs    r1, r1, r2
+    beq     2f
+
+1:  @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+    str     r0, [rGLUE, #offGlue_entryPoint]    @ store r0, need for debug/prof
+    add     rPC, rPC, r9                @ update rPC
+    mov     r1, #1                      @ "want switch" = true
+    b       common_gotoBail             @ side exit
+
+2:
+    bx      lr                          @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ *  r1 is "bool changeInterp", indicating if we want to switch to the
+ *     other interpreter or just bail all the way out
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    mov     r0, rGLUE                   @ r0<- glue ptr
+    b       dvmMterpStdBail             @ call(glue, changeInterp)
+
+    @add     r1, r1, #1                  @ using (boolean+1)
+    @add     r0, rGLUE, #offGlue_jmpBuf  @ r0<- &glue->jmpBuf
+    @bl      _longjmp                    @ does not return
+    @bl      common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+    @ 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
+
+    @ 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
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    ldrh    r3, [r0, #offMethod_outsSize]   @ r3<- methodToCall->outsSize
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    @ 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)
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, 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, r3=outSize, r9=regSize
+    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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    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]
+    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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [r2, #offThread_curFrame]   @ self->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, [r2, #offThread_curFrame]   @ self->curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+#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
+
+    @mov     lr, pc                      @ set return addr
+    @ldr     pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+    ldr     r3, [r3]                    @ r3 <- gDvmJit.pProfTable
+#endif
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+    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
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+#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, rGLUE                   @ A0<- glue
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    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:
+    mov     r0, #kInterpEntryReturn
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    cmp     r2, #0                      @ is this a break frame?
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    mov     r1, #0                      @ "want switch" = false
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame]  @ self->curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rGLUE, #offGlue_methodClassDex]
+    str     r10, [r3, #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, [rGLUE, #offGlue_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ 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:
+    mov     r0, #kInterpEntryThrow
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    ldr     r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+    ldr     r9, [r10, #offThread_exception] @ r9<- self->exception
+    mov     r1, r10                     @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [r10, #offThread_exception] @ self->exception = NULL
+
+    /* 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, [rGLUE, #offGlue_method] @ r1<- glue->method
+    mov     r0, r10                     @ 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, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, r10                     @ 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->curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rGLUE, #offGlue_method]    @ glue->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, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    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, [r10, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+    /* fix stack overflow if necessary */
+    ldrb    r1, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, r10                     @ 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, [rGLUE, #offGlue_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rGLUE, #offGlue_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+    ldr     r1, strLogTag
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [r10, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    mov     r1, #0                      @ "want switch" = false
+    b       common_gotoBail             @ bail out
+
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .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_GLUE()              @ pull rPC and rFP out of glue
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    ldr     r0, strArrayIndexException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+    EXPORT_PC()
+    ldr     r0, strArrayStoreException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strArithmeticException
+    ldr     r1, strDivideByZero
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    ldr     r0, strNegativeArraySizeException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    ldr     r0, strNoSuchMethodError
+    mov     r1, #0
+    bl      dvmThrowException
+    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()
+    ldr     r0, strNullPointerException
+    mov     r1, #0
+    bl      dvmThrowException
+    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
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+    /*
+     * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+    .align  2
+strArithmeticException:
+    .word   .LstrArithmeticException
+strArrayIndexException:
+    .word   .LstrArrayIndexException
+strArrayStoreException:
+    .word   .LstrArrayStoreException
+strDivideByZero:
+    .word   .LstrDivideByZero
+strNegativeArraySizeException:
+    .word   .LstrNegativeArraySizeException
+strNoSuchMethodError:
+    .word   .LstrNoSuchMethodError
+strNullPointerException:
+    .word   .LstrNullPointerException
+
+strLogTag:
+    .word   .LstrLogTag
+strExceptionNotCaughtLocally:
+    .word   .LstrExceptionNotCaughtLocally
+
+strNewline:
+    .word   .LstrNewline
+strSqueak:
+    .word   .LstrSqueak
+strPrintHex:
+    .word   .LstrPrintHex
+strPrintLong:
+    .word   .LstrPrintLong
+
+/*
+ * 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"
+.LstrArithmeticException:
+    .asciz  "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz  "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+    .asciz  "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+    .asciz  "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+    .asciz  "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+    .asciz  "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+    .asciz  "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+    .asciz  "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<0x%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..ac5b2c3
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv7-a.S
@@ -0,0 +1,10549 @@
+/*
+ * 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  rGLUE     MterpGlue 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 rGLUE   r6
+#define rINST   r7
+#define rIBASE  r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE()     ldr     rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE()       str     rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE()     ldr     rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE()       str     rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE()  ldmia   rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE()    stmia   rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects.  Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * 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 #2]!", 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_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]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg)    ldr     _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg)     ldr     _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * 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
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5.  Essentially a
+ * one-way branch.
+ *
+ * May modify IP.  Does not modify LR.
+ */
+.macro  LDR_PC source
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro  LDR_PC_LR source
+    mov     lr, pc
+    ldr     pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro  LDMFD_PC regs
+    ldmfd   sp!, {\regs,pc}
+.endm
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ * If the argument is nonzero, emit barrier; otherwise, emit nothing.
+ */
+.macro  SMP_DMB
+#if ANDROID_SMP != 0
+    dmb
+#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  MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value.  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, #offGlue_bailPtr]  @ save SP for eventual return
+
+    /* set up "named" registers, figure out entry point */
+    mov     rGLUE, r0                   @ set rGLUE
+    ldr     r1, [r0, #offGlue_entryPoint]   @ enum is 4 bytes in aapcs-EABI
+    LOAD_PC_FP_FROM_GLUE()              @ load rPC and rFP from "glue"
+    adr     rIBASE, dvmAsmInstructionStart  @ set rIBASE
+    cmp     r1, #kInterpEntryInstr      @ usual case?
+    bne     .Lnot_instr                 @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    /* Entry is always a possible trace start */
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_INST()
+    mov     r1, #0                      @ prepare the value for the new state
+    str     r1, [r10, #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, [r10, #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
+
+.Lnot_instr:
+    cmp     r1, #kInterpEntryReturn     @ were we returning from a method?
+    beq     common_returnFromMethod
+
+.Lnot_return:
+    cmp     r1, #kInterpEntryThrow      @ were we throwing an exception?
+    beq     common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+    ldr     r10,[rGLUE, #offGlue_jitResumeNPC]
+    ldr     r2,[rGLUE, #offGlue_jitResumeDPC]
+    cmp     r1, #kInterpEntryResume     @ resuming after Jit single-step?
+    bne     .Lbad_arg
+    cmp     rPC,r2
+    bne     .LentryInstr                @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+    @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+    b       jitSVShadowRunStart         @ re-enter the translation after the
+                                        @ single-stepped instruction
+    @noreturn
+#endif
+    mov     r1, #kInterpEntryInstr
+    str     r1, [rGLUE, #offGlue_entryPoint]
+    bx      r10                         @ re-enter the translation
+#endif
+
+.Lbad_arg:
+    ldr     r0, strBadEntryPoint
+    @ r1 holds value of entryPoint
+    bl      printf
+    bl      dvmAbort
+    .fnend
+
+
+    .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  MterpGlue* glue
+ *  r1  bool changeInterp
+ */
+dvmMterpStdBail:
+    ldr     sp, [r0, #offGlue_bailPtr]      @ sp<- saved SP
+    mov     r0, r1                          @ return the changeInterp value
+    add     sp, sp, #4                      @ un-align 64
+    LDMFD_PC "r4-r10,fp"                    @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+    .word   .LstrBadEntryPoint
+
+
+    .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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    stmia   r2, {r0-r1}                 @ fp[AAAA]<- r0/r1
+    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, [rGLUE, #offGlue_retval]    @ r0<- glue->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, rGLUE, #offGlue_retval  @ r3<- &glue->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
+    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_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, [rGLUE, #offGlue_retval]    @ r0<- glue->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 */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    mov     r2, rINST, lsr #8           @ r2<- AA
+    ldr     r3, [r0, #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, [r0, #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 "glue"
+     * 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, [rGLUE, #offGlue_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 "glue"
+     * 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, rGLUE, #offGlue_retval  @ r3<- &glue->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 "glue"
+     * 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    cmp     r1, #0                      @ null object?
+    EXPORT_PC()                         @ need for precise GC, MONITOR_TRACKING
+    beq     common_errNullObject        @ null object, throw an exception
+    FETCH_ADVANCE_INST(1)               @ advance rPC, load rINST
+    bl      dvmLockObject               @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r1, [r0, #offThread_exception] @ check for exception
+    cmp     r1, #0
+    bne     common_exceptionThrown      @ exception raised, bail out
+#endif
+    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
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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)
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    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, [r0, #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 */
+    mov     r0, rINST, lsl #16          @ r0<- AAxx0000
+    movs    r9, r0, asr #24             @ r9<- ssssssAA (sign-extended)
+    mov     r9, r9, lsl #1              @ r9<- byte offset
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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)
+    movs    r9, r0, asl #1              @ r9<- byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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".  The ORRS
+     * instruction doesn't affect the V flag, so we need to clear it
+     * explicitly.
+     */
+    /* goto/32 +AAAAAAAA */
+    FETCH(r0, 1)                        @ r0<- aaaa (lo)
+    FETCH(r1, 2)                        @ r1<- AAAA (hi)
+    cmp     ip, ip                      @ (clear V flag during stall)
+    orrs    r0, r0, r1, lsl #16         @ r0<- AAAAaaaa, check sign
+    mov     r9, r0, asl #1              @ r9<- byte offset
+    ble     common_backwardBranch       @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+    .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.
+     *
+     * 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
+    movs    r9, r0, asl #1              @ r9<- branch byte offset, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+    beq     common_backwardBranch       @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, r3                      @ compare (vA, vB)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    b        common_testUpdateProfile
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bne  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    beq  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bge  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    blt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    ble  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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
+    mov     r9, #4                      @ r0<- BYTE branch dist for not-taken
+    cmp     r2, #0                      @ compare (vA, 0)
+    bgt  1f                      @ branch to 1 if comparison failed
+    FETCH_S(r9, 1)                      @ r9<- branch offset, in code units
+    movs    r9, r9, asl #1              @ convert to bytes, check sign
+    bmi     common_backwardBranch       @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+    .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.
+     *
+     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+     * instructions.  We use a pair of FETCH_Bs instead.
+     */
+    /* 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(r0, r3)                    @ r0<- 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, r0, lsl #2      @ r10<- arrayObj + index*width
+    cmp     r0, 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodNoRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodNoRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodNoRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved baseMethod
+    cmp     r2, #0                      @ null "this"?
+    ldr     r9, [rGLUE, #offGlue_method] @ r9<- current method
+    beq     common_errNullObject        @ null "this", throw exception
+    cmp     r0, #0                      @ already resolved?
+    ldr     r9, [r9, #offMethod_clazz]  @ r9<- 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, [rGLUE, #offGlue_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(r2, r10)                   @ r2<- "this" ptr
+    beq     .LOP_INVOKE_DIRECT_RANGE_resolve         @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp     r2, #0                      @ null "this" ref?
+    bne     common_invokeMethodRange   @ no, continue on
+    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, [rGLUE, #offGlue_methodClassDex]    @ r3<- pDvmDex
+    FETCH(r1, 1)                        @ r1<- BBBB
+    ldr     r3, [r3, #offDvmDex_pResMethods]    @ r3<- pDvmDex->pResMethods
+    ldr     r0, [r3, r1, lsl #2]        @ r0<- resolved methodToCall
+    cmp     r0, #0                      @ already resolved?
+    EXPORT_PC()                         @ must export for invoke
+    bne     common_invokeMethodRange @ yes, continue on
+0:  ldr     r3, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    bne     common_invokeMethodRange @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+
+/* ------------------------------ */
+    .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(r0, r2)                    @ r0<- first arg ("this")
+    ldr     r3, [rGLUE, #offGlue_methodClassDex]    @ r3<- methodClassDex
+    cmp     r0, #0                      @ null obj?
+    ldr     r2, [rGLUE, #offGlue_method]  @ r2<- method
+    beq     common_errNullObject        @ yes, fail
+    ldr     r0, [r0, #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 @ jump to common handler
+
+
+/* ------------------------------ */
+    .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: 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: 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: armv6t2/OP_DIV_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 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: armv6t2/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* 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 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: 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: armv6t2/OP_DIV_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 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: armv6t2/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* 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 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: 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: 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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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                            @ releasing store
+    str     r1, [r0, #offStaticField_value] @ field<- vAA
+    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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]  @ r0<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+    mov     r9, rINST, lsr #8           @ r9<- AA
+    ldr     r2, [r0, 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      dvmQuasiAtomicSwap64        @ 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 */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method]    @ r0<- glue->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.
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #12          @ r0<- B
+    str     r1, [sp]                    @ push &glue->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 */
+    FETCH(r10, 1)                       @ r10<- BBBB
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &glue->retval
+    EXPORT_PC()                         @ can throw
+    sub     sp, sp, #8                  @ make room for arg, +64 bit align
+    mov     r0, rINST, lsr #8           @ r0<- AA
+    str     r1, [sp]                    @ push &glue->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_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+    /*
+     * invoke-direct-empty is a no-op in a "standard" interpreter.
+     */
+    FETCH_ADVANCE_INST(3)               @ advance to next instr, load rINST
+    GET_INST_OPCODE(ip)                 @ ip<- opcode from rINST
+    GOTO_OPCODE(ip)                     @ execute it
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+    bl      common_abort
+
+
+/* ------------------------------ */
+    .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
+    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: 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, [rGLUE, #offGlue_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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+/* ------------------------------ */
+    .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(r2, r3)                    @ r2<- vC ("this" ptr)
+    cmp     r2, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r2, [r2, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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(r3, r10)                   @ r3<- "this"
+    ldr     r2, [r2, #offClassObject_vtable]    @ r2<- ...clazz->super->vtable
+    cmp     r3, #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 @ continue on
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, 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, [rGLUE, #offGlue_methodClassDex]    @ r2<- DvmDex
+    FETCH(r1, 1)                        @ r1<- field ref BBBB
+    ldr     r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+    ldr     r0, [r2, r1, lsl #2]        @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ is resolved entry null?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ no, continue
+    ldr     r9, [rGLUE, #offGlue_method]    @ r9<- current method
+    EXPORT_PC()                         @ resolve() could throw, so export now
+    ldr     r0, [r9, #offMethod_clazz]  @ r0<- method->clazz
+    bl      dvmResolveStaticField       @ r0<- resolved StaticField ptr
+    cmp     r0, #0                      @ success?
+    bne     .LOP_SPUT_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+
+
+/* ------------------------------ */
+    .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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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, [rGLUE, #offGlue_method] @ r0<- glue->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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r9)                    @ vAA<- r0
+    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 class resolved from BBBB
+     *  r9 holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    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 with the
+    @ class of the object that failed to be cast.
+    EXPORT_PC()                         @ about to throw
+    ldr     r3, [r9, #offObject_clazz]  @ r3<- obj->clazz
+    ldr     r0, .LstrClassCastExceptionPtr
+    ldr     r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+    bl      dvmThrowExceptionWithClassMessage
+    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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrClassCastExceptionPtr:
+    .word   .LstrClassCastException
+
+/* 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, [rGLUE, #offGlue_method]    @ r0<- glue->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?
+    beq     common_exceptionThrown      @ yes, handle the exception
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SET_VREG(r0, r3)                    @ vAA<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+    /*
+     * 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, [rGLUE, #offGlue_method] @ r3<- glue->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
+
+.LstrInstantiationErrorPtr:
+    .word   .LstrInstantiationError
+
+/* 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, [rGLUE, #offGlue_method] @ r3<- glue->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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!0)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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, [rGLUE, #offGlue_retval]      @ retval.l <- new array
+    str     rINST, [rGLUE, #offGlue_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, [rGLUE, #offGlue_retval]     @ r0<- object
+    ldr     r1, [rGLUE, #offGlue_retval+4]   @ r1<- type
+    ldr     r2, [rGLUE, #offGlue_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_strInternalError
+    ldr     r1, .L_strFilledNewArrayNotImpl
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+    .if     (!1)                 @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+    .word   .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+    .word   .LstrInternalError
+    .endif
+
+/* 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     common_errArrayStore        @ no
+    mov     r1, rINST                   @ r1<- arrayObj
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    ldr     r2, [rGLUE, #offGlue_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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str     r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* 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
+    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
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    @ no-op                             @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_OBJECT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SGET_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.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, [rGLUE, #offGlue_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
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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 for OP_SPUT_BOOLEAN */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BOOLEAN_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_BYTE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_BYTE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_CHAR_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_CHAR_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_SHORT_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_SHORT_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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(r1, r10)                   @ r1<- "this" ptr
+    ldrh    r2, [r0, #offMethod_methodIndex]    @ r2<- baseMethod->methodIndex
+    cmp     r1, #0                      @ is "this" null?
+    beq     common_errNullObject        @ null "this", throw exception
+    ldr     r3, [r1, #offObject_clazz]  @ r1<- thisPtr->clazz
+    ldr     r3, [r3, #offClassObject_vtable]    @ r3<- thisPtr->clazz->vtable
+    ldr     r0, [r3, r2, lsl #2]        @ r3<- vtable[methodIndex]
+    bl      common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * At this point:
+     *  r0 = resolved base method
+     *  r9 = method->clazz
+     */
+.LOP_INVOKE_SUPER_RANGE_continue:
+    ldr     r1, [r9, #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, r9                      @ 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, [rGLUE, #offGlue_method] @ r3<- glue->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?
+    GET_VREG(r2, r10)                   @ r2<- "this" ptr (reload)
+    bne     .LOP_INVOKE_DIRECT_RANGE_finish          @ no, continue
+    b       common_exceptionThrown      @ yes, handle exception
+
+/* 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
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str  r0, [r9, r3]                @ obj.field (8/16/32 bits)<- r0
+    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
+     */
+.LOP_SGET_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SPUT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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      dvmQuasiAtomicSwap64        @ 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
+     *
+     * Returns StaticField pointer in r0.
+     */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+    /*
+     * Continuation if the field has not yet been resolved.
+     *  r1: BBBB field ref
+     *  r9: &fp[AA]
+     *
+     * Returns StaticField pointer in r2.
+     */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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
+    bne     .LOP_SPUT_WIDE_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* 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(r9, 2)                        @ r9<- FEDC
+    add     pc, pc, r0, lsl #3          @ computed goto, 2 instrs each
+    bl      common_abort                @ (skipped due to ARM prefetch)
+4:  and     ip, r9, #0xf000             @ isolate F
+    ldr     r3, [rFP, ip, lsr #10]      @ r3<- vF (shift right 12, left 2)
+3:  and     ip, r9, #0x0f00             @ isolate E
+    ldr     r2, [rFP, ip, lsr #6]       @ r2<- vE
+2:  and     ip, r9, #0x00f0             @ isolate D
+    ldr     r1, [rFP, ip, lsr #2]       @ r1<- vD
+1:  and     ip, r9, #0x000f             @ isolate C
+    ldr     r0, [rFP, ip, lsl #2]       @ r0<- vC
+0:
+    ldr     r9, .LOP_EXECUTE_INLINE_table       @ table of InlineOperation
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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
+    LDR_PC  "[r9, r10, lsl #4]"         @ sizeof=16, "func" is first entry
+    @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+    .word   gDvmInlineOpsTable
+
+/* 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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    beq     common_errNullObject        @ object was null
+    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r0, [r9, r3]                @ obj.field (32 bits)<- r0
+    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
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    ldr     r2, [rGLUE, #offGlue_method]    @ r2<- current method
+    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?
+    bne     .LOP_SGET_OBJECT_VOLATILE_finish          @ yes, finish
+    b       common_exceptionThrown      @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.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, [rGLUE, #offGlue_cardTable]  @ r2<- card table base
+    ldr     r9, [r0, #offField_clazz]   @ r9<- field->clazz
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    SMP_DMB                            @ releasing store
+    str     r1, [r0, #offStaticField_value]  @ field<- vAA
+    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
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ *  Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+    .text
+    .align  2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+    .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r2,#kSVSPunt                 @ r2<- interpreter entry point
+    mov    r3, #0
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoProfile            @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    ldr    r0,[lr, #-1]                 @ pass our target PC
+    mov    r2,#kSVSNormal               @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+
+    .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSNoChain              @ r2<- interpreter entry point
+    mov    r3, #0                       @ 0 means !inJitCodeCache
+    str    r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    b      jitSVShadowRunEnd            @ doesn't return
+#else
+/*
+ * 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:
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    rPC, r0
+#if defined(WITH_JIT_TUNING)
+    mov    r0,lr
+    bl     dvmBumpPunt;
+#endif
+    EXPORT_PC()
+    mov    r0, #0
+    str    r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ *    r0 <= PC
+ *    r1 <= PC of resume instruction
+ *    lr <= resume point in translation
+ */
+    .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+    str    lr,[rGLUE,#offGlue_jitResumeNPC]
+    str    r1,[rGLUE,#offGlue_jitResumeDPC]
+    mov    r1,#kInterpEntryInstr
+    @ enum is 4 byte in aapcs-EABI
+    str    r1, [rGLUE, #offGlue_entryPoint]
+    mov    rPC,r0
+    EXPORT_PC()
+
+    adrl   rIBASE, dvmAsmInstructionStart
+    mov    r2,#kJitSingleStep     @ Ask for single step and then revert
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r1,#1                  @ set changeInterp to bail to debug interp
+    b      common_gotoBail
+
+/*
+ * 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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    add    rINST,lr,#-5            @ save start of chain branch
+    add    rINST, #-4              @  .. which is 9 bytes back
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr       @ Is there a translation?
+    str    r0, [r10, #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:
+    adrl   rIBASE, dvmAsmInstructionStart
+    GET_JIT_PROF_TABLE(r0)
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    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
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    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
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #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.
+ * rGLUE & 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()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_JIT_PROF_TABLE(r0)
+    @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate.  On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+    cmp     r0,#0
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE_IFEQ(ip)       @ if not profiling, fallthrough otherwise */
+
+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 */
+
+/*
+ * 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).
+ */
+    GET_JIT_THRESHOLD(r1)
+    ldr     r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+    strb    r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+    EXPORT_PC()
+    mov     r0,rPC
+    bl      dvmJitGetCodeAddr           @ r0<- dvmJitGetCodeAddr(rPC)
+    str     r0, [r10, #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
+    ldr    r10, [rGLUE, #offGlue_self]  @ r10 <- glue->self
+    mov    r3, #0                       @ 0 means not in the JIT code cache
+    str    r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+    /* no return */
+#endif
+
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+    str     r2,[rGLUE,#offGlue_jitState]
+    mov     r2,#kInterpEntryInstr       @ normal entry reason
+    str     r2,[rGLUE,#offGlue_entryPoint]
+    mov     r1,#1                       @ set changeInterp
+    b       common_gotoBail
+
+#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, rGLUE: 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,rGLUE                    @ r2<- InterpState pointer
+    mov     r3,r10                      @ r3<- target translation
+    bl      dvmSelfVerificationSaveState @ save registers to shadow space
+    ldr     rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+    add     rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+    bx      r10                         @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+    mov    r1,rFP                        @ pass ending fp
+    bl     dvmSelfVerificationRestoreState @ restore pc and fp values
+    ldr    rPC,[r0,#offShadowSpace_startPC] @ restore PC
+    ldr    rFP,[r0,#offShadowSpace_fp]   @ restore FP
+    ldr    rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+    ldr    r1,[r0,#offShadowSpace_svState] @ get self verification state
+    cmp    r1,#0                         @ check for punt condition
+    beq    1f
+    mov    r2,#kJitSelfVerification      @ ask for self verification
+    str    r2,[rGLUE,#offGlue_jitState]
+    mov    r2,#kInterpEntryInstr         @ normal entry reason
+    str    r2,[rGLUE,#offGlue_entryPoint]
+    mov    r1,#1                         @ set changeInterp
+    b      common_gotoBail
+
+1:                                       @ exit to interpreter without check
+    EXPORT_PC()
+    adrl   rIBASE, dvmAsmInstructionStart
+    FETCH_INST()
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ *  r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+    mov     r0, #kInterpEntryInstr
+    bl      common_periodicChecks
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    cmp     r0,#0
+    bne     common_updateProfile
+    GET_INST_OPCODE(ip)
+    GOTO_OPCODE(ip)
+#else
+    FETCH_ADVANCE_INST_RB(r9)           @ update rPC, load rINST
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun.  If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly.  If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ *  r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ *  r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+    ldr     r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+    orrne   ip, ip, r1                  @ ip<- suspendCount | debuggerActive
+    orrs    ip, ip, r2                  @ ip<- suspend|debugger|profiler; set Z
+
+    bxeq    lr                          @ all zero, return
+
+    /*
+     * One or more interesting events have happened.  Figure out what.
+     *
+     * If debugging or profiling are compiled in, we need to disambiguate.
+     *
+     * r0 still holds the reentry type.
+     */
+    ldr     ip, [r3]                    @ ip<- suspendCount (int)
+    cmp     ip, #0                      @ want suspend?
+    beq     1f                          @ no, must be debugger/profiler
+
+    stmfd   sp!, {r0, lr}               @ preserve r0 and lr
+#if defined(WITH_JIT)
+    /*
+     * Refresh the Jit's cached copy of profile table pointer.  This pointer
+     * doubles as the Jit's on/off switch.
+     */
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    ldr     r3, [r3] @ r3 <- pJitProfTable
+    EXPORT_PC()                         @ need for precise GC
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- glue->self
+    EXPORT_PC()                         @ need for precise GC
+#endif
+    bl      dvmCheckSuspendPending      @ do full check, suspend if necessary
+    ldmfd   sp!, {r0, lr}               @ restore r0 and lr
+
+    /*
+     * Reload the debugger/profiler enable flags.  We're checking to see
+     * if either of these got set while we were suspended.
+     *
+     * We can't really avoid the #ifdefs here, because the fields don't
+     * exist when the feature is disabled.
+     */
+    ldr     r1, [rGLUE, #offGlue_pDebuggerActive]   @ r1<- &debuggerActive
+    cmp     r1, #0                      @ debugger enabled?
+    ldrneb  r1, [r1]                    @ yes, r1<- debuggerActive (boolean)
+    ldr     r2, [rGLUE, #offGlue_pActiveProfilers]  @ r2<- &activeProfilers
+    ldr     r2, [r2]                    @ r2<- activeProfilers (int)
+
+    orrs    r1, r1, r2
+    beq     2f
+
+1:  @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+    str     r0, [rGLUE, #offGlue_entryPoint]    @ store r0, need for debug/prof
+    add     rPC, rPC, r9                @ update rPC
+    mov     r1, #1                      @ "want switch" = true
+    b       common_gotoBail             @ side exit
+
+2:
+    bx      lr                          @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ *  r1 is "bool changeInterp", indicating if we want to switch to the
+ *     other interpreter or just bail all the way out
+ */
+common_gotoBail:
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    mov     r0, rGLUE                   @ r0<- glue ptr
+    b       dvmMterpStdBail             @ call(glue, changeInterp)
+
+    @add     r1, r1, #1                  @ using (boolean+1)
+    @add     r0, rGLUE, #offGlue_jmpBuf  @ r0<- &glue->jmpBuf
+    @bl      _longjmp                    @ does not return
+    @bl      common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+    @ 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
+
+    @ 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
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+1:  ldr     r1, [r3], #4                @ val = *fp++
+    subs    r2, r2, #1                  @ count--
+    str     r1, [r10], #4               @ *outs++ = val
+    bne     1b                          @ ...while count != 0
+    ldrh    r3, [r0, #offMethod_outsSize]   @ r3<- methodToCall->outsSize
+    b       .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ *  r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    @ 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)
+    ldrh    r9, [r0, #offMethod_registersSize]  @ r9<- methodToCall->regsSize
+    ldrh    r3, [r0, #offMethod_outsSize]  @ r3<- methodToCall->outsSize
+    beq     .LinvokeArgsDone
+
+    @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, 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, r3=outSize, r9=regSize
+    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, [rGLUE, #offGlue_interpStackEnd]    @ r9<- interpStackEnd
+    sub     r3, r10, r3, lsl #2         @ r3<- bottom (newsave - outsSize)
+    cmp     r3, r9                      @ bottom < interpStackEnd?
+    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]
+    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
+    ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
+
+    @ Update "glue" values for the new method
+    @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+    str     r0, [rGLUE, #offGlue_method]    @ glue->method = methodToCall
+    str     r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+    GET_JIT_PROF_TABLE(r0)
+    mov     rFP, r1                         @ fp = newFp
+    GET_PREFETCHED_OPCODE(ip, r9)           @ extract prefetched opcode from r9
+    mov     rINST, r9                       @ publish new rINST
+    str     r1, [r2, #offThread_curFrame]   @ self->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, [r2, #offThread_curFrame]   @ self->curFrame = newFp
+    GOTO_OPCODE(ip)                         @ jump to next instruction
+#endif
+
+.LinvokeNative:
+    @ Prep for the native call
+    @ r0=methodToCall, r1=newFp, r10=newSaveArea
+    ldr     r3, [rGLUE, #offGlue_self]      @ r3<- glue->self
+    ldr     r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+    str     r1, [r3, #offThread_curFrame]   @ self->curFrame = newFp
+    str     r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+    mov     r9, r3                      @ r9<- glue->self (preserve)
+
+    mov     r2, r0                      @ r2<- methodToCall
+    mov     r0, r1                      @ r0<- newFp (points to args)
+    add     r1, rGLUE, #offGlue_retval  @ r1<- &retval
+
+#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
+
+    @mov     lr, pc                      @ set return addr
+    @ldr     pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+    LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+    ldr     r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+    @ native return; r9=self, r10=newSaveArea
+    @ equivalent to dvmPopJniLocals
+    ldr     r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+    ldr     r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+    ldr     r3, [r3]                    @ r3 <- gDvmJit.pProfTable
+#endif
+    str     rFP, [r9, #offThread_curFrame]  @ self->curFrame = fp
+    cmp     r1, #0                      @ null?
+    str     r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+    str     r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+    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
+
+.LstackOverflow:    @ r0=methodToCall
+    mov     r1, r0                      @ r1<- methodToCall
+    ldr     r0, [rGLUE, #offGlue_self]  @ r0<- self
+    bl      dvmHandleStackOverflow
+    b       common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+    .fnend
+#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, rGLUE                   @ A0<- glue
+    SAVE_PC_FP_TO_GLUE()                @ export state to "glue"
+    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:
+    mov     r0, #kInterpEntryReturn
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    SAVEAREA_FROM_FP(r0, rFP)           @ r0<- saveArea (old)
+    ldr     rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+    ldr     r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+    ldr     r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+                                        @ r2<- method we're returning to
+    ldr     r3, [rGLUE, #offGlue_self]  @ r3<- glue->self
+    cmp     r2, #0                      @ is this a break frame?
+    ldrne   r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+    mov     r1, #0                      @ "want switch" = false
+    beq     common_gotoBail             @ break frame, bail out completely
+
+    PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+    str     r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+    ldr     r1, [r10, #offClassObject_pDvmDex]   @ r1<- method->clazz->pDvmDex
+    str     rFP, [r3, #offThread_curFrame]  @ self->curFrame = fp
+#if defined(WITH_JIT)
+    ldr     r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+    mov     rPC, r9                     @ publish new rPC
+    str     r1, [rGLUE, #offGlue_methodClassDex]
+    str     r10, [r3, #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, [rGLUE, #offGlue_methodClassDex]
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+#endif
+
+    /*
+     * Return handling, calls through "glue code".
+     */
+     .if    0
+.LreturnOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ 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:
+    mov     r0, #kInterpEntryThrow
+    mov     r9, #0
+    bl      common_periodicChecks
+
+    ldr     r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+    ldr     r9, [r10, #offThread_exception] @ r9<- self->exception
+    mov     r1, r10                     @ r1<- self
+    mov     r0, r9                      @ r0<- exception
+    bl      dvmAddTrackedAlloc          @ don't let the exception be GCed
+    mov     r3, #0                      @ r3<- NULL
+    str     r3, [r10, #offThread_exception] @ self->exception = NULL
+
+    /* 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, [rGLUE, #offGlue_method] @ r1<- glue->method
+    mov     r0, r10                     @ 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, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    beq     1f                          @ no, skip ahead
+    mov     rFP, r0                     @ save relPc result in rFP
+    mov     r0, r10                     @ 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->curFrame and updated PC */
+    SAVEAREA_FROM_FP(r1, rFP)           @ r1<- new save area
+    ldr     r1, [r1, #offStackSaveArea_method] @ r1<- new method
+    str     r1, [rGLUE, #offGlue_method]    @ glue->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, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+    /* release the tracked alloc on the exception */
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+
+    /* restore the exception if the handler wants it */
+    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, [r10, #offThread_exception] @ yes, restore the exception
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+    /* fix stack overflow if necessary */
+    ldrb    r1, [r10, #offThread_stackOverflowed]
+    cmp     r1, #0                      @ did we overflow earlier?
+    movne   r0, r10                     @ 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, [rGLUE, #offGlue_method]
+    ldr     r1, [r0, #offMethod_insns]
+    sub     r1, rPC, r1
+    asr     r1, r1, #1
+    bl      dvmLineNumFromPC
+    str     r0, [sp, #-4]!
+    @ dvmGetMethodSourceFile(method)
+    ldr     r0, [rGLUE, #offGlue_method]
+    bl      dvmGetMethodSourceFile
+    str     r0, [sp, #-4]!
+    @ exception->clazz->descriptor
+    ldr     r3, [r9, #offObject_clazz]
+    ldr     r3, [r3, #offClassObject_descriptor]
+    @
+    ldr     r2, strExceptionNotCaughtLocally
+    ldr     r1, strLogTag
+    mov     r0, #3                      @ LOG_DEBUG
+    bl      __android_log_print
+#endif
+    str     r9, [r10, #offThread_exception] @ restore exception
+    mov     r0, r9                      @ r0<- exception
+    mov     r1, r10                     @ r1<- self
+    bl      dvmReleaseTrackedAlloc      @ release the exception
+    mov     r1, #0                      @ "want switch" = false
+    b       common_gotoBail             @ bail out
+
+
+    /*
+     * Exception handling, calls through "glue code".
+     */
+    .if     0
+.LexceptionOld:
+    SAVE_PC_FP_TO_GLUE()                @ export state
+    mov     r0, rGLUE                   @ arg to function
+    bl      dvmMterp_exceptionThrown
+    b       common_resumeAfterGlueCall
+    .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_GLUE()              @ pull rPC and rFP out of glue
+    FETCH_INST()                        @ load rINST from rPC
+    GET_INST_OPCODE(ip)                 @ extract opcode from rINST
+    GOTO_OPCODE(ip)                     @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    ldr     r0, strArrayIndexException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+    EXPORT_PC()
+    ldr     r0, strArrayStoreException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    ldr     r0, strArithmeticException
+    ldr     r1, strDivideByZero
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    ldr     r0, strNegativeArraySizeException
+    mov     r1, #0
+    bl      dvmThrowException
+    b       common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+    EXPORT_PC()
+    ldr     r0, strNoSuchMethodError
+    mov     r1, #0
+    bl      dvmThrowException
+    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()
+    ldr     r0, strNullPointerException
+    mov     r1, #0
+    bl      dvmThrowException
+    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
+    mov     r1, #\num
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+    .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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+    stmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    ldr     r0, strNewline
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+    /*
+     * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+    bl      printf
+    ldmfd   sp!, {r0, r1, r2, r3, ip, lr}
+    bx      lr
+
+/*
+ * 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
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+    .align  2
+strArithmeticException:
+    .word   .LstrArithmeticException
+strArrayIndexException:
+    .word   .LstrArrayIndexException
+strArrayStoreException:
+    .word   .LstrArrayStoreException
+strDivideByZero:
+    .word   .LstrDivideByZero
+strNegativeArraySizeException:
+    .word   .LstrNegativeArraySizeException
+strNoSuchMethodError:
+    .word   .LstrNoSuchMethodError
+strNullPointerException:
+    .word   .LstrNullPointerException
+
+strLogTag:
+    .word   .LstrLogTag
+strExceptionNotCaughtLocally:
+    .word   .LstrExceptionNotCaughtLocally
+
+strNewline:
+    .word   .LstrNewline
+strSqueak:
+    .word   .LstrSqueak
+strPrintHex:
+    .word   .LstrPrintHex
+strPrintLong:
+    .word   .LstrPrintLong
+
+/*
+ * 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"
+.LstrArithmeticException:
+    .asciz  "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz  "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+    .asciz  "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+    .asciz  "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+    .asciz  "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+    .asciz  "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+    .asciz  "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+    .asciz  "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+    .asciz  "mterp"
+.LstrExceptionNotCaughtLocally:
+    .asciz  "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+    .asciz  "\n"
+.LstrSqueak:
+    .asciz  "<%d>"
+.LstrPrintHex:
+    .asciz  "<0x%x>"
+.LstrPrintLong:
+    .asciz  "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-x86-atom.S b/vm/mterp/out/InterpAsm-x86-atom.S
new file mode 100644
index 0000000..9d384dd
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-x86-atom.S
@@ -0,0 +1,18547 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86-atom'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: x86-atom/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.
+    */
+
+   /*
+    * File: header.S
+    */
+
+   /*
+    * IA32 calling convention and general notes:
+    *
+    * EAX, ECX, EDX - general purpose scratch registers (caller-saved);
+    *
+    * The stack (%esp) - used to pass arguments to functions
+    *
+    * EAX - holds the first 4 bytes of a return
+    * EDX - holds the second 4 bytes of a return
+    *
+    * EBX, ESI, EDI, EBP - are callee saved
+    *
+    * CS, DS, SS - are segment registers
+    * ES, FS, GS - are segment registers. We will try to avoid using these registers
+    *
+    * The stack is "full descending". Only the arguments that do not fit    * in the first two arg registers are placed on the stack.
+    * "%esp" points to the first stacked argument (i.e. the 3rd arg).
+    */
+
+   /*
+    * Mterp and IA32 notes
+    *
+    * mem          nick      purpose
+    * (%ebp)       rGLUE     InterpState base pointer (A.K.A. MterpGlue Pointer)
+    * %esi         rPC       interpreted program counter, used for fetching
+    *                        instructions
+    * %ebx         rINST     first 16-bit code unit of current instruction
+    * %edi         rFP       interpreted frame pointer, used for accessing
+    *                        locals and args
+    */
+
+   /*
+    * Includes
+    */
+
+#include "../common/asm-constants.h"
+
+   /*
+    * Reserved registers
+    */
+
+#define rGLUE  (%ebp)
+#define rINST   %ebx
+#define rINSTbl  %bl
+#define rINSTbh  %bh
+#define rINSTw  %bx
+#define rPC     %esi
+#define rFP     %edi
+
+   /*
+    * Temporary register used when finishing an opcode
+    */
+
+#define rFinish %edx
+
+   /*
+    * Stack locations used for temporary data. For convenience.
+    */
+
+#define sReg0    4(%ebp)
+#define sReg1    8(%ebp)
+#define sReg2   12(%ebp)
+#define sReg3   16(%ebp)
+
+   /*
+    * Save the PC and FP to the glue struct
+    */
+
+    .macro      SAVE_PC_FP_TO_GLUE _reg
+    movl        rGLUE, \_reg
+    movl        rPC, offGlue_pc(\_reg)
+    movl        rFP, offGlue_fp(\_reg)
+    .endm
+
+   /*
+    * Restore the PC and FP from the glue struct
+    */
+
+    .macro      LOAD_PC_FP_FROM_GLUE
+    movl        rGLUE, rFP
+    movl        offGlue_pc(rFP), rPC
+    movl        offGlue_fp(rFP), rFP
+    .endm
+
+   /*
+    * "Export" the PC to the stack frame, f/b/o future exception objects. This must
+    * be done *before* something calls dvmThrowException.
+    *
+    * 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
+
+   /*
+    * Given a frame pointer, find the stack save area.
+    * In C this is "((StackSaveArea*)(_fp) -1)".
+    */
+
+    .macro      SAVEAREA_FROM_FP  _reg
+    lea         -sizeofStackSaveArea(rFP), \_reg
+    .endm
+
+   /*
+    * Get the 32-bit value from a dalvik register.
+    */
+
+    .macro      GET_VREG _vreg
+    movl        (rFP,\_vreg, 4), \_vreg
+    .endm
+
+   /*
+    * Set the 32-bit value from a dalvik register.
+    */
+
+    .macro      SET_VREG _reg _vreg
+    movl        \_reg, (rFP,\_vreg, 4)
+    .endm
+
+   /*
+    * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+    */
+
+    .macro      FETCH_INST
+    movzwl      (rPC), rINST
+    .endm
+
+   /*
+    * Fetch the next instruction from the specified offset. Advances rPC
+    * to point to the next instruction. "_count" is in 16-bit code units.
+    *
+    * 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())
+    */
+
+    .macro      FETCH_ADVANCE_INST _count
+    add         $(\_count*2), rPC
+    movzwl      (rPC), rINST
+    .endm
+
+   /*
+    * 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.
+    */
+
+    .macro      FETCH_ADVANCE_INST_RB _reg
+    addl        \_reg, rPC
+    movzwl      (rPC), rINST
+    .endm
+
+   /*
+    * 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.
+    * For example, given instruction of format: AA|op BBBB, it
+    * fetches BBBB.
+    */
+
+    .macro      FETCH _count _reg
+    movzwl      (\_count*2)(rPC), \_reg
+    .endm
+
+   /*
+    * 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.
+    * This variant treats the value as signed.
+    */
+
+    .macro      FETCHs _count _reg
+    movswl      (\_count*2)(rPC), \_reg
+    .endm
+
+   /*
+    * Fetch the first byte from an offset past the current PC. The
+    * "_count" value is in 16-bit code units. Does not advance rPC.
+    * For example, given instruction of format: AA|op CC|BB, it
+    * fetches BB.
+    */
+
+    .macro      FETCH_BB _count _reg
+    movzbl      (\_count*2)(rPC), \_reg
+    .endm
+
+    /*
+    * Fetch the second byte from an offset past the current PC. The
+    * "_count" value is in 16-bit code units. Does not advance rPC.
+    * For example, given instruction of format: AA|op CC|BB, it
+    * fetches CC.
+    */
+
+    .macro      FETCH_CC _count _reg
+    movzbl      (\_count*2 + 1)(rPC), \_reg
+    .endm
+
+   /*
+    * Fetch the second byte from an offset past the current PC. The
+    * "_count" value is in 16-bit code units. Does not advance rPC.
+    * This variant treats the value as signed.
+    */
+
+    .macro      FETCH_CCs _count _reg
+    movsbl      (\_count*2 + 1)(rPC), \_reg
+    .endm
+
+
+   /*
+    * 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).
+    */
+
+    .macro      FETCH_B _reg  _count  _byte
+    movzbl      (\_count*2+\_byte)(rPC), \_reg
+    .endm
+
+   /*
+    * Put the instruction's opcode field into the specified register.
+    */
+
+    .macro      GET_INST_OPCODE _reg
+    movzbl      rINSTbl, \_reg
+    .endm
+
+   /*
+    * Begin executing the opcode in _reg.
+    */
+
+    .macro      GOTO_OPCODE _reg
+    shl         $6, \_reg
+    addl        $dvmAsmInstructionStart,\_reg
+    jmp         *\_reg
+    .endm
+
+
+
+   /*
+    * Macros pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+    * by using a jump table. _rFinish should must be the same register for
+    * both macros.
+    */
+
+    .macro      FFETCH _rFinish
+    movzbl      (rPC), \_rFinish
+    .endm
+
+    .macro      FGETOP_JMPa _rFinish
+    movzbl      1(rPC), rINST
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .endm
+
+   /*
+    * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+    * by using a jump table. _rFinish and _count should must be the same register for
+    * both macros.
+    */
+
+    .macro      FFETCH_ADV _count _rFinish
+    movzbl      (\_count*2)(rPC), \_rFinish
+    .endm
+
+    .macro      FGETOP_JMP _count _rFinish
+    movzbl      (\_count*2 + 1)(rPC), rINST
+    addl        $(\_count*2), rPC
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .endm
+
+   /*
+    * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+    * by using a jump table. _rFinish and _reg should must be the same register for
+    * both macros.
+    */
+
+    .macro      FFETCH_ADV_RB _reg _rFinish
+    movzbl      (\_reg, rPC), \_rFinish
+    .endm
+
+    .macro      FGETOP_RB_JMP _reg _rFinish
+    movzbl      1(\_reg, rPC), rINST
+    addl        \_reg, rPC
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_INST, GET_INST_OPCODE using
+    * a jump table. This macro should be called before FINISH_JMP where
+    * rFinish should be the same register containing the opcode value.
+    * This is an attempt to split up FINISH in order to reduce or remove
+    * potential stalls due to the wait for rFINISH.
+    */
+
+    .macro      FINISH_FETCH _rFinish
+    movzbl      (rPC), \_rFinish
+    movzbl      1(rPC), rINST
+    .endm
+
+
+   /*
+    * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE using
+    * a jump table. This macro should be called before FINISH_JMP where
+    * rFinish should be the same register containing the opcode value.
+    * This is an attempt to split up FINISH in order to reduce or remove
+    * potential stalls due to the wait for rFINISH.
+    */
+
+    .macro      FINISH_FETCH_ADVANCE _count _rFinish
+    movzbl      (\_count*2)(rPC), \_rFinish
+    movzbl      (\_count*2 + 1)(rPC), rINST
+    addl        $(\_count*2), rPC
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE using
+    * a jump table. This macro should be called before FINISH_JMP where
+    * rFinish should be the same register containing the opcode value.
+    * This is an attempt to split up FINISH in order to reduce or remove
+    * potential stalls due to the wait for rFINISH.
+    */
+
+    .macro      FINISH_FETCH_ADVANCE_RB _reg _rFinish
+    movzbl      (\_reg, rPC), \_rFinish
+    movzbl      1(\_reg, rPC), rINST
+    addl        \_reg, rPC
+    .endm
+
+   /*
+    * Attempts to speed up GOTO_OPCODE using a jump table. This macro should
+    * be called after a FINISH_FETCH* instruction where rFinish should be the
+    * same register containing the opcode value. This is an attempt to split up
+    * FINISH in order to reduce or remove potential stalls due to the wait for rFINISH.
+    */
+
+    .macro      FINISH_JMP _rFinish
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_INST, GET_INST_OPCODE, GOTO_OPCODE by using
+    * a jump table. Uses a single macro - but it should be faster if we
+    * split up the fetch for rFinish and the jump using rFinish.
+    */
+
+    .macro      FINISH_A
+    movzbl      (rPC), rFinish
+    movzbl      1(rPC), rINST
+    jmp         *dvmAsmInstructionJmpTable(,rFinish, 4)
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE,
+    * GOTO_OPCODE by using a jump table. Uses a single macro -
+    * but it should be faster if we split up the fetch for rFinish
+    * and the jump using rFinish.
+    */
+
+    .macro      FINISH _count
+    movzbl      (\_count*2)(rPC), rFinish
+    movzbl      (\_count*2 + 1)(rPC), rINST
+    addl        $(\_count*2), rPC
+    jmp         *dvmAsmInstructionJmpTable(,rFinish, 4)
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE,
+    * GOTO_OPCODE by using a jump table. Uses a single macro -
+    * but it should be faster if we split up the fetch for rFinish
+    * and the jump using rFinish.
+    */
+
+    .macro      FINISH_RB _reg _rFinish
+    movzbl      (\_reg, rPC), \_rFinish
+    movzbl      1(\_reg, rPC), rINST
+    addl        \_reg, rPC
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .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
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: x86-atom/OP_NOP.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.
+    */
+
+   /*
+    * File: OP_NOP.S
+    *
+    * Code: Use a cycle. Uses no substitutions.
+    *
+    * For: nop
+    *
+    * Description: No operation. Use a cycle
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    FINISH      1                       # jump to next instruction
+
+#ifdef ASSIST_DEBUGGER
+
+   /*
+    * insert fake function header to help gdb find the stack frame
+    */
+
+    .type       dalvik_inst, %function
+dalvik_inst:
+    MTERP_ENTRY
+#endif
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: x86-atom/OP_MOVE.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.
+    */
+
+   /*
+    * File: OP_MOVE.S
+    *
+    * Code: Copies contents from one register to another. Uses no
+    *       substitutions.
+    *
+    * For: move, move-object, long-to-int
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              vA<- vB; fp[A]<- fp[B]
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vB
+    SET_VREG    rINST, %ecx             # vA<- vB; %edx
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: x86-atom/OP_MOVE_FROM16.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.
+    */
+
+   /*
+    * File: OP_MOVE_FROM16.S
+    *
+    * Code: Copies contents from one register to another
+    *
+    * For: move/from16, move-object/from16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              vA<- vB; fp[A]<- fp[B]
+    *
+    * Format: AA|op BBBB (22x)
+    *
+    * Syntax: op vAA, vBBBB
+    */
+
+    FETCH       1, %edx                 # %edx<- BBBB
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    SET_VREG    %edx, rINST             # vA<- vB; %edx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: x86-atom/OP_MOVE_16.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.
+    */
+
+   /*
+    * File: OP_MOVE_16.S
+    *
+    * Code: Copies contents from one register to another
+    *
+    * For: move/16, move-object/16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              fp[A]<- fp[B]
+    *
+    * Format: ØØ|op AAAA BBBB (32x)
+    *
+    * Syntax: op vAAAA, vBBBB
+    */
+
+    FETCH       2, %edx                 # %edx<- BBBB
+    FETCH       1, %ecx                 # %ecx<- AAAA
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    SET_VREG    %edx, %ecx              # vA<- vB; %edx
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: x86-atom/OP_MOVE_WIDE.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.
+    */
+
+   /*
+    * File: OP_MOVE_WIDE.S
+    *
+    * Code: Copies contents from one register to another. Uses no
+    *       substitutions.
+    *
+    * For: move-wide
+    *
+    * Description: Copies contents from one non-object register to another.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA+
+    shr         $4, %edx               # %edx<- B
+    and         $15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vB
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- vB
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: x86-atom/OP_MOVE_WIDE_FROM16.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.
+    */
+
+   /*
+    * File: OP_MOVE_WIDE_FROM16.S
+    *
+    * Code: Copies contents from one register to another
+    *
+    * For: move-wide/from16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *
+    * Format: AA|op BBBB (22x)
+    *
+    * Syntax: op vAA, vBBBB
+    */
+
+    FETCH       1, %edx                 # %edx<- BBBB
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vB
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- vB
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: x86-atom/OP_MOVE_WIDE_16.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.
+    */
+
+   /*
+    * File: OP_MOVE_WIDE_16.S
+    *
+    * Code: Copies contents from one register to another. Uses no
+    *       substitutions.
+    *
+    * For: move-wide/16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *
+    * Format: ØØ|op AAAA BBBB (32x)
+    *
+    * Syntax: op vAAAA, vBBBB
+    */
+
+    FETCH       2, %edx                 # %edx<- BBBB
+    FETCH       1, %ecx                 # %ecx<- AAAA
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vB
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- vB; %xmm0
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: x86-atom/OP_MOVE_OBJECT.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.
+    */
+
+   /*
+    * File: OP_MOVE_OBJECT.S
+    */
+
+/* File: x86-atom/OP_MOVE.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.
+    */
+
+   /*
+    * File: OP_MOVE.S
+    *
+    * Code: Copies contents from one register to another. Uses no
+    *       substitutions.
+    *
+    * For: move, move-object, long-to-int
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              vA<- vB; fp[A]<- fp[B]
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vB
+    SET_VREG    rINST, %ecx             # vA<- vB; %edx
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: x86-atom/OP_MOVE_OBJECT_FROM16.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.
+    */
+
+   /*
+    * File: OP_MOVE_OBJECT_FROM16.S
+    */
+
+/* File: x86-atom/OP_MOVE_FROM16.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.
+    */
+
+   /*
+    * File: OP_MOVE_FROM16.S
+    *
+    * Code: Copies contents from one register to another
+    *
+    * For: move/from16, move-object/from16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              vA<- vB; fp[A]<- fp[B]
+    *
+    * Format: AA|op BBBB (22x)
+    *
+    * Syntax: op vAA, vBBBB
+    */
+
+    FETCH       1, %edx                 # %edx<- BBBB
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    SET_VREG    %edx, rINST             # vA<- vB; %edx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: x86-atom/OP_MOVE_OBJECT_16.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.
+    */
+
+   /*
+    * File: OP_MOVE_OBJECT_16.S
+    */
+
+/* File: x86-atom/OP_MOVE_16.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.
+    */
+
+   /*
+    * File: OP_MOVE_16.S
+    *
+    * Code: Copies contents from one register to another
+    *
+    * For: move/16, move-object/16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              fp[A]<- fp[B]
+    *
+    * Format: ØØ|op AAAA BBBB (32x)
+    *
+    * Syntax: op vAAAA, vBBBB
+    */
+
+    FETCH       2, %edx                 # %edx<- BBBB
+    FETCH       1, %ecx                 # %ecx<- AAAA
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    SET_VREG    %edx, %ecx              # vA<- vB; %edx
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: x86-atom/OP_MOVE_RESULT.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.
+    */
+
+   /*
+    * File: OP_MOVE_RESULT.S
+    *
+    * Code: Copies a return value to a register
+    *
+    * For: move-result, move-result-object
+    *
+    * Description: Move the single-word non-object result of the most
+    *              recent method invocation into the indicated register. This
+    *              must be done as the instruction immediately after a
+    *              method invocation whose (single-word, non-object) result
+    *              is not to be ignored; anywhere else is invalid.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    FFETCH_ADV  1, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    movl        offGlue_retval(%eax), %edx # %edx<- glue->retval
+    SET_VREG    %edx, rINST             # vA<- glue->retval
+    FGETOP_JMP  1, %ecx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: x86-atom/OP_MOVE_RESULT_WIDE.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.
+    */
+
+   /*
+    * File: OP_MOVE_RESULT_WIDE.S
+    *
+    * Code: Copies a return value to a register
+    *
+    * For: move-result-wide
+    *
+    * Description: Move the double-word non-object result of the most
+    *              recent method invocation into the indicated register. This
+    *              must be done as the instruction immediately after a
+    *              method invocation whose (single-word, non-object) result
+    *              is not to be ignored; anywhere else is invalid.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movq        offGlue_retval(%eax), %xmm0 # %xmm0<- glue->retval
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- glue->retval
+    FFETCH_ADV  1, %edx                 # %edx<- next instruction hi; fetch, advance
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: x86-atom/OP_MOVE_RESULT_OBJECT.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.
+    */
+
+   /*
+    * File: OP_MOVE_RESULT_OBJECT.S
+    */
+
+/* File: x86-atom/OP_MOVE_RESULT.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.
+    */
+
+   /*
+    * File: OP_MOVE_RESULT.S
+    *
+    * Code: Copies a return value to a register
+    *
+    * For: move-result, move-result-object
+    *
+    * Description: Move the single-word non-object result of the most
+    *              recent method invocation into the indicated register. This
+    *              must be done as the instruction immediately after a
+    *              method invocation whose (single-word, non-object) result
+    *              is not to be ignored; anywhere else is invalid.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    FFETCH_ADV  1, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    movl        offGlue_retval(%eax), %edx # %edx<- glue->retval
+    SET_VREG    %edx, rINST             # vA<- glue->retval
+    FGETOP_JMP  1, %ecx                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: x86-atom/OP_MOVE_EXCEPTION.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.
+    */
+
+   /*
+    * File: OP_MOVE_EXCEPTION.S
+    *
+    * Code: Moves an exception to a register
+    *
+    * For: move-exception
+    *
+    * Description: Save a just-caught exception into the given register. This
+    *              instruction is only valid as the first instruction of an
+    *              exception handler.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_self(%eax), %ecx # %ecx<- glue->self
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl        offThread_exception(%ecx), %edx # %edx<- glue->self->exception
+    movl        $0, offThread_exception(%ecx) # clear exception
+    SET_VREG    %edx, rINST             # vAA<- glue->self->exception
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: x86-atom/OP_RETURN_VOID.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.
+    */
+
+   /*
+    * File: OP_RETURN_VOID.S
+    */
+
+    jmp         common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: x86-atom/OP_RETURN.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.
+    */
+
+   /*
+    * File: OP_RETURN.S
+    */
+
+/* File: x86-atom/OP_RETURN_COMMON.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.
+    */
+
+   /*
+    * File: OP_RETURN_COMMON.S
+    *
+    * Code: Return a 32-bit value. Uses no substitutions.
+    *
+    * For: return, return-object
+    *
+    * Description: Copies the return value into the "glue"
+    *              structure, then jumps to the return handler.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offGlue_retval(%edx) # glue->retval<- vAA
+    jmp         common_returnFromMethod # jump to common return code
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: x86-atom/OP_RETURN_WIDE.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.
+    */
+
+   /*
+    * File: OP_RETURN_WIDE.S
+    *
+    * Code: Return a 64-bit value. Uses no substitutions.
+    *
+    * For: return-wide
+    *
+    * Description: Copies the return value into the "glue"
+    *              structure, then jumps to the return handler.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vAA
+    movq        %xmm0, offGlue_retval(%edx)# glue->retval<- vAA
+    jmp         common_returnFromMethod # jump to common return code
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: x86-atom/OP_RETURN_OBJECT.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.
+    */
+
+   /*
+    * File: OP_RETURN_OBJECT.S
+    */
+
+/* File: x86-atom/OP_RETURN_COMMON.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.
+    */
+
+   /*
+    * File: OP_RETURN_COMMON.S
+    *
+    * Code: Return a 32-bit value. Uses no substitutions.
+    *
+    * For: return, return-object
+    *
+    * Description: Copies the return value into the "glue"
+    *              structure, then jumps to the return handler.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offGlue_retval(%edx) # glue->retval<- vAA
+    jmp         common_returnFromMethod # jump to common return code
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: x86-atom/OP_CONST_4.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.
+    */
+
+   /*
+    * File: OP_CONST_4.S
+    *
+    * Code: Moves a literal to a register. Uses no substitutions.
+    *
+    * For: const/4
+    *
+    * Description: Move the given literal value (right-zero-extended to 32
+    *              bits) into the specified register.
+    *
+    * Format: B|A|op (11n)
+    *
+    * Syntax: op vA, #+B
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    andl        $15, rINST             # rINST<- A
+    shl         $24, %edx              # %edx<- B000
+    sar         $28, %edx              # %edx<- right-zero-extended B
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    SET_VREG    %edx, rINST             # vA<- %edx; literal
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: x86-atom/OP_CONST_16.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.
+    */
+
+   /*
+    * File: OP_CONST_16.S
+    *
+    * Code: Moves a literal to a register. Uses no substitutions.
+    *
+    * For: const/16
+    *
+    * Description: Move the given literal value (right-zero-extended to 32
+    *              bits) into the specified register
+    *
+    * Format: AA|op BBBB (21s)
+    *
+    * Syntax: op vAA, #+BBBB
+    */
+
+    FETCHs      1, %edx                 # %edx<- BBBB
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    SET_VREG    %edx rINST              # vAA<- BBBB; literal
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: x86-atom/OP_CONST.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.
+    */
+
+   /*
+    * File: OP_CONST.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const
+    *
+    * Description: Move the given literal value into the specified register
+    *
+    * Format: AA|op BBBBlo BBBBhi (31i)
+    *
+    * Syntax: op vAA, #+BBBBBBBB
+    */
+
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    shl         $16, %edx              # move BBBB to high bits
+    or          %edx, %ecx              # %ecx<- #+BBBBBBBB
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; literal
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: x86-atom/OP_CONST_HIGH16.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.
+    */
+
+   /*
+    * File: OP_CONST_HIGH16.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const/high16
+    *
+    * Description: Move the given literal value (right-zero-extended to 32
+    *              bits) into the specified register
+    *
+    * Format: AA|op BBBB (21h)
+    *
+    * Syntax: op vAA, #+BBBB0000
+    */
+
+    FETCH       1, %ecx                 # %ecx<- 0000BBBB (zero-extended)
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    shl         $16, %ecx              # %ecx<- BBBB0000
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; BBBB0000
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: x86-atom/OP_CONST_WIDE_16.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.
+    */
+
+   /*
+    * File: OP_CONST_WIDE_16.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const-wide/16
+    *
+    * Description: Move the given literal value (sign-extended to 64 bits)
+    *              into the specified register-pair
+    *
+    * Format: AA|op BBBB (21s)
+    *
+    * Syntax: op vAA, #+BBBB
+    */
+
+    FETCHs      1, %ecx                 # %ecx<- ssssBBBB (sign-extended)
+    movl        %ecx, %edx              # %edx<- ssssBBBB (sign-extended)
+    sar         $31, %ecx              # %ecx<- sign bit
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl        %edx, (rFP, rINST, 4)   # vAA<- ssssBBBB
+    movl        %ecx, 4(rFP, rINST, 4)  # vAA+1<- ssssssss
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: x86-atom/OP_CONST_WIDE_32.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.
+    */
+
+   /*
+    * File: OP_CONST_WIDE_32.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const-wide/32
+    *
+    * Description: Move the given literal value (sign-extended to 64 bits)
+    *              into the specified register-pair
+    *
+    * Format: AA|op BBBBlo BBBBhi (31i)
+    *
+    * Syntax: op vAA, #+BBBBBBBB
+    */
+
+    FETCH       1,  %edx                # %edx<- BBBBlo
+    FETCHs      2, %ecx                 # %ecx<- BBBBhi
+    shl         $16, %ecx              # prepare to create #+BBBBBBBB
+    or          %ecx, %edx              # %edx<- %edx<- #+BBBBBBBB
+    sar         $31, %ecx              # %ecx<- sign bit
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl        %edx, (rFP, rINST, 4)   # vAA<-  BBBBBBBB
+    movl        %ecx, 4(rFP, rINST, 4)  # vAA+1<- ssssssss
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: x86-atom/OP_CONST_WIDE.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.
+    */
+
+   /*
+    * File: OP_CONST_WIDE.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const-wide
+    *
+    * Description: Move the given literal value into the specified
+    *              register pair
+    *
+    * Format: AA|op BBBBlolo BBBBlohi BBBBhilo BBBBhihi (51l)
+    *
+    * Syntax: op vAA, #+BBBBBBBBBBBBBBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBBlolo
+    FETCH       2, %edx                 # %edx<- BBBBlohi
+    shl         $16, %edx              # %edx<- prepare to create #+BBBBBBBBlo
+    or          %edx, %ecx              # %ecx<- #+BBBBBBBBlo
+    movl        %ecx, (rFP, rINST, 4)   # vAA <- #+BBBBBBBBlo
+    FETCH       3, %ecx                 # %ecx<- BBBBhilo
+    FETCH       4, %edx                 # %edx<- BBBBhihi
+    FFETCH_ADV  5, %eax                 # %eax<- next instruction hi; fetch, advance
+    shl         $16, %edx              # %edx<- prepare to create #+BBBBBBBBhi
+    or          %edx, %ecx              # %ecx<- #+BBBBBBBBhi
+    movl        %ecx, 4(rFP, rINST, 4)  # vAA+1 <- #+BBBBBBBBlo
+    FGETOP_JMP  5, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: x86-atom/OP_CONST_WIDE_HIGH16.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.
+    */
+
+   /*
+    * File: OP_CONST_WIDE_HIGH16.S
+    *
+    * Code: Move a literal value to a register. Uses no substitutions.
+    *
+    * For: const-wide/high16
+    *
+    * Description: Move the given literal value (right-zero-extended to 64
+    *              bits) into the specified register
+    *
+    * Format: AA|op BBBB (21h)
+    *
+    * Syntax: op vAA, #+BBBB000000000000
+    */
+
+    FETCH       1, %ecx                 # %ecx<- 0000BBBB (zero-extended)
+    shl         $16, %ecx              # rINST<- AA
+    movl        $0, (rFP, rINST, 4)    # vAAlow<- 00000000
+    movl        %ecx, 4(rFP, rINST, 4)  # vAAhigh<- %ecx; BBBB0000
+    FINISH      2                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: x86-atom/OP_CONST_STRING.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.
+    */
+
+   /*
+    * File: OP_CONST_STRING.S
+    *
+    * Code: Move a string reference to a register. Uses no substitutions.
+    *
+    * For: const/string
+    *
+    * Description: Move a referece to the string specified by the given
+    *              index into the specified register. vAA <- pResString[BBBB]
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %eax # %eax<- glue->methodClassDex
+    movl        offDvmDex_pResStrings(%eax), %eax # %eax<- glue->methodClassDex->pResStrings
+    movl        (%eax, %ecx, 4), %eax   # %eax<- pResStrings[BBBB]
+    cmp         $0, %eax               # check if string is resolved
+    je          .LOP_CONST_STRING_resolve     # resolve string reference
+    SET_VREG    %eax, rINST             # vAA<- %eax; pResString[BBBB]
+    FINISH      2                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: x86-atom/OP_CONST_STRING_JUMBO.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.
+    */
+
+   /*
+    * File: OP_CONST_STRING_JUMBO.S
+    *
+    * Code: Move a string reference to a register. Uses no substitutions.
+    *
+    * For: const/string-jumbo
+    *
+    * Description: Move a reference to the string specified by the given
+    *              index into the specified register. vAA <- pResString[BBBB]
+    *
+    * Format: AA|op BBBBlo BBBBhi (31c)
+    *
+    * Syntax: op vAA, string@BBBBBBBB
+    */
+
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %eax # %eax<- glue->methodClassDex
+    movl        offDvmDex_pResStrings(%eax), %eax # %eax<- glue->methodClassDex->pResStrings
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    shl         $16, %edx              # %edx<- prepare to create &BBBBBBBB
+    or          %edx, %ecx              # %ecx<- &BBBBBBBB
+    movl        (%eax, %ecx, 4), %eax   # %eax<- pResStrings[BBBB]
+    cmp         $0, %eax               # check if string is resolved
+    je          .LOP_CONST_STRING_JUMBO_resolve     # resolve string reference
+    SET_VREG    %eax, rINST             # vAA<- %eax; pResString[BBBB]
+    FINISH      3                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: x86-atom/OP_CONST_CLASS.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.
+    */
+
+   /*
+    * File: OP_CONST_CLASS.S
+    *
+    * Code: Move a class reference to a register. Uses no substitutions.
+    *
+    * For: const/class
+    *
+    * Description: Move a reference to the class specified
+    *              by the given index into the specified register.
+    *              In the case where the indicated type is primitive,
+    *              this will store a reference to the primitive type's
+    *              degenerate class.
+    *
+    * Format: AA|op BBBBlo BBBBhi (21c)
+    *
+    * Syntax: op vAA, field@BBBB
+    */
+
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        offGlue_methodClassDex(%edx), %eax # %eax<- pDvmDex
+    movl        offDvmDex_pResClasses(%eax), %eax # %eax<- pDvmDex->pResClasses
+    movl        (%eax, %ecx, 4), %eax   # %eax<- resolved class
+    cmp         $0, %eax               # check if classes is resolved before?
+    je          .LOP_CONST_CLASS_resolve     # resolve class
+    SET_VREG    %eax, rINST             # vAA<- resolved class
+    FINISH      2                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: x86-atom/OP_MONITOR_ENTER.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.
+    */
+
+   /*
+    * File: OP_MONITOR_ENTER.S
+    *
+    * Code: Aquire a monitor
+    *
+    * For: monitor-enter
+    *
+    * Description: Aquire a monitor for the indicated object.
+    *
+    *
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    GET_VREG    rINST                   # rINST<- vAA
+    cmp         $0, rINST              # check for null object
+    movl        offGlue_self(%eax), %eax # %eax<- glue->self
+#ifdef WITH_MONITOR_TRACKING
+    EXPORT_PC   # export PC so we can grab stack trace
+#endif
+    je          common_errNullObject    # handle null object
+#    jmp         .LOP_MONITOR_ENTER_finish
+#%break
+#.LOP_MONITOR_ENTER_finish:
+    movl        rINST, -4(%esp)         # push parameter reference
+    movl        %eax, -8(%esp)          # push parameter
+    lea         -8(%esp), %esp
+    call        dvmLockObject           # call: (struct Thread* self,
+                                        #       struct Object* obj)
+                                        # return: void
+    FFETCH_ADV  1, %edx                 # %edx<- next instruction hi; fetch, advance
+    lea         8(%esp), %esp
+#ifdef WITH_DEADLOCK_PREDICTION
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_self(%eax), %eax # %eax<- glue->self
+    movl        offThread_exception(%eax), %eax # %eax<- glue->self->exception
+    cmp         $0, %eax               # check for exception
+    jne         common_exceptionThrown  # handle exception
+#endif
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: x86-atom/OP_MONITOR_EXIT.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.
+    */
+
+   /*
+    * File: OP_MONITOR_EXIT.S
+    *
+    * Code: Release a monitor
+    *
+    * For: monitor-exit
+    *
+    * Description: Release a monitor for the indicated object. If this instruction needs
+    *              to throw an execption, it must do so as if the pc has already
+    *              advanced pased the instruction.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    EXPORT_PC                           # export the pc
+    GET_VREG    rINST                   # rINST<- vAA
+    cmp         $0, rINST              # rINST<- check for null object
+    je          common_errNullObject    # handle null object
+    push        rINST                   # push parameter object
+    push        offGlue_self(%eax)      # push parameter self
+    call        dvmUnlockObject         # call: (struct Thread* self,
+                                        #       struct Object* obj)
+                                        # return: bool
+    FINISH_FETCH_ADVANCE 1, %edx        # advance pc before exception
+    cmp         $0, %eax               # check for success
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # handle exception
+    FINISH_JMP  %edx                    # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: x86-atom/OP_CHECK_CAST.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.
+    */
+
+   /*
+    * File: OP_CHECK_CAST.S
+    *
+    * Code: Checks to see if a cast is allowed. Uses no substitutions.
+    *
+    * For: check-cast
+    *
+    * Description: Throw if the reference in the given register cannot be
+    *              cast to the indicated type. The type must be a reference
+    *              type (not a primitive type).
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, type@BBBB
+    */
+
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %eax # %eax<- pDvmDex
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        offDvmDex_pResClasses(%eax), %eax # %eax<- pDvmDex->pResClasses
+    cmp         $0, rINST              # check for null reference object
+    je          .LOP_CHECK_CAST_okay        # can always cast null object
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        (%eax, %ecx, 4), %ecx   # %ecx<- resolved class
+    cmp         $0, %ecx               # check if classes is resolved before?
+    je          .LOP_CHECK_CAST_resolve     # resolve class
+    jmp         .LOP_CHECK_CAST_resolved    # continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: x86-atom/OP_INSTANCE_OF.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.
+    */
+
+   /*
+    * File: OP_INSTANCE_OF.S
+    *
+    * Code: Checks if object is instance of a class. Uses no substitutions.
+    *
+    * For: instance-of
+    *
+    * Description: Store in the given destination register 1 if the indicated
+    *              reference is an instance of the given type, or 0 if not.
+    *              The type must be a reference type (not a primitive type).
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    GET_VREG    %edx                    # %edx<- vB
+    cmp         $0, %edx               # check for null object
+    je          .LOP_INSTANCE_OF_store       # null object
+    jmp         .LOP_INSTANCE_OF_break
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: x86-atom/OP_ARRAY_LENGTH.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.
+    */
+
+   /*
+    * File: OP_ARRAY_LENGTH.S
+    *
+    * Code: 32-bit array length operation.
+    *
+    * For: array-length
+    *
+    * Description: Store the length of the indicated array in the given
+    *              destination register. vB <- offArrayObject_length(vA)
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $4, %eax               # %eax<- B
+    andl        $15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB
+    cmp         $0, %eax               # check for null array object
+    je          common_errNullObject    # handle null array object
+    FFETCH_ADV  1, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl        offArrayObject_length(%eax), %eax # %eax<- array length
+    movl        %eax, (rFP, rINST, 4)   # vA<- %eax; array length
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: x86-atom/OP_NEW_INSTANCE.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.
+    */
+
+   /*
+    * File: OP_NEW_INSTANCE.S
+    *
+    * Code: Create a new instance of a given type. Uses no substitutions.
+    *
+    * For: new-instance
+    *
+    * Description: Construct a new instance of the indicated type,
+    *              storing a reference to it in the destination.
+    *              The type must refer to a non-array class.
+    *
+    *
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, type@BBBB
+    *         op vAA, field@BBBB
+    *         op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %edx                 # %edx<- BBBB
+    movl        offDvmDex_pResClasses(%ecx), %ecx # %ecx<- glue->pDvmDex->pResClasses
+    movl        (%ecx, %edx, 4), %edx   # %edx<- vB
+    EXPORT_PC                           # required for resolve
+    cmp         $0, %edx               # check for null
+    je          .LOP_NEW_INSTANCE_resolve     # need to resolve
+
+   /*
+    *  %edx holds class object
+    */
+
+.LOP_NEW_INSTANCE_resolved:
+    movzbl      offClassObject_status(%edx), %eax # %eax<- class status
+    cmp         $CLASS_INITIALIZED, %eax # check if class is initialized
+    jne         .LOP_NEW_INSTANCE_needinit    # initialize class
+
+   /*
+    *  %edx holds class object
+    */
+
+.LOP_NEW_INSTANCE_initialized:
+    testl       $(ACC_INTERFACE|ACC_ABSTRACT), offClassObject_accessFlags(%edx)
+    mov         $ALLOC_DONT_TRACK, %eax # %eax<- flag for alloc call
+    je          .LOP_NEW_INSTANCE_finish      # continue
+    jmp         .LOP_NEW_INSTANCE_abstract    # handle abstract or interface
+
+   /*
+    *  %edx holds class object
+    *  %eax holds flags for alloc call
+    */
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: x86-atom/OP_NEW_ARRAY.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.
+    */
+
+   /*
+    * File: OP_NEW_ARRAY.S
+    *
+    * Code: Create a new array. Uses no substitutions.
+    *
+    * For: new-array
+    *
+    * Description: Construct a new array of the indicated type and size.
+    *              The type must be an array type.
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    movl        offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    GET_VREG    %edx                    # %edx<- vB
+    movl        offDvmDex_pResClasses(%eax), %eax # %eax<- glue->pDvmDex->pResClasses
+    cmp         $0, %edx               # check for negative length
+    movl        (%eax, %ecx, 4), %eax   # %eax<- resolved class
+    js          common_errNegativeArraySize # handle negative array length
+    cmp         $0, %eax               # check for null
+    EXPORT_PC                           # required for resolve
+    jne         .LOP_NEW_ARRAY_finish      # already resovled so continue
+    jmp         .LOP_NEW_ARRAY_resolve     # need to resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: x86-atom/OP_FILLED_NEW_ARRAY.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.
+    */
+
+   /*
+    * File: OP_FILLED_NEW_ARRAY.S
+    *
+    * Code: Constructs and fills an array with the given data. Provides
+    *
+    * For: float-to-int
+    *
+    * Description: Construct an array of the given type and size,
+    *              filling it with the supplied contents. The type
+    *              must be an array type. The array's contents
+    *              must be single-word. The constructed instance
+    *              is stored as a result in the same way that the
+    *              method invocation instructions store their results,
+    *              so the constructed instance must be moved to a
+    *              register with a subsequent move-result-object
+    *              instruction.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc) (range)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, vtaboff@CCCC
+    *         [B=4] op {vD, vE, vF, vG}, vtaboff@CCCC
+    *         [B=3] op {vD, vE, vF}, vtaboff@CCCC
+    *         [B=2] op {vD, vE}, vtaboff@CCCC
+    *         [B=1] op {vD}, vtaboff@CCCC
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB
+    *         op {vCCCC .. vNNNN}, type@BBBB
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- glue->methodClassDex
+    movl        offDvmDex_pResClasses(%edx), %edx # %edx<- glue->methodClassDex->pResClasses
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    EXPORT_PC
+    movl (%edx, %ecx, 4), %eax # %eax<- possibly resolved class
+    cmp         $0, %eax               # %eax<- check if already resolved
+    jne         .LOP_FILLED_NEW_ARRAY_continue
+    jmp         .LOP_FILLED_NEW_ARRAY_break
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: x86-atom/OP_FILLED_NEW_ARRAY_RANGE.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.
+    */
+
+   /*
+    * File: OP_FILLED_NEW_ARRAY_RANGE.S
+    */
+
+/* File: x86-atom/OP_FILLED_NEW_ARRAY.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.
+    */
+
+   /*
+    * File: OP_FILLED_NEW_ARRAY.S
+    *
+    * Code: Constructs and fills an array with the given data. Provides
+    *
+    * For: float-to-int
+    *
+    * Description: Construct an array of the given type and size,
+    *              filling it with the supplied contents. The type
+    *              must be an array type. The array's contents
+    *              must be single-word. The constructed instance
+    *              is stored as a result in the same way that the
+    *              method invocation instructions store their results,
+    *              so the constructed instance must be moved to a
+    *              register with a subsequent move-result-object
+    *              instruction.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc) (range)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, vtaboff@CCCC
+    *         [B=4] op {vD, vE, vF, vG}, vtaboff@CCCC
+    *         [B=3] op {vD, vE, vF}, vtaboff@CCCC
+    *         [B=2] op {vD, vE}, vtaboff@CCCC
+    *         [B=1] op {vD}, vtaboff@CCCC
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB
+    *         op {vCCCC .. vNNNN}, type@BBBB
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- glue->methodClassDex
+    movl        offDvmDex_pResClasses(%edx), %edx # %edx<- glue->methodClassDex->pResClasses
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    EXPORT_PC
+    movl (%edx, %ecx, 4), %eax # %eax<- possibly resolved class
+    cmp         $0, %eax               # %eax<- check if already resolved
+    jne         .LOP_FILLED_NEW_ARRAY_RANGE_continue
+    jmp         .LOP_FILLED_NEW_ARRAY_RANGE_break
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: x86-atom/OP_FILL_ARRAY_DATA.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.
+    */
+
+   /*
+    * File: OP_FILL_ARRAY_DATA.S
+    *
+    * Code: Fills an array with given data. Uses no substitutions.
+    *
+    * For: fill-array-data
+    *
+    * Description: Fill the given array with the idicated data. The reference
+    *              must be an array of primitives, and the data table must
+    *              match it in type and size
+    *
+    * Format: AA|op BBBBlo BBBBhi (31t)
+    *
+    * Syntax: op vAA, +BBBBBBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    shl         $16, %edx              # prepare to create +BBBBBBBB
+    or          %ecx, %edx              # %edx<- +BBBBBBBB
+    lea         (rPC, %edx, 2), %edx    # %edx<- PC + +BBBBBBBB; array data location
+    EXPORT_PC
+    push        %edx
+    push        (rFP, rINST, 4)
+    call        dvmInterpHandleFillArrayData # call: (ArrayObject* arrayObject, const u2* arrayData)
+                                             # return: bool
+    FFETCH_ADV  3, %edx                 # %edx<- next instruction hi; fetch, advance
+    cmp         $0, %eax
+    lea         8(%esp), %esp
+    je          common_exceptionThrown
+    FGETOP_JMP  3, %edx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: x86-atom/OP_THROW.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.
+    */
+
+   /*
+    * File: OP_THROW.S
+    *
+    * Code: Throw an exception
+    *
+    * For: throw
+    *
+    * Description: Throw an exception object in the current thread.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    EXPORT_PC                           # export the pc
+    GET_VREG    rINST                   # rINST<- vAA
+    cmp         $0, rINST              # check for null
+    movl        offGlue_self(%eax), %ecx # %ecx<- glue->self
+    je          common_errNullObject    # handle null object
+    movl        rINST, offThread_exception(%ecx) # thread->exception<- object
+    jmp         common_exceptionThrown  # handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: x86-atom/OP_GOTO.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.
+    */
+
+   /*
+    * File: OP_GOTO.S
+    *
+    * Code: Do an unconditional branch. Uses no substitutions.
+    *
+    * For: goto
+    *
+    * Description: Performs an unconditionally jump to the indicated instruction.
+    *              The branch uses an 8-bit offset that cannot be zero.
+    *
+    * Format: AA|op (10t)
+    *
+    * Syntax: op +AA
+    */
+
+LOP_GOTO.S:
+
+    movsbl      rINSTbl, %edx           # %edx<- +AA
+    shl         $1, %edx               # %edx is shifted for byte offset
+    js          common_periodicChecks_backwardBranch  # do check on backwards branch
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: x86-atom/OP_GOTO_16.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.
+    */
+
+   /*
+    * File: OP_GOTO_16.S
+    *
+    * Code: Do an unconditional branch. Uses no substitutions.
+    *
+    * For: goto/16
+    *
+    * Description: Performs an unconditionally jump to the indicated instruction.
+    *              The branch uses a 16-bit offset that cannot be zero.
+    *
+    * Format: ØØ|op AAAA (20t)
+    *
+    * Syntax: op +AAAA
+    */
+
+    FETCHs      1, %edx                 # %edx<- ssssAAAA (sign-extended)
+    shl         $1, %edx               # %edx is doubled to get the byte offset
+    js          common_periodicChecks_backwardBranch  # do check on backwards branch
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: x86-atom/OP_GOTO_32.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.
+    */
+
+   /*
+    * File: OP_GOTO_32.S
+    *
+    * Code: Do an unconditional branch. Uses no substitutions.
+    *
+    * For: goto/32
+    *
+    * Description:  Performs an unconditionally jump to the indicated instruction.
+    *               The branch uses a 32-bit offset that can be zero.
+    *
+    * Format: ØØ|op AAAAlo AAAAhi (30t)
+    *
+    * Syntax: op +AAAAAAAA
+    */
+
+    FETCH       1, %edx                 # %edx<- AAAAlo
+    FETCH       2, %ecx                 # %ecx<- AAAAhi
+    shl         $16, %ecx              # prepare to create +AAAAAAAA
+    or          %ecx, %edx              # %edx<- +AAAAAAAA
+    shl         $1, %edx               # %edx is doubled to get the byte offset
+    jle          common_periodicChecks_backwardBranch  # do check on backwards branch
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: x86-atom/OP_PACKED_SWITCH.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.
+    */
+
+   /*
+    * File: OP_PACKED_SWITCH.S
+    *
+    * Code: Jump to a new instruction using a jump table
+    *
+    * For: packed-switch, sparse-switch
+    *
+    * Description: Jump to a new instruction based on the value in the given
+    *              register, using a table of offsets corresponding to each
+    *              value in a particular integral range, or fall through to
+    *              the next instruction if there is no match.
+    *
+    * Format: AA|op BBBBlo BBBBhi (31t)
+    *
+    * Syntax: op vAA, +BBBBBBBB
+    */
+
+
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    shl         $16, %edx              # prepare to create +BBBBBBBB
+    or          %edx, %ecx              # %ecx<- +BBBBBBBB
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, -4(%esp)         # push parameter vAA
+    lea         (rPC, %ecx, 2), %ecx    # %ecx<- PC + +BBBBBBBB*2
+    movl        %ecx, -8(%esp)          # push parameter PC + +BBBBBBBB*2
+    lea         -8(%esp), %esp
+    call        dvmInterpHandlePackedSwitch                   # call code-unit branch offset
+    shl         $1, %eax               # shift for byte offset
+    movl        %eax, %edx              # %edx<- offset
+    lea         8(%esp), %esp
+    jle         common_periodicChecks_backwardBranch  # do backward branch
+    jmp         .LOP_PACKED_SWITCH_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: x86-atom/OP_SPARSE_SWITCH.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.
+    */
+
+   /*
+    * File: OP_SPARSE_SWITCH.S
+    */
+
+/* File: x86-atom/OP_PACKED_SWITCH.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.
+    */
+
+   /*
+    * File: OP_PACKED_SWITCH.S
+    *
+    * Code: Jump to a new instruction using a jump table
+    *
+    * For: packed-switch, sparse-switch
+    *
+    * Description: Jump to a new instruction based on the value in the given
+    *              register, using a table of offsets corresponding to each
+    *              value in a particular integral range, or fall through to
+    *              the next instruction if there is no match.
+    *
+    * Format: AA|op BBBBlo BBBBhi (31t)
+    *
+    * Syntax: op vAA, +BBBBBBBB
+    */
+
+
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    shl         $16, %edx              # prepare to create +BBBBBBBB
+    or          %edx, %ecx              # %ecx<- +BBBBBBBB
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, -4(%esp)         # push parameter vAA
+    lea         (rPC, %ecx, 2), %ecx    # %ecx<- PC + +BBBBBBBB*2
+    movl        %ecx, -8(%esp)          # push parameter PC + +BBBBBBBB*2
+    lea         -8(%esp), %esp
+    call        dvmInterpHandleSparseSwitch                   # call code-unit branch offset
+    shl         $1, %eax               # shift for byte offset
+    movl        %eax, %edx              # %edx<- offset
+    lea         8(%esp), %esp
+    jle         common_periodicChecks_backwardBranch  # do backward branch
+    jmp         .LOP_SPARSE_SWITCH_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: x86-atom/OP_CMPL_FLOAT.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.
+    */
+
+   /*
+    * File: OP_CMPL_FLOAT.S
+    *
+    * Code: Provides a "nan" variable to specify the return value for
+    *       NaN. Provides a variable "sod" which appends a "s" or a "d"
+    *       to the move and comparison instructions, depending on if we
+    *       are working with a float or a double. For instructions
+    *       cmpx-float and cmpx-double, the x will be eiher a g or a l
+    *       to specify positive or negative bias for NaN.
+    *
+    * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+    *
+    * Description: Perform the indicated floating point or long comparison,
+    *              storing 0 if the two arguments are equal, 1 if the second
+    *              argument is larger, or -1 if the first argument is larger.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movss    (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    comiss   (rFP, %edx, 4), %xmm0   # do comparison
+    ja          .LOP_CMPL_FLOAT_greater
+    jp          .LOP_CMPL_FLOAT_finalNan
+    jz          .LOP_CMPL_FLOAT_final
+
+.LOP_CMPL_FLOAT_less:
+    movl        $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: x86-atom/OP_CMPG_FLOAT.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.
+    */
+
+   /*
+    * File: OP_CMPG_FLOAT.S
+    */
+
+/* File: x86-atom/OP_CMPL_FLOAT.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.
+    */
+
+   /*
+    * File: OP_CMPL_FLOAT.S
+    *
+    * Code: Provides a "nan" variable to specify the return value for
+    *       NaN. Provides a variable "sod" which appends a "s" or a "d"
+    *       to the move and comparison instructions, depending on if we
+    *       are working with a float or a double. For instructions
+    *       cmpx-float and cmpx-double, the x will be eiher a g or a l
+    *       to specify positive or negative bias for NaN.
+    *
+    * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+    *
+    * Description: Perform the indicated floating point or long comparison,
+    *              storing 0 if the two arguments are equal, 1 if the second
+    *              argument is larger, or -1 if the first argument is larger.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movss    (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    comiss   (rFP, %edx, 4), %xmm0   # do comparison
+    ja          .LOP_CMPG_FLOAT_greater
+    jp          .LOP_CMPG_FLOAT_finalNan
+    jz          .LOP_CMPG_FLOAT_final
+
+.LOP_CMPG_FLOAT_less:
+    movl        $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: x86-atom/OP_CMPL_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_CMPL_DOUBLE.S
+    */
+
+/* File: x86-atom/OP_CMPL_FLOAT.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.
+    */
+
+   /*
+    * File: OP_CMPL_FLOAT.S
+    *
+    * Code: Provides a "nan" variable to specify the return value for
+    *       NaN. Provides a variable "sod" which appends a "s" or a "d"
+    *       to the move and comparison instructions, depending on if we
+    *       are working with a float or a double. For instructions
+    *       cmpx-float and cmpx-double, the x will be eiher a g or a l
+    *       to specify positive or negative bias for NaN.
+    *
+    * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+    *
+    * Description: Perform the indicated floating point or long comparison,
+    *              storing 0 if the two arguments are equal, 1 if the second
+    *              argument is larger, or -1 if the first argument is larger.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movsd    (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    comisd   (rFP, %edx, 4), %xmm0   # do comparison
+    ja          .LOP_CMPL_DOUBLE_greater
+    jp          .LOP_CMPL_DOUBLE_finalNan
+    jz          .LOP_CMPL_DOUBLE_final
+
+.LOP_CMPL_DOUBLE_less:
+    movl        $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: x86-atom/OP_CMPG_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_CMPG_DOUBLE.S
+    */
+
+/* File: x86-atom/OP_CMPL_FLOAT.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.
+    */
+
+   /*
+    * File: OP_CMPL_FLOAT.S
+    *
+    * Code: Provides a "nan" variable to specify the return value for
+    *       NaN. Provides a variable "sod" which appends a "s" or a "d"
+    *       to the move and comparison instructions, depending on if we
+    *       are working with a float or a double. For instructions
+    *       cmpx-float and cmpx-double, the x will be eiher a g or a l
+    *       to specify positive or negative bias for NaN.
+    *
+    * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+    *
+    * Description: Perform the indicated floating point or long comparison,
+    *              storing 0 if the two arguments are equal, 1 if the second
+    *              argument is larger, or -1 if the first argument is larger.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movsd    (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    comisd   (rFP, %edx, 4), %xmm0   # do comparison
+    ja          .LOP_CMPG_DOUBLE_greater
+    jp          .LOP_CMPG_DOUBLE_finalNan
+    jz          .LOP_CMPG_DOUBLE_final
+
+.LOP_CMPG_DOUBLE_less:
+    movl        $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: x86-atom/OP_CMP_LONG.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.
+    */
+
+   /*
+    * File: OP_CMP_LONG.S
+    *
+    * Code: Compare floating point values. Uses no substitutions.
+    *
+    * For: cmp-long
+    *
+    * Description: Perform a long comparison, storing 0 if the two
+    *              arguments are equal, 1 if the second argument is larger
+    *              or -1 if the first argument is larger.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    movl        4(rFP, %ecx, 4), %eax   # %eax<- vBBhi
+    cmp         4(rFP, %edx, 4), %eax   # compare vCChi and vBBhi
+    jl          .LOP_CMP_LONG_less
+    jg          .LOP_CMP_LONG_greater
+    movl        (rFP, %ecx, 4), %eax    # %eax<- vBBlo
+    cmp         (rFP, %edx, 4), %eax    # compare vCClo and vBBlo
+    ja          .LOP_CMP_LONG_greater
+    jne         .LOP_CMP_LONG_less
+    jmp         .LOP_CMP_LONG_final
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: x86-atom/OP_IF_EQ.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.
+    */
+
+   /*
+    * File: OP_IF_EQ.S
+    */
+
+/* File: x86-atom/bincmp.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.
+    */
+
+   /*
+    * File: bincmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform.
+    *
+    * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+    *
+    * Description: Branch to the given destination if the comparison
+    *              test between the given registers values is true.
+    *
+    * Format: B|A|op CCCC (22t)
+    *
+    * Syntax: op vA, vB, +CCCC
+    */
+
+    movl        rINST,  %eax            # %eax<- BA
+    andl        $15, rINST             # rINST<- A
+    shr         $4, %eax               # %eax<- B
+    GET_VREG    rINST                   # rINST<- vA
+    movl        $4, %edx               # %edx<- 4
+    cmp         (rFP, %eax, 4), rINST   # compare vA and vB
+    jne  1f                      # goto next instruction if reverse
+                                        # comparison is true
+    FETCHs      1, %edx                 # %edx<- +CCCC, Branch offset
+    sal         $1, %edx
+    js          common_periodicChecks_backwardBranch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: x86-atom/OP_IF_NE.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.
+    */
+
+   /*
+    * File: OP_IF_NE.S
+    */
+
+/* File: x86-atom/bincmp.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.
+    */
+
+   /*
+    * File: bincmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform.
+    *
+    * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+    *
+    * Description: Branch to the given destination if the comparison
+    *              test between the given registers values is true.
+    *
+    * Format: B|A|op CCCC (22t)
+    *
+    * Syntax: op vA, vB, +CCCC
+    */
+
+    movl        rINST,  %eax            # %eax<- BA
+    andl        $15, rINST             # rINST<- A
+    shr         $4, %eax               # %eax<- B
+    GET_VREG    rINST                   # rINST<- vA
+    movl        $4, %edx               # %edx<- 4
+    cmp         (rFP, %eax, 4), rINST   # compare vA and vB
+    je  1f                      # goto next instruction if reverse
+                                        # comparison is true
+    FETCHs      1, %edx                 # %edx<- +CCCC, Branch offset
+    sal         $1, %edx
+    js          common_periodicChecks_backwardBranch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: x86-atom/OP_IF_LT.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.
+    */
+
+   /*
+    * File: OP_IF_LT.S
+    */
+
+/* File: x86-atom/bincmp.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.
+    */
+
+   /*
+    * File: bincmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform.
+    *
+    * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+    *
+    * Description: Branch to the given destination if the comparison
+    *              test between the given registers values is true.
+    *
+    * Format: B|A|op CCCC (22t)
+    *
+    * Syntax: op vA, vB, +CCCC
+    */
+
+    movl        rINST,  %eax            # %eax<- BA
+    andl        $15, rINST             # rINST<- A
+    shr         $4, %eax               # %eax<- B
+    GET_VREG    rINST                   # rINST<- vA
+    movl        $4, %edx               # %edx<- 4
+    cmp         (rFP, %eax, 4), rINST   # compare vA and vB
+    jge  1f                      # goto next instruction if reverse
+                                        # comparison is true
+    FETCHs      1, %edx                 # %edx<- +CCCC, Branch offset
+    sal         $1, %edx
+    js          common_periodicChecks_backwardBranch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: x86-atom/OP_IF_GE.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.
+    */
+
+   /*
+    * File: OP_IF_GE.S
+    */
+
+/* File: x86-atom/bincmp.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.
+    */
+
+   /*
+    * File: bincmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform.
+    *
+    * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+    *
+    * Description: Branch to the given destination if the comparison
+    *              test between the given registers values is true.
+    *
+    * Format: B|A|op CCCC (22t)
+    *
+    * Syntax: op vA, vB, +CCCC
+    */
+
+    movl        rINST,  %eax            # %eax<- BA
+    andl        $15, rINST             # rINST<- A
+    shr         $4, %eax               # %eax<- B
+    GET_VREG    rINST                   # rINST<- vA
+    movl        $4, %edx               # %edx<- 4
+    cmp         (rFP, %eax, 4), rINST   # compare vA and vB
+    jl  1f                      # goto next instruction if reverse
+                                        # comparison is true
+    FETCHs      1, %edx                 # %edx<- +CCCC, Branch offset
+    sal         $1, %edx
+    js          common_periodicChecks_backwardBranch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: x86-atom/OP_IF_GT.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.
+    */
+
+   /*
+    * File: OP_IF_GT.S
+    */
+
+/* File: x86-atom/bincmp.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.
+    */
+
+   /*
+    * File: bincmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform.
+    *
+    * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+    *
+    * Description: Branch to the given destination if the comparison
+    *              test between the given registers values is true.
+    *
+    * Format: B|A|op CCCC (22t)
+    *
+    * Syntax: op vA, vB, +CCCC
+    */
+
+    movl        rINST,  %eax            # %eax<- BA
+    andl        $15, rINST             # rINST<- A
+    shr         $4, %eax               # %eax<- B
+    GET_VREG    rINST                   # rINST<- vA
+    movl        $4, %edx               # %edx<- 4
+    cmp         (rFP, %eax, 4), rINST   # compare vA and vB
+    jle  1f                      # goto next instruction if reverse
+                                        # comparison is true
+    FETCHs      1, %edx                 # %edx<- +CCCC, Branch offset
+    sal         $1, %edx
+    js          common_periodicChecks_backwardBranch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: x86-atom/OP_IF_LE.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.
+    */
+
+   /*
+    * File: OP_IF_LE.S
+    */
+
+/* File: x86-atom/bincmp.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.
+    */
+
+   /*
+    * File: bincmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform.
+    *
+    * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+    *
+    * Description: Branch to the given destination if the comparison
+    *              test between the given registers values is true.
+    *
+    * Format: B|A|op CCCC (22t)
+    *
+    * Syntax: op vA, vB, +CCCC
+    */
+
+    movl        rINST,  %eax            # %eax<- BA
+    andl        $15, rINST             # rINST<- A
+    shr         $4, %eax               # %eax<- B
+    GET_VREG    rINST                   # rINST<- vA
+    movl        $4, %edx               # %edx<- 4
+    cmp         (rFP, %eax, 4), rINST   # compare vA and vB
+    jg  1f                      # goto next instruction if reverse
+                                        # comparison is true
+    FETCHs      1, %edx                 # %edx<- +CCCC, Branch offset
+    sal         $1, %edx
+    js          common_periodicChecks_backwardBranch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: x86-atom/OP_IF_EQZ.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.
+    */
+
+   /*
+    * File: OP_IF_EQZ.S
+    */
+
+/* File: x86-atom/zcmp.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.
+    */
+
+   /*
+    * File: zcmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform
+    *
+    * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+    *
+    * Description: Branch to the given destination if the given register's
+    *              value compares with 0 as specified.
+    *
+    * Format: AA|op BBBB (21t)
+    *
+    * Syntax: op vAA, +BBBB
+    */
+
+    cmp         $0, (rFP, rINST, 4)    # compare vAA with zero
+    jne  OP_IF_EQZ_2f                    # goto next instruction or branch
+    FETCHs      1, %edx                 # %edx<- BBBB; branch offset
+    sal         $1, %edx               # %edx<- adjust byte offset
+
+   /*
+    * Inline common_backwardBranch
+    */
+
+    js          common_periodicChecks_backwardBranch  # jump on backwards branch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+   /*
+    * FINISH code
+    */
+
+OP_IF_EQZ_2f:
+    movzbl      4(rPC), %edx            # grab the next opcode
+    movzbl      5(rPC), rINST           # update the instruction
+    addl        $4, rPC                # update the program counter
+    jmp         *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: x86-atom/OP_IF_NEZ.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.
+    */
+
+   /*
+    * File: OP_IF_NEZ.S
+    */
+
+/* File: x86-atom/zcmp.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.
+    */
+
+   /*
+    * File: zcmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform
+    *
+    * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+    *
+    * Description: Branch to the given destination if the given register's
+    *              value compares with 0 as specified.
+    *
+    * Format: AA|op BBBB (21t)
+    *
+    * Syntax: op vAA, +BBBB
+    */
+
+    cmp         $0, (rFP, rINST, 4)    # compare vAA with zero
+    je  OP_IF_NEZ_2f                    # goto next instruction or branch
+    FETCHs      1, %edx                 # %edx<- BBBB; branch offset
+    sal         $1, %edx               # %edx<- adjust byte offset
+
+   /*
+    * Inline common_backwardBranch
+    */
+
+    js          common_periodicChecks_backwardBranch  # jump on backwards branch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+   /*
+    * FINISH code
+    */
+
+OP_IF_NEZ_2f:
+    movzbl      4(rPC), %edx            # grab the next opcode
+    movzbl      5(rPC), rINST           # update the instruction
+    addl        $4, rPC                # update the program counter
+    jmp         *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: x86-atom/OP_IF_LTZ.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.
+    */
+
+   /*
+    * File: OP_IF_LTZ.S
+    */
+
+/* File: x86-atom/zcmp.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.
+    */
+
+   /*
+    * File: zcmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform
+    *
+    * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+    *
+    * Description: Branch to the given destination if the given register's
+    *              value compares with 0 as specified.
+    *
+    * Format: AA|op BBBB (21t)
+    *
+    * Syntax: op vAA, +BBBB
+    */
+
+    cmp         $0, (rFP, rINST, 4)    # compare vAA with zero
+    jge  OP_IF_LTZ_2f                    # goto next instruction or branch
+    FETCHs      1, %edx                 # %edx<- BBBB; branch offset
+    sal         $1, %edx               # %edx<- adjust byte offset
+
+   /*
+    * Inline common_backwardBranch
+    */
+
+    js          common_periodicChecks_backwardBranch  # jump on backwards branch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+   /*
+    * FINISH code
+    */
+
+OP_IF_LTZ_2f:
+    movzbl      4(rPC), %edx            # grab the next opcode
+    movzbl      5(rPC), rINST           # update the instruction
+    addl        $4, rPC                # update the program counter
+    jmp         *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: x86-atom/OP_IF_GEZ.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.
+    */
+
+   /*
+    * File: OP_IF_GEZ.S
+    */
+
+/* File: x86-atom/zcmp.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.
+    */
+
+   /*
+    * File: zcmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform
+    *
+    * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+    *
+    * Description: Branch to the given destination if the given register's
+    *              value compares with 0 as specified.
+    *
+    * Format: AA|op BBBB (21t)
+    *
+    * Syntax: op vAA, +BBBB
+    */
+
+    cmp         $0, (rFP, rINST, 4)    # compare vAA with zero
+    jl  OP_IF_GEZ_2f                    # goto next instruction or branch
+    FETCHs      1, %edx                 # %edx<- BBBB; branch offset
+    sal         $1, %edx               # %edx<- adjust byte offset
+
+   /*
+    * Inline common_backwardBranch
+    */
+
+    js          common_periodicChecks_backwardBranch  # jump on backwards branch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+   /*
+    * FINISH code
+    */
+
+OP_IF_GEZ_2f:
+    movzbl      4(rPC), %edx            # grab the next opcode
+    movzbl      5(rPC), rINST           # update the instruction
+    addl        $4, rPC                # update the program counter
+    jmp         *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: x86-atom/OP_IF_GTZ.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.
+    */
+
+   /*
+    * File: OP_IF_GTZ.S
+    */
+
+/* File: x86-atom/zcmp.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.
+    */
+
+   /*
+    * File: zcmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform
+    *
+    * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+    *
+    * Description: Branch to the given destination if the given register's
+    *              value compares with 0 as specified.
+    *
+    * Format: AA|op BBBB (21t)
+    *
+    * Syntax: op vAA, +BBBB
+    */
+
+    cmp         $0, (rFP, rINST, 4)    # compare vAA with zero
+    jle  OP_IF_GTZ_2f                    # goto next instruction or branch
+    FETCHs      1, %edx                 # %edx<- BBBB; branch offset
+    sal         $1, %edx               # %edx<- adjust byte offset
+
+   /*
+    * Inline common_backwardBranch
+    */
+
+    js          common_periodicChecks_backwardBranch  # jump on backwards branch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+   /*
+    * FINISH code
+    */
+
+OP_IF_GTZ_2f:
+    movzbl      4(rPC), %edx            # grab the next opcode
+    movzbl      5(rPC), rINST           # update the instruction
+    addl        $4, rPC                # update the program counter
+    jmp         *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: x86-atom/OP_IF_LEZ.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.
+    */
+
+   /*
+    * File: OP_IF_LEZ.S
+    */
+
+/* File: x86-atom/zcmp.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.
+    */
+
+   /*
+    * File: zcmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform
+    *
+    * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+    *
+    * Description: Branch to the given destination if the given register's
+    *              value compares with 0 as specified.
+    *
+    * Format: AA|op BBBB (21t)
+    *
+    * Syntax: op vAA, +BBBB
+    */
+
+    cmp         $0, (rFP, rINST, 4)    # compare vAA with zero
+    jg  OP_IF_LEZ_2f                    # goto next instruction or branch
+    FETCHs      1, %edx                 # %edx<- BBBB; branch offset
+    sal         $1, %edx               # %edx<- adjust byte offset
+
+   /*
+    * Inline common_backwardBranch
+    */
+
+    js          common_periodicChecks_backwardBranch  # jump on backwards branch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+   /*
+    * FINISH code
+    */
+
+OP_IF_LEZ_2f:
+    movzbl      4(rPC), %edx            # grab the next opcode
+    movzbl      5(rPC), rINST           # update the instruction
+    addl        $4, rPC                # update the program counter
+    jmp         *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: x86-atom/OP_UNUSED_3E.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.
+    */
+
+   /*
+    * File: OP_UNUSED_3E.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: x86-atom/OP_UNUSED_3F.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.
+    */
+
+   /*
+    * File: OP_UNUSED_3F.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: x86-atom/OP_UNUSED_40.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.
+    */
+
+   /*
+    * File: OP_UNUSED_40.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: x86-atom/OP_UNUSED_41.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.
+    */
+
+   /*
+    * File: OP_UNUSED_41.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: x86-atom/OP_UNUSED_42.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.
+    */
+
+   /*
+    * File: OP_UNUSED_42.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: x86-atom/OP_UNUSED_43.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.
+    */
+
+   /*
+    * File: OP_UNUSED_43.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: x86-atom/OP_AGET.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.
+    */
+
+   /*
+    * File: OP_AGET.S
+    *
+    * Code: Generic 32-bit array "get" operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *
+    * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the value
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    lea         (%ecx, %edx, 4), %ecx # %ecx<- &vBB[vCC]
+                                           # trying: lea (%ecx, %edx, scale), %ecx
+                                           # to reduce code size
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+                                                # doing this and the previous instr
+                                                # with one instr was not faster
+    SET_VREG    %edx  rINST             # vAA<- %edx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: x86-atom/OP_AGET_WIDE.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.
+    */
+
+   /*
+    * File: OP_AGET_WIDE.S
+    *
+    * Code: 64-bit array get operation.
+    *
+    * For: aget-wide
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the destination
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        offArrayObject_contents(%ecx, %edx, 8), %xmm0 # %xmm0<- vBB[vCC]
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %xmm0; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: x86-atom/OP_AGET_OBJECT.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.
+    */
+
+   /*
+    * File: OP_AGET_OBJECT.S
+    */
+
+/* File: x86-atom/OP_AGET.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.
+    */
+
+   /*
+    * File: OP_AGET.S
+    *
+    * Code: Generic 32-bit array "get" operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *
+    * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the value
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    lea         (%ecx, %edx, 4), %ecx # %ecx<- &vBB[vCC]
+                                           # trying: lea (%ecx, %edx, scale), %ecx
+                                           # to reduce code size
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+                                                # doing this and the previous instr
+                                                # with one instr was not faster
+    SET_VREG    %edx  rINST             # vAA<- %edx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: x86-atom/OP_AGET_BOOLEAN.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.
+    */
+
+   /*
+    * File: OP_AGET_BOOLEAN.S
+    */
+
+/* File: x86-atom/OP_AGET.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.
+    */
+
+   /*
+    * File: OP_AGET.S
+    *
+    * Code: Generic 32-bit array "get" operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *
+    * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the value
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    lea         (%ecx, %edx, 1), %ecx # %ecx<- &vBB[vCC]
+                                           # trying: lea (%ecx, %edx, scale), %ecx
+                                           # to reduce code size
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movzbl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+                                                # doing this and the previous instr
+                                                # with one instr was not faster
+    SET_VREG    %edx  rINST             # vAA<- %edx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: x86-atom/OP_AGET_BYTE.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.
+    */
+
+   /*
+    * File: OP_AGET_BYTE.S
+    */
+
+/* File: x86-atom/OP_AGET.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.
+    */
+
+   /*
+    * File: OP_AGET.S
+    *
+    * Code: Generic 32-bit array "get" operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *
+    * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the value
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    lea         (%ecx, %edx, 1), %ecx # %ecx<- &vBB[vCC]
+                                           # trying: lea (%ecx, %edx, scale), %ecx
+                                           # to reduce code size
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movsbl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+                                                # doing this and the previous instr
+                                                # with one instr was not faster
+    SET_VREG    %edx  rINST             # vAA<- %edx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: x86-atom/OP_AGET_CHAR.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.
+    */
+
+   /*
+    * File: OP_AGET_CHAR.S
+    */
+
+/* File: x86-atom/OP_AGET.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.
+    */
+
+   /*
+    * File: OP_AGET.S
+    *
+    * Code: Generic 32-bit array "get" operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *
+    * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the value
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    lea         (%ecx, %edx, 2), %ecx # %ecx<- &vBB[vCC]
+                                           # trying: lea (%ecx, %edx, scale), %ecx
+                                           # to reduce code size
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movzwl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+                                                # doing this and the previous instr
+                                                # with one instr was not faster
+    SET_VREG    %edx  rINST             # vAA<- %edx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: x86-atom/OP_AGET_SHORT.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.
+    */
+
+   /*
+    * File: OP_AGET_SHORT.S
+    */
+
+/* File: x86-atom/OP_AGET.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.
+    */
+
+   /*
+    * File: OP_AGET.S
+    *
+    * Code: Generic 32-bit array "get" operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *
+    * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the value
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    lea         (%ecx, %edx, 2), %ecx # %ecx<- &vBB[vCC]
+                                           # trying: lea (%ecx, %edx, scale), %ecx
+                                           # to reduce code size
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movswl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+                                                # doing this and the previous instr
+                                                # with one instr was not faster
+    SET_VREG    %edx  rINST             # vAA<- %edx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: x86-atom/OP_APUT.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.
+    */
+
+   /*
+    * File: OP_APUT.S
+    *
+    * Code: Generic 32-bit array put operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       move performed also dependent on the type of the array element.
+    *       Provides a "value" register to specify the source of the move
+    *
+    * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         (%ecx, %edx, 4), %ecx # %ecx<- &vBB[vCC]
+    GET_VREG    rINST                   # rINST<- vAA
+    movl     rINST, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: x86-atom/OP_APUT_WIDE.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.
+    */
+
+   /*
+    * File: OP_APUT_WIDE.S
+    *
+    * Code: 64-bit array put operation.
+    *
+    * For: aput-wide
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vAA
+    movq        %xmm0, offArrayObject_contents(%ecx, %edx, 8) # vBB[vCC]<- %xmm0; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: x86-atom/OP_APUT_OBJECT.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.
+    */
+
+   /*
+    * File: OP_APUT_OBJECT.S
+    *
+    * Code: 32-bit array put operation.  Provides an "scale" variable
+    *       specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *       Provides a "value" register to specify the source of the mov
+    *
+    * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %eax                    # %eax<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %eax               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%eax), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    GET_VREG    rINST                   # rINST<- vAA
+    lea         (%eax, %edx, 4), %edx   # %edx<- &vBB[vCC]
+    cmp         $0, rINST              # check for null reference
+    je          .LOP_APUT_OBJECT_skip_check  # reference is null so skip type check
+    jmp         .LOP_APUT_OBJECT_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: x86-atom/OP_APUT_BOOLEAN.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.
+    */
+
+   /*
+    * File: OP_APUT_BOOLEAN.S
+    */
+
+/* File: x86-atom/OP_APUT.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.
+    */
+
+   /*
+    * File: OP_APUT.S
+    *
+    * Code: Generic 32-bit array put operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       move performed also dependent on the type of the array element.
+    *       Provides a "value" register to specify the source of the move
+    *
+    * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         (%ecx, %edx, 1), %ecx # %ecx<- &vBB[vCC]
+    GET_VREG    rINST                   # rINST<- vAA
+    movb     rINSTbl, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: x86-atom/OP_APUT_BYTE.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.
+    */
+
+   /*
+    * File: OP_APUT_BYTE.S
+    */
+
+/* File: x86-atom/OP_APUT.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.
+    */
+
+   /*
+    * File: OP_APUT.S
+    *
+    * Code: Generic 32-bit array put operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       move performed also dependent on the type of the array element.
+    *       Provides a "value" register to specify the source of the move
+    *
+    * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         (%ecx, %edx, 1), %ecx # %ecx<- &vBB[vCC]
+    GET_VREG    rINST                   # rINST<- vAA
+    movb     rINSTbl, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: x86-atom/OP_APUT_CHAR.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.
+    */
+
+   /*
+    * File: OP_APUT_CHAR.S
+    */
+
+/* File: x86-atom/OP_APUT.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.
+    */
+
+   /*
+    * File: OP_APUT.S
+    *
+    * Code: Generic 32-bit array put operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       move performed also dependent on the type of the array element.
+    *       Provides a "value" register to specify the source of the move
+    *
+    * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         (%ecx, %edx, 2), %ecx # %ecx<- &vBB[vCC]
+    GET_VREG    rINST                   # rINST<- vAA
+    movw     rINSTw, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: x86-atom/OP_APUT_SHORT.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.
+    */
+
+   /*
+    * File: OP_APUT_SHORT.S
+    */
+
+/* File: x86-atom/OP_APUT.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.
+    */
+
+   /*
+    * File: OP_APUT.S
+    *
+    * Code: Generic 32-bit array put operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       move performed also dependent on the type of the array element.
+    *       Provides a "value" register to specify the source of the move
+    *
+    * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         (%ecx, %edx, 2), %ecx # %ecx<- &vBB[vCC]
+    GET_VREG    rINST                   # rINST<- vAA
+    movw     rINSTw, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: x86-atom/OP_IGET.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.
+    */
+
+   /*
+    * File: OP_IGET.S
+    *
+    * Code: Generic 32-bit instance field "get" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iget's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+    *      iget-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IGET_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %edx # %edx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    jmp         .LOP_IGET_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: x86-atom/OP_IGET_WIDE.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.
+    */
+
+   /*
+    * File: OP_IGET_WIDE.S
+    *
+    * Code: 64 bit instance field "get" operation. Uses no substitutions.
+    *
+    * For: iget-wide
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    * Format:  B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rGLUE, %eax             # %eax<- MterpGlue pointer
+    movl        offGlue_methodClassDex(%eax), %ecx # %ecx<- pDvmDex
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- CCCC
+    FETCH       1, %edx                 # %edx<- pDvmDex->pResFields
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved InstField ptr
+    cmp         $0, %ecx               # check for null ptr; resolved InstField ptr
+    jne         .LOP_IGET_WIDE_finish
+    movl        offGlue_method(%eax), %ecx # %ecx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%ecx), %ecx # %ecx<- method->clazz
+    movl        %ecx, -8(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -4(%esp)          # push parameter method->clazz
+    jmp         .LOP_IGET_WIDE_finish2
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: x86-atom/OP_IGET_OBJECT.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.
+    */
+
+   /*
+    * File: OP_IGET_OBJECT.S
+    */
+
+/* File: x86-atom/OP_IGET.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.
+    */
+
+   /*
+    * File: OP_IGET.S
+    *
+    * Code: Generic 32-bit instance field "get" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iget's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+    *      iget-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IGET_OBJECT_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %edx # %edx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    jmp         .LOP_IGET_OBJECT_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: x86-atom/OP_IGET_BOOLEAN.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.
+    */
+
+   /*
+    * File: OP_IGET_BOOLEAN.S
+    */
+
+/* File: x86-atom/OP_IGET.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.
+    */
+
+   /*
+    * File: OP_IGET.S
+    *
+    * Code: Generic 32-bit instance field "get" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iget's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+    *      iget-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IGET_BOOLEAN_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %edx # %edx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    jmp         .LOP_IGET_BOOLEAN_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: x86-atom/OP_IGET_BYTE.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.
+    */
+
+   /*
+    * File: OP_IGET_BYTE.S
+    */
+
+/* File: x86-atom/OP_IGET.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.
+    */
+
+   /*
+    * File: OP_IGET.S
+    *
+    * Code: Generic 32-bit instance field "get" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iget's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+    *      iget-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IGET_BYTE_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %edx # %edx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    jmp         .LOP_IGET_BYTE_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: x86-atom/OP_IGET_CHAR.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.
+    */
+
+   /*
+    * File: OP_IGET_CHAR.S
+    */
+
+/* File: x86-atom/OP_IGET.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.
+    */
+
+   /*
+    * File: OP_IGET.S
+    *
+    * Code: Generic 32-bit instance field "get" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iget's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+    *      iget-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IGET_CHAR_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %edx # %edx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    jmp         .LOP_IGET_CHAR_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: x86-atom/OP_IGET_SHORT.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.
+    */
+
+   /*
+    * File: OP_IGET_SHORT.S
+    */
+
+/* File: x86-atom/OP_IGET.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.
+    */
+
+   /*
+    * File: OP_IGET.S
+    *
+    * Code: Generic 32-bit instance field "get" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iget's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+    *      iget-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IGET_SHORT_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %edx # %edx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    jmp         .LOP_IGET_SHORT_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: x86-atom/OP_IPUT.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.
+    */
+
+    /*
+    * File: OP_IPUT.S
+    *
+    * Code: Generic 32-bit instance field "put" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iput's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+    *      iput-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IPUT_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    jmp         .LOP_IPUT_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: x86-atom/OP_IPUT_WIDE.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.
+    */
+
+   /*
+    * File: OP_IPUT_WIDE.S
+    *
+    * Code: 64 bit instance field "put" operation. Uses no substitutions.
+    *
+    * For: iget-wide
+    *
+    * Description: Perform the object instance field "put" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    * Format:  B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rGLUE, %eax             # %eax<- MterpGlue pointer
+    movl        offGlue_methodClassDex(%eax), %ecx # %ecx<- pDvmDex
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- CCCC
+    FETCH       1, %edx                 # %edx<- pDvmDex->pResFields
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved InstField ptr
+    cmp         $0, %ecx               # check for null ptr; resolved InstField ptr
+    jne         .LOP_IPUT_WIDE_finish
+    movl        offGlue_method(%eax), %ecx # %ecx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%ecx), %ecx # %ecx<- method->clazz
+    movl        %ecx, -8(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -4(%esp)          # push parameter method->clazz
+    jmp         .LOP_IPUT_WIDE_finish2
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: x86-atom/OP_IPUT_OBJECT.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.
+    */
+
+    /*
+    * File: OP_IPUT.S
+    *
+    * Code: Generic 32-bit instance field "put" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iput's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+    *      iput-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IPUT_OBJECT_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    jmp         .LOP_IPUT_OBJECT_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: x86-atom/OP_IPUT_BOOLEAN.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.
+    */
+
+   /*
+    * File: OP_IPUT_BOOLEAN.S
+    */
+
+/* File: x86-atom/OP_IPUT.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.
+    */
+
+    /*
+    * File: OP_IPUT.S
+    *
+    * Code: Generic 32-bit instance field "put" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iput's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+    *      iput-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IPUT_BOOLEAN_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    jmp         .LOP_IPUT_BOOLEAN_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: x86-atom/OP_IPUT_BYTE.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.
+    */
+
+   /*
+    * File: OP_IPUT_BYTE.S
+    */
+
+/* File: x86-atom/OP_IPUT.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.
+    */
+
+    /*
+    * File: OP_IPUT.S
+    *
+    * Code: Generic 32-bit instance field "put" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iput's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+    *      iput-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IPUT_BYTE_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    jmp         .LOP_IPUT_BYTE_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: x86-atom/OP_IPUT_CHAR.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.
+    */
+
+   /*
+    * File: OP_IPUT_CHAR.S
+    */
+
+/* File: x86-atom/OP_IPUT.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.
+    */
+
+    /*
+    * File: OP_IPUT.S
+    *
+    * Code: Generic 32-bit instance field "put" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iput's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+    *      iput-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IPUT_CHAR_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    jmp         .LOP_IPUT_CHAR_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: x86-atom/OP_IPUT_SHORT.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.
+    */
+
+   /*
+    * File: OP_IPUT_SHORT.S
+    */
+
+/* File: x86-atom/OP_IPUT.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.
+    */
+
+    /*
+    * File: OP_IPUT.S
+    *
+    * Code: Generic 32-bit instance field "put" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iput's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+    *      iput-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .LOP_IPUT_SHORT_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    jmp         .LOP_IPUT_SHORT_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: x86-atom/OP_SGET.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.
+    */
+
+   /*
+    * File: OP_SGET.S
+    *
+    * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; load the field value
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SGET_resolve
+    jmp         .LOP_SGET_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: x86-atom/OP_SGET_WIDE.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.
+    */
+
+   /*
+    * File: OP_SGET_WIDE.S
+    *
+    * Code: 64-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-wide
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field, loading or storing
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_methodClassDex(%eax), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %edx                 # %edx<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %edx, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SGET_WIDE_resolve
+
+.LOP_SGET_WIDE_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        offStaticField_value(%ecx), %xmm0 # %xmm0<- field value
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- field value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: x86-atom/OP_SGET_OBJECT.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.
+    */
+
+   /*
+    * File: OP_SGET_OBJECT.S
+    */
+
+/* File: x86-atom/OP_SGET.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.
+    */
+
+   /*
+    * File: OP_SGET.S
+    *
+    * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; load the field value
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SGET_OBJECT_resolve
+    jmp         .LOP_SGET_OBJECT_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: x86-atom/OP_SGET_BOOLEAN.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.
+    */
+
+   /*
+    * File: OP_SGET_BOOLEAN.S
+    */
+
+/* File: x86-atom/OP_SGET.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.
+    */
+
+   /*
+    * File: OP_SGET.S
+    *
+    * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; load the field value
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SGET_BOOLEAN_resolve
+    jmp         .LOP_SGET_BOOLEAN_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: x86-atom/OP_SGET_BYTE.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.
+    */
+
+   /*
+    * File: OP_SGET_BYTE.S
+    */
+
+/* File: x86-atom/OP_SGET.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.
+    */
+
+   /*
+    * File: OP_SGET.S
+    *
+    * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; load the field value
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SGET_BYTE_resolve
+    jmp         .LOP_SGET_BYTE_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: x86-atom/OP_SGET_CHAR.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.
+    */
+
+   /*
+    * File: OP_SGET_CHAR.S
+    */
+
+/* File: x86-atom/OP_SGET.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.
+    */
+
+   /*
+    * File: OP_SGET.S
+    *
+    * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; load the field value
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SGET_CHAR_resolve
+    jmp         .LOP_SGET_CHAR_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: x86-atom/OP_SGET_SHORT.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.
+    */
+
+   /*
+    * File: OP_SGET_SHORT.S
+    */
+
+/* File: x86-atom/OP_SGET.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.
+    */
+
+   /*
+    * File: OP_SGET.S
+    *
+    * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; load the field value
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SGET_SHORT_resolve
+    jmp         .LOP_SGET_SHORT_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: x86-atom/OP_SPUT.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.
+    */
+
+   /*
+    * File: OP_SPUT.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SPUT_resolve
+    jmp         .LOP_SPUT_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: x86-atom/OP_SPUT_WIDE.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.
+    */
+
+   /*
+    * File: OP_SPUT_WIDE.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_methodClassDex(%eax), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %edx                 # %edx<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %edx, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SPUT_WIDE_resolve
+
+.LOP_SPUT_WIDE_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vAA
+    movq        %xmm0, offStaticField_value(%ecx) # field value<- field value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: x86-atom/OP_SPUT_OBJECT.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.
+    */
+
+   /*
+    * File: OP_SPUT_OBJECT.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField
+    je          .LOP_SPUT_OBJECT_resolve
+    jmp         .LOP_SPUT_OBJECT_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: x86-atom/OP_SPUT_BOOLEAN.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.
+    */
+
+   /*
+    * File: OP_SPUT_BOOLEAN.S
+    */
+
+/* File: x86-atom/OP_SPUT.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.
+    */
+
+   /*
+    * File: OP_SPUT.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SPUT_BOOLEAN_resolve
+    jmp         .LOP_SPUT_BOOLEAN_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: x86-atom/OP_SPUT_BYTE.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.
+    */
+
+   /*
+    * File: OP_SPUT_BYTE.S
+    */
+
+/* File: x86-atom/OP_SPUT.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.
+    */
+
+   /*
+    * File: OP_SPUT.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SPUT_BYTE_resolve
+    jmp         .LOP_SPUT_BYTE_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: x86-atom/OP_SPUT_CHAR.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.
+    */
+
+   /*
+    * File: OP_SPUT_CHAR.S
+    */
+
+/* File: x86-atom/OP_SPUT.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.
+    */
+
+   /*
+    * File: OP_SPUT.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SPUT_CHAR_resolve
+    jmp         .LOP_SPUT_CHAR_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: x86-atom/OP_SPUT_SHORT.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.
+    */
+
+   /*
+    * File: OP_SPUT_SHORT.S
+    */
+
+/* File: x86-atom/OP_SPUT.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.
+    */
+
+   /*
+    * File: OP_SPUT.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .LOP_SPUT_SHORT_resolve
+    jmp         .LOP_SPUT_SHORT_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: x86-atom/OP_INVOKE_VIRTUAL.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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL.S
+    *
+    * Code: Call a virtual method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_direct that allows up to 255 arguments.
+    *
+    * For: invoke-virtual, invoke-virtual/range
+    *
+    * Description: invoke-virtual is used to invoke a normal virtual method;
+    *              a method that is not static or final, and is not a constructor.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    EXPORT_PC                           # must export pc for invoke
+    movl        offGlue_methodClassDex(%eax), %eax # %eax<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        offDvmDex_pResMethods(%eax), %eax # %eax<- pDvmDex->pResMethods
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    .if         (!0)
+    and         $15, %edx              # %edx<- D if not range
+    .endif
+    cmp         $0, (%eax, %ecx, 4)    # check if already resolved
+    je          .LOP_INVOKE_VIRTUAL_break
+    movl        (%eax, %ecx, 4), %eax   # %eax<- resolved base method
+    jmp         .LOP_INVOKE_VIRTUAL_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: x86-atom/OP_INVOKE_SUPER.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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER.S
+    *
+    * Code: Call super method.
+    *
+    * For: invoke-super, invoke-super/range
+    *
+    * Description: invoke-super is used to invoke the closest superclass's virtual
+    *              method (as opposed to the one with the same method_id in the
+    *              calling class).
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    FETCH       2, %eax                 # %eax<- GFED or CCCC
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+    .if         (!0)
+    and         $15, %eax              # %eax<- D if not range
+    .endif
+    FETCH       1, %edx                 # %edx<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+    cmp         $0, (rFP, %eax, 4)     # check for null object
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved base method
+    je          common_errNullObject    # handle null object
+    jmp         .LOP_INVOKE_SUPER_continue2
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: x86-atom/OP_INVOKE_DIRECT.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.
+    */
+
+   /*
+    * File: OP_INVOKE_DIRECT.S
+    *
+    * Code: Call a non-static direct method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_direct that allows up to 255 arguments.
+    *
+    * For: invoke-direct, invoke-direct/range
+    *
+    * Description: invoke-direct is used to invoke a non-static direct method;
+    *              an instance method that is non-overridable, for example,
+    *              either a private instance method or a constructor.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved method to call
+    .if         (!0)
+    andl        $15, %edx              # %edx<- D if not range
+    .endif
+    EXPORT_PC                           # must export for invoke
+    movl        %edx, -4(%esp)          # save "this" pointer register
+    cmp         $0, %ecx               # check if already resolved
+    GET_VREG    %edx                    # %edx<- "this" pointer
+    je          .LOP_INVOKE_DIRECT_resolve     # handle resolve
+
+.LOP_INVOKE_DIRECT_finish:
+    cmp         $0, %edx               # check for null "this"
+    jne         common_invokeMethodNoRange # invoke method common code
+    jmp         common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: x86-atom/OP_INVOKE_STATIC.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.
+    */
+
+   /*
+    * File: OP_INVOKE_STATIC.S
+    *
+    * Code: Call static direct method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_static that allows up to 255 arguments.
+    *
+    * For: invoke-static, invoke-static/range
+    *
+    * Description: invoke-static is used to invoke static direct method.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %edx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %edx<- pDvmDex->pResMethods
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved method to call
+    cmp         $0, %ecx               # check if already resolved
+    EXPORT_PC                           # must export for invoke
+    jne         common_invokeMethodNoRange # invoke method common code
+    jmp         .LOP_INVOKE_STATIC_break
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: x86-atom/OP_INVOKE_INTERFACE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_INTERFACE.S
+    *
+    * Code: Call at method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_interface that allows up to 255 arguments.
+    *
+    * For: invoke-interface, invoke-interface-range
+    *
+    * Description: invoke-interface is used to invoke an interface method; on an
+    *              object whose concrete class isn't known, using a method_id that
+    *              refers to an interface.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        %ecx, -12(%esp)         # push argument method index
+    .if         (!0)
+    and         $15, %edx              # %edx<- D if not range
+    .endif
+    EXPORT_PC                           # must export for invoke
+    GET_VREG    %edx                    # %edx<- first arg "this pointer"
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+    movl        %eax, -4(%esp)          # push parameter class
+    cmp         $0, %edx               # check for null object
+    je          common_errNullObject    # handle null object
+    jmp         .LOP_INVOKE_INTERFACE_break
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: x86-atom/OP_UNUSED_73.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.
+    */
+
+   /*
+    * File: OP_UNUSED_73.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: x86-atom/OP_INVOKE_VIRTUAL_RANGE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL_RANGE.S
+    */
+
+/* File: x86-atom/OP_INVOKE_VIRTUAL.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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL.S
+    *
+    * Code: Call a virtual method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_direct that allows up to 255 arguments.
+    *
+    * For: invoke-virtual, invoke-virtual/range
+    *
+    * Description: invoke-virtual is used to invoke a normal virtual method;
+    *              a method that is not static or final, and is not a constructor.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    EXPORT_PC                           # must export pc for invoke
+    movl        offGlue_methodClassDex(%eax), %eax # %eax<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        offDvmDex_pResMethods(%eax), %eax # %eax<- pDvmDex->pResMethods
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    .if         (!1)
+    and         $15, %edx              # %edx<- D if not range
+    .endif
+    cmp         $0, (%eax, %ecx, 4)    # check if already resolved
+    je          .LOP_INVOKE_VIRTUAL_RANGE_break
+    movl        (%eax, %ecx, 4), %eax   # %eax<- resolved base method
+    jmp         .LOP_INVOKE_VIRTUAL_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: x86-atom/OP_INVOKE_SUPER_RANGE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER_RANGE.S
+    */
+
+/* File: x86-atom/OP_INVOKE_SUPER.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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER.S
+    *
+    * Code: Call super method.
+    *
+    * For: invoke-super, invoke-super/range
+    *
+    * Description: invoke-super is used to invoke the closest superclass's virtual
+    *              method (as opposed to the one with the same method_id in the
+    *              calling class).
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    FETCH       2, %eax                 # %eax<- GFED or CCCC
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+    .if         (!1)
+    and         $15, %eax              # %eax<- D if not range
+    .endif
+    FETCH       1, %edx                 # %edx<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+    cmp         $0, (rFP, %eax, 4)     # check for null object
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved base method
+    je          common_errNullObject    # handle null object
+    jmp         .LOP_INVOKE_SUPER_RANGE_continue2
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: x86-atom/OP_INVOKE_DIRECT_RANGE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_DIRECT_RANGE.S
+    */
+
+/* File: x86-atom/OP_INVOKE_DIRECT.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.
+    */
+
+   /*
+    * File: OP_INVOKE_DIRECT.S
+    *
+    * Code: Call a non-static direct method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_direct that allows up to 255 arguments.
+    *
+    * For: invoke-direct, invoke-direct/range
+    *
+    * Description: invoke-direct is used to invoke a non-static direct method;
+    *              an instance method that is non-overridable, for example,
+    *              either a private instance method or a constructor.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved method to call
+    .if         (!1)
+    andl        $15, %edx              # %edx<- D if not range
+    .endif
+    EXPORT_PC                           # must export for invoke
+    movl        %edx, -4(%esp)          # save "this" pointer register
+    cmp         $0, %ecx               # check if already resolved
+    GET_VREG    %edx                    # %edx<- "this" pointer
+    je          .LOP_INVOKE_DIRECT_RANGE_resolve     # handle resolve
+
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    cmp         $0, %edx               # check for null "this"
+    jne         common_invokeMethodRange # invoke method common code
+    jmp         common_errNullObject
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: x86-atom/OP_INVOKE_STATIC_RANGE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_STATIC_RANGE.S
+    */
+
+/* File: x86-atom/OP_INVOKE_STATIC.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.
+    */
+
+   /*
+    * File: OP_INVOKE_STATIC.S
+    *
+    * Code: Call static direct method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_static that allows up to 255 arguments.
+    *
+    * For: invoke-static, invoke-static/range
+    *
+    * Description: invoke-static is used to invoke static direct method.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %edx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %edx<- pDvmDex->pResMethods
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved method to call
+    cmp         $0, %ecx               # check if already resolved
+    EXPORT_PC                           # must export for invoke
+    jne         common_invokeMethodRange # invoke method common code
+    jmp         .LOP_INVOKE_STATIC_RANGE_break
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: x86-atom/OP_INVOKE_INTERFACE_RANGE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_INTERFACE_RANGE.S
+    */
+
+/* File: x86-atom/OP_INVOKE_INTERFACE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_INTERFACE.S
+    *
+    * Code: Call at method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_interface that allows up to 255 arguments.
+    *
+    * For: invoke-interface, invoke-interface-range
+    *
+    * Description: invoke-interface is used to invoke an interface method; on an
+    *              object whose concrete class isn't known, using a method_id that
+    *              refers to an interface.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        %ecx, -12(%esp)         # push argument method index
+    .if         (!1)
+    and         $15, %edx              # %edx<- D if not range
+    .endif
+    EXPORT_PC                           # must export for invoke
+    GET_VREG    %edx                    # %edx<- first arg "this pointer"
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+    movl        %eax, -4(%esp)          # push parameter class
+    cmp         $0, %edx               # check for null object
+    je          common_errNullObject    # handle null object
+    jmp         .LOP_INVOKE_INTERFACE_RANGE_break
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: x86-atom/OP_UNUSED_79.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.
+    */
+
+   /*
+    * File: OP_UNUSED_79.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: x86-atom/OP_UNUSED_7A.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.
+    */
+
+   /*
+    * File: OP_UNUSED_7A.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: x86-atom/OP_NEG_INT.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.
+    */
+
+   /*
+    * File: OP_NEG_INT.S
+    */
+
+/* File: x86-atom/unop.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.
+    */
+
+   /*
+    * File: unop.S
+    *
+    * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%ecx = op %edx".
+    *
+    * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+                               # do operation part 1
+    neg        %ecx                              # do operation part 2
+    SET_VREG    %ecx, rINST             # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: x86-atom/OP_NOT_INT.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.
+    */
+
+   /*
+    * File: OP_NOT_INT.S
+    */
+
+/* File: x86-atom/unop.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.
+    */
+
+   /*
+    * File: unop.S
+    *
+    * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%ecx = op %edx".
+    *
+    * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+                               # do operation part 1
+    not        %ecx                              # do operation part 2
+    SET_VREG    %ecx, rINST             # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: x86-atom/OP_NEG_LONG.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.
+    */
+
+   /*
+    * File: OP_NEG_LONG.S
+    */
+
+/* File: x86-atom/unopWide.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.
+    */
+
+   /*
+    * File: unopWide.S
+    *
+    * Code: Generic 64-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%xmm0 = op %xmm1".
+    *
+    * For:  neg-double, neg-long, not-long
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, rINST              # rINST<- B
+    and         $15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vB
+    xorps %xmm1, %xmm1                           # do operation part 1
+    psubq %xmm0, %xmm1                              # do operation part 2
+    movq        %xmm1, (rFP, %ecx, 4) # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: x86-atom/OP_NOT_LONG.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.
+    */
+
+   /*
+    * File: OP_NOT_LONG.S
+    */
+
+/* File: x86-atom/unopWide.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.
+    */
+
+   /*
+    * File: unopWide.S
+    *
+    * Code: Generic 64-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%xmm0 = op %xmm1".
+    *
+    * For:  neg-double, neg-long, not-long
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, rINST              # rINST<- B
+    and         $15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vB
+                               # do operation part 1
+    pandn  0xFFFFFFFF, %xmm0                              # do operation part 2
+    movq        %xmm0, (rFP, %ecx, 4) # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: x86-atom/OP_NEG_FLOAT.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.
+    */
+
+   /*
+    * File: OP_NEG_FLOAT.S
+    */
+
+/* File: x86-atom/unop.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.
+    */
+
+   /*
+    * File: unop.S
+    *
+    * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%ecx = op %edx".
+    *
+    * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+                               # do operation part 1
+    addl      $0x80000000, %ecx                              # do operation part 2
+    SET_VREG    %ecx, rINST             # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: x86-atom/OP_NEG_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_NEG_DOUBLE.S
+    */
+
+/* File: x86-atom/unopWide.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.
+    */
+
+   /*
+    * File: unopWide.S
+    *
+    * Code: Generic 64-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%xmm0 = op %xmm1".
+    *
+    * For:  neg-double, neg-long, not-long
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, rINST              # rINST<- B
+    and         $15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vB
+    movq .LdoubNeg, %xmm1                           # do operation part 1
+    pxor %xmm1, %xmm0                              # do operation part 2
+    movq        %xmm0, (rFP, %ecx, 4) # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: x86-atom/OP_INT_TO_LONG.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.
+    */
+
+   /*
+    * File: OP_INT_TO_LONG.S
+    *
+    * Code:  Convert an int to a long. Uses no substitutions.
+    *
+    * For:
+    *
+    * Description: Convert an int in the source register, to a long, and
+    *              stores the result in the destintation register. vA<- (long) vB
+    *
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %eax             # %eax<- BA+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %eax               # %eax<- B
+    andl        $15, %ecx              # %ecx<- A
+    GET_VREG    %eax                    # %eax<- vB
+    cdq                                 # %edx:%eax<- sign-extend of %eax
+    movl        %eax, (rFP, %ecx, 4)    # vA<- lo part
+    movl        %edx, 4(rFP, %ecx, 4)   # vA+1<- hi part
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: x86-atom/OP_INT_TO_FLOAT.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.
+    */
+
+   /*
+    * File: OP_INT_TO_FLOAT.S
+    *
+    * Code: Convert an int to a float. Uses no substitutions.
+    *
+    * For: int-to-float
+    *
+    * Description: Convert an int in the source register, to a float, and
+    *              stores the result in the destintation register. vA<- (float) vB
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %eax             # %eax<- BA+
+    shr         $4, %eax               # %eax<- B
+    andl        $15,  rINST            # rINST<- A
+    cvtsi2ss    (rFP,%eax,4), %xmm0     # %xmm0<- vB
+    movss       %xmm0, (rFP, rINST, 4)  # vA<- %xmm0
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: x86-atom/OP_INT_TO_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_INT_TO_DOUBLE.S
+    *
+    * Code: Convert an int to a double. Uses no substitutions.
+    *
+    * For: int-to-double
+    *
+    * Description: Converts an int in the source register, to a double, and
+    *              stores the result in the destination register. vA<- (double) vB
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %eax             # %eax<- BA+
+    shr         $4, %eax               # %eax<- B
+    andl        $15, rINST             # rINST<- A
+    cvtsi2sd    (rFP, %eax, 4), %xmm0   # %xmm0<- vB
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- %xmm0; (double) vB
+    FFETCH_ADV  1, %edx                 # %edx<- next instruction hi; fetch, advance
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: x86-atom/OP_LONG_TO_INT.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.
+    */
+
+   /*
+    * File: OP_LONG_TO_INT.S
+    */
+
+/* File: x86-atom/OP_MOVE.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.
+    */
+
+   /*
+    * File: OP_MOVE.S
+    *
+    * Code: Copies contents from one register to another. Uses no
+    *       substitutions.
+    *
+    * For: move, move-object, long-to-int
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              vA<- vB; fp[A]<- fp[B]
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vB
+    SET_VREG    rINST, %ecx             # vA<- vB; %edx
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: x86-atom/OP_LONG_TO_FLOAT.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.
+    */
+
+   /*
+    * File: OP_LONG_TO_FLOAT.S
+    *
+    * Code: Convert a long to a float. Uses no substitutions.
+    *
+    * For: int-to-float
+    *
+    * Description: Converts a float in the source register, to a float, and
+    *              stores the result in the destination register. vA<- (double) vB
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, rINST              # rINST<- B
+    and         $15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    fildll      (rFP, rINST, 4)         # FPU<- vB
+    fstps       (rFP, %ecx, 4)          # vA<- FPU; (float) vB
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: x86-atom/OP_LONG_TO_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_LONG_TO_DOUBLE.S
+    *
+    * Code: Convert a long to a dobule. Uses no substitutions.
+    *
+    * For: long-to-double
+    *
+    * Description: Converts a long in the source register to a double, and
+    *              stores the result in the destination register. vA<- (double) vB
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, rINST              # rINST<- B
+    and         $15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    fildll      (rFP, rINST, 4)         # FPU<- vB
+    fstpl       (rFP, %ecx, 4)          # vA<- FPU; (double) vB
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: x86-atom/OP_FLOAT_TO_INT.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.
+    */
+
+   /*
+    * File: OP_FLOAT_TO_INT.S
+    *
+    * Code: Converts a float to a int. Uses no substitutions.
+    *
+    * For: float-to-int
+    *
+    * Description: Convert the float in source register to a int
+    *              and store the result in the destintation register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %edx              # %edx<- A
+    flds        (rFP, rINST, 4)         # push vB to floating point stack
+    fildl       .LintMax                # push max int value
+    fildl       .LintMin                # push min int value
+    fucomip     %st(2), %st(0)          # check for negInf
+    jae         .LOP_FLOAT_TO_INT_negInf      # handle negInf
+    fucomip     %st(1), %st(0)          # check for posInf or NaN
+    jc          .LOP_FLOAT_TO_INT_nanInf      # handle posInf or NaN
+    jmp         .LOP_FLOAT_TO_INT_break       # do conversion
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: x86-atom/OP_FLOAT_TO_LONG.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.
+    */
+
+   /*
+    * File: OP_FLOAT_TO_LONG.S
+    *
+    * Code: Converts a float to a long. Uses no substitutions.
+    *
+    * For: float-to-long
+    *
+    * Description: Convert the float in source register to a long
+    *              and store the result in the destintation register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %edx              # %edx<- A
+    flds        (rFP, rINST, 4)         # push vB to floating point stack
+    fildll      .LvaluePosInfLong       # push max int value
+    fildll      .LvalueNegInfLong       # push min int value
+    fucomip     %st(2), %st(0)          # check for negInf
+    jae         .LOP_FLOAT_TO_LONG_negInf      # handle negInf
+    fucomip     %st(1), %st(0)          # check for posInf or NaN
+    jc          .LOP_FLOAT_TO_LONG_nanInf      # handle posInf or NaN
+    jmp         .LOP_FLOAT_TO_LONG_break       # do conversion
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: x86-atom/OP_FLOAT_TO_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_FLOAT_TO_DOUBLE.S
+    *
+    * Code: Converts a float to a double. Uses no substitutions.
+    *
+    * For: float-to-double
+    *
+    * Description: Convert the float in source register to a double
+    *              and store the result in the destintation register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %edx              # %edx<- A
+    flds        (rFP, rINST, 4)         # load float
+    fstpl       (rFP, %edx, 4)          # store double
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: x86-atom/OP_DOUBLE_TO_INT.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.
+    */
+
+   /*
+    * File: OP_DOUBLE_TO_INT.S
+    *
+    * Code: Converts a double to an integer. Uses no substitutions.
+    *
+    * For: double-to-int
+    *
+    * Description: Convert the source register (a double) to an integer
+    *              and store the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %edx              # %edx<- A
+    fldl        (rFP, rINST, 4)         # load &vB
+    fildl       .LintMax                # push max int value
+    fildl       .LintMin                # push min int value
+    fucomip     %st(2), %st(0)          # check for negInf
+    jae         .LOP_DOUBLE_TO_INT_negInf      # handle negInf
+    fucomip     %st(1), %st(0)          # check for posInf or NaN
+    jc          .LOP_DOUBLE_TO_INT_nanInf      # handle posInf or NaN
+    jmp         .LOP_DOUBLE_TO_INT_break       # do conversion
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: x86-atom/OP_DOUBLE_TO_LONG.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.
+    */
+
+   /*
+    * File: OP_DOUBLE_TO_LONG.S
+    *
+    * Code: Converts a double to a long. Uses no substitutions.
+    *
+    * For: double-to-long
+    *
+    * Description: Convert the double in source register to a long
+    *              and store in the destintation register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %ecx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %edx              # %ecx<- A
+    fldl        (rFP, rINST, 4)         # push vB to floating point stack
+    fildll      .LvaluePosInfLong       # push max int value
+    fildll      .LvalueNegInfLong       # push min int value
+    fucomip     %st(2), %st(0)          # check for negInf
+    jae         .LOP_DOUBLE_TO_LONG_negInf      # handle negInf
+    fucomip     %st(1), %st(0)          # check for posInf or NaN
+    jc          .LOP_DOUBLE_TO_LONG_nanInf      # handle posInf or NaN
+    jmp         .LOP_DOUBLE_TO_LONG_break       # do conversion
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: x86-atom/OP_DOUBLE_TO_FLOAT.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.
+    */
+
+   /*
+    * File: OP_DOUBLE_TO_FLOAT.S
+    *
+    * Code: Converts a double to a float. Uses no substitutions.
+    *
+    * For: double-to-float
+    *
+    * Description: Convert the source register (a double) to a float
+    *              and store the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    and         $15, %edx              # %edx<- A
+    fldl        (rFP, rINST, 4)         # load &vB
+    fstps       (rFP, %edx, 4)          # store float
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: x86-atom/OP_INT_TO_BYTE.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.
+    */
+
+   /*
+    * File: OP_INT_TO_BYTE.S
+    */
+
+/* File: x86-atom/unop.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.
+    */
+
+   /*
+    * File: unop.S
+    *
+    * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%ecx = op %edx".
+    *
+    * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    sal $24, %ecx                           # do operation part 1
+    sar $24, %ecx                              # do operation part 2
+    SET_VREG    %ecx, rINST             # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: x86-atom/OP_INT_TO_CHAR.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.
+    */
+
+   /*
+    * File: OP_INT_TO_CHAR.S
+    */
+
+/* File: x86-atom/unop.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.
+    */
+
+   /*
+    * File: unop.S
+    *
+    * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%ecx = op %edx".
+    *
+    * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    sal $16, %ecx                           # do operation part 1
+    shr $16, %ecx                              # do operation part 2
+    SET_VREG    %ecx, rINST             # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: x86-atom/OP_INT_TO_SHORT.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.
+    */
+
+   /*
+    * File: OP_INT_TO_SHORT.S
+    */
+
+/* File: x86-atom/unop.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.
+    */
+
+   /*
+    * File: unop.S
+    *
+    * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%ecx = op %edx".
+    *
+    * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    sal $16, %ecx                           # do operation part 1
+    sar $16, %ecx                              # do operation part 2
+    SET_VREG    %ecx, rINST             # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: x86-atom/OP_ADD_INT.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.
+    */
+
+   /*
+    * File: OP_ADD_INT.S
+    */
+
+/* File: x86-atom/binop.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.
+    */
+
+   /*
+    * File: binop.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    addl     %edx, %ecx                              # %ecx<- vBB op vCC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: x86-atom/OP_SUB_INT.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.
+    */
+
+   /*
+    * File: OP_SUB_INT.S
+    */
+
+/* File: x86-atom/binop.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.
+    */
+
+   /*
+    * File: binop.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    subl     %edx, %ecx                              # %ecx<- vBB op vCC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: x86-atom/OP_MUL_INT.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.
+    */
+
+   /*
+    * File: OP_MUL_INT.S
+    */
+
+/* File: x86-atom/binop.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.
+    */
+
+   /*
+    * File: binop.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    imul     %edx, %ecx                              # %ecx<- vBB op vCC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: x86-atom/OP_DIV_INT.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.
+    */
+
+   /*
+    * File: OP_DIV_INT.S
+    */
+
+/* File: x86-atom/binopD.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.
+    */
+
+   /*
+    * File: binopD.S
+    *
+    * Code: 32-bit integer divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int, rem-int
+    *
+    * Description: Perform a binary operation on two source
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    GET_VREG    %eax                    # %eax<- vBB
+    GET_VREG    %ecx                    # %ecx<- vCC
+    cmp         $0, %ecx               # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    cmpl        $-1, %ecx              # handle -1 special case divide error
+    jne         .LOP_DIV_INT_noerror
+    cmpl        $0x80000000,%eax       # handle min int special case divide error
+    je         .LOP_DIV_INT_break
+.LOP_DIV_INT_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    .if  1
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .LOP_DIV_INT_break2
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: x86-atom/OP_REM_INT.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.
+    */
+
+   /*
+    * File: OP_REM_INT.S
+    */
+
+/* File: x86-atom/binopD.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.
+    */
+
+   /*
+    * File: binopD.S
+    *
+    * Code: 32-bit integer divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int, rem-int
+    *
+    * Description: Perform a binary operation on two source
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    GET_VREG    %eax                    # %eax<- vBB
+    GET_VREG    %ecx                    # %ecx<- vCC
+    cmp         $0, %ecx               # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    cmpl        $-1, %ecx              # handle -1 special case divide error
+    jne         .LOP_REM_INT_noerror
+    cmpl        $0x80000000,%eax       # handle min int special case divide error
+    je         .LOP_REM_INT_break
+.LOP_REM_INT_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    .if  0
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .LOP_REM_INT_break2
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: x86-atom/OP_AND_INT.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.
+    */
+
+   /*
+    * File: OP_AND_INT.S
+    */
+
+/* File: x86-atom/binop.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.
+    */
+
+   /*
+    * File: binop.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    andl     %edx, %ecx                              # %ecx<- vBB op vCC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: x86-atom/OP_OR_INT.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.
+    */
+
+   /*
+    * File: OP_OR_INT.S
+    */
+
+/* File: x86-atom/binop.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.
+    */
+
+   /*
+    * File: binop.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    or %edx, %ecx                              # %ecx<- vBB op vCC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: x86-atom/OP_XOR_INT.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.
+    */
+
+   /*
+    * File: OP_XOR_INT.S
+    */
+
+/* File: x86-atom/binop.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.
+    */
+
+   /*
+    * File: binop.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    xor     %edx, %ecx                              # %ecx<- vBB op vCC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: x86-atom/OP_SHL_INT.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.
+    */
+
+   /*
+    * File: OP_SHL_INT.S
+    */
+
+/* File: x86-atom/binopS.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.
+    */
+
+   /*
+    * File: binopS.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%edx = %edx op %cl"
+    *
+    * For: shl-int, shr-int, ushr-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vBB
+    GET_VREG    %ecx                    # %ecx<- vCC
+    sal     %cl, %edx                              # %edx<- vBB op +CC
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: x86-atom/OP_SHR_INT.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.
+    */
+
+   /*
+    * File: OP_SHR_INT.S
+    */
+
+/* File: x86-atom/binopS.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.
+    */
+
+   /*
+    * File: binopS.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%edx = %edx op %cl"
+    *
+    * For: shl-int, shr-int, ushr-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vBB
+    GET_VREG    %ecx                    # %ecx<- vCC
+    sar     %cl, %edx                              # %edx<- vBB op +CC
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: x86-atom/OP_USHR_INT.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.
+    */
+
+   /*
+    * File: OP_USHR_INT.S
+    */
+
+/* File: x86-atom/binopS.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.
+    */
+
+   /*
+    * File: binopS.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%edx = %edx op %cl"
+    *
+    * For: shl-int, shr-int, ushr-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vBB
+    GET_VREG    %ecx                    # %ecx<- vCC
+    shr     %cl, %edx                              # %edx<- vBB op +CC
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: x86-atom/OP_ADD_LONG.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.
+    */
+
+   /*
+    * File: OP_ADD_LONG.S
+    */
+
+/* File: x86-atom/binopWide.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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    paddq   %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: x86-atom/OP_SUB_LONG.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.
+    */
+
+   /*
+    * File: OP_SUB_LONG.S
+    */
+
+/* File: x86-atom/binopWide.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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    psubq    %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: x86-atom/OP_MUL_LONG.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.
+    */
+
+   /*
+    * File: OP_MUL_LONG.S
+    *
+    * Code: 64-bit integer multiply
+    *
+    * For: mul-long
+    *
+    * Description: Multiply two source registers and store the
+    *              result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+   /*
+    * 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.
+    */
+
+    movl        rINST, -4(%esp)         # -4(%esp)<- AA+
+    FETCH_BB    1, rINST                # rINST<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    jmp         .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: x86-atom/OP_DIV_LONG.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.
+    */
+
+   /*
+    * File: OP_DIV_LONG.S
+    */
+
+/* File: x86-atom/binopDivRemLong.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.
+    */
+
+   /*
+    * File: binopDivRemLong.S
+    *
+    * Code: 64-bit long divide operation. Variable
+    *       "func" defines the function called to do the operation.
+    *
+    * For: div-long, rem-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_CC    1, %edx                 # %edx<- CC
+    movl        (rFP, %edx, 4), %eax    # %eax<- vCC
+    movl        4(rFP, %edx, 4), %ecx   # %ecx<- vCC+1
+    movl        %eax, -8(%esp)          # push arg vCC
+    or          %ecx, %eax              # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    FETCH_BB    1, %edx                 # %edx<- BB
+    movl        %ecx, -4(%esp)          # push arg vCC+1
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vBB,vBB+1
+    jmp         .LOP_DIV_LONG_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: x86-atom/OP_REM_LONG.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.
+    */
+
+   /*
+    * File: OP_REM_LONG.S
+    */
+
+/* File: x86-atom/binopDivRemLong.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.
+    */
+
+   /*
+    * File: binopDivRemLong.S
+    *
+    * Code: 64-bit long divide operation. Variable
+    *       "func" defines the function called to do the operation.
+    *
+    * For: div-long, rem-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+
+    FETCH_CC    1, %edx                 # %edx<- CC
+    movl        (rFP, %edx, 4), %eax    # %eax<- vCC
+    movl        4(rFP, %edx, 4), %ecx   # %ecx<- vCC+1
+    movl        %eax, -8(%esp)          # push arg vCC
+    or          %ecx, %eax              # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    FETCH_BB    1, %edx                 # %edx<- BB
+    movl        %ecx, -4(%esp)          # push arg vCC+1
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vBB,vBB+1
+    jmp         .LOP_REM_LONG_finish
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: x86-atom/OP_AND_LONG.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.
+    */
+
+   /*
+    * File: OP_AND_LONG.S
+    */
+
+/* File: x86-atom/binopWide.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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    pand   %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: x86-atom/OP_OR_LONG.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.
+    */
+
+   /*
+    * File: OP_OR_LONG.S
+    */
+
+/* File: x86-atom/binopWide.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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    por %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: x86-atom/OP_XOR_LONG.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.
+    */
+
+   /*
+    * File: OP_XOR_LONG.S
+    */
+
+/* File: x86-atom/binopWide.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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    pxor   %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: x86-atom/OP_SHL_LONG.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.
+    */
+
+   /*
+    * File: OP_SHL_LONG.S
+    *
+    * Code: Performs a shift left long. Uses no substitutions.
+    *
+    * For: shl-long
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where one is the shift amount and the other is the value to shift.
+    *              Store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_CC    1, %eax                 # %eax<- CC
+    FETCH_BB    1, %edx                 # %edx<- BB
+    movq        .LshiftMask, %xmm2      # %xmm2<- mask for the shift bits
+    movss       (rFP, %eax, 4), %xmm0   # %xmm0<- vCC
+    pand        %xmm2, %xmm0            # %xmm0<- masked shift bits
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vBB
+    psllq       %xmm0, %xmm1            # %xmm1<- shifted vBB
+    movq        %xmm1, (rFP, rINST, 4)  # vAA<- shifted vBB
+    FINISH      2                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: x86-atom/OP_SHR_LONG.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.
+    */
+
+   /*
+    * File: OP_SHR_LONG.S
+    *
+    * Code: Performs a shift right long
+    *
+    * For: shl-long
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where one is the shift amount and the other is the value to shift.
+    *              Store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CC    1, %eax                 # %eax<- CC
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vBB
+    movss       (rFP, %eax, 4), %xmm0   # %xmm0<- vCC
+    movq        .LshiftMask, %xmm2
+    pand        %xmm2, %xmm0            # %xmm0<- masked for the shift bits
+    psrlq       %xmm0, %xmm1            # %xmm1<- shifted vBB
+    cmpl        $0, 4(rFP, %edx, 4)    # check if we need to consider sign
+    jl          .LOP_SHR_LONG_finish      # consider sign
+    jmp         .LOP_SHR_LONG_final       # sign is fine, finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: x86-atom/OP_USHR_LONG.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.
+    */
+
+   /*
+    * File: OP_USHR_LONG.S
+    *
+    * Code: Performs an unsigned shift right long operation. Uses no substitutions.
+    *
+    * For: ushr-long
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where one is the shift amount and the other is the value to shift.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_CC    1, %eax                 # %eax<- CC
+    FETCH_BB    1, %edx                 # %edx<- BB
+    movsd        .LshiftMask, %xmm2     # %xmm2<- mask for the shift bits
+    movss       (rFP, %eax, 4), %xmm0   # %xmm0<- vCC
+    pand        %xmm2, %xmm0            # %xmm0<- masked shift bits
+    movsd       (rFP, %edx, 4), %xmm1   # %xmm1<- vBB
+    psrlq       %xmm0, %xmm1            # %xmm1<- shifted vBB
+    movsd       %xmm1, (rFP, rINST, 4)  # vAA<- shifted vBB
+    FINISH      2                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: x86-atom/OP_ADD_FLOAT.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.
+    */
+
+   /*
+    * File: OP_ADD_FLOAT.S
+    */
+
+/* File: x86-atom/binopF.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.
+    */
+
+   /*
+    * File: binopF.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-float, mul-float, sub-float
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movss       (rFP, %ecx, 4), %xmm0   # %xmm0<-vBB
+    movss       (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    addss     %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movss       %xmm0, (rFP, rINST, 4)  # vAA<- %xmm0; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: x86-atom/OP_SUB_FLOAT.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.
+    */
+
+   /*
+    * File: OP_SUB_FLOAT.S
+    */
+
+/* File: x86-atom/binopF.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.
+    */
+
+   /*
+    * File: binopF.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-float, mul-float, sub-float
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movss       (rFP, %ecx, 4), %xmm0   # %xmm0<-vBB
+    movss       (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    subss     %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movss       %xmm0, (rFP, rINST, 4)  # vAA<- %xmm0; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: x86-atom/OP_MUL_FLOAT.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.
+    */
+
+   /*
+    * File: OP_MUL_FLOAT.S
+    */
+
+/* File: x86-atom/binopF.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.
+    */
+
+   /*
+    * File: binopF.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-float, mul-float, sub-float
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movss       (rFP, %ecx, 4), %xmm0   # %xmm0<-vBB
+    movss       (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    mulss %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movss       %xmm0, (rFP, rINST, 4)  # vAA<- %xmm0; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: x86-atom/OP_DIV_FLOAT.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.
+    */
+
+   /*
+    * File: OP_DIV_FLOAT.S
+    *
+    * Code: Divides floats. Uses no substitutions.
+    *
+    * For: div-float
+    *
+    * Description: Divide operation on two source registers, storing
+    *              the result in a destiniation register
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    flds        (rFP, %eax, 4)          # floating point stack vBB
+    fdivs       (rFP, %ecx, 4)          # divide double; vBB/vCC
+    fstps       (rFP, rINST, 4)         # vAA<- result
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: x86-atom/OP_REM_FLOAT.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.
+    */
+
+   /*
+    * File: OP_REM_FLOAT.S
+    *
+    * Code: Computes the remainder of a division. Performs no substitutions.
+    *
+    * For: rem-float
+    *
+    * Description: Calls fmod to compute the remainder of the result of dividing a
+    *              source register by a second, and stores the result in a
+    *              destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    movl        %ecx, -8(%esp)          # push parameter float
+    movl        %edx, -4(%esp)          # push parameter float
+    lea         -8(%esp), %esp
+    call        fmodf                   # call: (float x, float y)
+                                        # return: float
+    lea         8(%esp), %esp
+    fstps       (rFP, rINST, 4)         # vAA<- remainder; return of fmod
+    FINISH      2                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: x86-atom/OP_ADD_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_ADD_DOUBLE.S
+    */
+
+/* File: x86-atom/binopWide.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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    addsd   %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: x86-atom/OP_SUB_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_SUB_DOUBLE.S
+    */
+
+/* File: x86-atom/binopWide.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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    subsd   %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: x86-atom/OP_MUL_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_MUL_DOUBLE.S
+    */
+
+/* File: x86-atom/binopWide.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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    mulsd   %xmm1, %xmm0                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: x86-atom/OP_DIV_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_DIV_DOUBLE.S
+    *
+    * Code: Divides doubles. Uses no substitutions.
+    *
+    * For: div-double
+    *
+    * Description: Divide operation on two source registers, storing
+    *              the result in a destination register
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    fldl        (rFP, %ecx, 4)          # floating point stack vBB
+    fdivl       (rFP, %edx, 4)          # divide double; vBB/vCC
+    fstpl       (rFP, rINST, 4)         # vAA<- result
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: x86-atom/OP_REM_DOUBLE.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.
+    */
+
+   /*
+    * File: OP_REM_DOUBLE.S
+    *
+    * Code: Computes the remainder of a division. Performs no substitutions.
+    *
+    * For: rem-double
+    *
+    * Description: Calls fmod to compute the remainder of the result of dividing a
+    *              source register by a second, and stores the result in a
+    *              destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    movl        (rFP, %ecx, 4), %eax    # %eax<- vBBlo
+    movl        %eax, -16(%esp)         # push parameter double lo
+    movl        4(rFP, %ecx, 4), %eax   # %eax<- vBBhi
+    movl        %eax, -12(%esp)         # push parameter double hi
+    movl        (rFP, %edx, 4), %eax    # %eax<- vCClo
+    movl        %eax, -8(%esp)          # push parameter double lo
+    movl        4(rFP, %edx, 4), %eax   # %eax<- vCChi
+    movl        %eax, -4(%esp)          # push parameter double hi
+    lea         -16(%esp), %esp
+    jmp         .LOP_REM_DOUBLE_break
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: x86-atom/OP_ADD_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_ADD_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binop2addr.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.
+    */
+
+   /*
+    * File: binop2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%ecx = %ecx op %edx".
+    *
+    * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+    *      sub-int/2addr, xor-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %ecx             # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    GET_VREG    %ecx                    # %ecx<- vA
+    addl     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: x86-atom/OP_SUB_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_SUB_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binop2addr.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.
+    */
+
+   /*
+    * File: binop2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%ecx = %ecx op %edx".
+    *
+    * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+    *      sub-int/2addr, xor-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %ecx             # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    GET_VREG    %ecx                    # %ecx<- vA
+    subl     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: x86-atom/OP_MUL_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_MUL_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binop2addr.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.
+    */
+
+   /*
+    * File: binop2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%ecx = %ecx op %edx".
+    *
+    * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+    *      sub-int/2addr, xor-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %ecx             # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    GET_VREG    %ecx                    # %ecx<- vA
+    imul     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: x86-atom/OP_DIV_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_DIV_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binopD2addr.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.
+    */
+
+   /*
+    * File: binopD2addr.S
+    *
+    * Code: 32-bit "/2addr" integer divde operation. If "div"
+    *       is set, the code returns the quotient, else it returns
+    *       the remainder. Also, a divide-by-zero check is done.
+    *
+    * For: div-int/2addr, rem-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $15, rINST             # rINST<- A, to be used as dest
+    movl        rINST, %eax             # %eax<- A
+    shr         $4, %ecx               # %ecx<- B
+    GET_VREG    %eax                    # %eax<- vA
+    GET_VREG    %ecx                    # %edx<- vB
+    cmp         $0, %ecx               # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    cmpl        $-1, %ecx              # handle -1 special case divide error
+    jne         .LOP_DIV_INT_2ADDR_noerror
+    cmpl        $0x80000000,%eax       # handle min int special case divide error
+    je         .LOP_DIV_INT_2ADDR_break
+.LOP_DIV_INT_2ADDR_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+     .if  1
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .LOP_DIV_INT_2ADDR_break2
+    #FFETCH_ADV 1, %edx  # %ecx<- next instruction hi; fetch, advance
+    #FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: x86-atom/OP_REM_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_REM_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binopD2addr.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.
+    */
+
+   /*
+    * File: binopD2addr.S
+    *
+    * Code: 32-bit "/2addr" integer divde operation. If "div"
+    *       is set, the code returns the quotient, else it returns
+    *       the remainder. Also, a divide-by-zero check is done.
+    *
+    * For: div-int/2addr, rem-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $15, rINST             # rINST<- A, to be used as dest
+    movl        rINST, %eax             # %eax<- A
+    shr         $4, %ecx               # %ecx<- B
+    GET_VREG    %eax                    # %eax<- vA
+    GET_VREG    %ecx                    # %edx<- vB
+    cmp         $0, %ecx               # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    cmpl        $-1, %ecx              # handle -1 special case divide error
+    jne         .LOP_REM_INT_2ADDR_noerror
+    cmpl        $0x80000000,%eax       # handle min int special case divide error
+    je         .LOP_REM_INT_2ADDR_break
+.LOP_REM_INT_2ADDR_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+     .if  0
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .LOP_REM_INT_2ADDR_break2
+    #FFETCH_ADV 1, %edx  # %ecx<- next instruction hi; fetch, advance
+    #FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: x86-atom/OP_AND_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_AND_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binop2addr.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.
+    */
+
+   /*
+    * File: binop2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%ecx = %ecx op %edx".
+    *
+    * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+    *      sub-int/2addr, xor-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %ecx             # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    GET_VREG    %ecx                    # %ecx<- vA
+    andl     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: x86-atom/OP_OR_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_OR_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binop2addr.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.
+    */
+
+   /*
+    * File: binop2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%ecx = %ecx op %edx".
+    *
+    * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+    *      sub-int/2addr, xor-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %ecx             # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    GET_VREG    %ecx                    # %ecx<- vA
+    or %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: x86-atom/OP_XOR_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_XOR_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binop2addr.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.
+    */
+
+   /*
+    * File: binop2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%ecx = %ecx op %edx".
+    *
+    * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+    *      sub-int/2addr, xor-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %ecx             # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    GET_VREG    %ecx                    # %ecx<- vA
+    xor     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: x86-atom/OP_SHL_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_SHL_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binopS2addr.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.
+    */
+
+   /*
+    * File: binopS2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%edx = %edx op %cl".
+    *
+    * For: shl-int/2addr, shr-int/2addr, ushr-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %edx             # %edx<- A
+    FFETCH_ADV  1, %eax                 # %ecx<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    GET_VREG    %edx                    # %edx<- vA
+    sal     %cl, %edx                              # %edx<- vA op vB
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: x86-atom/OP_SHR_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_SHR_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binopS2addr.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.
+    */
+
+   /*
+    * File: binopS2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%edx = %edx op %cl".
+    *
+    * For: shl-int/2addr, shr-int/2addr, ushr-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %edx             # %edx<- A
+    FFETCH_ADV  1, %eax                 # %ecx<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    GET_VREG    %edx                    # %edx<- vA
+    sar     %cl, %edx                              # %edx<- vA op vB
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: x86-atom/OP_USHR_INT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_USHR_INT_2ADDR.S
+    */
+
+/* File: x86-atom/binopS2addr.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.
+    */
+
+   /*
+    * File: binopS2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%edx = %edx op %cl".
+    *
+    * For: shl-int/2addr, shr-int/2addr, ushr-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    movl        rINST, %edx             # %edx<- A
+    FFETCH_ADV  1, %eax                 # %ecx<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    GET_VREG    %edx                    # %edx<- vA
+    shr     %cl, %edx                              # %edx<- vA op vB
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: x86-atom/OP_ADD_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_ADD_LONG_2ADDR.S
+    */
+
+/* File: x86-atom/binopWide2addr.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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    paddq   %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: x86-atom/OP_SUB_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_SUB_LONG_2ADDR.S
+    */
+
+/* File: x86-atom/binopWide2addr.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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    psubq    %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: x86-atom/OP_MUL_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_MUL_LONG_2ADDR.S
+    *
+    * Code:  64-bit integer multiply
+    *
+    * For: mul-long/2addr
+    *
+    * Description: Multiply two sources registers and store the result
+    *              in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+   /*
+    * 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.
+    */
+
+    movl        rINST, %edx             # %edx<- BA+
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movl        %edx, sReg0             # sReg0<- A
+    jmp         .LOP_MUL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: x86-atom/OP_DIV_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_DIV_LONG_2ADDR.S
+    */
+
+/* File: x86-atom/binopDivRemLong2Addr.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.
+    */
+
+   /*
+    * File: binopDivRemLong2Addr.S
+    *
+    * Code: 64-bit "/2addr" long divide operation. Variable
+    *       "func" defines the function called to do the operation.
+    *
+    * For: div-long/2addr, rem-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    and         $15, rINST             # rINST<- A
+    movl        (rFP, %edx, 4), %eax    # %eax<- vB
+    movl        %eax, -12(%esp)         # push arg vB
+    movl        4(rFP, %edx, 4), %ecx   # %ecx<- vB+1
+    or          %ecx, %eax              # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    movl        %ecx, -8(%esp)          # push arg vB+1
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vA,vA+1
+    jmp         .LOP_DIV_LONG_2ADDR_break
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: x86-atom/OP_REM_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_REM_LONG_2ADDR.S
+    */
+
+/* File: x86-atom/binopDivRemLong2Addr.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.
+    */
+
+   /*
+    * File: binopDivRemLong2Addr.S
+    *
+    * Code: 64-bit "/2addr" long divide operation. Variable
+    *       "func" defines the function called to do the operation.
+    *
+    * For: div-long/2addr, rem-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    and         $15, rINST             # rINST<- A
+    movl        (rFP, %edx, 4), %eax    # %eax<- vB
+    movl        %eax, -12(%esp)         # push arg vB
+    movl        4(rFP, %edx, 4), %ecx   # %ecx<- vB+1
+    or          %ecx, %eax              # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    movl        %ecx, -8(%esp)          # push arg vB+1
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vA,vA+1
+    jmp         .LOP_REM_LONG_2ADDR_break
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: x86-atom/OP_AND_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_AND_LONG_2ADDR.S
+    */
+
+/* File: x86-atom/binopWide2addr.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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    pand   %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: x86-atom/OP_OR_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_OR_LONG_2ADDR.S
+    */
+
+/* File: x86-atom/binopWide2addr.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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    por %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: x86-atom/OP_XOR_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_XOR_LONG_2ADDR.S
+    */
+
+/* File: x86-atom/binopWide2addr.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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    pxor   %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: x86-atom/OP_SHL_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_SHL_LONG_2ADDR.S
+    *
+    * Code: Performs a shift left long. Uses no substitutions.
+    *
+    * For: shl-long/2addr
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where the fist is the value to shift and the second is the
+    *              shift amount. Store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    movss       (rFP, %edx, 4),  %xmm0  # %xmm0<- vB
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vA
+    movq        .LshiftMask, %xmm2      # %xmm2<- mask for the shift bits
+    pand        %xmm2, %xmm0            # %xmm0<- masked shift bits
+    psllq       %xmm0, %xmm1            # %xmm1<- shifted vA
+    movq        %xmm1, (rFP, rINST, 4)  # vA<- shifted vA
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: x86-atom/OP_SHR_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_SHR_LONG_2ADDR.S
+    *
+    * Code: Performs a shift left long
+    *
+    * For: shl-long/2addr
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where the fist is the value to shift and the second is the
+    *              shift amount. Store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- BA
+    movss       (rFP, %edx, 4),  %xmm0  # %xmm0<- vB
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vA
+    movq        .LshiftMask, %xmm2
+    pand        %xmm2, %xmm0            # %xmm0<- masked for the shift bits
+    psrlq       %xmm0, %xmm1            # %xmm1<- shifted vBB
+    cmpl        $0, 4(rFP, rINST, 4)   # check if we need to consider sign
+    jl          .LOP_SHR_LONG_2ADDR_finish      # consider sign
+    jmp         .LOP_SHR_LONG_2ADDR_final       # sign is fine, finish
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: x86-atom/OP_USHR_LONG_2ADDR.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.
+    */
+
+   /*
+    * File: OP_USHR_LONG_2ADDR.S
+    *
+    * Code: Performs an unsigned shift right long operation. Uses no substiutions.
+    *
+    * For: ushr-long/2addr
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where the fist is the value to shift and the second is the
+    *              shift amount. Store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    movq        .LshiftMask, %xmm2      # %xmm2<- mask for the shift bits
+    movss       (rFP, %edx, 4),  %xmm0  # %xmm0<- vB
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vA
+    pand        %xmm2, %xmm0            # %xmm0<- masked shift bits
+    psrlq       %xmm0, %xmm1            # %xmm1<- shifted vA
+    movq        %xmm1, (rFP, rINST, 4)  # vA<- shifted vA
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: x86-atom/OP_ADD_FLOAT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_ADD_FLOAT_2ADDR.S
+    */
+
+/* File: x86-atom/binopF2addr.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.
+    */
+
+   /*
+    * File: binopF2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0 = %xmm0 op %xmm1".
+    *
+    * For: add-float/2addr, mul-float/2addr, sub-float/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $15, %ecx              # %ecx<- A
+    shr         $4, rINST              # rINST<- B
+    FFETCH_ADV  1, %edx                 # %ecx<- next instruction hi; fetch, advance
+    movss       (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    movss       (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    addss     %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movss       %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: x86-atom/OP_SUB_FLOAT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_SUB_FLOAT_2ADDR.S
+    */
+
+/* File: x86-atom/binopF2addr.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.
+    */
+
+   /*
+    * File: binopF2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0 = %xmm0 op %xmm1".
+    *
+    * For: add-float/2addr, mul-float/2addr, sub-float/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $15, %ecx              # %ecx<- A
+    shr         $4, rINST              # rINST<- B
+    FFETCH_ADV  1, %edx                 # %ecx<- next instruction hi; fetch, advance
+    movss       (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    movss       (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    subss     %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movss       %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: x86-atom/OP_MUL_FLOAT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_MUL_FLOAT_2ADDR.S
+    */
+
+/* File: x86-atom/binopF2addr.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.
+    */
+
+   /*
+    * File: binopF2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0 = %xmm0 op %xmm1".
+    *
+    * For: add-float/2addr, mul-float/2addr, sub-float/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $15, %ecx              # %ecx<- A
+    shr         $4, rINST              # rINST<- B
+    FFETCH_ADV  1, %edx                 # %ecx<- next instruction hi; fetch, advance
+    movss       (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    movss       (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    mulss %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movss       %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: x86-atom/OP_DIV_FLOAT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_DIV_FLOAT_2ADDR.S
+    *
+    * Code: Divides floats. Uses no substitutions.
+    *
+    * For: div-float/2addr
+    *
+    * Description: Divide operation on two source registers, storing
+    *              the result in the first source reigster
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $15, %ecx              # %ecx<- A
+    shr         $4, rINST              # rINST<- B
+    flds        (rFP, %ecx, 4)          # %xmm0<- vA
+    fdivs       (rFP, rINST, 4)         # divide double; vA/vB
+    fstps       (rFP, %ecx, 4)          # vAA<- result
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: x86-atom/OP_REM_FLOAT_2ADDR.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.
+    */
+
+   /*
+    * File: OP_REM_FLOAT_2ADDR.S
+    *
+    * Code: Computes the remainder of a division. Performs no substitutions.
+    *
+    * For: rem-float/2addr
+    *
+    * Description: Calls fmod to compute the remainder of the result of dividing a
+    *              source register by a second, and stores the result in the first
+    *              source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB
+    movl        (rFP, rINST, 4), %ecx   # %ecx<- vA
+    movl        %ecx, -8(%esp)          # push parameter vA
+    movl        %edx, -4(%esp)          # push parameter vB
+    lea         -8(%esp), %esp
+    call        fmodf                   # call: (float x, float y)
+                                        # return: float
+    lea         8(%esp), %esp
+    fstps       (rFP, rINST, 4)
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: x86-atom/OP_ADD_DOUBLE_2ADDR.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.
+    */
+
+   /*
+    * File: OP_ADD_DOUBLE_2ADDR.S
+    */
+
+/* File: x86-atom/binopWide2addr.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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    addsd   %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: x86-atom/OP_SUB_DOUBLE_2ADDR.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.
+    */
+
+   /*
+    * File: OP_SUB_DOUBLE_2ADDR.S
+    */
+
+/* File: x86-atom/binopWide2addr.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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    subsd   %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: x86-atom/OP_MUL_DOUBLE_2ADDR.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.
+    */
+
+   /*
+    * File: OP_MUL_DOUBLE_2ADDR.S
+    */
+
+/* File: x86-atom/binopWide2addr.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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, rINST              # rINST<- B
+    andl        $15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    mulsd   %xmm1, %xmm0                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: x86-atom/OP_DIV_DOUBLE_2ADDR.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.
+    */
+
+   /*
+    * File: OP_DIV_DOUBLE_2ADDR.S
+    *
+    * Code: Divides doubles. Uses no substitutions.
+    *
+    * For: div-double/2addr
+    *
+    * Description: Divide operation on two source registers, storing
+    *              the result in the first source reigster
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    andl        $15, %edx              # %edx<- A
+    shr         $4, rINST              # rINST<- B
+    fldl        (rFP, %edx, 4)          # %xmm0<- vA
+    fdivl       (rFP, rINST, 4)         # divide double; vA/vB
+    fstpl       (rFP, %edx, 4)          # vAA<- result
+    FINISH      1                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: x86-atom/OP_REM_DOUBLE_2ADDR.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.
+    */
+
+   /*
+    * File: OP_REM_DOUBLE_2ADDR.S
+    *
+    * Code: Computes the remainder of a division. Performs no substitutions.
+    *
+    * For: rem-double/2addr
+    *
+    * Description: Calls fmod to compute the remainder of the result of dividing a
+    *              source register by a second, and stores the result in the first
+    *              source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    and         $15, rINST             # rINST<- A
+    shr         $4, %edx               # %edx<- B
+    movl        (rFP, rINST, 4), %eax   # %eax<- vAlo
+    movl        %eax, -20(%esp)         # push parameter vAAlo
+    movl        4(rFP, rINST, 4), %eax  # %eax<- vAhi
+    movl        %eax, -16(%esp)         # push parameter vAAhi
+    movl        (rFP, %edx, 4), %eax    # %eax<- vBlo
+    movl        %eax, -12(%esp)         # push parameter vBBlo
+    movl        4(rFP, %edx, 4), %eax   # %eax<- vBhi
+    movl        %eax, -8(%esp)          # push parameter vBBhi
+    lea         -20(%esp), %esp
+    jmp         .LOP_REM_DOUBLE_2ADDR_break
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: x86-atom/OP_ADD_INT_LIT16.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.
+    */
+
+   /*
+    * File: OP_ADD_INT_LIT16.S
+    */
+
+/* File: x86-atom/binopLit16.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.
+    */
+
+   /*
+    * File: binopLit16.S
+    *
+    * Code: 32-bit "lit16" operation. Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+    *      xor-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value and store the
+    *              result in a destination register.
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    FETCHs      1, %edx                 # %edx<- +CCCC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    addl     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: x86-atom/OP_RSUB_INT.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.
+    */
+
+   /*
+    * File: OP_RSUB_INT.S
+    *
+    * Code: 32-bit reverse-subtraction. Uses no substitutions.
+    *
+    * For: rsub-int
+    *
+    * Description: Perform a reverse subtraction on a register and a
+    *              signed extended 16-bit literal value and store the
+    *              result in a destination register.
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    FETCHs      1, %edx                 # %edx<- +CCCC, sign-extended literal
+    GET_VREG    %ecx                    # %ecx<- vB
+    subl        %ecx, %edx              # %edx<- +CCCC sub vB
+    SET_VREG    %edx, rINST             # vA<- %edx; result
+    FINISH      2                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: x86-atom/OP_MUL_INT_LIT16.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.
+    */
+
+   /*
+    * File: OP_MUL_INT_LIT16.S
+    */
+
+/* File: x86-atom/binopLit16.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.
+    */
+
+   /*
+    * File: binopLit16.S
+    *
+    * Code: 32-bit "lit16" operation. Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+    *      xor-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value and store the
+    *              result in a destination register.
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    FETCHs      1, %edx                 # %edx<- +CCCC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    imul     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: x86-atom/OP_DIV_INT_LIT16.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.
+    */
+
+   /*
+    * File: OP_DIV_INT_LIT16.S
+    */
+
+/* File: x86-atom/binopDLit16.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.
+    */
+
+   /*
+    * File: binopDLit16.S
+    *
+    * Code: 32-bit "lit16" divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int/lit16, rem-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $4, %eax               # %eax<- B
+    FETCHs      1, %ecx                 # %ecx<- +CCCC, sign-extended literal
+    cmp         $0, %ecx               # check for divide by zero
+    GET_VREG    %eax                    # %eax<- vB
+    je          common_errDivideByZero  # handle divide by zero
+    andl        $15, rINST             # rINST<- A
+    cmpl        $-1, %ecx              # handle -1 special case divide error
+    jne         .LOP_DIV_INT_LIT16_noerror
+    cmpl        $0x80000000,%eax       # handle min int special case divide error
+    je         .LOP_DIV_INT_LIT16_break
+.LOP_DIV_INT_LIT16_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    #FFETCH_ADV 2, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    .if  1
+    SET_VREG    %eax rINST              # vA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vA<- %edx (remainder)
+    .endif
+    jmp         .LOP_DIV_INT_LIT16_break2
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: x86-atom/OP_REM_INT_LIT16.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.
+    */
+
+   /*
+    * File: OP_REM_INT_LIT16.S
+    */
+
+/* File: x86-atom/binopDLit16.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.
+    */
+
+   /*
+    * File: binopDLit16.S
+    *
+    * Code: 32-bit "lit16" divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int/lit16, rem-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $4, %eax               # %eax<- B
+    FETCHs      1, %ecx                 # %ecx<- +CCCC, sign-extended literal
+    cmp         $0, %ecx               # check for divide by zero
+    GET_VREG    %eax                    # %eax<- vB
+    je          common_errDivideByZero  # handle divide by zero
+    andl        $15, rINST             # rINST<- A
+    cmpl        $-1, %ecx              # handle -1 special case divide error
+    jne         .LOP_REM_INT_LIT16_noerror
+    cmpl        $0x80000000,%eax       # handle min int special case divide error
+    je         .LOP_REM_INT_LIT16_break
+.LOP_REM_INT_LIT16_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    #FFETCH_ADV 2, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    .if  0
+    SET_VREG    %eax rINST              # vA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vA<- %edx (remainder)
+    .endif
+    jmp         .LOP_REM_INT_LIT16_break2
+
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: x86-atom/OP_AND_INT_LIT16.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.
+    */
+
+   /*
+    * File: OP_AND_INT_LIT16.S
+    */
+
+/* File: x86-atom/binopLit16.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.
+    */
+
+   /*
+    * File: binopLit16.S
+    *
+    * Code: 32-bit "lit16" operation. Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+    *      xor-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value and store the
+    *              result in a destination register.
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    FETCHs      1, %edx                 # %edx<- +CCCC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    andl     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: x86-atom/OP_OR_INT_LIT16.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.
+    */
+
+   /*
+    * File: OP_OR_INT_LIT16.S
+    */
+
+/* File: x86-atom/binopLit16.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.
+    */
+
+   /*
+    * File: binopLit16.S
+    *
+    * Code: 32-bit "lit16" operation. Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+    *      xor-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value and store the
+    *              result in a destination register.
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    FETCHs      1, %edx                 # %edx<- +CCCC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    or %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: x86-atom/OP_XOR_INT_LIT16.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.
+    */
+
+   /*
+    * File: OP_XOR_INT_LIT16.S
+    */
+
+/* File: x86-atom/binopLit16.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.
+    */
+
+   /*
+    * File: binopLit16.S
+    *
+    * Code: 32-bit "lit16" operation. Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+    *      xor-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value and store the
+    *              result in a destination register.
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    andl        $15, rINST             # rINST<- A
+    FETCHs      1, %edx                 # %edx<- +CCCC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    xor     %edx, %ecx                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: x86-atom/OP_ADD_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_ADD_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopLit8.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.
+    */
+
+   /*
+    * File: binopLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs  "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+    *      xor-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CCs   1, %edx                 # %edx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    addl     %edx, %ecx                              # %ecx<- vBB op +CC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: x86-atom/OP_RSUB_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_RSUB_INT_LIT8.S
+    *
+    * Code: 32-bit reverse-subtraction. Uses no substitutions.
+    *
+    * For: rsub-int/lit8
+    *
+    * Description: Perform a reverse subtraction on a register and a
+    *              signed extended 8-bit literal value.
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CCs   1, %edx                 # %edx<- +CC, sign-extended literal
+    GET_VREG    %ecx                    # %ecx<- vBB
+    sub         %ecx, %edx              # %edx<- +CC sub vBB
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FINISH      2                       # jump to next instruction
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: x86-atom/OP_MUL_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_MUL_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopLit8.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.
+    */
+
+   /*
+    * File: binopLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs  "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+    *      xor-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CCs   1, %edx                 # %edx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    imul     %edx, %ecx                              # %ecx<- vBB op +CC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: x86-atom/OP_DIV_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_DIV_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopDLit8.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.
+    */
+
+   /*
+    * File: binopDLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int/lit8, rem-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signe extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CCs   1, %ecx                 # %ecx<- +CC, sign-extended literal
+    cmp         $0, %ecx               # check for divide by zero
+    GET_VREG    %eax                    # %eax<- vBB
+    je          common_errDivideByZero  # handle divide by zero
+
+    cmpl        $-1, %ecx              # handle -1 special case divide error
+    jne         .LOP_DIV_INT_LIT8_noerror
+    cmpl        $0x80000000,%eax       # handle min int special case divide error
+    je         .LOP_DIV_INT_LIT8_break
+.LOP_DIV_INT_LIT8_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    #FFETCH_ADV 2, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    .if  1
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .LOP_DIV_INT_LIT8_break2
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: x86-atom/OP_REM_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_REM_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopDLit8.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.
+    */
+
+   /*
+    * File: binopDLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int/lit8, rem-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signe extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CCs   1, %ecx                 # %ecx<- +CC, sign-extended literal
+    cmp         $0, %ecx               # check for divide by zero
+    GET_VREG    %eax                    # %eax<- vBB
+    je          common_errDivideByZero  # handle divide by zero
+
+    cmpl        $-1, %ecx              # handle -1 special case divide error
+    jne         .LOP_REM_INT_LIT8_noerror
+    cmpl        $0x80000000,%eax       # handle min int special case divide error
+    je         .LOP_REM_INT_LIT8_break
+.LOP_REM_INT_LIT8_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    #FFETCH_ADV 2, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    .if  0
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .LOP_REM_INT_LIT8_break2
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: x86-atom/OP_AND_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_AND_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopLit8.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.
+    */
+
+   /*
+    * File: binopLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs  "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+    *      xor-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CCs   1, %edx                 # %edx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    andl     %edx, %ecx                              # %ecx<- vBB op +CC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: x86-atom/OP_OR_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_OR_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopLit8.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.
+    */
+
+   /*
+    * File: binopLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs  "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+    *      xor-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CCs   1, %edx                 # %edx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    or %edx, %ecx                              # %ecx<- vBB op +CC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: x86-atom/OP_XOR_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_XOR_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopLit8.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.
+    */
+
+   /*
+    * File: binopLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs  "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+    *      xor-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CCs   1, %edx                 # %edx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    xor     %edx, %ecx                              # %ecx<- vBB op +CC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: x86-atom/OP_SHL_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_SHL_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopLit8S.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.
+    */
+
+   /*
+    * File: binopLit8S.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs "%edx = %edx op %cl"
+    *
+    *
+    * For: shl-int/lit8, shr-int/lit8, ushr-int/lit8
+    *
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CCs   1, %ecx                 # %ecx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vBB
+    sal     %cl, %edx                              # %edx<- vBB op +CC
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: x86-atom/OP_SHR_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_SHR_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopLit8S.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.
+    */
+
+   /*
+    * File: binopLit8S.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs "%edx = %edx op %cl"
+    *
+    *
+    * For: shl-int/lit8, shr-int/lit8, ushr-int/lit8
+    *
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CCs   1, %ecx                 # %ecx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vBB
+    sar     %cl, %edx                              # %edx<- vBB op +CC
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: x86-atom/OP_USHR_INT_LIT8.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.
+    */
+
+   /*
+    * File: OP_USHR_INT_LIT8.S
+    */
+
+/* File: x86-atom/binopLit8S.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.
+    */
+
+   /*
+    * File: binopLit8S.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs "%edx = %edx op %cl"
+    *
+    *
+    * For: shl-int/lit8, shr-int/lit8, ushr-int/lit8
+    *
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CCs   1, %ecx                 # %ecx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vBB
+    shr     %cl, %edx                              # %edx<- vBB op +CC
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_IGET_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_IPUT_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_SGET_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_SPUT_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_IGET_OBJECT_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_IGET_WIDE_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_IPUT_WIDE_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_SGET_WIDE_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_SPUT_WIDE_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_BREAKPOINT      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: x86-atom/OP_THROW_VERIFICATION_ERROR.S */
+   /* 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.
+    */
+
+   /*
+    * File: OP_THROW_VERIFICATION_ERROR.S
+    *
+    * Code:
+    *
+    * For: throw-verification-error
+    *
+    * Description: Throws an exception for an error discovered during verification.
+    *              The exception is indicated by AA with details provided by BBBB.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, ref@BBBB
+    */
+
+    movl        rGLUE, %edx                  # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %ecx   # %ecx<- glue->method
+    EXPORT_PC                                # in case an exception is thrown
+    FETCH       1, %eax                      # %eax<- BBBB
+    movl        %eax, -4(%esp)               # push parameter BBBB; ref
+    movl        rINST, -8(%esp)              # push parameter AA
+    movl        %ecx, -12(%esp)              # push parameter glue->method
+    lea         -12(%esp), %esp
+    call        dvmThrowVerificationError    # call: (const Method* method, int kind, int ref)
+    jmp         common_exceptionThrown       # failed; handle exception
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: x86-atom/OP_EXECUTE_INLINE.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.
+    */
+
+   /*
+    * File: OP_EXECUTE_INLINE.S
+    *
+    * Code: Executes a "native inline" instruction. Uses no substitutions.
+    *
+    * For: execute-inline
+    *
+    * Description: Executes a "native inline" instruction. This instruction
+    *              is generated by the optimizer.
+    *
+    * Format:
+    *
+    * Syntax: vAA, {vC, vD, vE, vF}, inline@BBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        rGLUE, %eax             # %eax<- MterpGlue pointer
+    addl        $offGlue_retval, %eax  # %eax<- &glue->retval
+    EXPORT_PC
+    shr         $4, rINST              # rINST<- B
+    movl        %eax, -8(%esp)          # push parameter glue->retval
+    lea         -24(%esp), %esp
+    jmp         .LOP_EXECUTE_INLINE_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_EXECUTE_INLINE_RANGE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: x86-atom/OP_INVOKE_DIRECT_EMPTY.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.
+    */
+
+   /*
+    * File: OP_INVOKE_DIRECT_EMPTY.S
+    *
+    * Code: Used as a no-op. Uses no substitutions.
+    *
+    * For: invoke-direct-empty
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    */
+
+    FINISH 3
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: x86-atom/OP_UNUSED_F1.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.
+    */
+
+   /*
+    * File: OP_UNUSED_F1.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: x86-atom/OP_IGET_QUICK.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.
+    */
+
+   /*
+    * File: OP_IGET_QUICK.S
+    *
+    * Code: Optimization for iget
+    *
+    * For: iget-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $4, %eax               # %eax<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB; object to operate on
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    cmp         $0, %eax               # check if object is null
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %edx                 # %eax<- next instruction hi; fetch, advance
+    movl        (%ecx, %eax), %eax      # %eax<- object field
+    SET_VREG    %eax, rINST             # fp[A]<- %eax
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: x86-atom/OP_IGET_WIDE_QUICK.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.
+    */
+
+   /*
+    * File: OP_IGET_WIDE_QUICK.S
+    *
+    * Code: Optimization for iget
+    *
+    * For: iget/wide-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB; object to operate on
+    cmp         $0, %edx               # check if object is null
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    je          common_errNullObject    # handle null object
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    movq        (%ecx, %edx), %xmm0     # %xmm0<- object field
+    movq        %xmm0, (rFP, rINST, 4)  # fp[A]<- %xmm0
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: x86-atom/OP_IGET_OBJECT_QUICK.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.
+    */
+
+   /*
+    * File: OP_IGET_OBJECT_QUICK.S
+    */
+
+/* File: x86-atom/OP_IGET_QUICK.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.
+    */
+
+   /*
+    * File: OP_IGET_QUICK.S
+    *
+    * Code: Optimization for iget
+    *
+    * For: iget-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $4, %eax               # %eax<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB; object to operate on
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    cmp         $0, %eax               # check if object is null
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %edx                 # %eax<- next instruction hi; fetch, advance
+    movl        (%ecx, %eax), %eax      # %eax<- object field
+    SET_VREG    %eax, rINST             # fp[A]<- %eax
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: x86-atom/OP_IPUT_QUICK.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.
+    */
+
+   /*
+    * File: OP_IPUT_QUICK.S
+    * Code: Optimization for iput
+    *
+    * For: iput-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $4, %eax               # %eax<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB; object to operate on
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    cmp         $0, %eax               # check if object is null
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl        rINST, (%eax, %ecx)     # object field<- vA
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: x86-atom/OP_IPUT_WIDE_QUICK.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.
+    */
+
+   /*
+    * File: OP_IPUT_WIDE_QUICK.S
+    *
+    * Code: Optimization for iput
+    *
+    * For: iput/wide-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB; object to operate on
+    cmp         $0, %edx               # check if object is null
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- fp[A]
+    movq        %xmm0, (%edx, %ecx)     # object field<- %xmm0; fp[A]
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: x86-atom/OP_IPUT_OBJECT_QUICK.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.
+    */
+
+   /*
+    * File: OP_IPUT_QUICK.S
+    * Code: Optimization for iput
+    *
+    * For: iput-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $4, %eax               # %eax<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB; object to operate on
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    cmp         $0, %eax               # check if object is null
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl        rINST, (%eax, %ecx)     # object field<- vA
+    testl       rINST, rINST            # did we write a null object
+    je          1f
+    movl        rGLUE, %ecx             # get glue
+    movl        offGlue_cardTable(%ecx), %ecx # get card table base
+    shrl        $GC_CARD_SHIFT, %eax   # get gc card index
+    movb        %cl, (%eax, %ecx)       # mark gc card in table
+1:
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: x86-atom/OP_INVOKE_VIRTUAL_QUICK.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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL_QUICK.S
+    *
+    * Code: Optimization for invoke-virtual and invoke-virtual/range
+    *
+    * For: invoke-virtual/quick, invoke-virtual/quick-range
+    */
+
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    .if (!0)
+    and         $15, %edx              # %edx<- D if not range
+    .endif
+    FETCH       1, %ecx                 # %ecx<- method index
+    GET_VREG    %edx                    # %edx<- "this" ptr
+    cmp         $0, %edx               # %edx<- check for null "this"
+    EXPORT_PC                           # must export pc for invoke
+    je          common_errNullObject
+    movl        offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+    movl        offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+    movl        (%edx, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethodNoRange # invoke method common code
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL_QUICK_RANGE.S
+    */
+
+/* File: x86-atom/OP_INVOKE_VIRTUAL_QUICK.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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL_QUICK.S
+    *
+    * Code: Optimization for invoke-virtual and invoke-virtual/range
+    *
+    * For: invoke-virtual/quick, invoke-virtual/quick-range
+    */
+
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    .if (!1)
+    and         $15, %edx              # %edx<- D if not range
+    .endif
+    FETCH       1, %ecx                 # %ecx<- method index
+    GET_VREG    %edx                    # %edx<- "this" ptr
+    cmp         $0, %edx               # %edx<- check for null "this"
+    EXPORT_PC                           # must export pc for invoke
+    je          common_errNullObject
+    movl        offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+    movl        offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+    movl        (%edx, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethodRange # invoke method common code
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: x86-atom/OP_INVOKE_SUPER_QUICK.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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER_QUICK.S
+    *
+    * Code: Optimization for invoke-super and invoke-super/range
+    *
+    * For: invoke-super/quick, invoke-super/quick-range
+    */
+
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_method(%ecx), %eax # %eax<- glue->method
+    .if         (!0)
+    and         $15, %edx              #  %edx<- D if not range
+    .endif
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    movl        offClassObject_super(%eax), %eax # %eax<- glue->method->clazz->super
+    EXPORT_PC                           # must export for invoke
+    movl        offClassObject_vtable(%eax), %eax # %edx<- glue->method->clazz->super->vtable
+    cmp         $0, (rFP, %edx, 4)     # check for null object
+    movl        (%eax, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    je          common_errNullObject    # handle null object
+    jmp         common_invokeMethodNoRange # invoke method common code
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER_QUICK_RANGE.S
+    */
+
+/* File: x86-atom/OP_INVOKE_SUPER_QUICK.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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER_QUICK.S
+    *
+    * Code: Optimization for invoke-super and invoke-super/range
+    *
+    * For: invoke-super/quick, invoke-super/quick-range
+    */
+
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_method(%ecx), %eax # %eax<- glue->method
+    .if         (!1)
+    and         $15, %edx              #  %edx<- D if not range
+    .endif
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    movl        offClassObject_super(%eax), %eax # %eax<- glue->method->clazz->super
+    EXPORT_PC                           # must export for invoke
+    movl        offClassObject_vtable(%eax), %eax # %edx<- glue->method->clazz->super->vtable
+    cmp         $0, (rFP, %edx, 4)     # check for null object
+    movl        (%eax, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    je          common_errNullObject    # handle null object
+    jmp         common_invokeMethodRange # invoke method common code
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_IPUT_OBJECT_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_SGET_OBJECT_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+   /* 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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_SPUT_OBJECT_VOLATILE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: x86-atom/OP_UNUSED_FF.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.
+    */
+
+   /*
+    * File: OP_UNUSED_FF.S
+    */
+
+/* File: x86-atom/unused.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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        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 Class has not yet been resolved.
+    *  %ecx: BBBB (Class ref)
+    *  need: target register
+    */
+
+.LOP_CONST_STRING_resolve:
+    EXPORT_PC
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %ecx, -4(%esp)          # push parameter class ref
+    movl        %edx, -8(%esp)          # push parameter glue->method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveString        # resolve string reference
+                                        # call: (const ClassObject* referrer, u4 stringIdx)
+                                        # return: StringObject*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if resolved string failed
+    je          common_exceptionThrown  # resolve failed; exception thrown
+    SET_VREG    %eax, rINST             # vAA<- %eax; pResString[BBBB]
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+
+   /*
+    * Continuation if the Class has not yet been resolved.
+    *  %ecx: BBBB (Class ref)
+    *  need: target register
+    */
+.LOP_CONST_STRING_JUMBO_resolve:
+    EXPORT_PC
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx <- glue->method->clazz
+    movl        %ecx, -4(%esp)          # push parameter class ref
+    movl        %edx, -8(%esp)          # push parameter glue->method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveString        # resolve string reference
+                                        # call: (const ClassObject* referrer, u4 stringIdx)
+                                        # return: StringObject*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if resolved string failed
+    je          common_exceptionThrown  # resolve failed; exception thrown
+    SET_VREG    %eax, rINST             # vAA<- %eax; pResString[BBBB]
+    FINISH      3                       # jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+   /*
+    * Continuation if the Class has not yet been resolved.
+    *  %ecx: BBBB (Class ref)
+    *  need: target register
+    */
+
+.LOP_CONST_CLASS_resolve:
+    EXPORT_PC
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        $1, -4(%esp)           # push parameter true
+    movl        %ecx, -8(%esp)          # push parameter
+    movl        %edx, -12(%esp)         # push parameter glue->method->clazz
+    lea         -12(%esp), %esp
+    call        dvmResolveClass         # resolve ClassObject pointer
+                                        # class: (const ClassObject* referrer, u4 classIdx,
+                                        #         bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         12(%esp), %esp
+    cmp         $0, %eax               # check for null pointer
+    je          common_exceptionThrown  # handle exception
+    SET_VREG    %eax, rINST             # vAA<- resolved class
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+.LOP_CHECK_CAST_resolved:
+    cmp         %ecx, offObject_clazz(rINST) # check for same class
+    jne         .LOP_CHECK_CAST_fullcheck   # not same class; do full check
+
+.LOP_CHECK_CAST_okay:
+    FINISH      2                       # jump to next instruction
+
+   /*
+    *  Trivial test failed, need to perform full check.
+    *  offObject_clazz(rINST) holds obj->clazz
+    *  %ecx holds class resolved from BBBB
+    *  rINST holds object
+    */
+
+.LOP_CHECK_CAST_fullcheck:
+    movl        offObject_clazz(rINST), %eax  # %eax<- obj->clazz
+    movl        %eax, -12(%esp)         # push parameter obj->clazz
+    movl        %ecx, -8(%esp)          # push parameter # push parameter resolved class
+    lea         -12(%esp), %esp
+    call        dvmInstanceofNonTrivial # call: (ClassObject* instance, ClassObject* clazz)
+                                        # return: int
+    lea         12(%esp), %esp
+    cmp         $0, %eax               # failed?
+    jne         .LOP_CHECK_CAST_okay        # success
+
+   /*
+    * A cast has failed.  We need to throw a ClassCastException with the
+    * class of the object that failed to be cast.
+    */
+
+    EXPORT_PC                           # we will throw an exception
+    movl        $.LstrClassCastExceptionPtr, -8(%esp) # push parameter message
+    movl        offObject_clazz(rINST), rINST # rINST<- obj->clazz
+    movl        offClassObject_descriptor(rINST), rINST # rINST<- obj->clazz->descriptor
+    movl        rINST, -4(%esp)         # push parameter obj->clazz->descriptor
+    lea         -8(%esp), %esp
+    call        dvmThrowExceptionWithClassMessage # call: (const char* exceptionDescriptor,
+                                                  #       const char* messageDescriptor, Object* cause)
+                                                  # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown
+
+   /*
+    * Resolution required.  This is the least-likely path.
+    *
+    *  rINST holds object
+    */
+
+.LOP_CHECK_CAST_resolve:
+    movl        offGlue_method(%edx), %eax # %eax<- glue->method
+    FETCH       1, %ecx                 # %ecx holds BBBB
+    EXPORT_PC                           # in case we throw an exception
+    movl        $0, -8(%esp)           # push parameter false
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    movl        %ecx, -12(%esp)         # push parameter BBBB
+    movl        %eax, -16(%esp)         # push parameter glue->method>clazz
+    lea         -16(%esp), %esp
+    call        dvmResolveClass         # resolve ClassObject pointer
+                                        # call: (const ClassObject* referrer, u4 classIdx,
+                                        #        bool fromUnverifiedConstant)
+                                        # return ClassObject*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null pointer
+    je          common_exceptionThrown  # handle excpetion
+    movl        %eax, %ecx              # %ecx<- resolved class
+    jmp         .LOP_CHECK_CAST_resolved
+
+.LstrClassCastExceptionPtr:
+.asciz      "Ljava/lang/ClassCastException;"
+
+/* continuation for OP_INSTANCE_OF */
+
+.LOP_INSTANCE_OF_break:
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- CCCC
+    movl        offDvmDex_pResClasses(%ecx), %ecx # %ecx<- pDvmDex->pResClasses
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved class
+    movl        offObject_clazz(%edx), %edx # %edx<- obj->clazz
+    cmp         $0, %ecx               # check if already resovled
+    je          .LOP_INSTANCE_OF_resolve     # not resolved before, so resolve now
+
+.LOP_INSTANCE_OF_resolved:
+    cmp         %ecx, %edx              # check if same class
+    je          .LOP_INSTANCE_OF_trivial     # yes, finish
+    jmp         .LOP_INSTANCE_OF_fullcheck   # no, do full check
+
+   /*
+    * The trivial test failed, we need to perform a full check.
+    * %edx holds obj->clazz
+    * %ecx holds class resolved from BBBB
+    */
+
+.LOP_INSTANCE_OF_fullcheck:
+    movl        %edx, -8(%esp)          # push parameter obj->clazz
+    movl        %ecx, -4(%esp)          # push parameter resolved class
+    lea         -8(%esp), %esp
+    call        dvmInstanceofNonTrivial # perform full check
+                                        # call: (ClassObject* instance, ClassObject* clazz)
+                                        # return: int
+    andl        $15, rINST             # rINST<- A
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    lea         8(%esp), %esp
+    SET_VREG    %eax, rINST             # vA<- r0
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+   /*
+    * %edx holds boolean result
+    */
+
+.LOP_INSTANCE_OF_store:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    andl        $15, rINST             # rINST<- A
+    SET_VREG    %edx, rINST             # vA<- r0
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+   /*
+    * Trivial test succeeded, save and bail.
+    */
+
+.LOP_INSTANCE_OF_trivial:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    andl        $15, rINST             # rINST<- A
+    SET_VREG    $1, rINST              # vA<- r0
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+   /*
+    * Resolution required.  This is the least-likely path.
+    * %eax holds BBBB
+    */
+
+.LOP_INSTANCE_OF_resolve:
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    EXPORT_PC
+    movl        offGlue_method(%ecx), %ecx # %ecx<- glue->method
+    movl        offMethod_clazz(%ecx), %ecx # %ecx<- glue->method->clazz
+    movl        %ecx, -12(%esp)         # push parameter glue->method->clazz
+    movl        %eax, -8(%esp)          # push parameter CCCC; type index
+    movl        $1, -4(%esp)           # push parameter true
+    lea         -12(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer, u4 classIdx,
+                                        #        bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         12(%esp), %esp
+    cmp         $0, %eax               # check for null
+    je          common_exceptionThrown  # handle exception
+    movl        rINST, %edx             # %edx<- BA+
+    shr         $4, %edx               # %edx<- B
+    movl        %eax, %ecx              # need class in %ecx
+    GET_VREG    %edx                    # %edx<- vB
+    movl        offObject_clazz(%edx), %edx # %edx<- obj->clazz
+    jmp         .LOP_INSTANCE_OF_resolved    # clazz resolved, continue
+
+/* continuation for OP_NEW_INSTANCE */
+.balign 32
+.LOP_NEW_INSTANCE_finish:
+    movl        %edx, -8(%esp)          # push parameter object
+    movl        %eax, -4(%esp)          # push parameter flags
+    lea         -8(%esp), %esp
+    call        dvmAllocObject          # call: (ClassObject* clazz, int flags)
+                                        # return: Object*
+    cmp         $0, %eax               # check for failure
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # handle exception
+    SET_VREG    %eax, rINST             # vAA<- pObject
+    FINISH      2                       # jump to next instruction
+
+   /*
+    * Class initialization required.
+    *
+    *  %edx holds class object
+    */
+
+.LOP_NEW_INSTANCE_needinit:
+    movl        %edx, -4(%esp)          # push parameter object
+    lea         -4(%esp), %esp
+    call        dvmInitClass            # call: (ClassObject* clazz)
+                                        # return: bool
+    lea         4(%esp), %esp
+    cmp         $0, %eax               # check for failure
+    movl        -4(%esp), %edx          # %edx<- object
+    je          common_exceptionThrown  # handle exception
+    testl       $(ACC_INTERFACE|ACC_ABSTRACT), offClassObject_accessFlags(%edx)
+    mov         $ALLOC_DONT_TRACK, %eax # %eax<- flag for alloc call
+    je          .LOP_NEW_INSTANCE_finish      # continue
+    jmp         .LOP_NEW_INSTANCE_abstract    # handle abstract or interface
+
+   /*
+    * Resolution required.  This is the least-likely path.
+    *
+    *  BBBB in %eax
+    */
+
+.LOP_NEW_INSTANCE_resolve:
+
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offGlue_method(%ecx), %ecx # %ecx<- glue->method
+    movl        offMethod_clazz(%ecx), %ecx # %ecx<- glue->method->clazz
+    movl        %ecx, -12(%esp)         # push parameter clazz
+    movl        $0, -4(%esp)           # push parameter false
+    movl        %eax, -8(%esp)          # push parameter BBBB
+    lea         -12(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer,
+                                        #       u4 classIdx, bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         12(%esp), %esp
+    movl        %eax, %edx              # %edx<- pObject
+    cmp         $0, %edx               # check for failure
+    jne         .LOP_NEW_INSTANCE_resolved    # continue
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * We can't instantiate an abstract class or interface, so throw an
+    * InstantiationError with the class descriptor as the message.
+    *
+    *  %edx holds class object
+    */
+
+.LOP_NEW_INSTANCE_abstract:
+    movl        offClassObject_descriptor(%edx), %ecx # %ecx<- descriptor
+    movl        %ecx, -4(%esp)          # push parameter descriptor
+    movl        $.LstrInstantiationErrorPtr, -8(%esp) # push parameter message
+    lea         -8(%esp), %esp
+    call        dvmThrowExceptionWithClassMessage # call: (const char* exceptionDescriptor,
+                                                  #        const char* messageDescriptor)
+                                                  # return: void
+    jmp         common_exceptionThrown  # handle exception
+
+.LstrInstantiationErrorPtr:
+.asciz      "Ljava/lang/InstantiationError;"
+
+/* continuation for OP_NEW_ARRAY */
+
+   /*
+    * Resolve class.  (This is an uncommon case.)
+    *
+    *  %edx holds array length
+    *  %ecx holds class ref CCCC
+    */
+
+.LOP_NEW_ARRAY_resolve:
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_method(%eax), %eax # %eax<- glue->method
+    movl        %edx, -4(%esp)          # save length
+    movl        $0, -8(%esp)           # push parameter false
+    movl        %ecx, -12(%esp)         # push parameter class ref
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    movl        %eax, -16(%esp)         # push parameter clazz
+    lea         -16(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer,
+                                        #       u4 classIdx, bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    cmp         $0, %eax               # check for failure
+    lea         16(%esp), %esp
+    je          common_exceptionThrown  # handle exception
+    movl        -4(%esp), %edx          # %edx<- length
+
+   /*
+    * Finish allocation.
+    *
+    *  %eax holds class
+    *  %edx holds array length
+    */
+
+.LOP_NEW_ARRAY_finish:
+    movl        %eax, -12(%esp)         # push parameter class
+    movl        %edx, -8(%esp)          # push parameter length
+    movl        $ALLOC_DONT_TRACK, -4(%esp)
+    lea         -12(%esp), %esp
+    call        dvmAllocArrayByClass    # call: (ClassObject* arrayClass,
+                                        # size_t length, int allocFlags)
+                                        # return: ArrayObject*
+    and         $15, rINST             # rINST<- A
+    cmp         $0, %eax               # check for allocation failure
+    lea         12(%esp), %esp
+    je          common_exceptionThrown  # handle exception
+    SET_VREG    %eax, rINST             # vA<- pArray
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+.LOP_FILLED_NEW_ARRAY_break:
+    movl        $0, -8(%esp)           # push parameter false
+    movl        %ecx, -12(%esp)         # push parameter BBBB
+    movl        rGLUE, %edx             # %edx<- MterpGlue pointer
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -16(%esp)         # push parameter glue->method->clazz
+    lea         -16(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer, u4 classIdx,
+                                        #        bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null return
+    je          common_exceptionThrown  # handle exception
+
+   /*
+    * On entry:
+    *  %eax holds array class
+    *  rINST holds BA or AA
+    */
+
+.LOP_FILLED_NEW_ARRAY_continue:
+    movl        offClassObject_descriptor(%eax), %eax # %eax<- arrayClass->descriptor
+    movzbl      1(%eax), %eax           # %eax<- descriptor[1]
+    cmpb        $'I', %al             # check if array of ints
+    je          1f
+    cmpb        $'L', %al
+    je          1f
+    cmpb        $'[', %al
+    jne         .LOP_FILLED_NEW_ARRAY_notimpl     # jump to not implemented
+1:
+    movl        %eax, sReg0             # save type
+    movl        rINST, -12(%esp)        # push parameter length
+    movl        %eax, -16(%esp)         # push parameter descriptor[1]
+    movl        $ALLOC_DONT_TRACK, -8(%esp) # push parameter to allocate flags
+    .if         (!0)
+    shrl        $4, -12(%esp)          # parameter length is B
+    .endif
+    lea         -16(%esp), %esp
+    call        dvmAllocPrimitiveArray  # call: (char type, size_t length, int allocFlags)
+                                        # return: ArrayObject*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null return
+    je          common_exceptionThrown  # handle exception
+
+    FETCH       2, %edx                 # %edx<- FEDC or CCCC
+    movl        rGLUE, %ecx             # %ecx<- MterpGlue pointer
+    movl        %eax, offGlue_retval(%ecx) # retval<- new array
+    lea         offArrayObject_contents(%eax), %eax # %eax<- newArray->contents
+    subl        $1, -12(%esp)          # length--; check for negative
+    js          2f                      # if length was zero, finish
+
+   /*
+    * copy values from registers into the array
+    * %eax=array, %edx=CCCC/FEDC, -12(%esp)=length (from AA or B), rINST=AA/BA
+    */
+
+    .if         0
+    lea         (rFP, %edx, 4), %ecx    # %ecx<- &fpp[CCCC]
+1:
+    movl        (%ecx), %edx            # %edx<- %ecx++
+    lea         4(%ecx), %ecx           # %ecx++
+    movl        %edx, (%eax)            # *contents<- vX
+    lea         4(%eax), %eax           # %eax++; contents++
+    subl        $1, -12(%esp)          # length--
+    jns         1b                      # or continue at 2
+    .else
+    cmp         $4, -12(%esp)          # check length
+    jne         1f                      # has four args
+    and         $15, rINST             # rINST<- A
+    GET_VREG    rINST                   # rINST<- vA
+    subl        $1, -12(%esp)          # count--
+    movl        rINST, 16(%eax)         # contents[4]<- vA
+1:
+    movl        %edx, %ecx              # %ecx<- %edx; ecx for temp
+    andl        $15, %ecx              # %ecx<- G/F/E/D
+    GET_VREG    %ecx                    # %ecx<- vG/vF/vE/vD
+    shr         $4, %edx               # %edx<- put next reg in low 4
+    subl        $1, -12(%esp)          # count--
+    movl        %ecx, (%eax)            # *contents<- vX
+    lea         4(%eax), %eax           # %eax++; contents++
+    jns         1b                      # or continue at 2
+    .endif
+2:
+    cmpb        $'I', sReg0            # check for int array
+    je          3f
+    movl        rGLUE, %ecx             # %ecx<- MterpGlue pointer
+    movl        offGlue_retval(%ecx), %eax # Object head
+    movl        offGlue_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
+3:
+    FINISH      3                       # jump to next instruction
+
+   /*
+    * Throw an exception to indicate this mode of filled-new-array
+    * has not been implemented.
+    */
+
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    movl        $.LstrInternalError, -8(%esp)
+    movl        $.LstrFilledNewArrayNotImpl, -4(%esp)
+    lea         -8(%esp), %esp
+    call        dvmThrowException # call: (const char* exceptionDescriptor,
+                                  #        const char* msg)
+                                  # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown
+
+.if         (!0)                 # define in one or the other, not both
+.LstrFilledNewArrayNotImpl:
+.asciz      "filled-new-array only implemented for 'int'"
+.LstrInternalError:
+.asciz  "Ljava/lang/InternalError;"
+.endif
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+.LOP_FILLED_NEW_ARRAY_RANGE_break:
+    movl        $0, -8(%esp)           # push parameter false
+    movl        %ecx, -12(%esp)         # push parameter BBBB
+    movl        rGLUE, %edx             # %edx<- MterpGlue pointer
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -16(%esp)         # push parameter glue->method->clazz
+    lea         -16(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer, u4 classIdx,
+                                        #        bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null return
+    je          common_exceptionThrown  # handle exception
+
+   /*
+    * On entry:
+    *  %eax holds array class
+    *  rINST holds BA or AA
+    */
+
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+    movl        offClassObject_descriptor(%eax), %eax # %eax<- arrayClass->descriptor
+    movzbl      1(%eax), %eax           # %eax<- descriptor[1]
+    cmpb        $'I', %al             # check if array of ints
+    je          1f
+    cmpb        $'L', %al
+    je          1f
+    cmpb        $'[', %al
+    jne         .LOP_FILLED_NEW_ARRAY_RANGE_notimpl     # jump to not implemented
+1:
+    movl        %eax, sReg0             # save type
+    movl        rINST, -12(%esp)        # push parameter length
+    movl        %eax, -16(%esp)         # push parameter descriptor[1]
+    movl        $ALLOC_DONT_TRACK, -8(%esp) # push parameter to allocate flags
+    .if         (!1)
+    shrl        $4, -12(%esp)          # parameter length is B
+    .endif
+    lea         -16(%esp), %esp
+    call        dvmAllocPrimitiveArray  # call: (char type, size_t length, int allocFlags)
+                                        # return: ArrayObject*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null return
+    je          common_exceptionThrown  # handle exception
+
+    FETCH       2, %edx                 # %edx<- FEDC or CCCC
+    movl        rGLUE, %ecx             # %ecx<- MterpGlue pointer
+    movl        %eax, offGlue_retval(%ecx) # retval<- new array
+    lea         offArrayObject_contents(%eax), %eax # %eax<- newArray->contents
+    subl        $1, -12(%esp)          # length--; check for negative
+    js          2f                      # if length was zero, finish
+
+   /*
+    * copy values from registers into the array
+    * %eax=array, %edx=CCCC/FEDC, -12(%esp)=length (from AA or B), rINST=AA/BA
+    */
+
+    .if         1
+    lea         (rFP, %edx, 4), %ecx    # %ecx<- &fpp[CCCC]
+1:
+    movl        (%ecx), %edx            # %edx<- %ecx++
+    lea         4(%ecx), %ecx           # %ecx++
+    movl        %edx, (%eax)            # *contents<- vX
+    lea         4(%eax), %eax           # %eax++; contents++
+    subl        $1, -12(%esp)          # length--
+    jns         1b                      # or continue at 2
+    .else
+    cmp         $4, -12(%esp)          # check length
+    jne         1f                      # has four args
+    and         $15, rINST             # rINST<- A
+    GET_VREG    rINST                   # rINST<- vA
+    subl        $1, -12(%esp)          # count--
+    movl        rINST, 16(%eax)         # contents[4]<- vA
+1:
+    movl        %edx, %ecx              # %ecx<- %edx; ecx for temp
+    andl        $15, %ecx              # %ecx<- G/F/E/D
+    GET_VREG    %ecx                    # %ecx<- vG/vF/vE/vD
+    shr         $4, %edx               # %edx<- put next reg in low 4
+    subl        $1, -12(%esp)          # count--
+    movl        %ecx, (%eax)            # *contents<- vX
+    lea         4(%eax), %eax           # %eax++; contents++
+    jns         1b                      # or continue at 2
+    .endif
+2:
+    cmpb        $'I', sReg0            # check for int array
+    je          3f
+    movl        rGLUE, %ecx             # %ecx<- MterpGlue pointer
+    movl        offGlue_retval(%ecx), %eax # Object head
+    movl        offGlue_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
+3:
+    FINISH      3                       # jump to next instruction
+
+   /*
+    * Throw an exception to indicate this mode of filled-new-array
+    * has not been implemented.
+    */
+
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    movl        $.LstrInternalError, -8(%esp)
+    movl        $.LstrFilledNewArrayNotImpl, -4(%esp)
+    lea         -8(%esp), %esp
+    call        dvmThrowException # call: (const char* exceptionDescriptor,
+                                  #        const char* msg)
+                                  # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown
+
+.if         (!1)                 # define in one or the other, not both
+.LstrFilledNewArrayNotImpl:
+.asciz      "filled-new-array only implemented for 'int'"
+.LstrInternalError:
+.asciz  "Ljava/lang/InternalError;"
+.endif
+
+/* continuation for OP_PACKED_SWITCH */
+.LOP_PACKED_SWITCH_finish:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+/* continuation for OP_SPARSE_SWITCH */
+.LOP_SPARSE_SWITCH_finish:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_greater:
+    movl        $0x1, (rFP, rINST, 4)  # vAA<- greater than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.LOP_CMPL_FLOAT_final:
+    movl        $0x0, (rFP, rINST, 4)  # vAA<- equal
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.LOP_CMPL_FLOAT_finalNan:
+    movl        $0xFFFFFFFF, (rFP, rINST, 4)   # vAA<- NaN
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_greater:
+    movl        $0x1, (rFP, rINST, 4)  # vAA<- greater than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.LOP_CMPG_FLOAT_final:
+    movl        $0x0, (rFP, rINST, 4)  # vAA<- equal
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.LOP_CMPG_FLOAT_finalNan:
+    movl        $0x1, (rFP, rINST, 4)   # vAA<- NaN
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_greater:
+    movl        $0x1, (rFP, rINST, 4)  # vAA<- greater than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.LOP_CMPL_DOUBLE_final:
+    movl        $0x0, (rFP, rINST, 4)  # vAA<- equal
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.LOP_CMPL_DOUBLE_finalNan:
+    movl        $0xFFFFFFFF, (rFP, rINST, 4)   # vAA<- NaN
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_greater:
+    movl        $0x1, (rFP, rINST, 4)  # vAA<- greater than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.LOP_CMPG_DOUBLE_final:
+    movl        $0x0, (rFP, rINST, 4)  # vAA<- equal
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.LOP_CMPG_DOUBLE_finalNan:
+    movl        $0x1, (rFP, rINST, 4)   # vAA<- NaN
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_final:
+    movl        $0x0, (rFP, rINST, 4)  # vAA<- equal
+    FINISH      2                       # jump to next instruction
+
+.LOP_CMP_LONG_less:
+    movl        $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+    FINISH      2                       # jump to next instruction
+
+.LOP_CMP_LONG_greater:
+    movl        $0x1, (rFP, rINST, 4)  # vAA<- greater than
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+
+.LOP_APUT_OBJECT_finish:
+    movl        %edx, sReg0             # save &vBB[vCC]
+    movl        %eax, sReg1             # save object head
+    movl        offObject_clazz(rINST), %edx # %edx<- obj->clazz
+    movl        %edx, -8(%esp)          # push parameter obj->clazz
+    movl        offObject_clazz(%eax), %eax # %eax<- arrayObj->clazz
+    movl        %eax, -4(%esp)          # push parameter arrayObj->clazz
+    lea         -8(%esp), %esp
+    call        dvmCanPutArrayElement   # test object type vs. array type
+                                        # call: ClassObject* elemClass, ClassObject* arrayClass)
+                                        # return: bool
+    lea         8(%esp), %esp
+    testl       %eax, %eax              # check for invalid array value
+    je          common_errArrayStore    # handle invalid array value
+    movl        sReg0, %edx             # restore &vBB[vCC]
+    movl        rINST, offArrayObject_contents(%edx)
+    movl        rGLUE, %eax
+    FFETCH_ADV  2, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    movl        offGlue_cardTable(%eax), %eax # get card table base
+    movl        sReg1, %edx             # restore object head
+    shrl        $GC_CARD_SHIFT, %edx   # object head to card number
+    movb        %al, (%eax, %edx)       # mark card using object head
+    FGETOP_JMP  2, %ecx                 # jump to next instruction; getop, jmp
+.LOP_APUT_OBJECT_skip_check:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl        rINST, offArrayObject_contents(%edx)
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET */
+
+.LOP_IGET_finish:
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # not resolved; handle exception
+
+    /*
+     *  %eax holds resolved field
+     */
+
+.LOP_IGET_finish2:
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl     (%ecx, %edx), %edx      # %edx<- object field
+    SET_VREG    %edx, rINST             # vA<- %edx; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_WIDE */
+
+.LOP_IGET_WIDE_finish2:
+    lea         -8(%esp), %esp
+    call        dvmResolveInstField     # resolve InstField ptr
+                                        # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    movl        %eax, %ecx              # %ecx<- %eax; %ecx expected to hold field
+    je          common_exceptionThrown
+
+   /*
+    *  %ecx holds resolved field
+    */
+
+.LOP_IGET_WIDE_finish:
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB
+    cmp         $0, %edx               # check for null object
+    je          common_errNullObject
+    movl        offInstField_byteOffset(%ecx), %ecx # %ecx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (%ecx, %edx), %xmm0     # %xmm0<- object field
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- %xmm0; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_OBJECT */
+
+.LOP_IGET_OBJECT_finish:
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # not resolved; handle exception
+
+    /*
+     *  %eax holds resolved field
+     */
+
+.LOP_IGET_OBJECT_finish2:
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl     (%ecx, %edx), %edx      # %edx<- object field
+    SET_VREG    %edx, rINST             # vA<- %edx; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_BOOLEAN */
+
+.LOP_IGET_BOOLEAN_finish:
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # not resolved; handle exception
+
+    /*
+     *  %eax holds resolved field
+     */
+
+.LOP_IGET_BOOLEAN_finish2:
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl     (%ecx, %edx), %edx      # %edx<- object field
+    SET_VREG    %edx, rINST             # vA<- %edx; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_BYTE */
+
+.LOP_IGET_BYTE_finish:
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # not resolved; handle exception
+
+    /*
+     *  %eax holds resolved field
+     */
+
+.LOP_IGET_BYTE_finish2:
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl     (%ecx, %edx), %edx      # %edx<- object field
+    SET_VREG    %edx, rINST             # vA<- %edx; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_CHAR */
+
+.LOP_IGET_CHAR_finish:
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # not resolved; handle exception
+
+    /*
+     *  %eax holds resolved field
+     */
+
+.LOP_IGET_CHAR_finish2:
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl     (%ecx, %edx), %edx      # %edx<- object field
+    SET_VREG    %edx, rINST             # vA<- %edx; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_SHORT */
+
+.LOP_IGET_SHORT_finish:
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # not resolved; handle exception
+
+    /*
+     *  %eax holds resolved field
+     */
+
+.LOP_IGET_SHORT_finish2:
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl     (%ecx, %edx), %edx      # %edx<- object field
+    SET_VREG    %edx, rINST             # vA<- %edx; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT */
+
+.LOP_IPUT_finish:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    lea         -8(%esp), %esp
+    movl        %edx, (%esp)            # push parameter method->clazz
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if resolved
+    jne         .LOP_IPUT_finish2
+    jmp         common_exceptionThrown  # not resolved; handle exception
+
+.LOP_IPUT_finish2:
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl     rINST, (%edx, %ecx)     # object field<- vA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_WIDE */
+
+.LOP_IPUT_WIDE_finish2:
+    lea         -8(%esp), %esp
+    call        dvmResolveInstField     # resolve InstField ptr
+    cmp         $0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    movl        %eax, %ecx              # %ecx<- %eax; %ecx expected to hold field
+    jne         .LOP_IPUT_WIDE_finish
+    jmp         common_exceptionThrown
+
+   /*
+    * Currently:
+    *  %ecx holds resolved field
+    *  %edx does not hold object yet
+    */
+
+.LOP_IPUT_WIDE_finish:
+    movl        rINST, %edx             # %edx<- BA
+    shr         $4, %edx               # %edx<- B
+    andl        $15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB
+    cmp         $0, %edx               # check for null object
+    je          common_errNullObject
+    movl        offInstField_byteOffset(%ecx), %ecx # %ecx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vA
+    movq        %xmm0, (%ecx, %edx)     # object field<- %xmm0; vA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_OBJECT */
+
+.LOP_IPUT_OBJECT_finish:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    lea         -8(%esp), %esp
+    movl        %edx, (%esp)            # push parameter method->clazz
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if resolved
+    jne         .LOP_IPUT_OBJECT_finish2
+    jmp         common_exceptionThrown  # not resolved; handle exception
+
+.LOP_IPUT_OBJECT_finish2:
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    GET_VREG    rINST                   # rINST<- vA
+    movl        rINST, (%edx, %ecx)     # object field<- vA
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl     rGLUE, %eax             # get glue
+    movl        offGlue_cardTable(%eax), %eax # get card table base
+    testl       rINST, rINST            # test if we stored a null value
+    je          1f                     # skip card mark if null stored
+    shrl        $GC_CARD_SHIFT, %ecx   # set obeject head to card number
+    movb        %al, (%eax, %ecx)
+1:
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+.LOP_IPUT_BOOLEAN_finish:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    lea         -8(%esp), %esp
+    movl        %edx, (%esp)            # push parameter method->clazz
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if resolved
+    jne         .LOP_IPUT_BOOLEAN_finish2
+    jmp         common_exceptionThrown  # not resolved; handle exception
+
+.LOP_IPUT_BOOLEAN_finish2:
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl     rINST, (%edx, %ecx)     # object field<- vA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_BYTE */
+
+.LOP_IPUT_BYTE_finish:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    lea         -8(%esp), %esp
+    movl        %edx, (%esp)            # push parameter method->clazz
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if resolved
+    jne         .LOP_IPUT_BYTE_finish2
+    jmp         common_exceptionThrown  # not resolved; handle exception
+
+.LOP_IPUT_BYTE_finish2:
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl     rINST, (%edx, %ecx)     # object field<- vA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_CHAR */
+
+.LOP_IPUT_CHAR_finish:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    lea         -8(%esp), %esp
+    movl        %edx, (%esp)            # push parameter method->clazz
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if resolved
+    jne         .LOP_IPUT_CHAR_finish2
+    jmp         common_exceptionThrown  # not resolved; handle exception
+
+.LOP_IPUT_CHAR_finish2:
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl     rINST, (%edx, %ecx)     # object field<- vA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_SHORT */
+
+.LOP_IPUT_SHORT_finish:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    lea         -8(%esp), %esp
+    movl        %edx, (%esp)            # push parameter method->clazz
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if resolved
+    jne         .LOP_IPUT_SHORT_finish2
+    jmp         common_exceptionThrown  # not resolved; handle exception
+
+.LOP_IPUT_SHORT_finish2:
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $4, %ecx               # %ecx<- B
+    and         $15, rINST             # rINST<- A
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl     rINST, (%edx, %ecx)     # object field<- vA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET */
+
+.LOP_SGET_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    mov         %eax, %ecx              # %ecx<- result
+
+.LOP_SGET_finish:
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl offStaticField_value(%ecx), %eax # %eax<- field value
+    SET_VREG    %eax, rINST             # vAA<- field value
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_WIDE */
+
+   /*
+    * Continuation if the field has not yet been resolved.
+    *  %edx: BBBB field ref
+    */
+
+.LOP_SGET_WIDE_resolve:
+    movl        offGlue_method(%eax), %eax # %eax <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %edx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%eax), %eax # %eax<- method->clazz
+    movl        %eax, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if initalization failed
+    movl        %eax, %ecx              # %ecx<- result
+    jne         .LOP_SGET_WIDE_finish      # success, continue
+    jmp         common_exceptionThrown  # failed; handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+.LOP_SGET_OBJECT_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    mov         %eax, %ecx              # %ecx<- result
+
+.LOP_SGET_OBJECT_finish:
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl offStaticField_value(%ecx), %eax # %eax<- field value
+    SET_VREG    %eax, rINST             # vAA<- field value
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_BOOLEAN */
+
+.LOP_SGET_BOOLEAN_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    mov         %eax, %ecx              # %ecx<- result
+
+.LOP_SGET_BOOLEAN_finish:
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl offStaticField_value(%ecx), %eax # %eax<- field value
+    SET_VREG    %eax, rINST             # vAA<- field value
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_BYTE */
+
+.LOP_SGET_BYTE_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    mov         %eax, %ecx              # %ecx<- result
+
+.LOP_SGET_BYTE_finish:
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl offStaticField_value(%ecx), %eax # %eax<- field value
+    SET_VREG    %eax, rINST             # vAA<- field value
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_CHAR */
+
+.LOP_SGET_CHAR_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    mov         %eax, %ecx              # %ecx<- result
+
+.LOP_SGET_CHAR_finish:
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl offStaticField_value(%ecx), %eax # %eax<- field value
+    SET_VREG    %eax, rINST             # vAA<- field value
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_SHORT */
+
+.LOP_SGET_SHORT_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    mov         %eax, %ecx              # %ecx<- result
+
+.LOP_SGET_SHORT_finish:
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl offStaticField_value(%ecx), %eax # %eax<- field value
+    SET_VREG    %eax, rINST             # vAA<- field value
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT */
+
+.LOP_SPUT_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    movl        %eax, %ecx              # %ecx<- result
+
+.LOP_SPUT_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offStaticField_value(%ecx) # field value<- vAA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_WIDE */
+
+   /*
+    * Continuation if the field has not yet been resolved.
+    *  %edx: BBBB field ref
+    */
+
+.LOP_SPUT_WIDE_resolve:
+    movl        offGlue_method(%eax), %eax # %eax <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %edx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%eax), %eax # %eax<- method->clazz
+    movl        %eax, -8(%esp)
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    lea         8(%esp), %esp
+    cmp         $0, %eax               # check if initalization failed
+    movl        %eax, %ecx              # %ecx<- result
+    jne         .LOP_SPUT_WIDE_finish      # success, continue
+    jmp         common_exceptionThrown  # failed; handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+
+.LOP_SPUT_OBJECT_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    movl        %eax, %ecx              # %ecx<- result
+
+.LOP_SPUT_OBJECT_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vAA
+
+
+    movl        rINST, offStaticField_value(%ecx) # field value<- vAA
+    testl       rINST, rINST            # stored null object ptr?
+    je          1f
+    movl        rGLUE, %edx             # get glue
+    movl        offField_clazz(%ecx), %ecx # ecx<- field->clazz
+    movl        offGlue_cardTable(%edx), %edx # get card table base
+    shrl        $GC_CARD_SHIFT, %ecx   # head to card number
+    movb        %dl, (%edx, %ecx)       # mark card
+1:
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+.LOP_SPUT_BOOLEAN_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    movl        %eax, %ecx              # %ecx<- result
+
+.LOP_SPUT_BOOLEAN_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offStaticField_value(%ecx) # field value<- vAA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_BYTE */
+
+.LOP_SPUT_BYTE_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    movl        %eax, %ecx              # %ecx<- result
+
+.LOP_SPUT_BYTE_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offStaticField_value(%ecx) # field value<- vAA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_CHAR */
+
+.LOP_SPUT_CHAR_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    movl        %eax, %ecx              # %ecx<- result
+
+.LOP_SPUT_CHAR_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offStaticField_value(%ecx) # field value<- vAA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_SHORT */
+
+.LOP_SPUT_SHORT_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    movl        %eax, %ecx              # %ecx<- result
+
+.LOP_SPUT_SHORT_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offStaticField_value(%ecx) # field value<- vAA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+.LOP_INVOKE_VIRTUAL_break:
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        %edx, -4(%esp)          # save "this" pointer register
+    movl        offGlue_method(%eax), %eax # %eax<- glue->method
+    movl        $METHOD_VIRTUAL, -8(%esp) # push parameter method type
+    movl        %ecx, -12(%esp)         # push paramter method index
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    lea         -16(%esp), %esp
+    movl        %eax, (%esp)            # push parameter clazz
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null method return
+    movl        -4(%esp), %edx          # get "this" pointer register
+    jne         .LOP_INVOKE_VIRTUAL_continue
+    jmp         common_exceptionThrown  # null pointer; handle exception
+
+   /*
+    * At this point:
+    *  %eax = resolved base method
+    *  %edx = D or CCCC (index of first arg, which is the "this" ptr)
+    */
+
+.LOP_INVOKE_VIRTUAL_continue:
+    GET_VREG    %edx                    # %edx<- "this" ptr
+    movzwl      offMethod_methodIndex(%eax), %eax # %eax<- baseMethod->methodIndex
+    cmp         $0, %edx               # %edx<- check for null "this"
+    je          common_errNullObject    # handle null object
+    movl        offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+    movl        offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+    movl        (%edx, %eax, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethodNoRange # invoke method common code
+
+/* continuation for OP_INVOKE_SUPER */
+
+.LOP_INVOKE_SUPER_continue2:
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_method(%eax), %eax # %eax<- glue->method
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    EXPORT_PC                           # must export for invoke
+    cmp         $0, %ecx               # check if already resolved
+    jne         .LOP_INVOKE_SUPER_continue
+    jmp         .LOP_INVOKE_SUPER_resolve     # handle resolve
+
+   /*
+    *  %ecx = resolved base method
+    *  %eax = method->clazz
+    */
+
+.LOP_INVOKE_SUPER_continue:
+    movl        offClassObject_super(%eax), %edx # %edx<- glue->method->clazz->super
+    movzwl      offMethod_methodIndex(%ecx), %ecx # %ecx<-  baseMethod->methodIndex
+    cmp          offClassObject_vtableCount(%edx), %ecx # compare vtableCount with methodIndex
+    EXPORT_PC                           # must export for invoke
+    jnc         .LOP_INVOKE_SUPER_nsm         # handle method not present
+    movl        offClassObject_vtable(%edx), %edx # %edx<- glue->method->clazz->super->vtable
+    movl        (%edx, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethodNoRange # invoke method common code
+
+.LOP_INVOKE_SUPER_resolve:
+    movl        %eax, -12(%esp)         # push parameter clazz
+    movl        %edx, -8(%esp)          # push parameter method index
+    movl        $METHOD_VIRTUAL, -4(%esp) # push parameter method type
+    lea         -12(%esp), %esp
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         12(%esp), %esp
+    movl        %eax, %ecx              # %ecx<- method
+    cmp         $0, %ecx               # check for null method return
+    movl        -12(%esp), %eax         # %eax<- glue->method->clazz
+    jne         .LOP_INVOKE_SUPER_continue
+    jmp         common_exceptionThrown  # null pointer; handle exception
+
+   /*
+    * Throw a NoSuchMethodError with the method name as the message.
+    * %ecx = resolved base method
+    */
+
+.LOP_INVOKE_SUPER_nsm:
+    movl        offMethod_name(%ecx), %edx # %edx<- method name
+    jmp         common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+   /*
+    * %eax = reference (BBBB or CCCC)
+    * -4(%esp) = "this" register
+    */
+
+.LOP_INVOKE_DIRECT_resolve:
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        $METHOD_DIRECT, -8(%esp) # push parameter method type
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        %eax, -12(%esp)         # push parameter reference
+    lea         -16(%esp), %esp
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, (%esp)            # push parameter clazz
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null method return
+    movl        -4(%esp), %edx          # get "this" pointer register
+    GET_VREG    %edx                    # get "this" pointer
+    je          common_exceptionThrown  # null pointer; handle exception
+    cmp         $0, %edx               # check for null "this"
+    movl        %eax, %ecx              # %ecx<- method
+    jne         common_invokeMethodNoRange # invoke method common code
+    jmp         common_errNullObject    # handle null object
+
+/* continuation for OP_INVOKE_STATIC */
+
+.LOP_INVOKE_STATIC_break:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        $METHOD_STATIC, -4(%esp) # resolver method type
+    movl        %eax, -8(%esp)          # push parameter method index
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -12(%esp)         # push parameter method
+    lea         -12(%esp), %esp
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         12(%esp), %esp
+    cmp         $0, %eax               # check for null method
+    je          common_exceptionThrown
+    movl        %eax, %ecx              # %ecx<- method
+    jmp         common_invokeMethodNoRange # invoke method common code
+
+/* continuation for OP_INVOKE_INTERFACE */
+.LOP_INVOKE_INTERFACE_break:
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_method(%ecx), %ecx # %ecx<- glue->method
+    movl        %ecx, -8(%esp)          # push parameter method
+    movl        offObject_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -16(%esp)         # push parameter
+    lea         -16(%esp), %esp
+    call        dvmFindInterfaceMethodInCache # call: (ClassObject* thisClass, u4 methodIdx,
+                                              #       const Method* method, DvmDex* methodClassDex)
+                                              # return: Method*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check if find failed
+    je          common_exceptionThrown  # handle exception
+    movl        %eax, %ecx              # %ecx<- method
+    jmp         common_invokeMethodNoRange # invoke method common code
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+.LOP_INVOKE_VIRTUAL_RANGE_break:
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        %edx, -4(%esp)          # save "this" pointer register
+    movl        offGlue_method(%eax), %eax # %eax<- glue->method
+    movl        $METHOD_VIRTUAL, -8(%esp) # push parameter method type
+    movl        %ecx, -12(%esp)         # push paramter method index
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    lea         -16(%esp), %esp
+    movl        %eax, (%esp)            # push parameter clazz
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null method return
+    movl        -4(%esp), %edx          # get "this" pointer register
+    jne         .LOP_INVOKE_VIRTUAL_RANGE_continue
+    jmp         common_exceptionThrown  # null pointer; handle exception
+
+   /*
+    * At this point:
+    *  %eax = resolved base method
+    *  %edx = D or CCCC (index of first arg, which is the "this" ptr)
+    */
+
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+    GET_VREG    %edx                    # %edx<- "this" ptr
+    movzwl      offMethod_methodIndex(%eax), %eax # %eax<- baseMethod->methodIndex
+    cmp         $0, %edx               # %edx<- check for null "this"
+    je          common_errNullObject    # handle null object
+    movl        offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+    movl        offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+    movl        (%edx, %eax, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethodRange # invoke method common code
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+.LOP_INVOKE_SUPER_RANGE_continue2:
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_method(%eax), %eax # %eax<- glue->method
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    EXPORT_PC                           # must export for invoke
+    cmp         $0, %ecx               # check if already resolved
+    jne         .LOP_INVOKE_SUPER_RANGE_continue
+    jmp         .LOP_INVOKE_SUPER_RANGE_resolve     # handle resolve
+
+   /*
+    *  %ecx = resolved base method
+    *  %eax = method->clazz
+    */
+
+.LOP_INVOKE_SUPER_RANGE_continue:
+    movl        offClassObject_super(%eax), %edx # %edx<- glue->method->clazz->super
+    movzwl      offMethod_methodIndex(%ecx), %ecx # %ecx<-  baseMethod->methodIndex
+    cmp          offClassObject_vtableCount(%edx), %ecx # compare vtableCount with methodIndex
+    EXPORT_PC                           # must export for invoke
+    jnc         .LOP_INVOKE_SUPER_RANGE_nsm         # handle method not present
+    movl        offClassObject_vtable(%edx), %edx # %edx<- glue->method->clazz->super->vtable
+    movl        (%edx, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethodRange # invoke method common code
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    movl        %eax, -12(%esp)         # push parameter clazz
+    movl        %edx, -8(%esp)          # push parameter method index
+    movl        $METHOD_VIRTUAL, -4(%esp) # push parameter method type
+    lea         -12(%esp), %esp
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         12(%esp), %esp
+    movl        %eax, %ecx              # %ecx<- method
+    cmp         $0, %ecx               # check for null method return
+    movl        -12(%esp), %eax         # %eax<- glue->method->clazz
+    jne         .LOP_INVOKE_SUPER_RANGE_continue
+    jmp         common_exceptionThrown  # null pointer; 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), %edx # %edx<- method name
+    jmp         common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+   /*
+    * %eax = reference (BBBB or CCCC)
+    * -4(%esp) = "this" register
+    */
+
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        $METHOD_DIRECT, -8(%esp) # push parameter method type
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        %eax, -12(%esp)         # push parameter reference
+    lea         -16(%esp), %esp
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, (%esp)            # push parameter clazz
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check for null method return
+    movl        -4(%esp), %edx          # get "this" pointer register
+    GET_VREG    %edx                    # get "this" pointer
+    je          common_exceptionThrown  # null pointer; handle exception
+    cmp         $0, %edx               # check for null "this"
+    movl        %eax, %ecx              # %ecx<- method
+    jne         common_invokeMethodRange # invoke method common code
+    jmp         common_errNullObject    # handle null object
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+.LOP_INVOKE_STATIC_RANGE_break:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        $METHOD_STATIC, -4(%esp) # resolver method type
+    movl        %eax, -8(%esp)          # push parameter method index
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -12(%esp)         # push parameter method
+    lea         -12(%esp), %esp
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         12(%esp), %esp
+    cmp         $0, %eax               # check for null method
+    je          common_exceptionThrown
+    movl        %eax, %ecx              # %ecx<- method
+    jmp         common_invokeMethodRange # invoke method common code
+
+/* continuation for OP_INVOKE_INTERFACE_RANGE */
+.LOP_INVOKE_INTERFACE_RANGE_break:
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_method(%ecx), %ecx # %ecx<- glue->method
+    movl        %ecx, -8(%esp)          # push parameter method
+    movl        offObject_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -16(%esp)         # push parameter
+    lea         -16(%esp), %esp
+    call        dvmFindInterfaceMethodInCache # call: (ClassObject* thisClass, u4 methodIdx,
+                                              #       const Method* method, DvmDex* methodClassDex)
+                                              # return: Method*
+    lea         16(%esp), %esp
+    cmp         $0, %eax               # check if find failed
+    je          common_exceptionThrown  # handle exception
+    movl        %eax, %ecx              # %ecx<- method
+    jmp         common_invokeMethodRange # invoke method common code
+
+/* continuation for OP_FLOAT_TO_INT */
+
+.LOP_FLOAT_TO_INT_break:
+    fnstcw      -2(%esp)                # save control word
+    orl         $0xc00, -2(%esp)       # reset control
+    fldcw       -2(%esp)                # load control word
+    xorl        $0xc00, -2(%esp)       # reset control
+    fistpl      (rFP, %edx, 4)          # move converted int
+    fldcw       -2(%esp)                # load saved control word
+    FINISH      1                       # jump to next instruction
+
+.LOP_FLOAT_TO_INT_nanInf:
+    jnp         .LOP_FLOAT_TO_INT_posInf      # handle posInf
+    fstps       (rFP, %edx, 4)          # pop floating point stack
+    movl        $0x00000000,  (rFP, %edx, 4) # vA<- NaN
+    FINISH      1                       # jump to next instruction
+
+.LOP_FLOAT_TO_INT_posInf:
+    fstps       (rFP, %edx, 4)          # pop floating point stack
+    movl        $0x7FFFFFFF,  (rFP, %edx, 4) # vA<- posInf
+    FINISH      1                       # jump to next instruction
+
+.LOP_FLOAT_TO_INT_negInf:
+    fstps       (rFP, %edx, 4)          # pop floating point stack
+    fstps       (rFP, %edx, 4)          # pop floating point stack
+    movl        $0x80000000, (rFP, %edx, 4) # vA<- negInf
+    FINISH      1                       # jump to next instruction
+
+/* continuation for OP_FLOAT_TO_LONG */
+
+.LOP_FLOAT_TO_LONG_break:
+    fnstcw      -2(%esp)                # save control word
+    orl         $0xc00, -2(%esp)       # update control
+    fldcw       -2(%esp)                # load control word
+    xorl        $0xc00, -2(%esp)       # reset control
+    fistpll     (rFP, %edx, 4)          # move converted int
+    fldcw       -2(%esp)                # load saved control word
+    FINISH      1                       # jump to next instruction
+
+.LOP_FLOAT_TO_LONG_nanInf:
+    jnp         .LOP_FLOAT_TO_LONG_posInf
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvalueNanLong, %xmm0   # %xmm0<- NaN
+    movq        %xmm0,  (rFP, %edx, 4)  # vA<- %xmm0; NaN
+    FINISH      1                       # jump to next instruction
+
+.LOP_FLOAT_TO_LONG_posInf:
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvaluePosInfLong, %xmm0 # %xmm0<- posInf
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; posInf
+    FINISH      1                       # jump to next instruction
+
+.LOP_FLOAT_TO_LONG_negInf:
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvalueNegInfLong, %xmm0 # %xmm0<- negInf
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; negInf
+    FINISH      1                       # jump to next instruction
+
+/* continuation for OP_DOUBLE_TO_INT */
+
+.LOP_DOUBLE_TO_INT_break:
+    fnstcw      -2(%esp)                # save control word
+    orl         $0xc00, -2(%esp)       # reset control
+    fldcw       -2(%esp)                # load control word
+    xorl        $0xc00, -2(%esp)       # reset control
+    fistpl      (rFP, %edx, 4)          # move converted int
+    fldcw       -2(%esp)                # load saved control word
+    FINISH      1                       # jump to next instruction
+
+.LOP_DOUBLE_TO_INT_nanInf:
+    jnp         .LOP_DOUBLE_TO_INT_posInf
+    fstps       (rFP, %edx, 4)
+    movl        $0x00000000,  (rFP, %edx, 4) # vA<- NaN
+    FINISH      1                       # jump to next instruction
+
+.LOP_DOUBLE_TO_INT_posInf:
+    fstps       (rFP, %edx, 4)
+    movl        $0x7FFFFFFF,  (rFP, %edx, 4) # vA<- posInf
+    FINISH      1                       # jump to next instruction
+
+.LOP_DOUBLE_TO_INT_negInf:
+    fstps       (rFP, %edx, 4)
+    fstps       (rFP, %edx, 4)
+    movl        $0x80000000,  (rFP, %edx, 4) # vA<- negInf
+    FINISH      1                       # jump to next instruction
+
+/* continuation for OP_DOUBLE_TO_LONG */
+
+.LOP_DOUBLE_TO_LONG_break:
+    fnstcw      -2(%esp)                # save control word
+    orl         $0xc00, -2(%esp)       # reset control
+    fldcw       -2(%esp)                # load control word
+    xorl        $0xc00, -2(%esp)       # reset control
+    fistpll     (rFP, %edx, 4)          # move converted int
+    fldcw       -2(%esp)                # load saved control word
+    FINISH      1                       # jump to next instruction
+
+.LOP_DOUBLE_TO_LONG_nanInf:
+    jnp         .LOP_DOUBLE_TO_LONG_posInf
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvalueNanLong, %xmm0   # %xmm0<- NaN
+    movq        %xmm0,  (rFP, %edx, 4)  # vA<- %xmm0; NaN
+    FINISH      1                       # jump to next instruction
+
+.LOP_DOUBLE_TO_LONG_posInf:
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvaluePosInfLong, %xmm0 # %xmm0<- posInf
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; posInf
+    FINISH      1                       # jump to next instruction
+
+.LOP_DOUBLE_TO_LONG_negInf:
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvalueNegInfLong, %xmm0 # %xmm0<- negInf
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; negInf
+    FINISH      1                       # jump to next instruction
+
+/* continuation for OP_DIV_INT */
+.LOP_DIV_INT_break:
+    .if  1
+    movl        $0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.LOP_DIV_INT_break2:
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_REM_INT */
+.LOP_REM_INT_break:
+    .if  0
+    movl        $0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.LOP_REM_INT_break2:
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_MUL_LONG */
+
+   /*
+    * X = (rFP, rINST, 4)
+    * W = 4(rFP, rINST, 4)
+    * Z = (rFP, %edx, 4)
+    * Y = 4(rFP, %edx, 4)
+    */
+
+.LOP_MUL_LONG_finish:
+    movl        4(rFP, rINST, 4), %ecx  # %ecx<- W
+    imull       (rFP, %edx, 4),  %ecx   # %ecx<- WxZ
+    mov         4(rFP, %edx, 4), %eax   # %ecx<- Y
+    imull       (rFP, rINST, 4), %eax   # %eax<- XxY
+    addl        %eax, %ecx              # %ecx<- (WZ + XY)
+    movl        (rFP, %edx, 4), %eax    # %eax<- Z
+    mull        (rFP, rINST, 4)         # %edx:eax<- XZ
+    movzbl      -4(%esp), rINST         # rINST<- AA
+    addl        %edx, %ecx              # %ecx<- carry + (WZ + XY)
+    movl        %ecx, 4(rFP, rINST, 4)  # vAA+1<- results hi
+    movl        %eax, (rFP, rINST, 4)   # vAA<- results lo
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_DIV_LONG */
+.LOP_DIV_LONG_finish:
+    movq        %xmm0, -16(%esp)        # push arg vBB,vBB+1
+    lea         -16(%esp), %esp
+    call        __divdi3                   # call func
+    lea         16(%esp), %esp
+    movl        %eax, (rFP, rINST, 4)   # vAA<- return low
+    movl        %edx, 4(rFP, rINST, 4)  # vAA+1<- return high
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_REM_LONG */
+.LOP_REM_LONG_finish:
+    movq        %xmm0, -16(%esp)        # push arg vBB,vBB+1
+    lea         -16(%esp), %esp
+    call        __moddi3                   # call func
+    lea         16(%esp), %esp
+    movl        %eax, (rFP, rINST, 4)   # vAA<- return low
+    movl        %edx, 4(rFP, rINST, 4)  # vAA+1<- return high
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+    movq        .Lvalue64, %xmm3        # %xmm3<- 64
+    psubq       %xmm0, %xmm3            # %xmm3<- 64 - shift amount
+    movq        .L64bits, %xmm4         # %xmm4<- lower 64 bits set
+    psllq       %xmm3, %xmm4            # %xmm4<- correct mask for sign bits
+    por         %xmm4, %xmm1            # %xmm1<- signed and shifted vBB
+
+.LOP_SHR_LONG_final:
+    movq        %xmm1, (rFP, rINST, 4)  # vAA<- shifted vBB
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_REM_DOUBLE */
+
+.LOP_REM_DOUBLE_break:
+    call        fmod                    # call: (long double x, long double y)
+                                        # return: double
+    lea         16(%esp), %esp
+    fstpl       (rFP, rINST, 4)         # vAA<- remainder; return of fmod
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_DIV_INT_2ADDR */
+.LOP_DIV_INT_2ADDR_break:
+    .if  1
+    movl        $0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.LOP_DIV_INT_2ADDR_break2:
+    FFETCH_ADV  1, %edx                 # %ecx<- next instruction hi; fetch, advance
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+
+/* continuation for OP_REM_INT_2ADDR */
+.LOP_REM_INT_2ADDR_break:
+    .if  0
+    movl        $0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.LOP_REM_INT_2ADDR_break2:
+    FFETCH_ADV  1, %edx                 # %ecx<- next instruction hi; fetch, advance
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+
+/* continuation for OP_MUL_LONG_2ADDR */
+
+   /*
+    * X = (rFP, rINST, 4)
+    * W = 4(rFP, rINST, 4)
+    * Z = (rFP, %edx, 4)
+    * Y = 4(rFP, %edx, 4)
+    */
+
+.LOP_MUL_LONG_2ADDR_finish:
+    movl        4(rFP, rINST, 4), %ecx  # %ecx<- W
+    imull       (rFP, %edx, 4), %ecx    # %ecx<- WxZ
+    movl                4(rFP, %edx, 4), %eax   # %eax<- Y
+    imull       (rFP, rINST, 4), %eax   # %eax<- X*Y
+    addl        %eax, %ecx              # %ecx<- (WZ + XY)
+    movl        (rFP, %edx, 4), %eax    # %eax<- Z
+    mull        (rFP, rINST, 4)         # %edx:eax<- XZ
+    addl        %edx, %ecx              # %ecx<- carry + (WZ + XY)
+    movl        sReg0, %edx             # %edx<- A
+    movl        %ecx, 4(rFP, %edx, 4)   # vA+1<- results hi
+    movl        %eax, (rFP, %edx, 4)    # vA<- results lo
+    FINISH      1                       # jump to next instruction
+
+/* continuation for OP_DIV_LONG_2ADDR */
+.LOP_DIV_LONG_2ADDR_break:
+    movq        %xmm0, -20(%esp)        # push arg vA, vA+1
+    lea         -20(%esp), %esp
+    call        __divdi3                   # call func
+    lea         20(%esp), %esp
+    movl        %eax, (rFP, rINST, 4)   # vA<- return low
+    movl        %edx, 4(rFP, rINST, 4)  # vA<- return high
+    FFETCH_ADV  1, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    FGETOP_JMP 1, %ecx                  # jump to next instruction; getop, jmp
+
+/* continuation for OP_REM_LONG_2ADDR */
+.LOP_REM_LONG_2ADDR_break:
+    movq        %xmm0, -20(%esp)        # push arg vA, vA+1
+    lea         -20(%esp), %esp
+    call        __moddi3                   # call func
+    lea         20(%esp), %esp
+    movl        %eax, (rFP, rINST, 4)   # vA<- return low
+    movl        %edx, 4(rFP, rINST, 4)  # vA<- return high
+    FFETCH_ADV  1, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    FGETOP_JMP 1, %ecx                  # jump to next instruction; getop, jmp
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+    movq        .Lvalue64, %xmm3        # %xmm3<- 64
+    psubq       %xmm0, %xmm3            # %xmm3<- 64 - shift amount
+    movq        .L64bits, %xmm4         # %xmm4<- lower 64 bits set
+    psllq       %xmm3, %xmm4            # %xmm4<- correct mask for sign bits
+    por         %xmm4, %xmm1            # %xmm1<- signed and shifted vBB
+
+.LOP_SHR_LONG_2ADDR_final:
+    movq        %xmm1, (rFP, rINST, 4)  # vAA<- shifted vBB
+    FINISH      1                       # jump to next instruction
+
+/* continuation for OP_REM_DOUBLE_2ADDR */
+
+.LOP_REM_DOUBLE_2ADDR_break:
+    call        fmod                    # call: (long double x, long double y)
+                                        # return: double
+    lea         20(%esp), %esp
+    fstpl       (rFP, rINST, 4)         # vAA<- remainder; return of fmod
+    FINISH      1                       # jump to next instruction
+
+/* continuation for OP_DIV_INT_LIT16 */
+.LOP_DIV_INT_LIT16_break:
+    .if  1
+    movl        $0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.LOP_DIV_INT_LIT16_break2:
+
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_REM_INT_LIT16 */
+.LOP_REM_INT_LIT16_break:
+    .if  0
+    movl        $0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.LOP_REM_INT_LIT16_break2:
+
+    FINISH      2                       # jump to next instruction
+
+/* continuation for OP_DIV_INT_LIT8 */
+.LOP_DIV_INT_LIT8_break:
+    .if  1
+    movl        $0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+
+.LOP_DIV_INT_LIT8_break2:
+    FINISH      2                       # jump to next instruction
+    #FGETOP_JMP 2, %ecx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_REM_INT_LIT8 */
+.LOP_REM_INT_LIT8_break:
+    .if  0
+    movl        $0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+
+.LOP_REM_INT_LIT8_break2:
+    FINISH      2                       # jump to next instruction
+    #FGETOP_JMP 2, %ecx                 # jump to next instruction; getop, jmp
+
+/* continuation for OP_EXECUTE_INLINE */
+
+   /*
+    * Extract args, call function.
+    *  rINST = #of args (0-4)
+    *  %ecx = call index
+    */
+
+.LOP_EXECUTE_INLINE_continue:
+    FETCH       2, %edx                 # %edx<- FEDC
+    cmp         $1, rINST              # determine number of arguments
+    jl          0f                      # handle zero args
+    je          1f                      # handle one arg
+    cmp         $3, rINST
+    jl          2f                      # handle two args
+    je          3f                      # handle three args
+4:
+    movl        %edx, rINST             # rINST<- FEDC
+    and         $0xf000, rINST         # isolate F
+    shr         $10, rINST
+    movl        (rFP, rINST), rINST     # rINST<- vF
+    movl        rINST, 12(%esp)         # push parameter vF
+3:
+    movl        %edx, rINST             # rINST<- FEDC
+    and         $0x0f00, rINST         # isolate E
+    shr         $6, rINST
+    movl        (rFP, rINST), rINST     # rINST<- vE
+    movl        rINST, 8(%esp)          # push parameter E
+2:
+    movl        %edx, rINST             # rINST<- FEDC
+    and         $0x00f0, rINST         # isolate D
+    shr         $2, rINST
+    movl        (rFP, rINST), rINST     # rINST<- vD
+    movl        rINST, 4(%esp)          # push parameter D
+1:
+    and         $0x000f, %edx          # isolate C
+    movl        (rFP, %edx, 4), %edx    # rINST<- vC
+    movl        %edx, (%esp)            # push parameter C
+0:
+    shl         $4, %ecx
+    movl        $gDvmInlineOpsTable, %eax # %eax<- address for table of inline operations
+    call        *(%eax, %ecx)           # call function
+
+    cmp         $0, %eax               # check boolean result of inline
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         24(%esp), %esp          # update stack pointer
+    je          common_exceptionThrown  # handle exception
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: x86-atom/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.
+    */
+
+   /*
+    * File: entry.S
+    */
+
+#define ASSIST_DEBUGGER 1
+    .text
+    .align      2
+    .global     dvmMterpStdRun
+    .type       dvmMterpStdRun, %function
+
+   /*
+    * Save registers, initialize sp and fp.
+    * On entry:
+    *     bool MterpGlue(glue *)
+    */
+
+    .macro      MTERP_ENTRY
+    movl        4(%esp), %ecx           # get first argument
+    movl        %ebp, -4(%esp)          # save caller base pointer
+    movl        %ebx, -8(%esp)          # save %ebx
+    movl        %esi, -12(%esp)         # save %esi
+    movl        %edi, -16(%esp)         # save %edi
+    lea         -40(%esp), %ebp         # set callee base pointer
+    lea         -40(%esp), %esp         # set callee stack pointer
+    .endm
+
+   /*
+    * Restore registers.
+    * This function returns a boolean "changeInterp" value.
+    * The return value is from dvmMterpStdBail().
+    */
+
+    .macro      MTERP_EXIT
+    lea         40(%esp), %esp          # correct stack pointer
+    movl        -16(%esp), %edi         # restore %edi
+    movl        -12(%esp), %esi         # restore %esi
+    movl        -8(%esp), %ebx          # restore %ebx
+    movl        -4(%esp), %ebp          # restore caller base pointer
+    ret                                 # return
+    .endm
+
+   /*
+    * DvmMterpStdRun entry point: save stack pointer, setup memory locations, get
+    * entry point, start executing instructions.
+    */
+
+dvmMterpStdRun:
+    MTERP_ENTRY
+    movl        %ecx, rGLUE             # save value for pMterpGlue
+    movl        offGlue_pc(%ecx), rPC   # get program counter
+    cmp         $kInterpEntryInstr, offGlue_entryPoint(%ecx) # check instruction
+    movl        offGlue_fp(%ecx), rFP   # get frame pointer
+    movl        %esp, offGlue_bailPtr(%ecx) # save SP for eventual return
+    FFETCH      %edx                    # %edx<- opcode
+    jne         .Lnot_instr             # no, handle it
+    FGETOP_JMPa %edx                    # start executing the instruction at rPC
+
+   /*
+    * Not an instruction. Are we returning from a method?
+    */
+
+.Lnot_instr:
+    cmpl        $kInterpEntryReturn, offGlue_entryPoint(%ecx)
+    je          common_returnFromMethod
+
+   /*
+    * No, are we throwing an exception?
+    */
+
+.Lnot_return:
+    cmpl        $kInterpEntryThrow, offGlue_entryPoint(%ecx)
+    je          common_exceptionThrown
+
+   /*
+    * No, then we must abort.
+    */
+
+.Lbad_arg:
+    pushl       offGlue_entryPoint(%ecx)
+    movl        $.LstrBadEntryPoint, -4(%esp)
+    lea         -4(%esp), %esp
+    call        printf
+    lea         8(%esp), %esp
+    call        dvmAbort                # call (void)
+
+   /*
+    * Restore the stack pointer and PC from the save point established on entry and
+    * return to whoever called dvmMterpStdRun.
+    *
+    * On entry:
+    *  4(%esp) MterpGlue* glue
+    *  8(%esp) bool changeInterp
+    */
+
+    .global     dvmMterpStdBail
+    .type       dvmMterpStdBail, %function
+
+dvmMterpStdBail:
+    movl        4(%esp), %ecx           # get first argument
+    movl        8(%esp), %eax           # get second argument
+    movl        offGlue_bailPtr(%ecx), %esp # sp <- saved SP
+    MTERP_EXIT
+
+   /*
+    * String references.
+    */
+
+.LstrBadEntryPoint:
+    .asciz "Bad entry point %d\n"
+
+
+dvmAsmInstructionJmpTable = .LdvmAsmInstructionJmpTable
+.LdvmAsmInstructionJmpTable:
+.long .L_OP_NOP
+.long .L_OP_MOVE
+.long .L_OP_MOVE_FROM16
+.long .L_OP_MOVE_16
+.long .L_OP_MOVE_WIDE
+.long .L_OP_MOVE_WIDE_FROM16
+.long .L_OP_MOVE_WIDE_16
+.long .L_OP_MOVE_OBJECT
+.long .L_OP_MOVE_OBJECT_FROM16
+.long .L_OP_MOVE_OBJECT_16
+.long .L_OP_MOVE_RESULT
+.long .L_OP_MOVE_RESULT_WIDE
+.long .L_OP_MOVE_RESULT_OBJECT
+.long .L_OP_MOVE_EXCEPTION
+.long .L_OP_RETURN_VOID
+.long .L_OP_RETURN
+.long .L_OP_RETURN_WIDE
+.long .L_OP_RETURN_OBJECT
+.long .L_OP_CONST_4
+.long .L_OP_CONST_16
+.long .L_OP_CONST
+.long .L_OP_CONST_HIGH16
+.long .L_OP_CONST_WIDE_16
+.long .L_OP_CONST_WIDE_32
+.long .L_OP_CONST_WIDE
+.long .L_OP_CONST_WIDE_HIGH16
+.long .L_OP_CONST_STRING
+.long .L_OP_CONST_STRING_JUMBO
+.long .L_OP_CONST_CLASS
+.long .L_OP_MONITOR_ENTER
+.long .L_OP_MONITOR_EXIT
+.long .L_OP_CHECK_CAST
+.long .L_OP_INSTANCE_OF
+.long .L_OP_ARRAY_LENGTH
+.long .L_OP_NEW_INSTANCE
+.long .L_OP_NEW_ARRAY
+.long .L_OP_FILLED_NEW_ARRAY
+.long .L_OP_FILLED_NEW_ARRAY_RANGE
+.long .L_OP_FILL_ARRAY_DATA
+.long .L_OP_THROW
+.long .L_OP_GOTO
+.long .L_OP_GOTO_16
+.long .L_OP_GOTO_32
+.long .L_OP_PACKED_SWITCH
+.long .L_OP_SPARSE_SWITCH
+.long .L_OP_CMPL_FLOAT
+.long .L_OP_CMPG_FLOAT
+.long .L_OP_CMPL_DOUBLE
+.long .L_OP_CMPG_DOUBLE
+.long .L_OP_CMP_LONG
+.long .L_OP_IF_EQ
+.long .L_OP_IF_NE
+.long .L_OP_IF_LT
+.long .L_OP_IF_GE
+.long .L_OP_IF_GT
+.long .L_OP_IF_LE
+.long .L_OP_IF_EQZ
+.long .L_OP_IF_NEZ
+.long .L_OP_IF_LTZ
+.long .L_OP_IF_GEZ
+.long .L_OP_IF_GTZ
+.long .L_OP_IF_LEZ
+.long .L_OP_UNUSED_3E
+.long .L_OP_UNUSED_3F
+.long .L_OP_UNUSED_40
+.long .L_OP_UNUSED_41
+.long .L_OP_UNUSED_42
+.long .L_OP_UNUSED_43
+.long .L_OP_AGET
+.long .L_OP_AGET_WIDE
+.long .L_OP_AGET_OBJECT
+.long .L_OP_AGET_BOOLEAN
+.long .L_OP_AGET_BYTE
+.long .L_OP_AGET_CHAR
+.long .L_OP_AGET_SHORT
+.long .L_OP_APUT
+.long .L_OP_APUT_WIDE
+.long .L_OP_APUT_OBJECT
+.long .L_OP_APUT_BOOLEAN
+.long .L_OP_APUT_BYTE
+.long .L_OP_APUT_CHAR
+.long .L_OP_APUT_SHORT
+.long .L_OP_IGET
+.long .L_OP_IGET_WIDE
+.long .L_OP_IGET_OBJECT
+.long .L_OP_IGET_BOOLEAN
+.long .L_OP_IGET_BYTE
+.long .L_OP_IGET_CHAR
+.long .L_OP_IGET_SHORT
+.long .L_OP_IPUT
+.long .L_OP_IPUT_WIDE
+.long .L_OP_IPUT_OBJECT
+.long .L_OP_IPUT_BOOLEAN
+.long .L_OP_IPUT_BYTE
+.long .L_OP_IPUT_CHAR
+.long .L_OP_IPUT_SHORT
+.long .L_OP_SGET
+.long .L_OP_SGET_WIDE
+.long .L_OP_SGET_OBJECT
+.long .L_OP_SGET_BOOLEAN
+.long .L_OP_SGET_BYTE
+.long .L_OP_SGET_CHAR
+.long .L_OP_SGET_SHORT
+.long .L_OP_SPUT
+.long .L_OP_SPUT_WIDE
+.long .L_OP_SPUT_OBJECT
+.long .L_OP_SPUT_BOOLEAN
+.long .L_OP_SPUT_BYTE
+.long .L_OP_SPUT_CHAR
+.long .L_OP_SPUT_SHORT
+.long .L_OP_INVOKE_VIRTUAL
+.long .L_OP_INVOKE_SUPER
+.long .L_OP_INVOKE_DIRECT
+.long .L_OP_INVOKE_STATIC
+.long .L_OP_INVOKE_INTERFACE
+.long .L_OP_UNUSED_73
+.long .L_OP_INVOKE_VIRTUAL_RANGE
+.long .L_OP_INVOKE_SUPER_RANGE
+.long .L_OP_INVOKE_DIRECT_RANGE
+.long .L_OP_INVOKE_STATIC_RANGE
+.long .L_OP_INVOKE_INTERFACE_RANGE
+.long .L_OP_UNUSED_79
+.long .L_OP_UNUSED_7A
+.long .L_OP_NEG_INT
+.long .L_OP_NOT_INT
+.long .L_OP_NEG_LONG
+.long .L_OP_NOT_LONG
+.long .L_OP_NEG_FLOAT
+.long .L_OP_NEG_DOUBLE
+.long .L_OP_INT_TO_LONG
+.long .L_OP_INT_TO_FLOAT
+.long .L_OP_INT_TO_DOUBLE
+.long .L_OP_LONG_TO_INT
+.long .L_OP_LONG_TO_FLOAT
+.long .L_OP_LONG_TO_DOUBLE
+.long .L_OP_FLOAT_TO_INT
+.long .L_OP_FLOAT_TO_LONG
+.long .L_OP_FLOAT_TO_DOUBLE
+.long .L_OP_DOUBLE_TO_INT
+.long .L_OP_DOUBLE_TO_LONG
+.long .L_OP_DOUBLE_TO_FLOAT
+.long .L_OP_INT_TO_BYTE
+.long .L_OP_INT_TO_CHAR
+.long .L_OP_INT_TO_SHORT
+.long .L_OP_ADD_INT
+.long .L_OP_SUB_INT
+.long .L_OP_MUL_INT
+.long .L_OP_DIV_INT
+.long .L_OP_REM_INT
+.long .L_OP_AND_INT
+.long .L_OP_OR_INT
+.long .L_OP_XOR_INT
+.long .L_OP_SHL_INT
+.long .L_OP_SHR_INT
+.long .L_OP_USHR_INT
+.long .L_OP_ADD_LONG
+.long .L_OP_SUB_LONG
+.long .L_OP_MUL_LONG
+.long .L_OP_DIV_LONG
+.long .L_OP_REM_LONG
+.long .L_OP_AND_LONG
+.long .L_OP_OR_LONG
+.long .L_OP_XOR_LONG
+.long .L_OP_SHL_LONG
+.long .L_OP_SHR_LONG
+.long .L_OP_USHR_LONG
+.long .L_OP_ADD_FLOAT
+.long .L_OP_SUB_FLOAT
+.long .L_OP_MUL_FLOAT
+.long .L_OP_DIV_FLOAT
+.long .L_OP_REM_FLOAT
+.long .L_OP_ADD_DOUBLE
+.long .L_OP_SUB_DOUBLE
+.long .L_OP_MUL_DOUBLE
+.long .L_OP_DIV_DOUBLE
+.long .L_OP_REM_DOUBLE
+.long .L_OP_ADD_INT_2ADDR
+.long .L_OP_SUB_INT_2ADDR
+.long .L_OP_MUL_INT_2ADDR
+.long .L_OP_DIV_INT_2ADDR
+.long .L_OP_REM_INT_2ADDR
+.long .L_OP_AND_INT_2ADDR
+.long .L_OP_OR_INT_2ADDR
+.long .L_OP_XOR_INT_2ADDR
+.long .L_OP_SHL_INT_2ADDR
+.long .L_OP_SHR_INT_2ADDR
+.long .L_OP_USHR_INT_2ADDR
+.long .L_OP_ADD_LONG_2ADDR
+.long .L_OP_SUB_LONG_2ADDR
+.long .L_OP_MUL_LONG_2ADDR
+.long .L_OP_DIV_LONG_2ADDR
+.long .L_OP_REM_LONG_2ADDR
+.long .L_OP_AND_LONG_2ADDR
+.long .L_OP_OR_LONG_2ADDR
+.long .L_OP_XOR_LONG_2ADDR
+.long .L_OP_SHL_LONG_2ADDR
+.long .L_OP_SHR_LONG_2ADDR
+.long .L_OP_USHR_LONG_2ADDR
+.long .L_OP_ADD_FLOAT_2ADDR
+.long .L_OP_SUB_FLOAT_2ADDR
+.long .L_OP_MUL_FLOAT_2ADDR
+.long .L_OP_DIV_FLOAT_2ADDR
+.long .L_OP_REM_FLOAT_2ADDR
+.long .L_OP_ADD_DOUBLE_2ADDR
+.long .L_OP_SUB_DOUBLE_2ADDR
+.long .L_OP_MUL_DOUBLE_2ADDR
+.long .L_OP_DIV_DOUBLE_2ADDR
+.long .L_OP_REM_DOUBLE_2ADDR
+.long .L_OP_ADD_INT_LIT16
+.long .L_OP_RSUB_INT
+.long .L_OP_MUL_INT_LIT16
+.long .L_OP_DIV_INT_LIT16
+.long .L_OP_REM_INT_LIT16
+.long .L_OP_AND_INT_LIT16
+.long .L_OP_OR_INT_LIT16
+.long .L_OP_XOR_INT_LIT16
+.long .L_OP_ADD_INT_LIT8
+.long .L_OP_RSUB_INT_LIT8
+.long .L_OP_MUL_INT_LIT8
+.long .L_OP_DIV_INT_LIT8
+.long .L_OP_REM_INT_LIT8
+.long .L_OP_AND_INT_LIT8
+.long .L_OP_OR_INT_LIT8
+.long .L_OP_XOR_INT_LIT8
+.long .L_OP_SHL_INT_LIT8
+.long .L_OP_SHR_INT_LIT8
+.long .L_OP_USHR_INT_LIT8
+.long .L_OP_IGET_VOLATILE
+.long .L_OP_IPUT_VOLATILE
+.long .L_OP_SGET_VOLATILE
+.long .L_OP_SPUT_VOLATILE
+.long .L_OP_IGET_OBJECT_VOLATILE
+.long .L_OP_IGET_WIDE_VOLATILE
+.long .L_OP_IPUT_WIDE_VOLATILE
+.long .L_OP_SGET_WIDE_VOLATILE
+.long .L_OP_SPUT_WIDE_VOLATILE
+.long .L_OP_BREAKPOINT
+.long .L_OP_THROW_VERIFICATION_ERROR
+.long .L_OP_EXECUTE_INLINE
+.long .L_OP_EXECUTE_INLINE_RANGE
+.long .L_OP_INVOKE_DIRECT_EMPTY
+.long .L_OP_UNUSED_F1
+.long .L_OP_IGET_QUICK
+.long .L_OP_IGET_WIDE_QUICK
+.long .L_OP_IGET_OBJECT_QUICK
+.long .L_OP_IPUT_QUICK
+.long .L_OP_IPUT_WIDE_QUICK
+.long .L_OP_IPUT_OBJECT_QUICK
+.long .L_OP_INVOKE_VIRTUAL_QUICK
+.long .L_OP_INVOKE_VIRTUAL_QUICK_RANGE
+.long .L_OP_INVOKE_SUPER_QUICK
+.long .L_OP_INVOKE_SUPER_QUICK_RANGE
+.long .L_OP_IPUT_OBJECT_VOLATILE
+.long .L_OP_SGET_OBJECT_VOLATILE
+.long .L_OP_SPUT_OBJECT_VOLATILE
+.long .L_OP_UNUSED_FF
+
+/* File: x86-atom/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.
+    */
+
+   /*
+    * File: footer.S
+    */
+
+    .text
+    .align      2
+
+   /*
+    * Check to see if the thread needs to be suspended or debugger/profiler
+    * activity has begun.
+    *
+    * On entry:
+    *  %ecx is reentry type, e.g. kInterpEntryInstr
+    *  %edx is PC adjustment in bytes
+    */
+
+common_periodicChecks:
+    movl        %edx, -8(%esp)          # save pc adjustments
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        %ebx, -4(%esp)          # save %ebx to the stack
+    movl        offGlue_pSelfSuspendCount(%edx), %ebx # %ebx<- pSuspendCount (int)
+4:
+    movl        offGlue_pDebuggerActive(%edx), %eax # %eax<- pDebuggerActive
+    testl       %eax, %eax
+    je          5f
+    movzbl        (%eax), %eax            # %eax<- get debuggerActive (boolean)
+5:
+    cmp         $0, (%ebx)             # check if suspend is pending
+    jne         2f                      # handle suspend
+    movl        offGlue_pActiveProfilers(%edx), %ebx # %ebx<- activeProfilers (int)
+    orl         (%ebx), %eax            # %eax<- merge activeProfilers and debuggerActive
+    movl        -8(%esp), %edx          # %edx<- restore %edx
+    jne         3f                      # debugger or profiler active; switch interp
+    movl        -4(%esp), %ebx          # %ebx<- restore %ebx
+    ret                                 # return
+2:                                      # check suspended
+    EXPORT_PC
+    movl        offGlue_self(%edx), %eax # %eax<- glue->self
+    movl        %eax, -12(%esp)         # push parameter boolean
+    lea         -12(%esp), %esp
+    call        dvmCheckSuspendPending  # call: (Thread* self)
+                                        # return: bool
+    movl        4(%esp), %edx           # %edx<- restore %edx
+    movl        8(%esp), %ebx           # %ebx<- restore %ebx
+    lea         12(%esp), %esp
+    ret
+3:                                      # debugger/profiler enabled, bail out
+    leal        (rPC, %edx, 2), rPC     # adjust pc to show target
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movb        $kInterpEntryInstr, offGlue_entryPoint(%ecx)
+    movl        $1, %edx               # switch interpreter
+    jmp         common_gotoBail         # bail
+
+   /*
+    * Check to see if the thread needs to be suspended or debugger/profiler
+    * activity has begun. With this variant, the reentry type is hard coded
+    * as kInterpEntryInstr.
+    *
+    * On entry:
+    *  %edx is PC adjustment in bytes
+    */
+
+common_periodicChecks_backwardBranch:
+    EXPORT_PC
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_pSelfSuspendCount(%ecx), rINST # %ebx<- pSuspendCount (int)
+4:
+    movl        offGlue_pDebuggerActive(%ecx), %eax # %eax<- pDebuggerActive
+    testl       %eax, %eax              # test for NULL pointer
+    je          5f
+    movzbl      (%eax), %eax            # %eax<- get debuggerActive count
+5:
+    cmp         $0, (rINST)            # check if suspend is pending
+    jne         2f                      # handle suspend
+    movl        offGlue_pActiveProfilers(%ecx), rINST # %edx<- activeProfilers (int)
+    orl          (rINST), %eax           # %eax<- merge activeProfilers and debuggerActive
+    jne         3f                      # debugger or profiler active; switch interp
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+2:                                      # check suspended
+    movl        offGlue_self(%ecx), %eax# %eax<- glue->self
+    movl        %edx, rINST
+    movl        %eax, -12(%esp)         # push parameter boolean
+    lea         -12(%esp), %esp
+    call        dvmCheckSuspendPending  # call: (Thread* self)
+                                        # return: bool
+    movl        rINST, %edx             # %edx<- restore %edx
+    lea         12(%esp), %esp
+    FINISH_RB   %edx, %ecx
+3:                                      # debugger/profiler enabled, bail out
+    leal        (rPC, %edx, 2), rPC     # adjust pc to show target
+    movb        $kInterpEntryInstr, offGlue_entryPoint(%ecx)
+    movl        $1, %edx               # switch interpreter
+    jmp         common_gotoBail         # bail
+
+   /*
+    * The equivalent of "goto bail", this calls through the "bail handler".
+    * State registers will be saved to the "glue" area before bailing.
+    *
+    * On entry:
+    *  %edx is "bool changeInterp", indicating if we want to switch to the
+    *     other interpreter or just bail all the way out
+    */
+
+common_gotoBail:
+    SAVE_PC_FP_TO_GLUE %ecx             # save program counter and frame pointer
+
+   /*
+    * Inlined dvmMterpStdBail
+    */
+
+    lea         40(%ebp), %esp
+    movl        %edx, %eax
+    movl        24(%ebp), %edi
+    movl        28(%ebp), %esi
+    movl        32(%ebp), %ebx
+    movl        36(%ebp), %ebp
+    ret
+
+   /*
+    * Common code for method invocation with range.
+    *
+    * On entry:
+    *  %ecx is "Method* methodToCall", the method we're trying to call
+    */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+
+   /*
+    * prepare to copy args to "outs" area of current frame
+    */
+
+    SAVEAREA_FROM_FP %eax               # %eax<- &outs; &StackSaveArea
+    test        rINST, rINST            # test for no args
+    movl        rINST, sReg0            # sReg0<- AA
+    jz          .LinvokeArgsDone        # no args; jump to args done
+    FETCH       2, %edx                 # %edx<- CCCC
+
+   /*
+    * %ecx=methodToCall, %edx=CCCC, sReg0=count, %eax=&outs (&stackSaveArea)
+    * (very few methods have > 10 args; could unroll for common cases)
+    */
+
+    movl        %ebx, sReg1             # sReg1<- save %ebx
+    lea         (rFP, %edx, 4), %edx    # %edx<- &vCCCC
+    shll        $2, sReg0              # sReg0<- offset
+    subl        sReg0, %eax             # %eax<- update &outs
+    shrl        $2, sReg0              # sReg0<- offset
+1:
+    movl        (%edx), %ebx            # %ebx<- vCCCC
+    lea         4(%edx), %edx           # %edx<- &vCCCC++
+    subl        $1, sReg0              # sReg<- sReg--
+    movl        %ebx, (%eax)            # *outs<- vCCCC
+    lea         4(%eax), %eax           # outs++
+    jne         1b                      # loop if count (sReg0) not zero
+    movl        sReg1, %ebx             # %ebx<- restore %ebx
+    jmp         .LinvokeArgsDone        # continue
+
+   /*
+    * %ecx is "Method* methodToCall", the method we're trying to call
+    * prepare to copy args to "outs" area of current frame
+    */
+
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    movl        rINST, sReg0            # sReg0<- BA
+    shrl        $4, sReg0              # sReg0<- B
+    je          .LinvokeArgsDone        # no args; jump to args done
+    SAVEAREA_FROM_FP %eax               # %eax<- &outs; &StackSaveArea
+    FETCH       2, %edx                 # %edx<- GFED
+
+   /*
+    * %ecx=methodToCall, %edx=GFED, sReg0=count, %eax=outs
+    */
+
+.LinvokeNonRange:
+    cmp         $2, sReg0              # compare sReg0 to 2
+    movl        %edx, sReg1             # sReg1<- GFED
+    jl          1f                      # handle 1 arg
+    je          2f                      # handle 2 args
+    cmp         $4, sReg0              # compare sReg0 to 4
+    jl          3f                      # handle 3 args
+    je          4f                      # handle 4 args
+5:
+    andl        $15, rINST             # rINST<- A
+    lea         -4(%eax), %eax          # %eax<- update &outs; &outs--
+    movl        (rFP, rINST, 4), %edx   # %edx<- vA
+    movl        %edx, (%eax)            # *outs<- vA
+    movl        sReg1, %edx             # %edx<- GFED
+4:
+    shr         $12, %edx              # %edx<- G
+    lea         -4(%eax), %eax          # %eax<- update &outs; &outs--
+    movl        (rFP, %edx, 4), %edx    # %edx<- vG
+    movl        %edx, (%eax)            # *outs<- vG
+    movl        sReg1, %edx             # %edx<- GFED
+3:
+    and         $0x0f00, %edx          # %edx<- 0F00
+    shr         $6, %edx               # %edx<- F at correct offset
+    lea         -4(%eax), %eax          # %eax<- update &outs; &outs--
+    movl        (rFP, %edx), %edx       # %edx<- vF
+    movl        %edx, (%eax)            # *outs<- vF
+    movl        sReg1, %edx             # %edx<- GFED
+2:
+    and         $0x00f0, %edx          # %edx<- 00E0
+    shr         $2, %edx               # %edx<- E at correct offset
+    lea         -4(%eax), %eax          # %eax<- update &outs; &outs--
+    movl        (rFP, %edx), %edx       # %edx<- vE
+    movl        %edx, (%eax)            # *outs<- vE
+    movl        sReg1, %edx             # %edx<- GFED
+1:
+    and         $0x000f, %edx          # %edx<- 000D
+    movl        (rFP, %edx, 4), %edx    # %edx<- vD
+    movl        %edx, -4(%eax)          # *--outs<- vD
+0:
+
+   /*
+    * %ecx is "Method* methodToCall", the method we're trying to call
+    * find space for the new stack frame, check for overflow
+    */
+
+.LinvokeArgsDone:
+    movzwl      offMethod_registersSize(%ecx), %eax # %eax<- methodToCall->regsSize
+    movzwl      offMethod_outsSize(%ecx), %edx # %edx<- methodToCall->outsSize
+    movl        %ecx, sReg0             # sReg<- methodToCall
+    shl         $2, %eax               # %eax<- update offset
+    SAVEAREA_FROM_FP %ecx               # %ecx<- &outs; &StackSaveArea
+    subl        %eax, %ecx              # %ecx<- newFP; (old savearea - regsSize)
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        %ecx, sReg1             # sReg1<- &outs
+    subl        $sizeofStackSaveArea, %ecx # %ecx<- newSaveArea (stack save area using newFP)
+    movl        offGlue_interpStackEnd(%eax), %eax # %eax<- glue->interpStackEnd
+    movl        %eax, sReg2             # sReg2<- glue->interpStackEnd
+    shl         $2, %edx               # %edx<- update offset for outsSize
+    movl        %ecx, %eax              # %eax<- newSaveArea
+    sub         %edx, %ecx              # %ecx<- bottom; (newSaveArea - outsSize)
+    cmp         sReg2, %ecx             # compare interpStackEnd and bottom
+    movl        sReg0, %ecx             # %ecx<- restore methodToCall
+    jl          .LstackOverflow         # handle frame overflow
+
+   /*
+    * set up newSaveArea
+    */
+
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP %edx               # %edx<- &outs; &StackSaveArea
+    movl        %edx, offStackSaveArea_prevSave(%eax) # newSaveArea->prevSave<- &outs
+#endif
+    movl        rFP, offStackSaveArea_prevFrame(%eax) # newSaveArea->prevFrame<- rFP
+    movl        rPC, offStackSaveArea_savedPc(%eax) # newSaveArea->savedPc<- rPC
+    testl       $ACC_NATIVE, offMethod_accessFlags(%ecx) # check for native call
+    movl        %ecx, offStackSaveArea_method(%eax) # newSaveArea->method<- method to call
+    jne         .LinvokeNative          # handle native call
+
+   /*
+    * Update "glue" values for the new method
+    * %ecx=methodToCall, sReg1=newFp
+    */
+
+    movl        offMethod_clazz(%ecx), %edx # %edx<- method->clazz
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        %ecx, offGlue_method(%eax) # glue->method<- methodToCall
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        offMethod_insns(%ecx), rPC # rPC<- methodToCall->insns
+    movl        %edx, offGlue_methodClassDex(%eax) # glue->methodClassDex<- method->clazz->pDvmDex
+    movl        offGlue_self(%eax), %ecx # %ecx<- glue->self
+    movl        sReg1, rFP              # rFP<- newFP
+    movl        rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP
+    FINISH_A                            # jump to methodToCall->insns
+
+   /*
+    * Prep for the native call
+    * %ecx=methodToCall, sReg1=newFP, %eax=newSaveArea
+    */
+
+.LinvokeNative:
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        %ecx, -20(%esp)         # push parameter methodToCall
+    movl        offGlue_self(%edx), %edx # %edx<- glue->self
+    movl        offThread_jniLocal_topCookie(%edx), %ecx # %ecx<- glue->self->thread->refNext
+    movl        %ecx, offStackSaveArea_localRefCookie(%eax) # newSaveArea->localRefCookie<- refNext
+    movl        %eax, -4(%esp)          # save newSaveArea
+    movl        sReg1, %eax             # %eax<- newFP
+    movl        %eax, offThread_curFrame(%edx) # glue->self->curFrame<- newFP
+    movl        %edx, -8(%esp)          # save glue->self
+    movl        %edx, -16(%esp)         # push parameter glue->self
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        -20(%esp), %ecx         # %ecx<- methodToCall
+    lea         offGlue_retval(%edx), %edx # %edx<- &retval
+    movl        %edx, -24(%esp)         # push parameter pMterpGlue
+    movl        %eax, -28(%esp)         # push parameter newFP
+    lea         -28(%esp), %esp
+
+#ifdef ASSIST_DEBUGGER
+    jmp         .Lskip
+    .type       dalvik_mterp, %function
+dalvik_mterp:
+    MTERP_ENTRY
+.Lskip:
+#endif
+    call        *offMethod_nativeFunc(%ecx) # call methodToCall->nativeFunc
+    lea         28(%esp), %esp
+    movl        -4(%esp), %edx          # %edx<- newSaveArea
+    movl        -8(%esp), %ecx          # %ecx<- glue->self
+    movl        offStackSaveArea_localRefCookie(%edx), %eax  # %eax<- newSaveArea->localRefCookie
+    FFETCH_ADV  3, %edx                 # %edx<- next instruction hi; fetch, advance
+    cmp         $0, offThread_exception(%ecx) # check for exception
+    movl        rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP
+    movl        %eax, offThread_jniLocal_topCookie(%ecx) # glue->self<- newSaveArea->localRefCookie
+    jne         common_exceptionThrown  # handle exception
+    FGETOP_JMP  3, %edx                 # jump to next instruction; getop, jmp
+
+.LstackOverflow:
+    movl        %ecx, -4(%esp)          # push method to call
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_self(%ecx), %ecx # %ecx<- glue->self
+    movl        %ecx, -8(%esp)          # push parameter self
+    lea         -8(%esp), %esp
+    call        dvmHandleStackOverflow  # call: (Thread* self, Method *meth)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+#ifdef ASSIST_DEBUGGER
+#endif
+
+   /*
+    * Common code for handling a return instruction.
+    *
+    * This does not return.
+    */
+
+common_returnFromMethod:
+.LreturnNew:
+
+   /*
+    * Inline common periodic checks
+    */
+
+    movl        rGLUE, rINST            # %ecx<- pMterpGlue
+    movl        offGlue_pSelfSuspendCount(rINST), %edx # %ebx<- pSuspendCount (int)
+    movl        offGlue_pDebuggerActive(rINST), %eax # %eax<- pDebuggerActive
+    movl        (%eax), %eax            # %eax<- get debuggerActive (boolean)
+    and         $7, %eax               # %eax<- mask for boolean (just how many bits does it take?)
+    cmp         $0, (%edx)             # check if suspend is pending
+    jne         2f                      # handle suspend
+    movl        offGlue_pActiveProfilers(rINST), %edx # %edx<- activeProfilers (int)
+    or          (%edx), %eax            # %eax<- merge activeProfilers and debuggerActive
+    cmp         $0, %eax               # check for debuggerActive
+    jne         3f                      # debugger or profiler active; switch interp
+    jmp         4f
+2:                                      # check suspended
+    movl        offGlue_self(rINST), %eax# %eax<- glue->self
+    movl        %eax, -12(%esp)         # push parameter boolean
+    lea         -12(%esp), %esp
+    call        dvmCheckSuspendPending  # call: (Thread* self)
+                                        # return: bool
+    lea         12(%esp), %esp
+    jmp         4f
+3:                                      # debugger/profiler enabled, bail out
+    movl        $kInterpEntryInstr, offGlue_entryPoint(rINST) # glue->entryPoint<- reentry type
+    movl        $1, %edx               # switch to interp<- true
+    jmp         common_gotoBail         # bail
+
+
+   /*
+    * Get save area; rGLUE is %ebx, rFP is %eax
+    */
+4:
+    SAVEAREA_FROM_FP %ecx               # %ecx<- saveArea(old)
+    movl        offStackSaveArea_prevFrame(%ecx), rFP # rFP<- saveArea->PrevFrame
+    movl        (offStackSaveArea_method - sizeofStackSaveArea)(rFP), %edx # %edx<- method we are returning to
+    cmpl        $0, %edx               # check for break frame
+    je          common_gotoBail         # bail if break frame
+    movl        offStackSaveArea_savedPc(%ecx), rPC # rPC<- saveAreaOld->savedPc
+    movl        offGlue_self(rINST), %ecx # %eax<- glue->self
+    movl        %edx, offGlue_method(rINST) # glue->method<- newSave->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    FFETCH_ADV  3, %eax                 # %ecx<- next instruction hi; fetch, advance
+    movl        rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        %edx, offGlue_methodClassDex(rINST) # glue->pDvmDex<- method->clazz->pDvmDex
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
+
+   /*
+    * Handle thrown an exception. 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.
+    */
+
+common_exceptionThrown:
+.LexceptionNew:
+    movl        $kInterpEntryThrow, %ecx # %ecx<- reentry type
+    movl        $0, %edx               # %edx<- pc adjustment
+    call        common_periodicChecks
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_self(%eax), %edx # %edx<- glue->self
+    movl        offThread_exception(%edx), %ecx # %ecx<- pMterpGlue->self->exception
+    movl        %edx, -4(%esp)          # push parameter self
+    movl        %ecx, -8(%esp)          # push parameter obj
+    lea         -8(%esp), %esp
+    call        dvmAddTrackedAlloc      # don't allow the exception to be GC'd
+                                        # call: (Object* obj, Thread* self)
+                                        # return: void
+    movl        4(%esp), %edx           # %edx<- glue->self
+    movl        $0, offThread_exception(%edx) # glue->self->exception<- NULL
+
+   /*
+    * set up args and a local for &fp
+    */
+
+    movl        rFP, -4(%esp)           # move fp to stack
+    lea         -4(%esp), %esp          # update %esp
+    movl        %esp, -4(%esp)          # push parameter 4<- &fp
+    movl        $0, -8(%esp)           # push parameter 3<- false
+    movl        4(%esp), %edx
+    movl        %edx, -12(%esp)         # push parameter 2<- glue->self->exception
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_method(%eax), %edx # %edx<- glue->method
+    movl        offMethod_insns(%edx), %edx # %edx<- glue->method->insns
+    movl        rPC, %ecx               # %ecx<- rPC
+    subl        %edx, %ecx              # %ecx<- pc - glue->method->insns
+    sar         $1, %ecx               # %ecx<- adjust %ecx for offset
+    movl        %ecx, -16(%esp)         # push parameter 1<- glue->method->insns
+    movl        8(%esp), %edx
+    movl        %edx, -20(%esp)         # push parameter 0<- glue->self
+    lea         -20(%esp), %esp
+
+   /*
+    * call dvmFindCatchBlock, %eax gets catchRelPc (a code-unit offset)
+    */
+
+    call        dvmFindCatchBlock       # call: (Thread* self, int relPc, Object* exception,
+                                        #      bool doUnroll, void** newFrame)
+                                        # return: int
+    lea         32(%esp), %esp
+    movl        -12(%esp), rFP          # rFP<- updated rFP
+    cmp         $0, %eax               # check for catchRelPc < 0
+    jl          .LnotCaughtLocally      # handle not caught locally
+
+   /*
+    * fix stack overflow if necessary
+    */
+
+    movl        -4(%esp), %ecx          # %ecx<- glue->self
+    cmp         $0, offThread_stackOverflowed(%ecx)
+    je          1f
+    movl        %eax, -4(%esp)          # save %eax for later
+    movl        %ecx, -12(%esp)         # push parameter 2 glue->self
+    lea         -12(%esp), %esp
+    call        dvmCleanupStackOverflow # call: (Thread* self, Object* exception)
+                                        # return: void
+    lea         12(%esp), %esp
+    movl        -4(%esp), %eax          # %eax<- restore %eax
+    jmp         2f
+1:
+    movl        %ecx, -12(%esp)         # push parameter 2 glue->self
+2:
+
+   /*
+    * adjust locals to match self->curFrame and updated PC
+    *
+    */
+
+    SAVEAREA_FROM_FP %edx               # %edx<- get newSaveArea
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offStackSaveArea_method(%edx), rPC # rPC<- newMethod
+    movl        rPC, offGlue_method(%ecx) # glue->method<- newMethod
+    movl        offMethod_clazz(rPC), %edx # %edx<- method->clazz
+    movl        offMethod_insns(rPC), rPC # rPC<- method->insns
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    lea         (rPC, %eax, 2), rPC     # rPC<- method->insns + catchRelPc
+    movl        %edx, offGlue_methodClassDex(%ecx) # glue->pDvmDex<- method->clazz->pDvmDex
+    movl        -8(%esp), %eax
+    movl        %eax, -16(%esp)         # push parameter 1 obj
+    lea         -16(%esp), %esp
+    call        dvmReleaseTrackedAlloc  # call: (Object* obj, Thread* self)
+                                        # return: void
+    lea         16(%esp), %esp
+    FINISH_FETCH %eax
+    cmp         $OP_MOVE_EXCEPTION, %eax # is it a move exception
+    jne         1f
+    movl        -12(%esp), %edx         # %edx<- glue->self
+    movl        -8(%esp), %ecx          # %ecx<- exception
+    movl        %ecx, offThread_exception(%edx) # restore the exception
+1:
+    FINISH_JMP  %eax
+
+   /*
+    * -8(%esp) = exception, -4(%esp) = self
+    */
+
+.LnotCaughtLocally:
+    movl        -4(%esp), %edx          # %edx<- glue->self
+    movzb       offThread_stackOverflowed(%edx), %eax # %eax<- self->stackOverflowed
+    cmp         $0, %eax               # check for stack overflow;
+                                        # maybe should use cmpb
+    je          1f                      #
+    movl        %edx, -12(%esp)         # push parameter 1 glue->self
+    lea         -12(%esp), %esp
+    call        dvmCleanupStackOverflow # call: (Thread* self, Object* exception)
+                                        # return: void
+    lea         12(%esp), %esp
+
+   /*
+    * Release the exception
+    * -8(%esp) = exception, -4(%esp) = self
+    */
+1:
+    movl        -8(%esp), %ecx          # %ecx<- exception
+    movl        -4(%esp), %edx          # %edx<- glue->self
+    movl        %ecx, offThread_exception(%edx) # glue->self<- exception
+    lea         -8(%esp), %esp
+    call        dvmReleaseTrackedAlloc  # call: (Object* obj, Thread* self)
+                                        # return: void
+    lea         8(%esp), %esp
+    movl        $0, %edx               # switch to interp<- false
+    jmp         common_gotoBail         # bail
+
+   /*
+    * After returning from a "glued" function, pull out the updated
+    * values and start executing at the next instruction.
+    */
+
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_GLUE                # pull rPC and rFP out of glue
+    FINISH_A                            # jump to next instruction
+
+   /*
+    * For debugging, cause an immediate fault.
+    */
+
+common_abort:
+    jmp         .LdeadFood
+
+.LdeadFood:
+.int 0xdeadf00d
+
+   /*
+    * Invalid array index.
+    */
+
+common_errArrayIndex:
+    EXPORT_PC
+    movl        $.LstrArrayIndexException, -8(%esp) # push parameter description
+    movl        $0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Invalid array value.
+    */
+
+common_errArrayStore:
+    EXPORT_PC
+    movl        $.LstrArrayStoreException, -8(%esp) # push parameter description
+    movl        $0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Integer divide or mod by zero.
+    */
+
+common_errDivideByZero:
+    EXPORT_PC
+    movl        $.LstrArithmeticException, -8(%esp) # push parameter description
+    movl        $.LstrDivideByZero, -4(%esp) # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Attempt to allocate an array with a negative size.
+    */
+
+common_errNegativeArraySize:
+    EXPORT_PC
+    movl        $.LstrNegativeArraySizeException, -8(%esp) # push parameter description
+    movl        $0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Invocation of a non-existent method.
+    */
+
+common_errNoSuchMethod:
+    EXPORT_PC
+    movl        $.LstrNoSuchMethodError, -8(%esp) # push parameter description
+    movl        $0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Unexpected null object.
+    */
+
+common_errNullObject:
+    EXPORT_PC
+    movl        $.LstrNullPointerException, -8(%esp) # push parameter description
+    movl        $0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * String references
+    */
+
+    .align 4
+    .section .rodata
+.LstrArithmeticException:
+    .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+    .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+    .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+    .asciz "divide by zero"
+.LstrInstantiationError:
+    .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+    .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+    .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+    .asciz "Ljava/lang/NullPointerException;"
+.LstrExceptionNotCaughtLocally:
+    .asciz "Exception %s from %s:%d not caught locally\n"
+
diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S
new file mode 100644
index 0000000..92b2d9d
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-x86.S
@@ -0,0 +1,9425 @@
+/*
+ * 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)
+
+Alignment of stack not strictly required, but should be for performance.  We'll
+align frame sizes to 16-byte multiples.
+
+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 used useful for those assigned
+to callee save registers).
+
+  nick     reg   purpose
+  rPC      edx   interpreted program counter, used for fetching instructions
+  rFP      esi   interpreted frame pointer, used for accessing locals and args
+  rIBASE   edi   Base pointer for instruction dispatch computed goto
+  rINST    bx    first 16-bit code of current instruction
+  rOPCODE  bl    opcode portion of instruction word
+  rINST_HI bh    high byte of instruction word, usually contains src/tgt reg names
+
+Notes:
+   o High order 16 bits of ebx must be zero on entry to handler
+   o rPC, rFP, rIBASE, rINST/rOPCODE valid on handler entry and exit
+   o eax and ecx are scratch, rINST/ebx sometimes scratch
+   o rPC is in the caller save set, and will be killed across external calls. Don't
+     forget to SPILL/UNSPILL it around call points
+
+*/
+
+#define rPC      %edx
+#define rFP      %esi
+#define rIBASE   %edi
+#define rINST_FULL %ebx
+#define rINST    %bx
+#define rINST_HI %bh
+#define rINST_LO %bl
+#define rOPCODE  %bl
+
+
+/* Frame diagram while executing dvmMterpStdRun, high to low addresses */
+#define IN_ARG0        (  8)
+#define CALLER_RP      (  4)
+#define PREV_FP        (  0) /* <- dvmMterpStdRun ebp */
+/* Spill offsets relative to %ebp */
+#define EDI_SPILL      ( -4)
+#define ESI_SPILL      ( -8)
+#define EDX_SPILL      (-12) /* <- esp following dmMterpStdRun header */
+#define rPC_SPILL      (-16)
+#define rFP_SPILL      (-20)
+#define rGLUE_SPILL    (-24)
+#define rIBASE_SPILL   (-28)
+#define rINST_FULL_SPILL    (-32)
+#define TMP_SPILL      (-36)
+#define LOCAL0_OFFSET  (-40)
+#define LOCAL1_OFFSET  (-44)
+#define LOCAL2_OFFSET  (-48)
+#define LOCAL3_OFFSET  (-52)
+/* Out Arg offsets, relative to %sp */
+#define OUT_ARG4       ( 16)
+#define OUT_ARG3       ( 12)
+#define OUT_ARG2       (  8)
+#define OUT_ARG1       (  4)
+#define OUT_ARG0       (  0)  /* <- dvmMterpStdRun esp */
+
+#define SPILL(reg) movl reg##,reg##_SPILL(%ebp)
+#define UNSPILL(reg) movl reg##_SPILL(%ebp),reg
+#define SPILL_TMP(reg) movl reg,TMP_SPILL(%ebp)
+#define UNSPILL_TMP(reg) movl TMP_SPILL(%ebp),reg
+
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE(_glu)     movl    offGlue_pc(_glu),rPC
+#define SAVE_PC_TO_GLUE(_glu)       movl    rPC,offGlue_pc(_glu)
+#define LOAD_FP_FROM_GLUE(_glu)     movl    offGlue_fp(_glu),rFP
+#define SAVE_FP_TO_GLUE(_glu)       movl    rFP,offGlue_fp(_glu)
+
+#define GET_GLUE(_reg)     movl   rGLUE_SPILL(%ebp),_reg
+
+/* 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 calls dvmThrowException.
+ *
+ * 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() \
+    movl     rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    leal    -sizeofStackSaveArea(_fpreg),_reg
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            movzwl    (rPC),rINST_FULL
+
+/*
+ * Fetch the nth instruction word from rPC into rINST.  Does not advance
+ * rPC, and _count is in words
+ */
+#define FETCH_INST_WORD(_count)  movzwl  _count*2(rPC),rINST_FULL
+
+/*
+ * Fetch instruction word indexed (used for branching).
+ * Index is in instruction word units.
+ */
+#define FETCH_INST_INDEXED(_reg) movzwl  (rPC,_reg,2),rINST_FULL
+
+/*
+ * Extract the opcode of the instruction in rINST
+ */
+#define EXTRACT_OPCODE(_reg)   movzx rOPCODE,_reg
+
+/*
+ * Advance rPC by instruction count
+ */
+#define ADVANCE_PC(_count)    leal  2*_count(rPC),rPC
+
+/*
+ * Advance rPC by branch offset in register
+ */
+#define ADVANCE_PC_INDEXED(_reg) leal (rPC,_reg,2),rPC
+
+/*
+ * Note: assumes opcode previously fetched and in rINST, and
+ *       %eax is killable at this point.
+ */
+#if 1
+.macro GOTO_NEXT
+    /* For computed next version */
+     movzx    rOPCODE,%eax
+     sall     $6,%eax
+     addl     rIBASE,%eax
+     jmp      *%eax
+.endm
+#else
+   /* For jump table version */
+.macro GOTO_NEXT
+     movzx   rOPCODE,%eax
+     jmp     *(rIBASE,%eax,4)
+.endm
+#endif
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   movl     (rFP,_vreg,4),_reg
+#define SET_VREG(_reg, _vreg)   movl     _reg,(rFP,_vreg,4)
+#define GET_VREG_WORD(_reg, _vreg, _offset)   movl     4*(_offset)(rFP,_vreg,4),_reg
+#define SET_VREG_WORD(_reg, _vreg, _offset)   movl     _reg,4*(_offset)(rFP,_vreg,4)
+
+/*
+ * 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"
+
+
+    .global dvmAsmInstructionStart
+    .type   dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+    .text
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: x86/OP_NOP.S */
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: x86/OP_MOVE.S */
+    /* for move, move-object, long-to-int */
+    /* op vA, vB */
+    movzbl rINST_HI,%eax         # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $12,rINST_FULL       # rINST_FULL<- B
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG(%ecx,%eax)          # fp[A]<-fp[B]
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: x86/OP_MOVE_FROM16.S */
+    /* for: move/from16, move-object/from16 */
+    /* op vAA, vBBBB */
+    movzx    rINST_HI,%eax            # eax <= AA
+    movw     2(rPC),rINST             # rINST <= BBBB
+    GET_VREG (%ecx,rINST_FULL)        # ecx<- fp[BBBB]
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG (%ecx,%eax)              # fp[AA]<- ecx]
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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(%ecx,%ecx)
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    SET_VREG(%ecx,%eax)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx                # ecx <- BA
+    sarl      $12,rINST_FULL              # rinst_FULL<- B
+    GET_VREG_WORD(%eax,rINST_FULL,0)       # eax<- v[B+0]
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- v[B+1]
+    andb      $0xf,%cl                    # ecx <- A
+    SET_VREG_WORD(rINST_FULL,%ecx,1)       # v[A+1]<- rINST_FULL
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG_WORD(%eax,%ecx,0)             # v[A+0]<- eax
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%eax            # eax<- AAAA
+    GET_VREG_WORD(rINST_FULL,%ecx,0)   # rINST_FULL<- v[BBBB+0]
+    GET_VREG_WORD(%ecx,%ecx,1)         # ecx<- v[BBBB+1]
+    SET_VREG_WORD(rINST_FULL,%eax,0)   # v[AAAA+0]<- rINST_FULL
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG_WORD(%ecx,%eax,1)         # v[AAAA+1]<- eax
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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_FULL,%ecx,0) # rINST_WORD<- v[BBBB+0]
+    GET_VREG_WORD(%ecx,%ecx,1)       # ecx<- v[BBBB+1]
+    SET_VREG_WORD(rINST_FULL,%eax,0) # v[AAAA+0]<- rINST_FULL
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    SET_VREG_WORD(%ecx,%eax,1)       # v[AAAA+1]<- ecx
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 rINST_HI,%eax         # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $12,rINST_FULL       # rINST_FULL<- B
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG(%ecx,%eax)          # fp[A]<-fp[B]
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%eax            # eax <= AA
+    movw     2(rPC),rINST             # rINST <= BBBB
+    GET_VREG (%ecx,rINST_FULL)        # ecx<- fp[BBBB]
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG (%ecx,%eax)              # fp[AA]<- ecx]
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%ecx,%ecx)
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    SET_VREG(%ecx,%eax)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: x86/OP_MOVE_RESULT.S */
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    GET_GLUE(%eax)                         # eax<- rGLUE
+    movzx    rINST_HI,%ecx                 # ecx<- AA
+    movl     offGlue_retval(%eax),%eax     # eax<- glue->retval.l
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG (%eax,%ecx)                   # fp[AA]<- retval.l
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: x86/OP_MOVE_RESULT_WIDE.S */
+    /* move-result-wide vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    movl    offGlue_retval(%ecx),%eax
+    movl    4+offGlue_retval(%ecx),%ecx
+    SET_VREG_WORD(%eax,rINST_FULL,0)    # v[AA+0] <- eax
+    SET_VREG_WORD(%ecx,rINST_FULL,1)    # v[AA+1] <- ecx
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%eax)                         # eax<- rGLUE
+    movzx    rINST_HI,%ecx                 # ecx<- AA
+    movl     offGlue_retval(%eax),%eax     # eax<- glue->retval.l
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG (%eax,%ecx)                   # fp[AA]<- retval.l
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: x86/OP_MOVE_EXCEPTION.S */
+    /* move-exception vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL        # rINST_FULL<- AA
+    movl    offGlue_self(%ecx),%ecx    # ecx<- glue->self
+    movl    offThread_exception(%ecx),%eax # eax<- dvmGetException bypass
+    SET_VREG(%eax,rINST_FULL)          # fp[AA]<- exception object
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    movl    $0,offThread_exception(%ecx) # dvmClearException bypass
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: x86/OP_RETURN_VOID.S */
+    jmp       common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: x86/OP_RETURN.S */
+    /*
+     * Return a 32-bit value.  Copies the return value into the "glue"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)           # eax<- vAA
+    movl    %eax,offGlue_retval(%ecx)   # retval.i <- AA
+    jmp     common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: x86/OP_RETURN_WIDE.S */
+    /*
+     * Return a 64-bit value.  Copies the return value into the "glue"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL            # rINST_FULL<- AA
+    GET_VREG_WORD(%eax,rINST_FULL,0)       # eax<- v[AA+0]
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- v[AA+1]
+    movl    %eax,offGlue_retval(%ecx)
+    movl    rINST_FULL,4+offGlue_retval(%ecx)
+    jmp     common_returnFromMethod
+
+/* ------------------------------ */
+    .balign 64
+.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 "glue"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)           # eax<- vAA
+    movl    %eax,offGlue_retval(%ecx)   # retval.i <- AA
+    jmp     common_returnFromMethod
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: x86/OP_CONST_4.S */
+    /* const/4 vA, #+B */
+    movsx   rINST_HI,%eax              # eax<-ssssssBx
+    movl    $0xf,%ecx
+    andl    %eax,%ecx                  # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    sarl    $4,%eax
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: x86/OP_CONST_16.S */
+    /* const/16 vAA, #+BBBB */
+    movswl  2(rPC),%ecx                # ecx<- ssssBBBB
+    movzx   rINST_HI,%eax              # eax<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%ecx,%eax)                # vAA<- ssssBBBB
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: x86/OP_CONST.S */
+    /* const vAA, #+BBBBbbbb */
+    movzbl    rINST_HI,%ecx           # ecx<- AA
+    movl      2(rPC),%eax               # grab all 32 bits at once
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    SET_VREG(%eax,%ecx)                 # vAA<- eax
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: x86/OP_CONST_HIGH16.S */
+    /* const/high16 vAA, #+BBBB0000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    movzbl     rINST_HI,%ecx              # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    sall       $16,%eax                  # eax<- BBBB0000
+    SET_VREG(%eax,%ecx)                   # vAA<- eax
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    movzbl    rINST_HI,%ecx             # ecx<- AA
+    FETCH_INST_WORD(2)
+    cltd                                # rPC:eax<- ssssssssssssBBBB
+    SET_VREG_WORD(rPC,%ecx,1)           # store msw
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)          # store lsw
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    movzbl    rINST_HI,%ecx             # ecx<- AA
+    FETCH_INST_WORD(3)
+    cltd                                # rPC:eax<- ssssssssssssBBBB
+    SET_VREG_WORD(rPC,%ecx,1)           # store msw
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)          # store lsw
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: x86/OP_CONST_WIDE.S */
+    /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+    movl      2(rPC),%eax         # eax<- lsw
+    movzbl    rINST_HI,%ecx       # ecx <- AA
+    movl      6(rPC),rINST_FULL   # rINST_FULL<- msw
+    leal      (rFP,%ecx,4),%ecx   # dst addr
+    movl      rINST_FULL,4(%ecx)
+    FETCH_INST_WORD(5)
+    movl      %eax,(%ecx)
+    ADVANCE_PC(5)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: x86/OP_CONST_WIDE_HIGH16.S */
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    movzbl     rINST_HI,%ecx              # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    sall       $16,%eax                  # eax<- BBBB0000
+    SET_VREG_WORD(%eax,%ecx,1)            # v[AA+1]<- eax
+    xorl       %eax,%eax
+    SET_VREG_WORD(%eax,%ecx,0)            # v[AA+0]<- eax
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: x86/OP_CONST_STRING.S */
+
+    /* const/string vAA, String@BBBB */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+    movzbl    rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_STRING_resolve
+    SET_VREG(%eax,%ecx)                # vAA<- rResString[BBBB]
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: x86/OP_CONST_STRING_JUMBO.S */
+
+    /* const/string vAA, String@BBBBBBBB */
+    GET_GLUE(%ecx)
+    movl      2(rPC),%eax              # eax<- BBBBBBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+    movzbl    rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(3)
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_STRING_JUMBO_resolve
+    SET_VREG(%eax,%ecx)                # vAA<- rResString[BBBB]
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: x86/OP_CONST_CLASS.S */
+
+    /* const/class vAA, Class@BBBB */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+    movzbl    rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- dvmDex->pResClasses
+    movl      (%ecx,%eax,4),%eax       # eax<- rResClasses[BBBB]
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    testl     %eax,%eax                # resolved yet?
+    je        .LOP_CONST_CLASS_resolve
+    SET_VREG(%eax,%ecx)                # vAA<- rResClasses[BBBB]
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: x86/OP_MONITOR_ENTER.S */
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)           # eax<- vAA
+    movl    offGlue_self(%ecx),%ecx     # ecx<- glue->self
+    FETCH_INST_WORD(1)
+    testl   %eax,%eax                   # null object?
+    EXPORT_PC()                         # need for precise GC, MONITOR_TRACKING
+    jne     .LOP_MONITOR_ENTER_continue
+    jmp     common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)
+    GET_GLUE(%ecx)
+    EXPORT_PC()
+    testl   %eax,%eax                   # null object?
+    je      .LOP_MONITOR_EXIT_errNullObject   # go if so
+    movl    offGlue_self(%ecx),%ecx     # ecx<- glue->self
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    movl    %ecx,OUT_ARG0(%esp)
+    jmp     .LOP_MONITOR_EXIT_continue
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- vAA (object)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    testl     rINST_FULL,rINST_FULL     # 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_FULL),%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_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzbl  rINST_HI,%eax               # eax<- BA
+    sarl    $4,%eax                    # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB (obj)
+    GET_GLUE(%ecx)
+    testl   %eax,%eax                   # object null?
+    movl    offGlue_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rPC)
+    je      .LOP_INSTANCE_OF_store           # null obj, not instance, store it
+    movzwl  2(rPC),rPC                  # rPC<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    movl    (%ecx,rPC,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
+    jmp     .LOP_INSTANCE_OF_fullcheck       # no, do full check
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: x86/OP_ARRAY_LENGTH.S */
+    /*
+     * Return the length of an array.
+     */
+   movzbl   rINST_HI,%eax              # eax<- BA
+   sarl     $12,rINST_FULL            # rINST_FULL<- B
+   GET_VREG(%ecx,rINST_FULL)           # ecx<- vB (object ref)
+   andb     $0xf,%al                  # eax<- A
+   testl    %ecx,%ecx                  # is null?
+   je       common_errNullObject
+   FETCH_INST_WORD(1)
+   movl     offArrayObject_length(%ecx),%ecx
+   ADVANCE_PC(1)
+   SET_VREG(%ecx,%eax)
+   GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: x86/OP_NEW_INSTANCE.S */
+    /*
+     * Create a new instance of a class.
+     */
+    /* new-instance vAA, class@BBBB */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    EXPORT_PC()
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved class
+    SPILL(rPC)
+    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)
+    je        .LOP_NEW_INSTANCE_initialized
+    jmp       .LOP_NEW_INSTANCE_needinit
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    EXPORT_PC()
+    movl    offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    movzwl  2(rPC),%eax                       # eax<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx  # ecx<- pDvmDex->pResClasses
+    movl    (%ecx,%eax,4),%ecx                # ecx<- resolved class
+    movzbl  rINST_HI,%eax
+    sarl    $4,%eax                          # eax<- B
+    GET_VREG(%eax,%eax)                       # eax<- vB (array length)
+    movzbl  rINST_HI,rINST_FULL
+    andb    $0xf,rINST_LO                    # rINST_FULL<- A
+    testl   %eax,%eax
+    js      common_errNegativeArraySize       # bail
+    testl   %ecx,%ecx                         # already resolved?
+    jne     .LOP_NEW_ARRAY_finish                # yes, fast path
+    jmp     .LOP_NEW_ARRAY_resolve               # resolve now
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%eax)
+    movzbl  rINST_HI,rINST_FULL               # rINST_FULL<- AA or BA
+    movl    offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rPC)
+    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
+    GET_GLUE(%eax)
+    movl    $0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offGlue_method(%eax),%eax         # eax<- glue->method
+    jmp     .LOP_FILLED_NEW_ARRAY_more
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%eax)
+    movzbl  rINST_HI,rINST_FULL               # rINST_FULL<- AA or BA
+    movl    offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rPC)
+    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
+    GET_GLUE(%eax)
+    movl    $0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offGlue_method(%eax),%eax         # eax<- glue->method
+    jmp     .LOP_FILLED_NEW_ARRAY_RANGE_more
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: x86/OP_FILL_ARRAY_DATA.S */
+    /* fill-array-data vAA, +BBBBBBBB */
+    movl    2(rPC),%ecx                # ecx<- BBBBbbbb
+    movzbl  rINST_HI,rINST_FULL        # rINST_FULL<- AA
+    leal    (rPC,%ecx,2),%ecx          # ecx<- PC + BBBBbbbb*2
+    GET_VREG(%eax,rINST_FULL)
+    SPILL(rPC)
+    EXPORT_PC()
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmInterpHandleFillArrayData
+    UNSPILL(rPC)
+    FETCH_INST_WORD(3)
+    testl   %eax,%eax                   # exception thrown?
+    je      common_exceptionThrown
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: x86/OP_THROW.S */
+    /*
+     * Throw an exception object in the current thread.
+     */
+    /* throw vAA */
+    GET_GLUE(%ecx)
+    EXPORT_PC()
+    movzbl   rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)          # eax<- exception object
+    movl     offGlue_self(%ecx),%ecx   # ecx<- glue->self
+    testl    %eax,%eax                 # null object?
+    je       common_errNullObject
+    movl     %eax,offThread_exception(%ecx) # thread->exception<- obj
+    jmp      common_exceptionThrown
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movsbl  rINST_HI,rINST_FULL         # ebx<- ssssssAA
+    testl   rINST_FULL,rINST_FULL       # test for <0
+    js      common_backwardBranch
+    movl    rINST_FULL,%eax
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movswl  2(rPC),rINST_FULL           # rINST_FULL<- ssssAAAA
+    testl   rINST_FULL,rINST_FULL       # test for <0
+    js      common_backwardBranch
+    movl    rINST_FULL,%eax
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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.
+     *
+     * 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 */
+    movl    2(rPC),rINST_FULL           # rINST_FULL<- AAAAAAAA
+    cmpl    $0,rINST_FULL              # test for <= 0
+    jle     common_backwardBranch
+    movl    rINST_FULL,%eax
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    movl    2(rPC),%ecx                 # ecx<- BBBBbbbb
+    GET_VREG(%eax,rINST_FULL)           # 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
+    SPILL(rPC)
+    call    dvmInterpHandlePackedSwitch
+    UNSPILL(rPC)
+    testl   %eax,%eax
+    movl    %eax,rINST_FULL             # set up word offset
+    jle     common_backwardBranch       # check on special actions
+    ADVANCE_PC_INDEXED(rINST_FULL)
+    FETCH_INST()
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    movl    2(rPC),%ecx                 # ecx<- BBBBbbbb
+    GET_VREG(%eax,rINST_FULL)           # 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
+    SPILL(rPC)
+    call    dvmInterpHandleSparseSwitch
+    UNSPILL(rPC)
+    testl   %eax,%eax
+    movl    %eax,rINST_FULL             # set up word offset
+    jle     common_backwardBranch       # check on special actions
+    ADVANCE_PC_INDEXED(rINST_FULL)
+    FETCH_INST()
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,rINST_FULL
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    movl      rINST_FULL,%eax
+    FETCH_INST_WORD(2)
+    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,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,rINST_FULL
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    movl      rINST_FULL,%eax
+    FETCH_INST_WORD(2)
+    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,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,rINST_FULL
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    movl      rINST_FULL,%eax
+    FETCH_INST_WORD(2)
+    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,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,rINST_FULL
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    movl      rINST_FULL,%eax
+    FETCH_INST_WORD(2)
+    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,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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.
+     */
+    /* cmp-long vAA, vBB, vCC */
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rPC)
+    movzbl    3(rPC),rPC               # rPC<- CC
+    GET_VREG_WORD(%eax,%ecx,1)         # eax<- v[BB+1]
+    GET_VREG_WORD(%ecx,%ecx,0)         # ecx<- v[BB+0]
+    movzbl    rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    cmpl      4(rFP,rPC,4),%eax
+    jl        .LOP_CMP_LONG_smaller
+    jg        .LOP_CMP_LONG_bigger
+    sub       (rFP,rPC,4),%ecx
+    ja        .LOP_CMP_LONG_bigger
+    jb        .LOP_CMP_LONG_smaller
+    UNSPILL(rPC)
+    jmp       .LOP_CMP_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx              # ecx <- A+
+    andb     $0xf,%cl                  # ecx <- A
+    GET_VREG(%eax,%ecx)                 # eax <- vA
+    sarl     $12,rINST_FULL            # rINST_FULL<- B
+    cmpl     (rFP,rINST_FULL,4),%eax    # compare (vA, vB)
+    movswl   2(rPC),rINST_FULL          # Get signed branch offset
+    movl     $2,%eax                   # assume not taken
+    jne   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx              # ecx <- A+
+    andb     $0xf,%cl                  # ecx <- A
+    GET_VREG(%eax,%ecx)                 # eax <- vA
+    sarl     $12,rINST_FULL            # rINST_FULL<- B
+    cmpl     (rFP,rINST_FULL,4),%eax    # compare (vA, vB)
+    movswl   2(rPC),rINST_FULL          # Get signed branch offset
+    movl     $2,%eax                   # assume not taken
+    je   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx              # ecx <- A+
+    andb     $0xf,%cl                  # ecx <- A
+    GET_VREG(%eax,%ecx)                 # eax <- vA
+    sarl     $12,rINST_FULL            # rINST_FULL<- B
+    cmpl     (rFP,rINST_FULL,4),%eax    # compare (vA, vB)
+    movswl   2(rPC),rINST_FULL          # Get signed branch offset
+    movl     $2,%eax                   # assume not taken
+    jge   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx              # ecx <- A+
+    andb     $0xf,%cl                  # ecx <- A
+    GET_VREG(%eax,%ecx)                 # eax <- vA
+    sarl     $12,rINST_FULL            # rINST_FULL<- B
+    cmpl     (rFP,rINST_FULL,4),%eax    # compare (vA, vB)
+    movswl   2(rPC),rINST_FULL          # Get signed branch offset
+    movl     $2,%eax                   # assume not taken
+    jl   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx              # ecx <- A+
+    andb     $0xf,%cl                  # ecx <- A
+    GET_VREG(%eax,%ecx)                 # eax <- vA
+    sarl     $12,rINST_FULL            # rINST_FULL<- B
+    cmpl     (rFP,rINST_FULL,4),%eax    # compare (vA, vB)
+    movswl   2(rPC),rINST_FULL          # Get signed branch offset
+    movl     $2,%eax                   # assume not taken
+    jle   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx              # ecx <- A+
+    andb     $0xf,%cl                  # ecx <- A
+    GET_VREG(%eax,%ecx)                 # eax <- vA
+    sarl     $12,rINST_FULL            # rINST_FULL<- B
+    cmpl     (rFP,rINST_FULL,4),%eax    # compare (vA, vB)
+    movswl   2(rPC),rINST_FULL          # Get signed branch offset
+    movl     $2,%eax                   # assume not taken
+    jg   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzx    rINST_HI,%ecx             # ecx <- AA
+    cmpl     $0,(rFP,%ecx,4)          # compare (vA, 0)
+    movswl   2(rPC),rINST_FULL         # fetch signed displacement
+    movl     $2,%eax                  # assume branch not taken
+    jne   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzx    rINST_HI,%ecx             # ecx <- AA
+    cmpl     $0,(rFP,%ecx,4)          # compare (vA, 0)
+    movswl   2(rPC),rINST_FULL         # fetch signed displacement
+    movl     $2,%eax                  # assume branch not taken
+    je   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzx    rINST_HI,%ecx             # ecx <- AA
+    cmpl     $0,(rFP,%ecx,4)          # compare (vA, 0)
+    movswl   2(rPC),rINST_FULL         # fetch signed displacement
+    movl     $2,%eax                  # assume branch not taken
+    jge   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzx    rINST_HI,%ecx             # ecx <- AA
+    cmpl     $0,(rFP,%ecx,4)          # compare (vA, 0)
+    movswl   2(rPC),rINST_FULL         # fetch signed displacement
+    movl     $2,%eax                  # assume branch not taken
+    jl   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzx    rINST_HI,%ecx             # ecx <- AA
+    cmpl     $0,(rFP,%ecx,4)          # compare (vA, 0)
+    movswl   2(rPC),rINST_FULL         # fetch signed displacement
+    movl     $2,%eax                  # assume branch not taken
+    jle   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    movzx    rINST_HI,%ecx             # ecx <- AA
+    cmpl     $0,(rFP,%ecx,4)          # compare (vA, 0)
+    movswl   2(rPC),rINST_FULL         # fetch signed displacement
+    movl     $2,%eax                  # assume branch not taken
+    jg   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: x86/OP_UNUSED_3E.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: x86/OP_UNUSED_3F.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: x86/OP_UNUSED_40.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: x86/OP_UNUSED_41.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: x86/OP_UNUSED_42.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: x86/OP_UNUSED_43.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    movl     offArrayObject_contents(%eax,%ecx,4),%eax
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%ecx,%ecx)                 # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jb        .LOP_AGET_WIDE_finish        # index < length, OK
+    jmp       common_errArrayIndex      # index >= length, bail
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    movl     offArrayObject_contents(%eax,%ecx,4),%eax
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    movzbl     offArrayObject_contents(%eax,%ecx,1),%eax
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    movsbl     offArrayObject_contents(%eax,%ecx,1),%eax
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    movzwl     offArrayObject_contents(%eax,%ecx,2),%eax
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    movswl     offArrayObject_contents(%eax,%ecx,2),%eax
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    leal      offArrayObject_contents(%eax,%ecx,4),%eax
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    movl     %ecx,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%ecx,%ecx)                 # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jb        .LOP_APUT_WIDE_finish        # index < length, OK
+    jmp       common_errArrayIndex      # index >= length, bail
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%ecx,%ecx)                 # ecs<- vCC (requested index)
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- vAA
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jb        .LOP_APUT_OBJECT_continue
+    jmp       common_errArrayIndex      # index >= length, bail
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    leal      offArrayObject_contents(%eax,%ecx,1),%eax
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    movb     %cl,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    leal      offArrayObject_contents(%eax,%ecx,1),%eax
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    movb     %cl,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    leal      offArrayObject_contents(%eax,%ecx,2),%eax
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    movw     %cx,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    leal      offArrayObject_contents(%eax,%ecx,2),%eax
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    movw     %cx,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: x86/OP_IGET_WIDE.S */
+    /*
+     * 64-bit instance field get.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)                 # needed by dvmResolveInstField
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_WIDE_resolve
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_OBJECT_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_BOOLEAN_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_BYTE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_CHAR_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_SHORT_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: x86/OP_IPUT_WIDE.S */
+    /*
+     * 64-bit instance field put.
+     *
+     */
+    /* op vA, vB, field@CCCC */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_WIDE_resolve
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: x86/OP_IPUT_OBJECT.S */
+    /*
+     * Object field put.
+     *
+     * for: iput-object
+     */
+    /* op vA, vB, field@CCCC */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_OBJECT_resolve
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_BOOLEAN_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_BYTE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_CHAR_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_SHORT_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: x86/OP_SGET_WIDE.S */
+    /*
+     * 64-bit SGET handler.
+     *
+     */
+    /* sget-wide vAA, field@BBBB */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,rINST_FULL                # rINST_FULL<- AA
+    SET_VREG_WORD(%ecx,rINST_FULL,0)
+    SET_VREG_WORD(%eax,rINST_FULL,1)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    FETCH_INST_WORD(2)
+    movl      %ecx,offStaticField_value(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG_WORD(rINST_FULL,%ecx,0)             # rINST_FULL<- lsw
+    GET_VREG_WORD(%ecx,%ecx,1)                   # ecx<- msw
+    movl      rINST_FULL,offStaticField_value(%eax)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    movl      %ecx,4+offStaticField_value(%eax)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: x86/OP_SPUT_OBJECT.S */
+    /*
+     * SPUT object handler.
+     */
+    /* op vAA, field@BBBB */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    jmp       .LOP_SPUT_OBJECT_continue
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    FETCH_INST_WORD(2)
+    movl      %ecx,offStaticField_value(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    FETCH_INST_WORD(2)
+    movl      %ecx,offStaticField_value(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    FETCH_INST_WORD(2)
+    movl      %ecx,offStaticField_value(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    FETCH_INST_WORD(2)
+    movl      %ecx,offStaticField_value(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%eax)
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offGlue_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
+    GET_GLUE(%eax)
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offGlue_method(%eax),%eax   # eax<- glue->method
+    SPILL(rPC)
+    jmp       .LOP_INVOKE_VIRTUAL_more
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(rINST_FULL)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(rINST_FULL),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offGlue_method(rINST_FULL),%eax # eax<- method
+    movzwl    4(rPC),rINST_FULL         # rINST_FULL<- GFED or CCCC
+    .if       (!0)
+    andl      $0xf,rINST_FULL          # rINST_FULL<- D (or stays CCCC)
+    .endif
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- "this" ptr
+    testl     rINST_FULL,rINST_FULL     # null "this"?
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    jne       .LOP_INVOKE_SUPER_continue      # yes - go on
+    jmp       .LOP_INVOKE_SUPER_resolve
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    SPILL(rPC)
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rPC               # rPC<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!0)
+    andl      $0xf,rPC                # rPC<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG(%ecx,rPC)                 # ecx<- "this" ptr
+    je        .LOP_INVOKE_DIRECT_resolve      # not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+    UNSPILL(rPC)
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethodNoRange  # no, continue on
+    jmp       common_errNullObject
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    testl     %eax,%eax
+    jne       common_invokeMethodNoRange
+    GET_GLUE(%ecx)
+    movl      offGlue_method(%ecx),%ecx # ecx<- glue->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
+    jmp       .LOP_INVOKE_STATIC_continue
+
+/* ------------------------------ */
+    .balign 64
+.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
+    GET_GLUE(%ecx)
+    .if        (!0)
+    andl       $0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG(%eax,%eax)                 # eax<- "this"
+    EXPORT_PC()
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offGlue_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offGlue_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
+    SPILL(rPC)
+    jmp        .LOP_INVOKE_INTERFACE_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: x86/OP_UNUSED_73.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%eax)
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offGlue_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
+    GET_GLUE(%eax)
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offGlue_method(%eax),%eax   # eax<- glue->method
+    SPILL(rPC)
+    jmp       .LOP_INVOKE_VIRTUAL_RANGE_more
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(rINST_FULL)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(rINST_FULL),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offGlue_method(rINST_FULL),%eax # eax<- method
+    movzwl    4(rPC),rINST_FULL         # rINST_FULL<- GFED or CCCC
+    .if       (!1)
+    andl      $0xf,rINST_FULL          # rINST_FULL<- D (or stays CCCC)
+    .endif
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- "this" ptr
+    testl     rINST_FULL,rINST_FULL     # null "this"?
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    jne       .LOP_INVOKE_SUPER_RANGE_continue      # yes - go on
+    jmp       .LOP_INVOKE_SUPER_RANGE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    SPILL(rPC)
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rPC               # rPC<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!1)
+    andl      $0xf,rPC                # rPC<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG(%ecx,rPC)                 # ecx<- "this" ptr
+    je        .LOP_INVOKE_DIRECT_RANGE_resolve      # not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+    UNSPILL(rPC)
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethodRange  # no, continue on
+    jmp       common_errNullObject
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    testl     %eax,%eax
+    jne       common_invokeMethodRange
+    GET_GLUE(%ecx)
+    movl      offGlue_method(%ecx),%ecx # ecx<- glue->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
+    jmp       .LOP_INVOKE_STATIC_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    GET_GLUE(%ecx)
+    .if        (!1)
+    andl       $0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG(%eax,%eax)                 # eax<- "this"
+    EXPORT_PC()
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offGlue_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offGlue_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
+    SPILL(rPC)
+    jmp        .LOP_INVOKE_INTERFACE_RANGE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: x86/OP_UNUSED_79.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: x86/OP_UNUSED_7A.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)        # eax<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    
+    negl %eax
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)        # eax<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    
+    notl %eax
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: x86/OP_NEG_LONG.S */
+    /* unop vA, vB */
+    movzbl    rINST_HI,%ecx            # ecx<- BA
+    sarl      $4,%ecx                 # ecx<- B
+    movzbl    rINST_HI,rINST_FULL      # ecx<- BA
+    andb      $0xf,rINST_LO           # rINST_FULL<- 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_FULL,0)   # v[A+0]<- eax
+    SET_VREG_WORD(%ecx,rINST_FULL,1)   # v[A+1]<- ecx
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: x86/OP_NOT_LONG.S */
+    /* unop vA, vB */
+    movzbl    rINST_HI,%ecx            # ecx<- BA
+    sarl      $4,%ecx                 # ecx<- B
+    movzbl    rINST_HI,rINST_FULL      # ecx<- BA
+    andb      $0xf,rINST_LO           # rINST_FULL<- 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_FULL,0)   # v[A+0]<- eax
+    SET_VREG_WORD(%ecx,rINST_FULL,1)   # v[A+1]<- ecx
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    flds    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fchs
+    fstps  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    fldl    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fchs
+    fstpl  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: x86/OP_INT_TO_LONG.S */
+    /* int to long vA, vB */
+    movzbl  rINST_HI,%ecx               # ecx<- +A
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    SPILL(rPC)                          # will step on edx later
+    andb    $0xf,%cl                   # ecx<- A
+    cltd                                # edx:eax<- sssssssBBBBBBBB
+    SET_VREG_WORD(%edx,%ecx,1)          # v[A+1]<- edx/rPC
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)          # v[A+0]<- %eax
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    fildl    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    fstps  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    fildl    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    fstpl  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 rINST_HI,%eax         # eax<- BA
+    andb   $0xf,%al             # eax<- A
+    shrl   $12,rINST_FULL       # rINST_FULL<- B
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG(%ecx,%eax)          # fp[A]<-fp[B]
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    fildll    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    fstps  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    fildll    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    fstpl  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx           # ecx<- A+
+    sarl      $12,rINST_FULL         # rINST_FULL<- B
+    .if 0
+    fldl     (rFP,rINST_FULL,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST_FULL,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
+    FETCH_INST_WORD(1)
+    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
+    jmp      .LOP_FLOAT_TO_INT_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx           # ecx<- A+
+    sarl      $12,rINST_FULL         # rINST_FULL<- B
+    .if 0
+    fldl     (rFP,rINST_FULL,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST_FULL,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
+    FETCH_INST_WORD(1)
+    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
+    jmp      .LOP_FLOAT_TO_LONG_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    flds    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    fstpl  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx           # ecx<- A+
+    sarl      $12,rINST_FULL         # rINST_FULL<- B
+    .if 1
+    fldl     (rFP,rINST_FULL,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST_FULL,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
+    FETCH_INST_WORD(1)
+    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
+    jmp      .LOP_DOUBLE_TO_INT_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx           # ecx<- A+
+    sarl      $12,rINST_FULL         # rINST_FULL<- B
+    .if 1
+    fldl     (rFP,rINST_FULL,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST_FULL,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
+    FETCH_INST_WORD(1)
+    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
+    jmp      .LOP_DOUBLE_TO_LONG_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    fldl    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    fstps  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)        # eax<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    
+    movsbl %al,%eax
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)        # eax<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    
+    movzwl %ax,%eax
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $12,rINST_FULL         # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)        # eax<- vB
+    andb     $0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    
+    
+    movswl %ax,%eax
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    addl (rFP,%ecx,4),%eax              # ex: addl    (rFP,%ecx,4),%eax
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    subl   (rFP,%ecx,4),%eax              # ex: addl    (rFP,%ecx,4),%eax
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    SPILL(rPC)
+    GET_VREG(%eax,%eax)             # eax<- vBB
+    imull    (rFP,%ecx,4),%eax      # trashes rPC/edx
+    UNSPILL(rPC)
+    movzbl   rINST_HI,%ecx          # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    SPILL(rPC)
+    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
+    jmp      .LOP_DIV_INT_finish_div
+
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    SPILL(rPC)
+    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,%edx
+    jmp      .LOP_REM_INT_finish_div
+
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    andl   (rFP,%ecx,4),%eax              # ex: addl    (rFP,%ecx,4),%eax
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    orl   (rFP,%ecx,4),%eax              # ex: addl    (rFP,%ecx,4),%eax
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    xorl   (rFP,%ecx,4),%eax              # ex: addl    (rFP,%ecx,4),%eax
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    sall    %cl,%eax                          # ex: addl    %ecx,%eax
+    movzbl   rINST_HI,%ecx          # tmp<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    sarl    %cl,%eax                          # ex: addl    %ecx,%eax
+    movzbl   rINST_HI,%ecx          # tmp<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(%eax,%eax)             # eax<- vBB
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    shrl    %cl,%eax                          # ex: addl    %ecx,%eax
+    movzbl   rINST_HI,%ecx          # tmp<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    GET_VREG_WORD(rPC,%eax,0)           # rPC<- v[BB+0]
+    GET_VREG_WORD(%eax,%eax,1)          # eax<- v[BB+1]
+    addl (rFP,%ecx,4),rPC         # ex: addl   (rFP,%ecx,4),rPC
+    adcl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    SET_VREG_WORD(rPC,rINST_FULL,0)     # v[AA+0] <- rPC
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,rINST_FULL,1)    # v[AA+1] <- eax
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    GET_VREG_WORD(rPC,%eax,0)           # rPC<- v[BB+0]
+    GET_VREG_WORD(%eax,%eax,1)          # eax<- v[BB+1]
+    subl (rFP,%ecx,4),rPC         # ex: addl   (rFP,%ecx,4),rPC
+    sbbl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    SET_VREG_WORD(rPC,rINST_FULL,0)     # v[AA+0] <- rPC
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,rINST_FULL,1)    # v[AA+1] <- eax
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 must spill rPC (edx) because it
+     * is used by imul.  We'll also spill rINST (ebx),
+     * giving us eax, ebc, ecx and edx as computational
+     * temps.  On top of that, we'll spill rIBASE (edi)
+     * for use as the vB pointer and rFP (esi) 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(rPC)
+    SPILL(rIBASE)
+    SPILL(rFP)
+    SPILL(rINST_FULL)
+    leal      (rFP,%eax,4),rIBASE      # rIBASE<- &v[B]
+    leal      (rFP,%ecx,4),rFP         # rFP<- &v[C]
+    movl      4(rIBASE),%ecx      # ecx<- Bmsw
+    imull     (rFP),%ecx          # ecx<- (Bmsw*Clsw)
+    movl      4(rFP),%eax         # eax<- Cmsw
+    imull     (rIBASE),%eax       # eax<- (Cmsw*Blsw)
+    addl      %eax,%ecx           # ecx<- (Bmsw*Clsw)+(Cmsw*Blsw)
+    movl      (rFP),%eax          # eax<- Clsw
+    mull      (rIBASE)            # eax<- (Clsw*Alsw)
+    UNSPILL(rINST_FULL)
+    UNSPILL(rFP)
+    jmp       .LOP_MUL_LONG_continue
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    GET_VREG_WORD(rPC,%eax,0)
+    GET_VREG_WORD(%eax,%eax,1)
+    movl     rPC,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(rPC,%ecx,0)
+    GET_VREG_WORD(%ecx,%ecx,1)
+.LOP_DIV_LONG_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rPC,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    jmp      .LOP_DIV_LONG_continue
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    GET_VREG_WORD(rPC,%eax,0)
+    GET_VREG_WORD(%eax,%eax,1)
+    movl     rPC,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(rPC,%ecx,0)
+    GET_VREG_WORD(%ecx,%ecx,1)
+.LOP_REM_LONG_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rPC,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    jmp      .LOP_REM_LONG_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    GET_VREG_WORD(rPC,%eax,0)           # rPC<- v[BB+0]
+    GET_VREG_WORD(%eax,%eax,1)          # eax<- v[BB+1]
+    andl (rFP,%ecx,4),rPC         # ex: addl   (rFP,%ecx,4),rPC
+    andl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    SET_VREG_WORD(rPC,rINST_FULL,0)     # v[AA+0] <- rPC
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,rINST_FULL,1)    # v[AA+1] <- eax
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    GET_VREG_WORD(rPC,%eax,0)           # rPC<- v[BB+0]
+    GET_VREG_WORD(%eax,%eax,1)          # eax<- v[BB+1]
+    orl (rFP,%ecx,4),rPC         # ex: addl   (rFP,%ecx,4),rPC
+    orl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    SET_VREG_WORD(rPC,rINST_FULL,0)     # v[AA+0] <- rPC
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,rINST_FULL,1)    # v[AA+1] <- eax
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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(rPC)
+    GET_VREG_WORD(rPC,%eax,0)           # rPC<- v[BB+0]
+    GET_VREG_WORD(%eax,%eax,1)          # eax<- v[BB+1]
+    xorl (rFP,%ecx,4),rPC         # ex: addl   (rFP,%ecx,4),rPC
+    xorl 4(rFP,%ecx,4),%eax         # ex: adcl   4(rFP,%ecx,4),%eax
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    SET_VREG_WORD(rPC,rINST_FULL,0)     # v[AA+0] <- rPC
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,rINST_FULL,1)    # v[AA+1] <- eax
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 edx */
+    /* rINST gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rPC)                          # spill edx
+    GET_VREG_WORD(%edx,%eax,1)          # ecx<- v[BB+1]
+    GET_VREG  (%ecx,%ecx)               # ecx<- vCC
+    GET_VREG_WORD(%eax,%eax,0)          # eax<- v[BB+0]
+    shldl     %eax,%edx
+    sall      %cl,%eax
+    testb     $32,%cl
+    je        2f
+    movl      %eax,%edx
+    xorl      %eax,%eax
+2:
+    movzbl    rINST_HI,%ecx
+    SET_VREG_WORD(%edx,%ecx,1)         # v[AA+1]<- %edx
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    jmp       .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.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 edx */
+    /* rINST gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rPC)                          # spill edx
+    GET_VREG_WORD(%edx,%eax,1)          # edx<- v[BB+1]
+    GET_VREG  (%ecx,%ecx)               # ecx<- vCC
+    GET_VREG_WORD(%eax,%eax,0)          # eax<- v[BB+0]
+    shrdl     %edx,%eax
+    sarl      %cl,%edx
+    testb     $32,%cl
+    je        2f
+    movl      %edx,%eax
+    sarl      $31,%edx
+2:
+    movzbl    rINST_HI,%ecx
+    SET_VREG_WORD(%edx,%ecx,1)         # v[AA+1]<- edx
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    jmp       .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.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 edx */
+    /* rINST gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rPC)                          # spill edx
+    GET_VREG_WORD(%edx,%eax,1)          # edx<- v[BB+1]
+    GET_VREG  (%ecx,%ecx)               # ecx<- vCC
+    GET_VREG_WORD(%eax,%eax,0)          # eax<- v[BB+0]
+    shrdl     %edx,%eax
+    shrl      %cl,%edx
+    testb     $32,%cl
+    je        2f
+    movl      %edx,%eax
+    xorl      %edx,%edx
+2:
+    movzbl    rINST_HI,%ecx
+    SET_VREG_WORD(%edx,%ecx,1)         # v[BB+1]<- edx
+    UNSPILL(rPC)
+    jmp       .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    fstps   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    fstps   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    fstps   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    fstps   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC(2)
+    fstps    (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: x86/OP_ADD_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
+    faddl   (rFP,%ecx,4)           # ex: faddp
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    fstpl   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: x86/OP_SUB_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
+    fsubl   (rFP,%ecx,4)           # ex: faddp
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    fstpl   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: x86/OP_MUL_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
+    fmull   (rFP,%ecx,4)           # ex: faddp
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    fstpl   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    fstpl   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzbl   rINST_HI,%ecx          # ecx<- AA
+    FETCH_INST_WORD(2)
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC(2)
+    fstpl    (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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 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 */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    FETCH_INST_WORD(1)
+    andb    $0xf,%cl                   # ecx<- A
+    addl     %eax,(rFP,%ecx,4)                              # for ex: addl   %eax,(rFP,%ecx,4)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 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 */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    FETCH_INST_WORD(1)
+    andb    $0xf,%cl                   # ecx<- A
+    subl     %eax,(rFP,%ecx,4)                              # for ex: addl   %eax,(rFP,%ecx,4)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: x86/OP_MUL_INT_2ADDR.S */
+    /* mul vA, vB */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    andb    $0xf,%cl                   # ecx<- A
+    SPILL(rPC)
+    imull   (rFP,%ecx,4),%eax
+    UNSPILL(rPC)
+    SET_VREG(%eax,%ecx)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx          # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    movzbl   rINST_HI,rINST_FULL    # rINST_FULL<- BA
+    andb     $0xf,rINST_LO         # rINST_FULL<- A
+    GET_VREG(%eax,rINST_FULL)       # eax<- vBB
+    SPILL(rPC)
+    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
+    jmp      .LOP_DIV_INT_2ADDR_finish_div2addr
+
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx          # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    movzbl   rINST_HI,rINST_FULL    # rINST_FULL<- BA
+    andb     $0xf,rINST_LO         # rINST_FULL<- A
+    GET_VREG(%eax,rINST_FULL)       # eax<- vBB
+    SPILL(rPC)
+    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,%edx
+    jmp      .LOP_REM_INT_2ADDR_finish_div2addr
+
+
+
+/* ------------------------------ */
+    .balign 64
+.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 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 */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    FETCH_INST_WORD(1)
+    andb    $0xf,%cl                   # ecx<- A
+    andl     %eax,(rFP,%ecx,4)                              # for ex: addl   %eax,(rFP,%ecx,4)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 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 */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    FETCH_INST_WORD(1)
+    andb    $0xf,%cl                   # ecx<- A
+    orl     %eax,(rFP,%ecx,4)                              # for ex: addl   %eax,(rFP,%ecx,4)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 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 */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    FETCH_INST_WORD(1)
+    andb    $0xf,%cl                   # ecx<- A
+    xorl     %eax,(rFP,%ecx,4)                              # for ex: addl   %eax,(rFP,%ecx,4)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx          # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    movzbl   rINST_HI,rINST_FULL    # rINST_FULL<- BA
+    andb     $0xf,rINST_LO         # rINST_FULL<- A
+    GET_VREG(%eax,rINST_FULL)       # eax<- vAA
+    sall    %cl,%eax                          # ex: sarl %cl,%eax
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx          # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    movzbl   rINST_HI,rINST_FULL    # rINST_FULL<- BA
+    andb     $0xf,rINST_LO         # rINST_FULL<- A
+    GET_VREG(%eax,rINST_FULL)       # eax<- vAA
+    sarl    %cl,%eax                          # ex: sarl %cl,%eax
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx          # eax<- BA
+    sarl     $4,%ecx               # ecx<- B
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    movzbl   rINST_HI,rINST_FULL    # rINST_FULL<- BA
+    andb     $0xf,rINST_LO         # rINST_FULL<- A
+    GET_VREG(%eax,rINST_FULL)       # eax<- vAA
+    shrl    %cl,%eax                          # ex: sarl %cl,%eax
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%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]
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- BA
+    andb      $0xF,rINST_LO            # rINST_FULL<- A
+    addl %eax,(rFP,rINST_FULL,4)         # example: addl   %eax,(rFP,rINST_FULL,4)
+    adcl %ecx,4(rFP,rINST_FULL,4)         # example: adcl   %ecx,4(rFP,rINST_FULL,4)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%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]
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- BA
+    andb      $0xF,rINST_LO            # rINST_FULL<- A
+    subl %eax,(rFP,rINST_FULL,4)         # example: addl   %eax,(rFP,rINST_FULL,4)
+    sbbl %ecx,4(rFP,rINST_FULL,4)         # example: adcl   %ecx,4(rFP,rINST_FULL,4)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 rPC (edx) because it
+     * is used by imul.  We'll also spill rINST (ebx),
+     * giving us eax, ebc, ecx and edx as computational
+     * temps.  On top of that, we'll spill rIBASE (edi)
+     * for use as the vA pointer and rFP (esi) for use
+     * as the vB pointer.  Yuck.
+     */
+    /* mul-long/2addr vA, vB */
+    movzbl    rINST_HI,%eax            # eax<- BA
+    andb      $0xf,%al                # eax<- A
+    sarl      $12,rINST_FULL          # rINST_FULL<- B
+    SPILL(rPC)
+    SPILL(rIBASE)
+    SPILL(rFP)
+    leal      (rFP,%eax,4),rIBASE      # rIBASE<- &v[A]
+    leal      (rFP,rINST_FULL,4),rFP   # rFP<- &v[B]
+    movl      4(rIBASE),%ecx      # ecx<- Amsw
+    imull     (rFP),%ecx          # ecx<- (Amsw*Blsw)
+    movl      4(rFP),%eax         # eax<- Bmsw
+    imull     (rIBASE),%eax       # eax<- (Bmsw*Alsw)
+    addl      %eax,%ecx           # ecx<- (Amsw*Blsw)+(Bmsw*Alsw)
+    movl      (rFP),%eax          # eax<- Blsw
+    mull      (rIBASE)            # eax<- (Blsw*Alsw)
+    jmp       .LOP_MUL_LONG_2ADDR_continue
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: x86/OP_DIV_LONG_2ADDR.S */
+    /* div/2addr vA, vB */
+    movzbl    rINST_HI,%eax
+    shrl      $4,%eax                  # eax<- B
+    movzbl    rINST_HI,rINST_FULL
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    SPILL(rPC)
+    GET_VREG_WORD(rPC,%eax,0)
+    GET_VREG_WORD(%eax,%eax,1)
+    movl     rPC,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(rPC,rINST_FULL,0)
+    GET_VREG_WORD(%ecx,rINST_FULL,1)
+.LOP_DIV_LONG_2ADDR_notSpecial1:
+    jmp      .LOP_DIV_LONG_2ADDR_continue
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%eax
+    shrl      $4,%eax                  # eax<- B
+    movzbl    rINST_HI,rINST_FULL
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    SPILL(rPC)
+    GET_VREG_WORD(rPC,%eax,0)
+    GET_VREG_WORD(%eax,%eax,1)
+    movl     rPC,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(rPC,rINST_FULL,0)
+    GET_VREG_WORD(%ecx,rINST_FULL,1)
+.LOP_REM_LONG_2ADDR_notSpecial1:
+    jmp      .LOP_REM_LONG_2ADDR_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%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]
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- BA
+    andb      $0xF,rINST_LO            # rINST_FULL<- A
+    andl %eax,(rFP,rINST_FULL,4)         # example: addl   %eax,(rFP,rINST_FULL,4)
+    andl %ecx,4(rFP,rINST_FULL,4)         # example: adcl   %ecx,4(rFP,rINST_FULL,4)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%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]
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- BA
+    andb      $0xF,rINST_LO            # rINST_FULL<- A
+    orl %eax,(rFP,rINST_FULL,4)         # example: addl   %eax,(rFP,rINST_FULL,4)
+    orl %ecx,4(rFP,rINST_FULL,4)         # example: adcl   %ecx,4(rFP,rINST_FULL,4)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%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]
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- BA
+    andb      $0xF,rINST_LO            # rINST_FULL<- A
+    xorl %eax,(rFP,rINST_FULL,4)         # example: addl   %eax,(rFP,rINST_FULL,4)
+    xorl %ecx,4(rFP,rINST_FULL,4)         # example: adcl   %ecx,4(rFP,rINST_FULL,4)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 edx */
+    /* rINST gets AA */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    movzbl    rINST_HI,rINST_FULL       # rINST_HI<- BA
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG_WORD(%eax,rINST_FULL,0)    # eax<- v[AA+0]
+    sarl      $4,%ecx                  # ecx<- B
+    SPILL(rPC)
+    GET_VREG_WORD(%edx,rINST_FULL,1)    # edx<- v[AA+1]
+    GET_VREG(%ecx,%ecx)                 # ecx<- vBB
+    shldl     %eax,%edx
+    sall      %cl,%eax
+    testb     $32,%cl
+    je        2f
+    movl      %eax,%edx
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD(%edx,rINST_FULL,1)   # v[AA+1]<- edx
+    UNSPILL(rPC)
+    jmp       .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.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 edx */
+    /* rINST gets AA */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    movzbl    rINST_HI,rINST_FULL       # rINST_HI<- BA
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG_WORD(%eax,rINST_FULL,0)    # eax<- v[AA+0]
+    sarl      $4,%ecx                  # ecx<- B
+    SPILL(rPC)
+    GET_VREG_WORD(%edx,rINST_FULL,1)    # edx<- v[AA+1]
+    GET_VREG(%ecx,%ecx)                 # ecx<- vBB
+    shrdl     %edx,%eax
+    sarl      %cl,%edx
+    testb     $32,%cl
+    je        2f
+    movl      %edx,%eax
+    sarl      $31,%edx
+2:
+    SET_VREG_WORD(%edx,rINST_FULL,1)   # v[AA+1]<- edx
+    UNSPILL(rPC)
+    jmp       .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.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 edx */
+    /* rINST gets AA */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    movzbl    rINST_HI,rINST_FULL       # rINST_HI<- BA
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG_WORD(%eax,rINST_FULL,0)    # eax<- v[AA+0]
+    sarl      $4,%ecx                  # ecx<- B
+    SPILL(rPC)
+    GET_VREG_WORD(%edx,rINST_FULL,1)    # edx<- v[AA+1]
+    GET_VREG(%ecx,%ecx)                 # ecx<- vBB
+    shrdl     %edx,%eax
+    shrl      %cl,%edx
+    testb     $32,%cl
+    je        2f
+    movl      %edx,%eax
+    xorl      %edx,%edx
+2:
+    SET_VREG_WORD(%edx,rINST_FULL,1)   # v[AA+1]<- edx
+    UNSPILL(rPC)
+    jmp       .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx               # ecx<- A+
+    andb    $0xf,%cl                   # ecx<- A
+    flds    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    fadds   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fstps    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx               # ecx<- A+
+    andb    $0xf,%cl                   # ecx<- A
+    flds    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    fsubs   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fstps    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx               # ecx<- A+
+    andb    $0xf,%cl                   # ecx<- A
+    flds    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    fmuls   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fstps    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx               # ecx<- A+
+    andb    $0xf,%cl                   # ecx<- A
+    flds    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    fdivs   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fstps    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: x86/OP_REM_FLOAT_2ADDR.S */
+    /* rem_float/2addr vA, vB */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    flds     (rFP,rINST_FULL,4)         # vBB to fp stack
+    andb    $0xf,%cl                   # ecx<- A
+    flds     (rFP,%ecx,4)               # vAA to fp stack
+    FETCH_INST_WORD(1)
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC(1)
+    fstps    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: x86/OP_ADD_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   rINST_HI,%ecx               # ecx<- A+
+    andb    $0xf,%cl                   # ecx<- A
+    fldl    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    faddl   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fstpl    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: x86/OP_SUB_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   rINST_HI,%ecx               # ecx<- A+
+    andb    $0xf,%cl                   # ecx<- A
+    fldl    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    fsubl   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fstpl    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: x86/OP_MUL_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   rINST_HI,%ecx               # ecx<- A+
+    andb    $0xf,%cl                   # ecx<- A
+    fldl    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    fmull   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fstpl    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%ecx               # ecx<- A+
+    andb    $0xf,%cl                   # ecx<- A
+    fldl    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    fdivl   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    fstpl    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: x86/OP_REM_DOUBLE_2ADDR.S */
+    /* rem_float/2addr vA, vB */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $12,rINST_FULL             # rINST_FULL<- B
+    fldl     (rFP,rINST_FULL,4)         # vBB to fp stack
+    andb    $0xf,%cl                   # ecx<- A
+    fldl     (rFP,%ecx,4)               # vAA to fp stack
+    FETCH_INST_WORD(1)
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC(1)
+    fstpl    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $0xf,rINST_LO             # rINST_FULL<- A
+    addl %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $0xf,rINST_LO             # rINST_FULL<- A
+    subl %eax,%ecx                              # for example: addl %ecx, %eax
+    SET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: x86/OP_MUL_INT_LIT16.S */
+    /* mul/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST_FULL, ssssCCCC in ecx, vB in eax */
+    movzbl   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    SPILL(rPC)
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $0xf,rINST_LO             # rINST_FULL<- A
+    imull     %ecx,%eax                 # trashes rPC
+    UNSPILL(rPC)
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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_FULL, ssssCCCC in ecx, vB in eax */
+    movzbl   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $0xf,rINST_LO             # rINST_FULL<- A
+    SPILL(rPC)
+    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
+    jmp      .LOP_DIV_INT_LIT16_finish_div
+
+
+
+/* ------------------------------ */
+    .balign 64
+.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_FULL, ssssCCCC in ecx, vB in eax */
+    movzbl   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $0xf,rINST_LO             # rINST_FULL<- A
+    SPILL(rPC)
+    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,%edx
+    jmp      .LOP_REM_INT_LIT16_finish_div
+
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $0xf,rINST_LO             # rINST_FULL<- A
+    andl %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $0xf,rINST_LO             # rINST_FULL<- A
+    orl     %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $0xf,rINST_LO             # rINST_FULL<- A
+    xor    %ecx,%eax                              # for example: addl %ecx, %eax
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    addl %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    subl  %eax,%ecx                             # ex: addl %ecx,%eax
+    SET_VREG  (%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    SPILL(rPC)
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    imull     %ecx,%eax                # trashes rPC
+    UNSPILL(rPC)
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    SPILL(rPC)
+    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
+    jmp      .LOP_DIV_INT_LIT8_finish_div
+
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    SPILL(rPC)
+    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,%edx
+    jmp      .LOP_REM_INT_LIT8_finish_div
+
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    andl %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    orl     %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    xor    %ecx,%eax                             # ex: addl %ecx,%eax
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    sall  %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    sarl    %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    shrl     %cl,%eax                             # ex: addl %ecx,%eax
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_VOLATILE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_VOLATILE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    FETCH_INST_WORD(2)
+    movl      %ecx,offStaticField_value(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IGET_OBJECT_VOLATILE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+    /* (stub) */
+    GET_GLUE(%ecx)
+    SAVE_PC_TO_GLUE(%ecx)            # only need to export these two
+    SAVE_FP_TO_GLUE(%ecx)            # only need to export these two
+    movl %ecx,OUT_ARG0(%esp)         # glue is first arg to function
+    call      dvmMterp_OP_IGET_WIDE_VOLATILE     # do the real work
+    GET_GLUE(%ecx)
+    LOAD_PC_FROM_GLUE(%ecx)          # retrieve updated values
+    LOAD_FP_FROM_GLUE(%ecx)          # retrieve updated values
+    FETCH_INST()
+    GOTO_NEXT
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+    /* (stub) */
+    GET_GLUE(%ecx)
+    SAVE_PC_TO_GLUE(%ecx)            # only need to export these two
+    SAVE_FP_TO_GLUE(%ecx)            # only need to export these two
+    movl %ecx,OUT_ARG0(%esp)         # glue is first arg to function
+    call      dvmMterp_OP_IPUT_WIDE_VOLATILE     # do the real work
+    GET_GLUE(%ecx)
+    LOAD_PC_FROM_GLUE(%ecx)          # retrieve updated values
+    LOAD_FP_FROM_GLUE(%ecx)          # retrieve updated values
+    FETCH_INST()
+    GOTO_NEXT
+/* ------------------------------ */
+    .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+    /* (stub) */
+    GET_GLUE(%ecx)
+    SAVE_PC_TO_GLUE(%ecx)            # only need to export these two
+    SAVE_FP_TO_GLUE(%ecx)            # only need to export these two
+    movl %ecx,OUT_ARG0(%esp)         # glue is first arg to function
+    call      dvmMterp_OP_SGET_WIDE_VOLATILE     # do the real work
+    GET_GLUE(%ecx)
+    LOAD_PC_FROM_GLUE(%ecx)          # retrieve updated values
+    LOAD_FP_FROM_GLUE(%ecx)          # retrieve updated values
+    FETCH_INST()
+    GOTO_NEXT
+/* ------------------------------ */
+    .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+    /* (stub) */
+    GET_GLUE(%ecx)
+    SAVE_PC_TO_GLUE(%ecx)            # only need to export these two
+    SAVE_FP_TO_GLUE(%ecx)            # only need to export these two
+    movl %ecx,OUT_ARG0(%esp)         # glue is first arg to function
+    call      dvmMterp_OP_SPUT_WIDE_VOLATILE     # do the real work
+    GET_GLUE(%ecx)
+    LOAD_PC_FROM_GLUE(%ecx)          # retrieve updated values
+    LOAD_FP_FROM_GLUE(%ecx)          # retrieve updated values
+    FETCH_INST()
+    GOTO_NEXT
+/* ------------------------------ */
+    .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: x86/OP_BREAKPOINT.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                     # eax<- BBBB
+    movl     offGlue_method(%ecx),%ecx       # ecx<- glue->method
+    EXPORT_PC()
+    movzbl   rINST_HI,rINST_FULL             # rINST_FULL<- AA
+    movl     %eax,OUT_ARG2(%esp)             # arg2<- BBBB
+    movl     rINST_FULL,OUT_ARG1(%esp)       # arg1<- AA
+    movl     %ecx,OUT_ARG0(%esp)             # arg0<- method
+    SPILL(rPC)
+    call     dvmThrowVerificationError       # call(method, kind, ref)
+    UNSPILL(rPC)
+    jmp      common_exceptionThrown          # handle exception
+
+/* ------------------------------ */
+    .balign 64
+.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)
+     *
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    GET_GLUE(%ecx)
+    EXPORT_PC()
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    leal      offGlue_retval(%ecx),%ecx # ecx<- & glue->retval
+    movl      %ecx,OUT_ARG4(%esp)
+    sarl      $12,rINST_FULL           # rINST_FULL<- arg count (0-4)
+    SPILL(rPC)
+    call      .LOP_EXECUTE_INLINE_continue      # make call; will return after
+    UNSPILL(rPC)
+    testl     %eax,%eax                 # successful?
+    FETCH_INST_WORD(3)
+    je        common_exceptionThrown    # no, handle exception
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+    /* (stub) */
+    GET_GLUE(%ecx)
+    SAVE_PC_TO_GLUE(%ecx)            # only need to export these two
+    SAVE_FP_TO_GLUE(%ecx)            # only need to export these two
+    movl %ecx,OUT_ARG0(%esp)         # glue is first arg to function
+    call      dvmMterp_OP_EXECUTE_INLINE_RANGE     # do the real work
+    GET_GLUE(%ecx)
+    LOAD_PC_FROM_GLUE(%ecx)          # retrieve updated values
+    LOAD_FP_FROM_GLUE(%ecx)          # retrieve updated values
+    FETCH_INST()
+    GOTO_NEXT
+/* ------------------------------ */
+    .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: x86/OP_INVOKE_DIRECT_EMPTY.S */
+    /*
+     * invoke-direct-empty is a no-op in a "standard" interpreter.
+     */
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: x86/OP_UNUSED_F1.S */
+/* File: x86/unused.S */
+    jmp     common_abort
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: x86/OP_IGET_QUICK.S */
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG(%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
+    movzbl    rINST_HI,%ecx
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    andb      $0xf,%cl                 # rINST_FULL<- A
+    SET_VREG  (%eax,%ecx)               # fp[A]<- result
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: x86/OP_IGET_WIDE_QUICK.S */
+    /* For: iget-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG(%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
+    movzbl    rINST_HI,rINST_FULL
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    SET_VREG_WORD(%ecx,rINST_FULL,0)    # v[A+0]<- lsw
+    SET_VREG_WORD(%eax,rINST_FULL,1)    # v[A+1]<- msw
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.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    rINST_HI,%ecx             # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG(%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
+    movzbl    rINST_HI,%ecx
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    andb      $0xf,%cl                 # rINST_FULL<- A
+    SET_VREG  (%eax,%ecx)               # fp[A]<- result
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: x86/OP_IPUT_QUICK.S */
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG(%ecx,%ecx)                 # vB (object we're operating on)
+    movzbl    rINST_HI,rINST_FULL
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                  # is object null?
+    je        common_errNullObject
+    movl      rINST_FULL,(%ecx,%eax,1)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: x86/OP_IPUT_WIDE_QUICK.S */
+    /* For: iput-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG(%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
+    movzbl    rINST_HI,rINST_FULL
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG_WORD(%eax,rINST_FULL,0)    # eax<- lsw
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- msw
+    movl      %eax,(%ecx)
+    movl      rINST_FULL,4(%ecx)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: x86/OP_IPUT_OBJECT_QUICK.S */
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $4,%ecx                  # ecx<- B
+    GET_VREG(%ecx,%ecx)                 # vB (object we're operating on)
+    movzbl    rINST_HI,rINST_FULL
+    andb      $0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST_FULL,(%ecx,%eax,1)
+    GET_GLUE(%eax)
+    jmp       .LOP_IPUT_OBJECT_QUICK_finish
+
+/* ------------------------------ */
+    .balign 64
+.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),%eax               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%ecx               # ecx<- BBBB
+    .if     (!0)
+    andl      $0xf,%eax                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG(%eax,%eax)                 # eax<- vC ("this" ptr)
+    testl     %eax,%eax                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%eax),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC()                         # might throw later - get ready
+    movl      (%eax,%ecx,4),%eax        # eax<- vtable[BBBB]
+    jmp       common_invokeMethodNoRange
+
+/* ------------------------------ */
+    .balign 64
+.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),%eax               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%ecx               # ecx<- BBBB
+    .if     (!1)
+    andl      $0xf,%eax                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG(%eax,%eax)                 # eax<- vC ("this" ptr)
+    testl     %eax,%eax                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%eax),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC()                         # might throw later - get ready
+    movl      (%eax,%ecx,4),%eax        # eax<- vtable[BBBB]
+    jmp       common_invokeMethodRange
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offGlue_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(%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
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC()
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    jmp       common_invokeMethodNoRange
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offGlue_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(%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
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC()
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    jmp       common_invokeMethodRange
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .LOP_IPUT_OBJECT_VOLATILE_resolve
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+
+
+/* ------------------------------ */
+    .balign 64
+.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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    jmp       .LOP_SPUT_OBJECT_VOLATILE_continue
+
+
+/* ------------------------------ */
+    .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: x86/OP_UNUSED_FF.S */
+/* File: x86/unused.S */
+    jmp     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 */
+
+/* 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:
+    GET_GLUE(%eax)
+    movl     %ecx,rINST_FULL           # rINST_FULL<- AA
+    EXPORT_PC()
+    movl     offGlue_method(%eax),%eax # eax<- glue->method
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    SPILL(rPC)
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rPC)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+/* 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:
+    GET_GLUE(%eax)
+    movl     %ecx,rINST_FULL           # rINST_FULL<- AA
+    EXPORT_PC()
+    movl     offGlue_method(%eax),%eax # eax<- glue->method
+    movl     2(rPC),%ecx               # ecx<- BBBBBBBB
+    movl     offMethod_clazz(%eax),%eax
+    SPILL(rPC)
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rPC)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+/* continuation for OP_CONST_CLASS */
+
+/* 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:
+    GET_GLUE(%eax)
+    movl     %ecx,rINST_FULL           # rINST_FULL<- AA
+    EXPORT_PC()
+    movl     offGlue_method(%eax),%eax # eax<- glue->method
+    movl     $1,OUT_ARG2(%esp)        # true
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    SPILL(rPC)
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    call     dvmResolveClass           # go resolve
+    UNSPILL(rPC)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_MONITOR_ENTER */
+
+.LOP_MONITOR_ENTER_continue:
+    SPILL(rPC)                          # have to - caller save
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    call    dvmLockObject               # dvmLockObject(self,object)
+    UNSPILL(rPC)
+#ifdef WITH_DEADLOCK_PREDICTION
+    GET_GLUE(%ecx)
+    movl    offGlueSelf(%ecx),%ecx      # ecx<- glue->self
+    movl    offThread_exception(%ecx),%eax
+    testl   %eax,%eax
+    jne     common_exceptionThrown
+#endif
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* continuation for OP_MONITOR_EXIT */
+
+.LOP_MONITOR_EXIT_continue:
+    call    dvmUnlockObject             # unlock(self,obj)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(1)
+    testl   %eax,%eax                   # success?
+    ADVANCE_PC(1)
+    je      common_exceptionThrown      # no, exception pending
+    GOTO_NEXT
+.LOP_MONITOR_EXIT_errNullObject:
+    ADVANCE_PC(1)                       # advance before throw
+    jmp     common_errNullObject
+
+/* continuation for OP_CHECK_CAST */
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  ecx holds obj->clazz
+     *  eax holds class resolved from BBBB
+     *  rINST_FULL holds object
+     */
+.LOP_CHECK_CAST_fullcheck:
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rPC)
+    call    dvmInstanceofNonTrivial     # eax<- boolean result
+    UNSPILL(rPC)
+    testl   %eax,%eax                   # failed?
+    jne     .LOP_CHECK_CAST_okay            # no, success
+
+    # A cast has failed.  We need to throw a ClassCastException with the
+    # class of the object that failed to be cast.
+    EXPORT_PC()
+    movl    offObject_clazz(rINST_FULL),%ecx  # ecx<- obj->clazz
+    movl    $.LstrClassCastException,%eax
+    movl    offClassObject_descriptor(%ecx),%ecx
+    movl    %eax,OUT_ARG0(%esp)     # arg0<- message
+    movl    %ecx,OUT_ARG1(%esp)     # arg1<- obj->clazz->descriptor
+    SPILL(rPC)
+    call    dvmThrowExceptionWithClassMessage
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path, and we're
+     * going to have to recreate some data.
+     *
+     *  rINST_FULL holds object
+     */
+.LOP_CHECK_CAST_resolve:
+    GET_GLUE(%ecx)
+    EXPORT_PC()
+    movzwl  2(rPC),%eax                # eax<- BBBB
+    movl    offGlue_method(%ecx),%ecx  # ecx<- glue->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(rPC)
+    call    dvmResolveClass            # eax<- resolved ClassObject ptr
+    UNSPILL(rPC)
+    testl   %eax,%eax                  # got null?
+    je      common_exceptionThrown     # yes, handle exception
+    movl    offObject_clazz(rINST_FULL),%ecx  # ecx<- obj->clazz
+    jmp     .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.
+     *  eax holds obj->clazz
+     *  ecx holds class resolved from BBBB
+     *  rINST_HI has BA
+     *  rPC already spilled
+     */
+.LOP_INSTANCE_OF_fullcheck:
+    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_HI holds BA
+     */
+.LOP_INSTANCE_OF_store:
+    UNSPILL(rPC)
+    movzbl  rINST_HI,%ecx               # ecx<- BA
+    FETCH_INST_WORD(2)
+    andb    $0xf,%cl                   # ecl<- A
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)                 # vA<- eax
+    GOTO_NEXT
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.LOP_INSTANCE_OF_trivial:
+    UNSPILL(rPC)
+    movzbl  rINST_HI,%ecx               # ecx<- BA
+    FETCH_INST_WORD(2)
+    andb    $0xf,%cl                   # ecl<- A
+    ADVANCE_PC(2)
+    movl    $1,%eax
+    SET_VREG(%eax,%ecx)                  # vA<- true
+    GOTO_NEXT
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  rPC holds BBBB
+     *  rINST_HI holds BA
+     */
+.LOP_INSTANCE_OF_resolve:
+    movl    rPC,OUT_ARG1(%esp)          # arg1<- BBBB
+    GET_GLUE(%ecx)
+    UNSPILL(rPC)
+    movl    offGlue_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
+    UNSPILL(rPC)
+    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
+    movzbl  rINST_HI,%eax               # eax<- BA
+    sarl    $4,%eax                    # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB (obj)
+    movl    offObject_clazz(%eax),%eax  # eax<- obj->clazz
+    jmp     .LOP_INSTANCE_OF_resolved
+
+/* continuation for OP_NEW_INSTANCE */
+
+.LOP_NEW_INSTANCE_initialized:  # on entry, ecx<- class
+    /* TODO: remove test for interface/abstract, now done in verifier */
+    testl     $(ACC_INTERFACE|ACC_ABSTRACT),offClassObject_accessFlags(%ecx)
+    movl      $ALLOC_DONT_TRACK,OUT_ARG1(%esp)
+    jne       .LOP_NEW_INSTANCE_abstract
+.LOP_NEW_INSTANCE_finish: # ecx=class
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmAllocObject             # eax<- new object
+    UNSPILL(rPC)
+    movl     rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    testl    %eax,%eax                  # success?
+    je       common_exceptionThrown     # no, bail out
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+    /*
+     * Class initialization required.
+     *
+     *  ecx holds class object
+     */
+.LOP_NEW_INSTANCE_needinit:
+    SPILL_TMP(%ecx)                     # save object
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmInitClass                # initialize class
+    UNSPILL_TMP(%ecx)                   # restore object
+    testl   %eax,%eax                   # success?
+    jne     .LOP_NEW_INSTANCE_initialized     # success, continue
+    UNSPILL(rPC)                        # failed, restore PC
+    jmp     common_exceptionThrown      # go deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     */
+.LOP_NEW_INSTANCE_resolve:
+    GET_GLUE(%ecx)
+    movzwl  2(rPC),%eax
+    movl    offGlue_method(%ecx),%ecx   # ecx<- glue->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
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown      # no, handle exception
+
+    /*
+     * TODO: remove this
+     * We can't instantiate an abstract class or interface, so throw an
+     * InstantiationError with the class descriptor as the message.
+     *
+     *  ecx holds class object
+     */
+.LOP_NEW_INSTANCE_abstract:
+    movl    offClassObject_descriptor(%ecx),%eax
+    movl    $.LstrInstantiationError,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    call    dvmThrowExceptionWithClassMessage
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/* continuation for OP_NEW_ARRAY */
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *  ecx holds class (null here)
+     *  eax holds array length (vB)
+     */
+.LOP_NEW_ARRAY_resolve:
+    GET_GLUE(%ecx)
+    SPILL_TMP(%eax)                    # save array length
+    movl    offGlue_method(%ecx),%ecx  # ecx<- glue->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)
+    SPILL(rPC)
+    call    dvmResolveClass            # eax<- call(clazz,ref,flag)
+    UNSPILL(rPC)
+    movl    %eax,%ecx
+    UNSPILL_TMP(%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)
+    SPILL(rPC)
+    call    dvmAllocArrayByClass    # eax<- call(clazz,length,flags)
+    UNSPILL(rPC)
+    testl   %eax,%eax               # failed?
+    je      common_exceptionThrown  # yup - go handle
+    movl    rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+.LOP_FILLED_NEW_ARRAY_more:
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    UNSPILL(rPC)
+    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_FULL holds AA or BB [r10]
+     *    ecx is scratch
+     *    rPC is valid, but has been spilled
+     */
+.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
+    GET_GLUE(%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,offGlue_retval+4(%eax)           # save type
+    .if      (!0)
+    SPILL_TMP(rINST_FULL)                         # save copy, need "B" later
+    sarl    $4,rINST_FULL
+    .endif
+    movl    rINST_FULL,OUT_ARG1(%esp)             # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass                  # eax<- call(arrayClass, length, flags)
+    UNSPILL(rPC)
+    GET_GLUE(%ecx)
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offGlue_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_FULL is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL is BA
+ *     rPC is valid, but spilled
+ *  We now need to copy values from registers into the array
+ */
+
+    .if 0
+    # set up src pointer
+    SPILL(rFP)     # esi
+    SPILL(rIBASE)   # edi
+    movl    %eax,%edi         # set up dst ptr
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    rINST_FULL,%ecx   # load count register
+    FETCH_INST_WORD(3)
+    rep
+    movsd
+    GET_GLUE(%ecx)
+    UNSPILL(rIBASE)
+    movl    offGlue_retval+4(%ecx),%eax      # eax<- type
+    UNSPILL(rFP)
+    .else
+    testl  rINST_FULL,rINST_FULL
+    je     4f
+    UNSPILL_TMP(rPC)
+    andl   $0x0f,rPC            # rPC<- 0000000A
+    sall   $16,rPC              # rPC<- 000A0000
+    orl    %ecx,rPC              # rpc<- 000AFEDC
+3:
+    movl   $0xf,%ecx
+    andl   rPC,%ecx           # ecx<- next reg to load
+    GET_VREG(%ecx,%ecx)
+    shrl   $4,rPC
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $1,rINST_FULL
+    jne    3b
+4:
+    GET_GLUE(%ecx)
+    UNSPILL(rPC)
+    movl    offGlue_retval+4(%ecx),%eax      # eax<- type
+    FETCH_INST_WORD(3)
+    .endif
+
+    cmpb    $'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offGlue_retval(%ecx),%eax        # eax<- object head
+    movl    offGlue_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:
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+    movl    $.LstrInternalError,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    $.LstrFilledNewArrayNotImpl,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+.LOP_FILLED_NEW_ARRAY_RANGE_more:
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    UNSPILL(rPC)
+    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_FULL holds AA or BB [r10]
+     *    ecx is scratch
+     *    rPC is valid, but has been spilled
+     */
+.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
+    GET_GLUE(%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,offGlue_retval+4(%eax)           # save type
+    .if      (!1)
+    SPILL_TMP(rINST_FULL)                         # save copy, need "B" later
+    sarl    $4,rINST_FULL
+    .endif
+    movl    rINST_FULL,OUT_ARG1(%esp)             # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass                  # eax<- call(arrayClass, length, flags)
+    UNSPILL(rPC)
+    GET_GLUE(%ecx)
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offGlue_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_FULL is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL is BA
+ *     rPC is valid, but spilled
+ *  We now need to copy values from registers into the array
+ */
+
+    .if 1
+    # set up src pointer
+    SPILL(rFP)     # esi
+    SPILL(rIBASE)   # edi
+    movl    %eax,%edi         # set up dst ptr
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    rINST_FULL,%ecx   # load count register
+    FETCH_INST_WORD(3)
+    rep
+    movsd
+    GET_GLUE(%ecx)
+    UNSPILL(rIBASE)
+    movl    offGlue_retval+4(%ecx),%eax      # eax<- type
+    UNSPILL(rFP)
+    .else
+    testl  rINST_FULL,rINST_FULL
+    je     4f
+    UNSPILL_TMP(rPC)
+    andl   $0x0f,rPC            # rPC<- 0000000A
+    sall   $16,rPC              # rPC<- 000A0000
+    orl    %ecx,rPC              # rpc<- 000AFEDC
+3:
+    movl   $0xf,%ecx
+    andl   rPC,%ecx           # ecx<- next reg to load
+    GET_VREG(%ecx,%ecx)
+    shrl   $4,rPC
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $1,rINST_FULL
+    jne    3b
+4:
+    GET_GLUE(%ecx)
+    UNSPILL(rPC)
+    movl    offGlue_retval+4(%ecx),%eax      # eax<- type
+    FETCH_INST_WORD(3)
+    .endif
+
+    cmpb    $'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offGlue_retval(%ecx),%eax        # eax<- object head
+    movl    offGlue_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:
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+    movl    $.LstrInternalError,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    $.LstrFilledNewArrayNotImpl,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/* continuation for OP_CMPL_FLOAT */
+
+.LOP_CMPL_FLOAT_isNaN:
+    movl      $-1,%ecx
+    jmp       .LOP_CMPL_FLOAT_finish
+
+/* continuation for OP_CMPG_FLOAT */
+
+.LOP_CMPG_FLOAT_isNaN:
+    movl      $1,%ecx
+    jmp       .LOP_CMPG_FLOAT_finish
+
+/* continuation for OP_CMPL_DOUBLE */
+
+.LOP_CMPL_DOUBLE_isNaN:
+    movl      $-1,%ecx
+    jmp       .LOP_CMPL_DOUBLE_finish
+
+/* continuation for OP_CMPG_DOUBLE */
+
+.LOP_CMPG_DOUBLE_isNaN:
+    movl      $1,%ecx
+    jmp       .LOP_CMPG_DOUBLE_finish
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_bigger:
+    UNSPILL(rPC)
+    movl      $1,%ecx
+    jmp       .LOP_CMP_LONG_finish
+.LOP_CMP_LONG_smaller:
+    UNSPILL(rPC)
+    movl      $-1,%ecx
+.LOP_CMP_LONG_finish:
+    SET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    movl      (%eax),%ecx
+    movl      4(%eax),%eax
+    SET_VREG_WORD(%ecx,rINST_FULL,0)
+    SET_VREG_WORD(%eax,rINST_FULL,1)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    GET_VREG_WORD(%ecx,rINST_FULL,0)
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1)
+    movl      rINST_FULL,4(%eax)
+    FETCH_INST_WORD(2)
+    movl      %ecx,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_APUT_OBJECT */
+
+    /* On entry:
+     *   eax<- array object
+     *   ecx<- index
+     *   rINST_FULL<- vAA
+     */
+.LOP_APUT_OBJECT_continue:
+    leal      offArrayObject_contents(%eax,%ecx,4),%ecx
+    testl     rINST_FULL,rINST_FULL     # storing null reference?
+    je        .LOP_APUT_OBJECT_skip_check
+    SPILL(rPC)
+    SPILL_TMP(%ecx)
+    movl      %eax,LOCAL0_OFFSET(%ebp)   # save copy of object head
+    movl      offObject_clazz(%eax),%eax # eax<- arrayObj->clazz
+    movl      offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+    movl      %eax,OUT_ARG1(%esp)
+    movl      %ecx,OUT_ARG0(%esp)
+    call      dvmCanPutArrayElement     # test object type vs. array type
+    UNSPILL(rPC)
+    UNSPILL_TMP(%ecx)
+    testl     %eax,%eax
+    GET_GLUE(%eax)
+    je        common_errArrayStore
+    movl      offGlue_cardTable(%eax),%eax   # get card table base
+    movl      rINST_FULL,(%ecx)
+    movl      LOCAL0_OFFSET(%ebp),%ecx       # recover object head
+    FETCH_INST_WORD(2)
+    shrl      $GC_CARD_SHIFT,%ecx           # object head to card number
+    movb      %al,(%eax,%ecx)                # mark card using object head
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+.LOP_APUT_OBJECT_skip_check:
+    movl      rINST_FULL,(%ecx)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IGET */
+
+
+.LOP_IGET_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IGET_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IGET_WIDE */
+
+
+.LOP_IGET_WIDE_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IGET_WIDE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_WIDE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    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_FULL,0)
+    SET_VREG_WORD(%eax,rINST_FULL,1)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IGET_OBJECT */
+
+
+.LOP_IGET_OBJECT_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IGET_OBJECT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_OBJECT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IGET_BOOLEAN */
+
+
+.LOP_IGET_BOOLEAN_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IGET_BOOLEAN_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_BOOLEAN_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movzbl   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IGET_BYTE */
+
+
+.LOP_IGET_BYTE_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IGET_BYTE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_BYTE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movsbl   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IGET_CHAR */
+
+
+.LOP_IGET_CHAR_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IGET_CHAR_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_CHAR_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movzwl   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IGET_SHORT */
+
+
+.LOP_IGET_SHORT_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IGET_SHORT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_SHORT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movswl   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT */
+
+
+.LOP_IPUT_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IPUT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   rINST_FULL,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT_WIDE */
+
+
+.LOP_IPUT_WIDE_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    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_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    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_FULL,0)             # ecx<- lsw
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1)       # rINST_FULL<- msw
+    movl    rINST_FULL,4(%eax)
+    FETCH_INST_WORD(2)
+    movl    %ecx,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT_OBJECT */
+
+
+.LOP_IPUT_OBJECT_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which 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_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl    rINST_FULL,(%ecx,%eax)          # obj.field <- v[A](8/16/32 bits)
+    GET_GLUE(%eax)
+    testl   rINST_FULL,rINST_FULL                # stored a NULL?
+    movl    offGlue_cardTable(%eax),%eax         # get card table base
+    FETCH_INST_WORD(2)
+    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:
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+
+.LOP_IPUT_BOOLEAN_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IPUT_BOOLEAN_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_BOOLEAN_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movb   rINST_LO,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT_BYTE */
+
+
+.LOP_IPUT_BYTE_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IPUT_BYTE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_BYTE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movb   rINST_LO,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT_CHAR */
+
+
+.LOP_IPUT_CHAR_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IPUT_CHAR_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_CHAR_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movw   rINST,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT_SHORT */
+
+
+.LOP_IPUT_SHORT_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IPUT_SHORT_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_SHORT_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movw   rINST,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_SGET */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_WIDE_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_WIDE_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_OBJECT_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_OBJECT_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_BOOLEAN_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_BOOLEAN_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_BYTE_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_BYTE_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_CHAR_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_CHAR_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_SHORT_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_SHORT_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_WIDE_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_WIDE_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_continue:
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    FETCH_INST_WORD(2)
+    je        1f                                 # skip card mark if null
+    GET_GLUE(%ecx)
+    movl      offField_clazz(%eax),%eax          # eax<- field->clazz
+    movl      offGlue_cardTable(%ecx),%ecx       # get card table base
+    shrl      $GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+.LOP_SPUT_OBJECT_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_OBJECT_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_BOOLEAN_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_BOOLEAN_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_BYTE_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_BYTE_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_CHAR_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_CHAR_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_SHORT_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_SHORT_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+
+.LOP_INVOKE_VIRTUAL_more:
+    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)
+    UNSPILL(rPC)
+    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(%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),%ecx  # ecx<- thisPtr->clazz
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- thisPtr->clazz->vtable
+    movl      (%ecx,%eax,4),%eax        # eax<- vtable[methodIndex]
+    jmp       common_invokeMethodNoRange
+
+/* continuation for OP_INVOKE_SUPER */
+
+    /*
+     * 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),%ecx  # ecx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%ecx # compare(methodIndex,vtableCount)
+    jae     .LOP_INVOKE_SUPER_nsm           # method not present in superclass
+    movl    offClassObject_vtable(%eax),%eax   # eax<- ...clazz->super->vtable
+    movl    (%eax,%ecx,4),%eax        # eax<- vtable[methodIndex]
+    jmp     common_invokeMethodNoRange
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.LOP_INVOKE_SUPER_resolve:
+    SPILL_TMP(%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
+    SPILL(rPC)
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    UNSPILL(rPC)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP(%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
+    mov     %eax,OUT_ARG1(%esp)
+    jmp     common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+    /*
+     * 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_TMP(%ecx)
+     GET_GLUE(%ecx)
+     UNSPILL(rPC)
+     movl     offGlue_method(%ecx),%ecx  # ecx<- glue->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_TMP(%ecx)
+     testl    %eax,%eax
+     jne      .LOP_INVOKE_DIRECT_finish
+     UNSPILL(rPC)
+     jmp      common_exceptionThrown
+
+/* continuation for OP_INVOKE_STATIC */
+
+.LOP_INVOKE_STATIC_continue:
+    movl      $METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rPC)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rPC)
+    testl     %eax,%eax                 # got null?
+    jne       common_invokeMethodNoRange
+    jmp       common_exceptionThrown
+
+/* continuation for OP_INVOKE_INTERFACE */
+
+.LOP_INVOKE_INTERFACE_continue:
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    UNSPILL(rPC)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    jmp        common_invokeMethodNoRange
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+
+.LOP_INVOKE_VIRTUAL_RANGE_more:
+    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)
+    UNSPILL(rPC)
+    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(%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),%ecx  # ecx<- thisPtr->clazz
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- thisPtr->clazz->vtable
+    movl      (%ecx,%eax,4),%eax        # eax<- vtable[methodIndex]
+    jmp       common_invokeMethodRange
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+    /*
+     * 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),%ecx  # ecx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%ecx # 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,%ecx,4),%eax        # eax<- vtable[methodIndex]
+    jmp     common_invokeMethodRange
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.LOP_INVOKE_SUPER_RANGE_resolve:
+    SPILL_TMP(%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
+    SPILL(rPC)
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    UNSPILL(rPC)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP(%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
+    mov     %eax,OUT_ARG1(%esp)
+    jmp     common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+    /*
+     * 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_TMP(%ecx)
+     GET_GLUE(%ecx)
+     UNSPILL(rPC)
+     movl     offGlue_method(%ecx),%ecx  # ecx<- glue->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_TMP(%ecx)
+     testl    %eax,%eax
+     jne      .LOP_INVOKE_DIRECT_RANGE_finish
+     UNSPILL(rPC)
+     jmp      common_exceptionThrown
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+.LOP_INVOKE_STATIC_RANGE_continue:
+    movl      $METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rPC)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rPC)
+    testl     %eax,%eax                 # got null?
+    jne       common_invokeMethodRange
+    jmp       common_exceptionThrown
+
+/* continuation for OP_INVOKE_INTERFACE_RANGE */
+
+.LOP_INVOKE_INTERFACE_RANGE_continue:
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    UNSPILL(rPC)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    jmp        common_invokeMethodRange
+
+/* continuation for OP_FLOAT_TO_INT */
+
+
+.LOP_FLOAT_TO_INT_continue:
+    .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:
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+.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
+
+/* continuation for OP_FLOAT_TO_LONG */
+
+
+.LOP_FLOAT_TO_LONG_continue:
+    .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:
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+.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
+
+/* continuation for OP_DOUBLE_TO_INT */
+
+
+.LOP_DOUBLE_TO_INT_continue:
+    .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:
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+.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
+
+/* continuation for OP_DOUBLE_TO_LONG */
+
+
+.LOP_DOUBLE_TO_LONG_continue:
+    .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:
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+.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
+
+/* continuation for OP_DIV_INT */
+.LOP_DIV_INT_continue_div:
+    cltd
+    idivl   %ecx
+.LOP_DIV_INT_finish_div:
+    movzbl   rINST_HI,%ecx         # ecl<- AA
+    SET_VREG(%eax,%ecx)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_REM_INT */
+.LOP_REM_INT_continue_div:
+    cltd
+    idivl   %ecx
+.LOP_REM_INT_finish_div:
+    movzbl   rINST_HI,%ecx         # ecl<- AA
+    SET_VREG(%edx,%ecx)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_continue:
+    leal      (%ecx,%edx),%edx    # full result now in %edx:%eax
+    movzbl    rINST_HI,%ecx       # ecx<- A
+    movl      %edx,4(rFP,%ecx,4)  # v[B+1]<- %edx
+    UNSPILL(rPC)                  # restore rPC/%edx
+    FETCH_INST_WORD(2)
+    UNSPILL(rIBASE)
+    movl      %eax,(rFP,%ecx,4)   # v[B]<- %eax
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_DIV_LONG */
+
+.LOP_DIV_LONG_continue:
+    call     __divdi3
+.LOP_DIV_LONG_finish:
+    movzbl   rINST_HI,%ecx
+    SET_VREG_WORD(rPC,%ecx,1)
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+.LOP_DIV_LONG_check_zero:
+    testl   rPC,rPC
+    jne     .LOP_DIV_LONG_notSpecial
+    UNSPILL(rPC)
+    jmp     common_errDivideByZero
+.LOP_DIV_LONG_check_neg1:
+    testl   rPC,%eax
+    jne     .LOP_DIV_LONG_notSpecial
+    GET_VREG_WORD(rPC,%ecx,0)
+    GET_VREG_WORD(%ecx,%ecx,1)
+    testl    rPC,rPC
+    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,%edx
+    jmp      .LOP_DIV_LONG_finish
+
+/* continuation for OP_REM_LONG */
+
+.LOP_REM_LONG_continue:
+    call     __moddi3
+.LOP_REM_LONG_finish:
+    movzbl   rINST_HI,%ecx
+    SET_VREG_WORD(rPC,%ecx,1)
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+.LOP_REM_LONG_check_zero:
+    testl   rPC,rPC
+    jne     .LOP_REM_LONG_notSpecial
+    UNSPILL(rPC)
+    jmp     common_errDivideByZero
+.LOP_REM_LONG_check_neg1:
+    testl   rPC,%eax
+    jne     .LOP_REM_LONG_notSpecial
+    GET_VREG_WORD(rPC,%ecx,0)
+    GET_VREG_WORD(%ecx,%ecx,1)
+    testl    rPC,rPC
+    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,%edx
+    jmp      .LOP_REM_LONG_finish
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+    SET_VREG_WORD(%eax,%ecx,0)         # v[AA+0]<- %eax
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_SHR_LONG */
+
+
+.LOP_SHR_LONG_finish:
+    SET_VREG_WORD(%eax,%ecx,0)         # v[AA+0]<- eax
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_USHR_LONG */
+
+
+.LOP_USHR_LONG_finish:
+    SET_VREG_WORD(%eax,%ecx,0)        # v[BB+0]<- eax
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_DIV_INT_2ADDR */
+.LOP_DIV_INT_2ADDR_continue_div2addr:
+    cltd
+    idivl   %ecx
+.LOP_DIV_INT_2ADDR_finish_div2addr:
+    SET_VREG(%eax,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* continuation for OP_REM_INT_2ADDR */
+.LOP_REM_INT_2ADDR_continue_div2addr:
+    cltd
+    idivl   %ecx
+.LOP_REM_INT_2ADDR_finish_div2addr:
+    SET_VREG(%edx,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* continuation for OP_MUL_LONG_2ADDR */
+
+.LOP_MUL_LONG_2ADDR_continue:
+    leal      (%ecx,%edx),%edx    # full result now in %edx:%eax
+    movl      %edx,4(rIBASE)      # v[A+1]<- %edx
+    UNSPILL(rPC)                  # restore rPC/%edx
+    FETCH_INST_WORD(1)
+    movl      %eax,(rIBASE)       # v[A]<- %eax
+    UNSPILL(rFP)
+    UNSPILL(rIBASE)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* continuation for OP_DIV_LONG_2ADDR */
+
+.LOP_DIV_LONG_2ADDR_continue:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rPC,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __divdi3
+.LOP_DIV_LONG_2ADDR_finish:
+    movl     rINST_FULL,%ecx
+    SET_VREG_WORD(rPC,%ecx,1)
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+.LOP_DIV_LONG_2ADDR_check_zero:
+    testl   rPC,rPC
+    jne     .LOP_DIV_LONG_2ADDR_notSpecial
+    UNSPILL(rPC)
+    jmp     common_errDivideByZero
+.LOP_DIV_LONG_2ADDR_check_neg1:
+    testl   rPC,%eax
+    jne     .LOP_DIV_LONG_2ADDR_notSpecial
+    GET_VREG_WORD(rPC,rINST_FULL,0)
+    GET_VREG_WORD(%ecx,rINST_FULL,1)
+    testl    rPC,rPC
+    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,%edx
+    jmp      .LOP_DIV_LONG_2ADDR_finish
+
+/* continuation for OP_REM_LONG_2ADDR */
+
+.LOP_REM_LONG_2ADDR_continue:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rPC,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     __moddi3
+.LOP_REM_LONG_2ADDR_finish:
+    movl     rINST_FULL,%ecx
+    SET_VREG_WORD(rPC,%ecx,1)
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+.LOP_REM_LONG_2ADDR_check_zero:
+    testl   rPC,rPC
+    jne     .LOP_REM_LONG_2ADDR_notSpecial
+    UNSPILL(rPC)
+    jmp     common_errDivideByZero
+.LOP_REM_LONG_2ADDR_check_neg1:
+    testl   rPC,%eax
+    jne     .LOP_REM_LONG_2ADDR_notSpecial
+    GET_VREG_WORD(rPC,rINST_FULL,0)
+    GET_VREG_WORD(%ecx,rINST_FULL,1)
+    testl    rPC,rPC
+    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,%edx
+    jmp      .LOP_REM_LONG_2ADDR_finish
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+
+.LOP_SHL_LONG_2ADDR_finish:
+    SET_VREG_WORD(%eax,rINST_FULL,0)  # v[AA+0]<- eax
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+
+.LOP_SHR_LONG_2ADDR_finish:
+    SET_VREG_WORD(%eax,rINST_FULL,0)  # v[AA+0]<- eax
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+
+.LOP_USHR_LONG_2ADDR_finish:
+    SET_VREG_WORD(%eax,rINST_FULL,0)   # v[AA+0]<- eax
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+/* continuation for OP_DIV_INT_LIT16 */
+.LOP_DIV_INT_LIT16_continue_div:
+    cltd
+    idivl   %ecx
+.LOP_DIV_INT_LIT16_finish_div:
+    SET_VREG(%eax,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_REM_INT_LIT16 */
+.LOP_REM_INT_LIT16_continue_div:
+    cltd
+    idivl   %ecx
+.LOP_REM_INT_LIT16_finish_div:
+    SET_VREG(%edx,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_DIV_INT_LIT8 */
+.LOP_DIV_INT_LIT8_continue_div:
+    cltd
+    idivl   %ecx
+.LOP_DIV_INT_LIT8_finish_div:
+    SET_VREG(%eax,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_REM_INT_LIT8 */
+.LOP_REM_INT_LIT8_continue_div:
+    cltd
+    idivl   %ecx
+.LOP_REM_INT_LIT8_finish_div:
+    SET_VREG(%edx,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IGET_VOLATILE */
+
+
+.LOP_IGET_VOLATILE_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IGET_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IGET_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT_VOLATILE */
+
+
+.LOP_IPUT_VOLATILE_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which returns InstrField ptr
+    jne     .LOP_IPUT_VOLATILE_finish
+    jmp     common_exceptionThrown
+
+.LOP_IPUT_VOLATILE_finish:
+    /*
+     * Currently:
+     *   eax holds resolved field
+     *   ecx holds object
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   rINST_FULL,(%ecx,%eax,1)            # obj.field <- v[A](8/16/32 bits)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_SGET_VOLATILE */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_VOLATILE_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_VOLATILE_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SPUT_VOLATILE_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_VOLATILE_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+
+.LOP_IGET_OBJECT_VOLATILE_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which 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
+     *   rIBASE is scratch, but needs to be unspilled
+     *   rINST_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_EXECUTE_INLINE */
+
+.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),rPC
+
+    movl      $0xf,%ecx
+    andl      rPC,%ecx
+    GET_VREG(%ecx,%ecx)
+    sarl      $4,rPC
+    movl      %ecx,4+OUT_ARG0(%esp)
+
+    movl      $0xf,%ecx
+    andl      rPC,%ecx
+    GET_VREG(%ecx,%ecx)
+    sarl      $4,rPC
+    movl      %ecx,4+OUT_ARG1(%esp)
+
+    movl      $0xf,%ecx
+    andl      rPC,%ecx
+    GET_VREG(%ecx,%ecx)
+    sarl      $4,rPC
+    movl      %ecx,4+OUT_ARG2(%esp)
+
+    movl      $0xf,%ecx
+    andl      rPC,%ecx
+    GET_VREG(%ecx,%ecx)
+    sarl      $4,rPC
+    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
+
+/* continuation for OP_IPUT_OBJECT_QUICK */
+
+.LOP_IPUT_OBJECT_QUICK_finish:
+    testl     rINST_FULL,rINST_FULL         # did we store null?
+    FETCH_INST_WORD(2)
+    movl      offGlue_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:
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+
+.LOP_IPUT_OBJECT_VOLATILE_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    testl   %eax,%eax                             #  ... which 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_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl    rINST_FULL,(%ecx,%eax)          # obj.field <- v[A](8/16/32 bits)
+    GET_GLUE(%eax)
+    testl   rINST_FULL,rINST_FULL                # stored a NULL?
+    movl    offGlue_cardTable(%eax),%eax         # get card table base
+    FETCH_INST_WORD(2)
+    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:
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+    /*
+     * Go resolve the field
+     */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SGET_OBJECT_VOLATILE_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_continue:
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    FETCH_INST_WORD(2)
+    je        1f                                 # skip card mark if null
+    GET_GLUE(%ecx)
+    movl      offField_clazz(%eax),%eax          # eax<- field->clazz
+    movl      offGlue_cardTable(%ecx),%ecx       # get card table base
+    shrl      $GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .LOP_SPUT_OBJECT_VOLATILE_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
+
+    .size   dvmAsmSisterStart, .-dvmAsmSisterStart
+    .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* 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(MterpGlue* glue)
+ *
+ * Interpreter entry point.  Returns changeInterp.
+ *
+ */
+dvmMterpStdRun:
+    push    %ebp
+    movl    %esp,%ebp
+    push    %edi
+    push    %esi
+    push    %ebx
+
+/* at this point, stack is misaligned by 1 word
+   We're allocating spill space for 6 words, plus
+   outgoing argument (5 words) and local variables
+   (4 words) - 15 words or 60 bytes total. See
+   diagram in header.S
+*/
+    subl   $60,%esp
+
+/* Set up "named" registers */
+    movl    IN_ARG0(%ebp),%ecx
+    movl    %ecx,rGLUE_SPILL(%ebp)
+    LOAD_PC_FROM_GLUE(%ecx)
+    LOAD_FP_FROM_GLUE(%ecx)
+    movl    $dvmAsmInstructionStart,rIBASE
+
+/* Remember %esp for future "longjmp" */
+    movl    %esp,offGlue_bailPtr(%ecx)
+
+/* How to start? */
+    movb    offGlue_entryPoint(%ecx),%al
+
+/* Normal start? */
+    cmpb    $kInterpEntryInstr,%al
+    jne     .Lnot_instr
+
+   /* Normal case: start executing the instruction at rPC */
+    FETCH_INST()
+    GOTO_NEXT
+
+.Lnot_instr:
+    /* Reset to normal case */
+    movb   $kInterpEntryInstr,offGlue_entryPoint(%ecx)
+    cmpb   $kInterpEntryReturn,%al
+    je     common_returnFromMethod
+    cmpb   $kInterpEntryThrow,%al
+    je     common_exceptionThrown
+    movzx  %al,%eax
+    movl   %eax,OUT_ARG1(%esp)
+    movl   $.LstrBadEntryPoint,OUT_ARG0(%esp)
+    call   printf
+    call   dvmAbort
+    /* Not reached */
+
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+/*
+ * void dvmMterpStdBail(MterpGlue* glue, 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)  MterpGlue* glue
+ *  esp+8 (arg1)  bool changeInterp
+ */
+dvmMterpStdBail:
+    movl    4(%esp),%ecx                 # grab glue
+    movl    8(%esp),%eax                 # changeInterp to return reg
+    movl    offGlue_bailPtr(%ecx),%esp   # Stack back to normal
+    addl    $60,%esp                    # Strip dvmMterpStdRun's frame
+    pop     %ebx
+    pop     %esi
+    pop     %edi
+    pop     %ebp
+    ret                                  # return to dvmMterpStdRun's caller
+
+
+/*
+ * 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.
+ */
+
+/*
+ * Common code when a backwards branch is taken
+ *
+ * On entry:
+ *   ebx (a.k.a. rINST_FULL) -> PC adjustment in 16-bit words
+ */
+common_backwardBranch:
+    GET_GLUE(%ecx)
+    call   common_periodicChecks  # Note: expects rPC to be preserved
+    ADVANCE_PC_INDEXED(rINST_FULL)
+    FETCH_INST()
+    GOTO_NEXT
+
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *   eax = Method* methodToCall
+ *   rINST trashed, must reload
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+
+   /*
+    * prepare to copy args to "outs" area of current frame
+    */
+
+    movzbl      1(rPC),rINST_FULL       # rINST_FULL<- AA
+    movzwl      4(rPC), %ecx            # %ecx<- CCCC
+    SPILL(rPC)
+    SAVEAREA_FROM_FP(%edx,rFP)          # %edx<- &StackSaveArea
+    test        rINST_FULL, rINST_FULL
+    movl        rINST_FULL, 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)
+    */
+
+    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
+    */
+
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    movzbl      1(rPC),rINST_FULL       # rINST_FULL<- BA
+    SPILL(rPC)
+    movl        rINST_FULL, 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,rFP)          # %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_FULL        # rINST<- A
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, rINST_FULL, 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,rFP)          # %eax<- &StackSaveArea
+    subl        %edx, %eax              # %eax<- newFP; (old savearea - regsSize)
+    GET_GLUE(%edx)                      # %edx<- pMterpGlue
+    movl        %eax, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- &outs
+    subl        $sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
+    movl        offGlue_interpStackEnd(%edx), %edx # %edx<- glue->interpStackEnd
+    movl        %edx, LOCAL2_OFFSET(%ebp)       # LOCAL2_OFFSET<- glue->interpStackEnd
+    shl         $2, %ecx               # %ecx<- update offset for outsSize
+    movl        %eax, %edx              # %edx<- newSaveArea
+    sub         %ecx, %eax              # %eax<- bottom; (newSaveArea - outsSize)
+    cmp         LOCAL2_OFFSET(%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,rFP)          # %ecx<- &StackSaveArea
+    movl        %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
+#endif
+    movl        rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
+    movl        rPC_SPILL(%ebp), %ecx
+    movl        %ecx, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
+    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 "glue" values for the new method
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
+    */
+
+    movl        offMethod_clazz(%eax), %edx # %edx<- method->clazz
+    GET_GLUE(%ecx)                      # %ecx<- pMterpGlue
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        %eax, offGlue_method(%ecx) # glue->method<- methodToCall
+    movl        %edx, offGlue_methodClassDex(%ecx) # glue->methodClassDex<- method->clazz->pDvmDex
+    movl        offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
+    movl        offGlue_self(%ecx), %eax # %eax<- glue->self
+    movl        LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
+    movl        rFP, offThread_curFrame(%eax) # glue->self->curFrame<- newFP
+    FETCH_INST()
+    GOTO_NEXT                           # jump to methodToCall->insns
+
+   /*
+    * Prep for the native call
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea
+    */
+
+.LinvokeNative:
+    GET_GLUE(%ecx)                      # %ecx<- pMterpGlue
+    movl        %eax, OUT_ARG1(%esp)    # push parameter methodToCall
+    movl        offGlue_self(%ecx), %ecx        # %ecx<- glue->self
+    movl        offThread_jniLocal_topCookie(%ecx), %eax # %eax<- self->localRef->...
+    movl        %eax, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
+    movl        %edx, OUT_ARG4(%esp)    # save newSaveArea
+    movl        LOCAL1_OFFSET(%ebp), %edx # %edx<- newFP
+    movl        %edx, offThread_curFrame(%ecx)  # glue->self->curFrame<- newFP
+    movl        %ecx, OUT_ARG3(%esp)    # save glue->self
+    movl        %ecx, OUT_ARG2(%esp)    # push parameter glue->self
+    GET_GLUE(%ecx)                      # %ecx<- pMterpGlue
+    movl        OUT_ARG1(%esp), %eax    # %eax<- methodToCall
+    lea         offGlue_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %ecx, OUT_ARG0(%esp)    # push parameter pMterpGlue
+    push        %edx                    # push parameter newFP
+
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+    lea         4(%esp), %esp
+    movl        OUT_ARG4(%esp), %ecx    # %ecx<- newSaveArea
+    movl        OUT_ARG3(%esp), %eax    # %eax<- glue->self
+    movl        offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
+    cmp         $0, offThread_exception(%eax) # check for exception
+    movl        rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
+    movl        %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
+    UNSPILL(rPC)
+    jne         common_exceptionThrown  # handle exception
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    GOTO_NEXT                           # jump to next instruction
+
+.LstackOverflow:    # eax=methodToCall
+    movl        %eax, OUT_ARG1(%esp)    # push parameter methodToCall
+    GET_GLUE(%eax)                      # %eax<- pMterpGlue
+    movl        offGlue_self(%eax), %eax # %eax<- glue->self
+    movl        %eax, OUT_ARG0(%esp)    # push parameter self
+    call        dvmHandleStackOverflow  # call: (Thread* self, Method* meth)
+    UNSPILL(rPC)                        # return: void
+    jmp         common_exceptionThrown  # handle exception
+
+
+/*
+ * Common invoke code (old-style).
+ * TUNING:  Rewrite along lines of new armv5 code?
+ *
+ * On entry:
+ *   eax = Method* methodToCall
+ *   ecx = bool methodCallRange
+ *   rINST trashed, must reload
+ */
+common_invokeOld:
+    movl     %ecx,OUT_ARG1(%esp)     # arg1<- methodCallRange
+    GET_GLUE(%ecx)
+    movzwl  (rPC),rINST_FULL         # recover rINST
+    movl     %eax,OUT_ARG2(%esp)     # arg2<- method
+    movzwl   4(rPC),%eax             # eax<- GFED or CCCC
+    SAVE_PC_TO_GLUE(%ecx)
+    SAVE_FP_TO_GLUE(%ecx)
+    movzbl   rINST_HI,rINST_FULL
+    movl     rINST_FULL,OUT_ARG3(%esp)# arg3<- AA
+    movl     %ecx,OUT_ARG0(%esp)     # arg0<- GLUE
+    movl     %eax,OUT_ARG4(%esp)     # arg4<- GFED/CCCC
+    call     dvmMterp_invokeMethod
+    jmp      common_resumeAfterGlueCall
+
+
+/*
+ * Do we need the thread to be suspended or have debugger/profiling activity?
+ *
+ * On entry:
+ *   ebx  -> PC adjustment in 16-bit words (must be preserved)
+ *   ecx  -> GLUE pointer
+ *   reentry type, e.g. kInterpEntryInstr stored in rGLUE->entryPoint
+ *
+ * Note: A call will normally kill %eax, rPC/%edx and %ecx.  To
+ *       streamline the normal case, this routine will preserve rPC and
+ *       %ecx in addition to the normal caller save regs.  The save/restore
+ *       is a bit ugly, but will happen in the relatively uncommon path.
+ * TODO: Basic-block style Jit will need a hook here as well.  Fold it into
+ *       the suspendCount check so we can get both in 1 shot.
+ */
+common_periodicChecks:
+    movl    offGlue_pSelfSuspendCount(%ecx),%eax    # eax <- &suspendCount
+    cmpl    $0,(%eax)
+    jne     1f
+
+6:
+    movl   offGlue_pDebuggerActive(%ecx),%eax      # eax <- &DebuggerActive
+    movl   offGlue_pActiveProfilers(%ecx),%ecx     # ecx <- &ActiveProfilers
+    testl  %eax,%eax               # debugger enabled?
+    je     2f
+    movzbl (%eax),%eax             # get active count
+2:
+    orl    (%ecx),%eax             # eax <- debuggerActive | activeProfilers
+    GET_GLUE(%ecx)                 # restore rGLUE
+    jne    3f                      # one or both active - switch interp
+
+5:
+    ret
+
+    /* Check for suspend */
+1:
+    /*  At this point, the return pointer to the caller of
+     *  common_periodicChecks is on the top of stack.  We need to preserve
+     *  rPC(edx) and GLUE(ecx).  We'll spill rPC, and reload GLUE.
+     *  The outgoing profile is:
+     *      bool dvmCheckSuspendPending(Thread* self)
+     *  Because we reached here via a call, go ahead and build a new frame.
+     */
+    EXPORT_PC()                         # need for precise GC
+    movl    offGlue_self(%ecx),%eax      # eax<- glue->self
+    SPILL(rPC)                      # save edx
+    push    %ebp
+    movl    %esp,%ebp
+    subl    $24,%esp
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmCheckSuspendPending
+    addl    $24,%esp
+    pop     %ebp
+    UNSPILL(rPC)
+    GET_GLUE(%ecx)
+
+    /*
+     * Need to check to see if debugger or profiler flags got set
+     * while we were suspended.
+     */
+    jmp    6b
+
+    /* Switch interpreters */
+    /* Note: %ebx contains the 16-bit word offset to be applied to rPC to
+     * "complete" the interpretation of backwards branches.  In effect, we
+     * are completing the interpretation of the branch instruction here,
+     * and the new interpreter will resume interpretation at the branch
+     * target. However, a switch request recognized during the handling
+     * of a return from method instruction results in an immediate abort,
+     * and the new interpreter will resume by re-interpreting the return
+     * instruction.
+     */
+3:
+    leal    (rPC,%ebx,2),rPC       # adjust pc to show target
+    GET_GLUE(%ecx)                 # bail expect GLUE already loaded
+    movl    $1,rINST_FULL         # set changeInterp to true
+    jmp     common_gotoBail
+
+
+/*
+ * Common code for handling a return instruction
+ */
+common_returnFromMethod:
+    GET_GLUE(%ecx)
+    /* Set entry mode in case we bail */
+    movb    $kInterpEntryReturn,offGlue_entryPoint(%ecx)
+    xorl    rINST_FULL,rINST_FULL   # zero offset in case we switch interps
+    call    common_periodicChecks   # Note: expects %ecx to be preserved
+
+    SAVEAREA_FROM_FP(%eax,rFP)                    # eax<- saveArea (old)
+    movl    offStackSaveArea_prevFrame(%eax),rFP  # rFP<- prevFrame
+    movl    (offStackSaveArea_method-sizeofStackSaveArea)(rFP),rINST_FULL
+    cmpl    $0,rINST_FULL                        # break?
+    je      common_gotoBail    # break frame, bail out completely
+
+    movl    offStackSaveArea_savedPc(%eax),rPC    # pc<- saveArea->savedPC
+    movl    offGlue_self(%ecx),%eax               # eax<- self
+    movl    rINST_FULL,offGlue_method(%ecx)  # glue->method = newSave->meethod
+    movl    rFP,offThread_curFrame(%eax)     # self->curFrame = fp
+    movl    offMethod_clazz(rINST_FULL),%eax # eax<- method->clazz
+    FETCH_INST_WORD(3)
+    movl    offClassObject_pDvmDex(%eax),%eax # eax<- method->clazz->pDvmDex
+    ADVANCE_PC(3)
+    movl    %eax,offGlue_methodClassDex(%ecx)
+    /* not bailing - restore entry mode to default */
+    movb    $kInterpEntryInstr,offGlue_entryPoint(%ecx)
+    GOTO_NEXT
+
+/*
+ * Prepare to strip the current frame and "longjump" back to caller of
+ * dvmMterpStdRun.
+ *
+ * on entry:
+ *    rINST_FULL holds changeInterp
+ *    ecx holds glue pointer
+ *
+ * expected profile: dvmMterpStdBail(MterpGlue *glue, bool changeInterp)
+ */
+common_gotoBail:
+    SAVE_PC_TO_GLUE(%ecx)                # export state to glue
+    SAVE_FP_TO_GLUE(%ecx)
+    movl   %ecx,OUT_ARG0(%esp)           # glue in arg0
+    movl   rINST_FULL,OUT_ARG1(%esp)     # changeInterp in arg1
+    call    dvmMterpStdBail              # bail out....
+
+
+/*
+ * After returning from a "glued" function, pull out the updated values
+ * and start executing at the next instruction.
+ */
+ common_resumeAfterGlueCall:
+     GET_GLUE(%ecx)
+     LOAD_PC_FROM_GLUE(%ecx)
+     LOAD_FP_FROM_GLUE(%ecx)
+     FETCH_INST()
+     GOTO_NEXT
+
+/*
+ * Integer divide or mod by zero
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    movl    $.LstrArithmeticException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    $.LstrDivideByZero,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    movl    $.LstrNegativeArraySizeException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNoSuchMethod:
+
+    EXPORT_PC()
+    movl    $.LstrNoSuchMethodError,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    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()
+    movl    $.LstrNullPointerException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/*
+ * Array index exceeds max.
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    movl    $.LstrArrayIndexException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+    EXPORT_PC()
+    movl    $.LstrArrayStoreException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    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.
+ *
+ * This does not return.
+ */
+common_exceptionThrown:
+    GET_GLUE(%ecx)
+    SAVE_PC_TO_GLUE(%ecx)
+    SAVE_FP_TO_GLUE(%ecx)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmMterp_exceptionThrown
+    jmp     common_resumeAfterGlueCall
+
+common_abort:
+    movl    $0xdeadf00d,%eax
+    call     *%eax
+
+
+/*
+ * Strings
+ */
+
+    .section     .rodata
+.LstrNullPointerException:
+    .asciz    "Ljava/lang/NullPointerException;"
+.LstrArithmeticException:
+    .asciz  "Ljava/lang/ArithmeticException;"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrArrayIndexException:
+    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz  "Ljava/lang/ArrayStoreException;"
+.LstrNegativeArraySizeException:
+    .asciz  "Ljava/lang/NegativeArraySizeException;"
+.LstrInstantiationError:
+    .asciz  "Ljava/lang/InstantiationError;"
+.LstrClassCastException:
+    .asciz  "Ljava/lang/ClassCastException;"
+.LstrNoSuchMethodError:
+    .asciz  "Ljava/lang/NoSuchMethodError;"
+.LstrInternalError:
+    .asciz  "Ljava/lang/InternalError;"
+.LstrFilledNewArrayNotImpl:
+    .asciz  "filled-new-array only implemented for 'int'"
+
diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c
new file mode 100644
index 0000000..ce6192c
--- /dev/null
+++ b/vm/mterp/out/InterpC-allstubs.c
@@ -0,0 +1,4132 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'allstubs'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_NOP.c */
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+/* File: c/OP_MOVE.c */
+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.c */
+/* File: c/OP_MOVE_FROM16.c */
+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.c */
+/* File: c/OP_MOVE_16.c */
+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.c */
+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.c */
+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.c */
+/* File: c/OP_MOVE_RESULT.c */
+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.c */
+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.c */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.c */
+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.c */
+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.c */
+/* File: c/OP_RETURN.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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\n", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC, also WITH_MONITOR_TRACKING */
+        dvmLockObject(self, obj);
+#ifdef WITH_DEADLOCK_PREDICTION
+        if (dvmCheckException(self))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.c */
+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\n", 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.c */
+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)) {
+                dvmThrowExceptionWithClassMessage(
+                    "Ljava/lang/ClassCastException;", obj->clazz->descriptor);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.c */
+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.c */
+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.c */
+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();
+
+        /*
+         * 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 (!dvmDexGetResolvedClass(methodClassDex, ref)) {
+            /* Class initialization is still ongoing - abandon the trace */
+            ABORT_JIT_TSELECT();
+        }
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+        //        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.c */
+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) {
+            dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+            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.c */
+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.c */
+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.c */
+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 */
+            dvmThrowException("Ljava/lang/InternalError;",
+                              "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.c */
+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\n");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
+
+/* File: c/OP_GOTO.c */
+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(kInterpEntryInstr, (s1)vdst);
+    FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.c */
+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(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_GOTO_32.c */
+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(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.c */
+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, vsrc2);
+        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();
+            dvmThrowException("Ljava/lang/InternalError;", "bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)\n", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.c */
+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, vsrc2);
+        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();
+            dvmThrowException("Ljava/lang/InternalError;", "bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)\n", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.c */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.c */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.c */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.c */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.c */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.c */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.c */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.c */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.c */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.c */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.c */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.c */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.c */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.c */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.c */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.c */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.c */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.c */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.c */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.c */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.c */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.c */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.c */
+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) {
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+                NULL);
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->obj.clazz)) {
+                LOGV("Can't put a '%s'(%p) into array type='%s'(%p)\n",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->obj.clazz->descriptor, arrayObj);
+                //dvmDumpClass(obj->clazz);
+                //dvmDumpClass(arrayObj->obj.clazz);
+                dvmThrowException("Ljava/lang/ArrayStoreException;", NULL);
+                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.c */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.c */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.c */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.c */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.c */
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.c */
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.c */
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.c */
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.c */
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.c */
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.c */
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.c */
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.c */
+/*
+ * 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.c */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.c */
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.c */
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.c */
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SGET.c */
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.c */
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.c */
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.c */
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.c */
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.c */
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.c */
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.c */
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.c */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.c */
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.c */
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.c */
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.c */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.c */
+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.c */
+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.c */
+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.c */
+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.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.c */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.c */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.c */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.c */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.c */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.c */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.c */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.c */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.c */
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.c */
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.c */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.c */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.c */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.c */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.c */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.c */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.c */
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.c */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.c */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.c */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.c */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.c */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.c */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.c */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.c */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.c */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.c */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.c */
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.c */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.c */
+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.c */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.c */
+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.c */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.c */
+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.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.c */
+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.c */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.c */
+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.c */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.c */
+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.c */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+    {
+        /*
+         * 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);
+        LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+            INST_REPLACE_OP(inst, originalOpCode));
+        inst = INST_REPLACE_OP(inst, originalOpCode);
+        FINISH_BKPT(originalOpCode);
+    }
+#else
+    LOGE("Breakpoint hit in non-debug interpreter\n");
+    dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.c */
+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.c */
+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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_EMPTY.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_EMPTY /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+#if INTERP_TYPE != INTERP_DBG
+    //LOGI("Ignoring empty\n");
+    FINISH(3);
+#else
+    if (!gDvm.debuggerActive) {
+        //LOGI("Skipping empty\n");
+        FINISH(3);      // don't want it to show up in profiler output
+    } else {
+        //LOGI("Running empty\n");
+        /* fall through to OP_INVOKE_DIRECT */
+        GOTO_invoke(invokeDirect, false);
+    }
+#endif
+OP_END
+
+/* File: c/OP_UNUSED_F1.c */
+HANDLE_OPCODE(OP_UNUSED_F1)
+OP_END
+
+/* File: c/OP_IGET_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.c */
+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.c */
+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.c */
+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.c */
+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.c */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.c */
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    LOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
+
+/* File: cstubs/entry.c */
+/*
+ * Handler function table, one entry per opcode.
+ */
+#undef H
+#define H(_op) 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.
+ */
+bool dvmMterpStdRun(MterpGlue* glue)
+{
+    jmp_buf jmpBuf;
+    int changeInterp;
+
+    glue->bailPtr = &jmpBuf;
+
+    /*
+     * We want to return "changeInterp" as a boolean, but we can't return
+     * zero through longjmp, so we return (boolean+1).
+     */
+    changeInterp = setjmp(jmpBuf) -1;
+    if (changeInterp >= 0) {
+        Thread* threadSelf = dvmThreadSelf();
+        LOGVV("mterp threadid=%d returning %d\n",
+            threadSelf->threadId, changeInterp);
+        return changeInterp;
+    }
+
+    /*
+     * We may not be starting at a point where we're executing instructions.
+     * We need to pick up where the other interpreter left off.
+     *
+     * In some cases we need to call into a throw/return handler which
+     * will do some processing and then either return to us (updating "glue")
+     * or longjmp back out.
+     */
+    switch (glue->entryPoint) {
+    case kInterpEntryInstr:
+        /* just start at the start */
+        break;
+    case kInterpEntryReturn:
+        dvmMterp_returnFromMethod(glue);
+        break;
+    case kInterpEntryThrow:
+        dvmMterp_exceptionThrown(glue);
+        break;
+    default:
+        dvmAbort();
+    }
+
+    /* run until somebody longjmp()s out */
+    while (true) {
+        typedef void (*Handler)(MterpGlue* glue);
+
+        u2 inst = /*glue->*/pc[0];
+        Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff];
+        LOGVV("handler %p %s\n",
+            handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]);
+        (*handler)(glue);
+    }
+}
+
+/*
+ * C mterp exit point.  Call here to bail out of the interpreter.
+ */
+void dvmMterpStdBail(MterpGlue* glue, bool changeInterp)
+{
+    jmp_buf* pJmpBuf = glue->bailPtr;
+    longjmp(*pJmpBuf, ((int)changeInterp)+1);
+}
+
+/* File: c/gotoTargets.c */
+/*
+ * 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)
+    {
+        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)) {
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "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'\n", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            LOGE("non-int primitives not implemented\n");
+            dvmThrowException("Ljava/lang/InternalError;",
+                "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*) 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 = newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+    {
+        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\n");
+                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) && (INTERP_TYPE == INTERP_DBG)
+        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.
+             */
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+                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\n");
+                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.
+             */
+            dvmThrowException("Ljava/lang/NoSuchMethodError;",
+                baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+            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;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisClass;
+#endif
+
+        /*
+         * 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 (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        EXPORT_PC();
+
+        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\n");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    EXPORT_PC();
+
+    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\n");
+            GOTO_exceptionThrown();
+        }
+
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+            /* Class initialization is still ongoing */
+            ABORT_JIT_TSELECT();
+        }
+    }
+    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();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisPtr->clazz;
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s\n",
+            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) {
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < 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)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s\n",
+            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(kInterpEntryReturn, 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
+#if (INTERP_TYPE == INTERP_DBG)
+        TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = saveArea->prevFrame;
+        assert(fp != NULL);
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+            /* Let the Jit know the return is terminating normally */
+            CHECK_JIT_VOID();
+#endif
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 {
+            //LOGE("Unknown invoke instr %02x at %d\n",
+            //    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;
+
+        /*
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+        // Something threw during trace selection - abort the current trace
+        ABORT_JIT_TSELECT();
+#endif
+        /*
+         * 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);
+
+        LOGV("Handling exception %s at %s:%d\n",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+        /*
+         * Tell the debugger about it.
+         *
+         * 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 (gDvm.debuggerActive) {
+            void* catchFrame;
+            catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                        exception, true, &catchFrame);
+            dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+                catchRelPc, exception);
+        }
+#endif
+
+        /*
+         * 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*)&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
+            LOGD("Exception %s from %s:%d not caught locally\n",
+                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;
+            LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 */
+                LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+            //    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)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+            debugIsMethodEntry = true;              // profiling, debugging
+#endif
+            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 */
+#ifdef USE_INDIRECT_REF
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+            self->curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+            {
+                ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                        methodToCall->name, methodToCall->shorty);
+            }
+
+#if defined(WITH_JIT)
+            /* Allow the Jit to end any pending trace building */
+            CHECK_JIT_VOID();
+#endif
+
+            /*
+             * 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 (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->curFrame = fp;
+
+            /*
+             * 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)) {
+                LOGV("Exception thrown by/below native code\n");
+                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 {
+                //LOGE("Unknown invoke instr %02x at %d\n",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.c */
+
+/* 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-armv4t.c b/vm/mterp/out/InterpC-armv4t.c
new file mode 100644
index 0000000..2e7716b
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv4t.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv4t'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#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)
+{
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rGLUE     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 rGLUE=%08x rINST=%08x\n",
+        rPC, rFP, rGLUE, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+    //MterpGlue* glue = (MterpGlue*) rGLUE;
+    //const Method* method = glue->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-vfp.c b/vm/mterp/out/InterpC-armv5te-vfp.c
new file mode 100644
index 0000000..48b8dbd
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv5te-vfp.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#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)
+{
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rGLUE     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 rGLUE=%08x rINST=%08x\n",
+        rPC, rFP, rGLUE, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+    //MterpGlue* glue = (MterpGlue*) rGLUE;
+    //const Method* method = glue->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.c b/vm/mterp/out/InterpC-armv5te.c
new file mode 100644
index 0000000..dfadf21
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv5te.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#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)
+{
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rGLUE     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 rGLUE=%08x rINST=%08x\n",
+        rPC, rFP, rGLUE, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+    //MterpGlue* glue = (MterpGlue*) rGLUE;
+    //const Method* method = glue->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.c b/vm/mterp/out/InterpC-armv7-a-neon.c
new file mode 100644
index 0000000..adccb2d
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv7-a-neon.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#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)
+{
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rGLUE     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 rGLUE=%08x rINST=%08x\n",
+        rPC, rFP, rGLUE, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+    //MterpGlue* glue = (MterpGlue*) rGLUE;
+    //const Method* method = glue->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.c b/vm/mterp/out/InterpC-armv7-a.c
new file mode 100644
index 0000000..d40fd7c
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv7-a.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#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)
+{
+    register uint32_t rPC       asm("r4");
+    register uint32_t rFP       asm("r5");
+    register uint32_t rGLUE     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 rGLUE=%08x rINST=%08x\n",
+        rPC, rFP, rGLUE, rINST);
+    printf("    : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+    //MterpGlue* glue = (MterpGlue*) rGLUE;
+    //const Method* method = glue->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-portdbg.c b/vm/mterp/out/InterpC-portdbg.c
new file mode 100644
index 0000000..e909db4
--- /dev/null
+++ b/vm/mterp/out/InterpC-portdbg.c
@@ -0,0 +1,4442 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'portdbg'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: portable/portdbg.c */
+#define INTERP_FUNC_NAME dvmInterpretDbg
+#define INTERP_TYPE INTERP_DBG
+
+#define CHECK_DEBUG_AND_PROF() \
+    checkDebugAndProf(pc, fp, self, curMethod, &debugIsMethodEntry)
+
+#if defined(WITH_JIT)
+#define CHECK_JIT_BOOL() (dvmCheckJit(pc, self, interpState, callsiteClass,\
+                          methodToCall))
+#define CHECK_JIT_VOID() (dvmCheckJit(pc, self, interpState, callsiteClass,\
+                          methodToCall))
+#define ABORT_JIT_TSELECT() (dvmJitAbortTraceSelect(interpState))
+#else
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT(x) ((void)0)
+#endif
+
+/* File: portable/stubdefs.c */
+/*
+ * 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)
+
+/*
+ * 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".
+ *
+ * TODO: remove "switch" version.
+ */
+#ifdef THREADED_INTERP
+# define H(_op)             &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) {                                                  \
+        ADJUST_PC(_offset);                                                 \
+        inst = FETCH(0);                                                    \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        if (CHECK_JIT_BOOL()) GOTO_bail_switch();                           \
+        goto *handlerTable[INST_INST(inst)];                                \
+    }
+# define FINISH_BKPT(_opcode) {                                             \
+        goto *handlerTable[_opcode];                                        \
+    }
+#else
+# define HANDLE_OPCODE(_op) case _op:
+# define FINISH(_offset)    { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
+#endif
+
+#define OP_END
+
+#if defined(WITH_TRACKREF_CHECKS)
+# define CHECK_TRACKED_REFS() \
+    dvmInterpCheckTrackedRefs(self, curMethod, debugTrackedRefStart)
+#else
+# define CHECK_TRACKED_REFS() ((void)0)
+#endif
+
+
+/*
+ * 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;
+#define GOTO_bail_switch() goto bail_switch;
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            interpState->entryPoint = _entryPoint;                          \
+            LOGVV("threadid=%d: switch to %s ep=%d adj=%d\n",               \
+                self->threadId,                                             \
+                (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",      \
+                (_entryPoint), (_pcadj));                                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: portable/debug.c */
+/* code in here is only included in portable-debug interpreter */
+
+/*
+ * 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,
+    bool methodEntry, 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().
+     */
+    EXPORT_PC();
+
+    if (methodEntry)
+        eventFlags |= DBG_METHOD_ENTRY;
+
+    /*
+     * 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 (INST_INST(*pc) == OP_BREAKPOINT) {
+        LOGV("+++ breakpoint hit at %p\n", 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) {
+            LOGV("#####S %s\n", 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 inst = INST_INST(FETCH(0));
+    if (inst == OP_RETURN_VOID || inst == OP_RETURN || inst == OP_RETURN_WIDE ||
+        inst == OP_RETURN_OBJECT)
+    {
+        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 && !dvmIsValidObject(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);
+            LOGE("HEY: invalid 'this' ptr %p (%s.%s %s)\n", thisPtr,
+                method->clazz->descriptor, method->name, desc);
+            free(desc);
+            dvmAbort();
+        }
+        dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
+            eventFlags);
+    }
+}
+
+/*
+ * Perform some operations at the "top" of the interpreter loop.
+ * This stuff is required to support debugging and profiling.
+ *
+ * Using" __attribute__((noinline))" seems to do more harm than good.  This
+ * is best when inlined due to the large number of parameters, most of
+ * which are local vars in the main interp loop.
+ */
+static void checkDebugAndProf(const u2* pc, const u4* fp, Thread* self,
+    const Method* method, bool* pIsMethodEntry)
+{
+    /* check to see if we've run off end of 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 (/*gDvm.debuggerActive &&*/
+            strcmp(method->clazz->descriptor, cd) == 0 &&
+            strcmp(method->name, mn) == 0 &&
+            strcmp(method->shorty, sg) == 0)
+        {
+            LOGW("Reached %s.%s, enabling verbose mode\n",
+                method->clazz->descriptor, method->name);
+            android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
+            dumpRegs(method, fp, true);
+        }
+
+        if (!gDvm.debuggerActive)
+            *pIsMethodEntry = false;
+    }
+#endif
+
+    /*
+     * If the debugger is attached, check for events.  If the profiler is
+     * enabled, update that too.
+     *
+     * This code is executed for every instruction we interpret, so for
+     * performance we use a couple of #ifdef blocks instead of runtime tests.
+     */
+    bool isEntry = *pIsMethodEntry;
+    if (isEntry) {
+        *pIsMethodEntry = false;
+        TRACE_METHOD_ENTER(self, method);
+    }
+    if (gDvm.debuggerActive) {
+        updateDebugger(method, pc, fp, isEntry, 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).
+         */
+        int inst = *pc & 0xff;
+        gDvm.executedInstrCounts[inst]++;
+    }
+}
+
+/* File: portable/entry.c */
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState)
+{
+#if defined(EASY_GDB)
+    StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->curFrame);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+    bool debugIsMethodEntry = false;
+    debugIsMethodEntry = interpState->debugIsMethodEntry;
+#endif
+#if defined(WITH_TRACKREF_CHECKS)
+    int debugTrackedRefStart = interpState->debugTrackedRefStart;
+#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 */
+    u2 ref;                     // 16-bit quantity fetched directly
+    u2 vsrc1, vsrc2, vdst;      // usually used for register indexes
+    /* method call setup */
+    const Method* methodToCall;
+    bool methodCallRange;
+
+
+#if defined(THREADED_INTERP)
+    /* static computed goto table */
+    DEFINE_GOTO_TABLE(handlerTable);
+#endif
+
+#if defined(WITH_JIT)
+#if 0
+    LOGD("*DebugInterp - entrypoint is %d, tgt is 0x%x, %s\n",
+         interpState->entryPoint,
+         interpState->pc,
+         interpState->method->name);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+    const ClassObject* callsiteClass = NULL;
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (interpState->jitState != kJitSelfVerification) {
+        interpState->self->shadowSpace->jitExitState = kSVSIdle;
+    }
+#endif
+
+    /* Check to see if we've got a trace selection request. */
+    if (
+         /*
+          * Only perform dvmJitCheckTraceRequest if the entry point is
+          * EntryInstr and the jit state is either kJitTSelectRequest or
+          * kJitTSelectRequestHot. If debugger/profiler happens to be attached,
+          * dvmJitCheckTraceRequest will change the jitState to kJitDone but
+          * but stay in the dbg interpreter.
+          */
+         (interpState->entryPoint == kInterpEntryInstr) &&
+         (interpState->jitState == kJitTSelectRequest ||
+          interpState->jitState == kJitTSelectRequestHot) &&
+         dvmJitCheckTraceRequest(self, interpState)) {
+        interpState->nextMode = INTERP_STD;
+        //LOGD("Invalid trace request, exiting\n");
+        return true;
+    }
+#endif /* INTERP_TYPE == INTERP_DBG */
+#endif /* WITH_JIT */
+
+    /* copy state in */
+    curMethod = interpState->method;
+    pc = interpState->pc;
+    fp = interpState->fp;
+    retval = interpState->retval;   /* only need for kInterpEntryReturn? */
+
+    methodClassDex = curMethod->clazz->pDvmDex;
+
+    LOGVV("threadid=%d: entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n",
+        self->threadId, (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",
+        curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns,
+        fp, interpState->entryPoint);
+
+    /*
+     * DEBUG: scramble this to ensure we're not relying on it.
+     */
+    methodToCall = (const Method*) -1;
+
+#if INTERP_TYPE == INTERP_DBG
+    if (debugIsMethodEntry) {
+        ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+                curMethod->name);
+        DUMP_REGS(curMethod, interpState->fp, false);
+    }
+#endif
+
+    switch (interpState->entryPoint) {
+    case kInterpEntryInstr:
+        /* just fall through to instruction loop or threaded kickstart */
+        break;
+    case kInterpEntryReturn:
+        CHECK_JIT_VOID();
+        goto returnFromMethod;
+    case kInterpEntryThrow:
+        goto exceptionThrown;
+    default:
+        dvmAbort();
+    }
+
+#ifdef THREADED_INTERP
+    FINISH(0);                  /* fetch and execute first instruction */
+#else
+    while (1) {
+        CHECK_DEBUG_AND_PROF(); /* service debugger and profiling */
+        CHECK_TRACKED_REFS();   /* check local reference tracking */
+
+        /* fetch the next 16 bits from the instruction stream */
+        inst = FETCH(0);
+
+        switch (INST_INST(inst)) {
+#endif
+
+/*--- start of opcodes ---*/
+
+/* File: c/OP_NOP.c */
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+/* File: c/OP_MOVE.c */
+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.c */
+/* File: c/OP_MOVE_FROM16.c */
+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.c */
+/* File: c/OP_MOVE_16.c */
+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.c */
+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.c */
+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.c */
+/* File: c/OP_MOVE_RESULT.c */
+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.c */
+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.c */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.c */
+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.c */
+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.c */
+/* File: c/OP_RETURN.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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\n", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC, also WITH_MONITOR_TRACKING */
+        dvmLockObject(self, obj);
+#ifdef WITH_DEADLOCK_PREDICTION
+        if (dvmCheckException(self))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.c */
+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\n", 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.c */
+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)) {
+                dvmThrowExceptionWithClassMessage(
+                    "Ljava/lang/ClassCastException;", obj->clazz->descriptor);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.c */
+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.c */
+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.c */
+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();
+
+        /*
+         * 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 (!dvmDexGetResolvedClass(methodClassDex, ref)) {
+            /* Class initialization is still ongoing - abandon the trace */
+            ABORT_JIT_TSELECT();
+        }
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+        //        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.c */
+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) {
+            dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+            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.c */
+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.c */
+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.c */
+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 */
+            dvmThrowException("Ljava/lang/InternalError;",
+                              "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.c */
+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\n");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
+
+/* File: c/OP_GOTO.c */
+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(kInterpEntryInstr, (s1)vdst);
+    FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.c */
+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(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_GOTO_32.c */
+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(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.c */
+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, vsrc2);
+        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();
+            dvmThrowException("Ljava/lang/InternalError;", "bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)\n", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.c */
+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, vsrc2);
+        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();
+            dvmThrowException("Ljava/lang/InternalError;", "bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)\n", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.c */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.c */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.c */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.c */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.c */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.c */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.c */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.c */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.c */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.c */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.c */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.c */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.c */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.c */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.c */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.c */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.c */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.c */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.c */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.c */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.c */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.c */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.c */
+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) {
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+                NULL);
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->obj.clazz)) {
+                LOGV("Can't put a '%s'(%p) into array type='%s'(%p)\n",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->obj.clazz->descriptor, arrayObj);
+                //dvmDumpClass(obj->clazz);
+                //dvmDumpClass(arrayObj->obj.clazz);
+                dvmThrowException("Ljava/lang/ArrayStoreException;", NULL);
+                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.c */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.c */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.c */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.c */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.c */
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.c */
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.c */
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.c */
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.c */
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.c */
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.c */
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.c */
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.c */
+/*
+ * 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.c */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.c */
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.c */
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.c */
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SGET.c */
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.c */
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.c */
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.c */
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.c */
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.c */
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.c */
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.c */
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.c */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.c */
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.c */
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.c */
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.c */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.c */
+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.c */
+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.c */
+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.c */
+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.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.c */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.c */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.c */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.c */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.c */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.c */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.c */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.c */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.c */
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.c */
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.c */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.c */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.c */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.c */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.c */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.c */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.c */
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.c */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.c */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.c */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.c */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.c */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.c */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.c */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.c */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.c */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.c */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.c */
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.c */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.c */
+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.c */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.c */
+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.c */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.c */
+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.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.c */
+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.c */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.c */
+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.c */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.c */
+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.c */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+    {
+        /*
+         * 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);
+        LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+            INST_REPLACE_OP(inst, originalOpCode));
+        inst = INST_REPLACE_OP(inst, originalOpCode);
+        FINISH_BKPT(originalOpCode);
+    }
+#else
+    LOGE("Breakpoint hit in non-debug interpreter\n");
+    dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.c */
+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.c */
+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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_EMPTY.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_EMPTY /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+#if INTERP_TYPE != INTERP_DBG
+    //LOGI("Ignoring empty\n");
+    FINISH(3);
+#else
+    if (!gDvm.debuggerActive) {
+        //LOGI("Skipping empty\n");
+        FINISH(3);      // don't want it to show up in profiler output
+    } else {
+        //LOGI("Running empty\n");
+        /* fall through to OP_INVOKE_DIRECT */
+        GOTO_invoke(invokeDirect, false);
+    }
+#endif
+OP_END
+
+/* File: c/OP_UNUSED_F1.c */
+HANDLE_OPCODE(OP_UNUSED_F1)
+OP_END
+
+/* File: c/OP_IGET_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.c */
+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.c */
+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.c */
+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.c */
+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.c */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.c */
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    LOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
+
+/* File: c/gotoTargets.c */
+/*
+ * 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)
+    {
+        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)) {
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "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'\n", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            LOGE("non-int primitives not implemented\n");
+            dvmThrowException("Ljava/lang/InternalError;",
+                "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*) 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 = newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+    {
+        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\n");
+                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) && (INTERP_TYPE == INTERP_DBG)
+        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.
+             */
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+                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\n");
+                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.
+             */
+            dvmThrowException("Ljava/lang/NoSuchMethodError;",
+                baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+            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;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisClass;
+#endif
+
+        /*
+         * 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 (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        EXPORT_PC();
+
+        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\n");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    EXPORT_PC();
+
+    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\n");
+            GOTO_exceptionThrown();
+        }
+
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+            /* Class initialization is still ongoing */
+            ABORT_JIT_TSELECT();
+        }
+    }
+    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();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisPtr->clazz;
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s\n",
+            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) {
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < 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)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s\n",
+            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(kInterpEntryReturn, 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
+#if (INTERP_TYPE == INTERP_DBG)
+        TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = saveArea->prevFrame;
+        assert(fp != NULL);
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+            /* Let the Jit know the return is terminating normally */
+            CHECK_JIT_VOID();
+#endif
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 {
+            //LOGE("Unknown invoke instr %02x at %d\n",
+            //    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;
+
+        /*
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+        // Something threw during trace selection - abort the current trace
+        ABORT_JIT_TSELECT();
+#endif
+        /*
+         * 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);
+
+        LOGV("Handling exception %s at %s:%d\n",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+        /*
+         * Tell the debugger about it.
+         *
+         * 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 (gDvm.debuggerActive) {
+            void* catchFrame;
+            catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                        exception, true, &catchFrame);
+            dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+                catchRelPc, exception);
+        }
+#endif
+
+        /*
+         * 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*)&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
+            LOGD("Exception %s from %s:%d not caught locally\n",
+                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;
+            LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 */
+                LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+            //    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)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+            debugIsMethodEntry = true;              // profiling, debugging
+#endif
+            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 */
+#ifdef USE_INDIRECT_REF
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+            self->curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+            {
+                ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                        methodToCall->name, methodToCall->shorty);
+            }
+
+#if defined(WITH_JIT)
+            /* Allow the Jit to end any pending trace building */
+            CHECK_JIT_VOID();
+#endif
+
+            /*
+             * 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 (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->curFrame = fp;
+
+            /*
+             * 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)) {
+                LOGV("Exception thrown by/below native code\n");
+                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 {
+                //LOGE("Unknown invoke instr %02x at %d\n",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: portable/enddefs.c */
+/*--- end of opcodes ---*/
+
+#ifndef THREADED_INTERP
+        } // end of "switch"
+    } // end of "while"
+#endif
+
+bail:
+    ILOGD("|-- Leaving interpreter loop");      // note "curMethod" may be NULL
+
+    interpState->retval = retval;
+    return false;
+
+bail_switch:
+    /*
+     * The standard interpreter currently doesn't set or care about the
+     * "debugIsMethodEntry" value, so setting this is only of use if we're
+     * switching between two "debug" interpreters, which we never do.
+     *
+     * TODO: figure out if preserving this makes any sense.
+     */
+#if INTERP_TYPE == INTERP_DBG
+    interpState->debugIsMethodEntry = debugIsMethodEntry;
+#else
+    interpState->debugIsMethodEntry = false;
+#endif
+
+    /* export state changes */
+    interpState->method = curMethod;
+    interpState->pc = pc;
+    interpState->fp = fp;
+    /* debugTrackedRefStart doesn't change */
+    interpState->retval = retval;   /* need for _entryPoint=ret */
+    interpState->nextMode =
+        (INTERP_TYPE == INTERP_STD) ? INTERP_DBG : INTERP_STD;
+    LOGVV(" meth='%s.%s' pc=0x%x fp=%p\n",
+        curMethod->clazz->descriptor, curMethod->name,
+        pc - curMethod->insns, fp);
+    return true;
+}
+
diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c
new file mode 100644
index 0000000..f82c97d
--- /dev/null
+++ b/vm/mterp/out/InterpC-portstd.c
@@ -0,0 +1,4192 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'portstd'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: portable/portstd.c */
+#define INTERP_FUNC_NAME dvmInterpretStd
+#define INTERP_TYPE INTERP_STD
+
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/* File: portable/stubdefs.c */
+/*
+ * 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)
+
+/*
+ * 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".
+ *
+ * TODO: remove "switch" version.
+ */
+#ifdef THREADED_INTERP
+# define H(_op)             &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) {                                                  \
+        ADJUST_PC(_offset);                                                 \
+        inst = FETCH(0);                                                    \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        if (CHECK_JIT_BOOL()) GOTO_bail_switch();                           \
+        goto *handlerTable[INST_INST(inst)];                                \
+    }
+# define FINISH_BKPT(_opcode) {                                             \
+        goto *handlerTable[_opcode];                                        \
+    }
+#else
+# define HANDLE_OPCODE(_op) case _op:
+# define FINISH(_offset)    { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
+#endif
+
+#define OP_END
+
+#if defined(WITH_TRACKREF_CHECKS)
+# define CHECK_TRACKED_REFS() \
+    dvmInterpCheckTrackedRefs(self, curMethod, debugTrackedRefStart)
+#else
+# define CHECK_TRACKED_REFS() ((void)0)
+#endif
+
+
+/*
+ * 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;
+#define GOTO_bail_switch() goto bail_switch;
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            interpState->entryPoint = _entryPoint;                          \
+            LOGVV("threadid=%d: switch to %s ep=%d adj=%d\n",               \
+                self->threadId,                                             \
+                (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",      \
+                (_entryPoint), (_pcadj));                                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: portable/entry.c */
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState)
+{
+#if defined(EASY_GDB)
+    StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->curFrame);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+    bool debugIsMethodEntry = false;
+    debugIsMethodEntry = interpState->debugIsMethodEntry;
+#endif
+#if defined(WITH_TRACKREF_CHECKS)
+    int debugTrackedRefStart = interpState->debugTrackedRefStart;
+#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 */
+    u2 ref;                     // 16-bit quantity fetched directly
+    u2 vsrc1, vsrc2, vdst;      // usually used for register indexes
+    /* method call setup */
+    const Method* methodToCall;
+    bool methodCallRange;
+
+
+#if defined(THREADED_INTERP)
+    /* static computed goto table */
+    DEFINE_GOTO_TABLE(handlerTable);
+#endif
+
+#if defined(WITH_JIT)
+#if 0
+    LOGD("*DebugInterp - entrypoint is %d, tgt is 0x%x, %s\n",
+         interpState->entryPoint,
+         interpState->pc,
+         interpState->method->name);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+    const ClassObject* callsiteClass = NULL;
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (interpState->jitState != kJitSelfVerification) {
+        interpState->self->shadowSpace->jitExitState = kSVSIdle;
+    }
+#endif
+
+    /* Check to see if we've got a trace selection request. */
+    if (
+         /*
+          * Only perform dvmJitCheckTraceRequest if the entry point is
+          * EntryInstr and the jit state is either kJitTSelectRequest or
+          * kJitTSelectRequestHot. If debugger/profiler happens to be attached,
+          * dvmJitCheckTraceRequest will change the jitState to kJitDone but
+          * but stay in the dbg interpreter.
+          */
+         (interpState->entryPoint == kInterpEntryInstr) &&
+         (interpState->jitState == kJitTSelectRequest ||
+          interpState->jitState == kJitTSelectRequestHot) &&
+         dvmJitCheckTraceRequest(self, interpState)) {
+        interpState->nextMode = INTERP_STD;
+        //LOGD("Invalid trace request, exiting\n");
+        return true;
+    }
+#endif /* INTERP_TYPE == INTERP_DBG */
+#endif /* WITH_JIT */
+
+    /* copy state in */
+    curMethod = interpState->method;
+    pc = interpState->pc;
+    fp = interpState->fp;
+    retval = interpState->retval;   /* only need for kInterpEntryReturn? */
+
+    methodClassDex = curMethod->clazz->pDvmDex;
+
+    LOGVV("threadid=%d: entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n",
+        self->threadId, (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",
+        curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns,
+        fp, interpState->entryPoint);
+
+    /*
+     * DEBUG: scramble this to ensure we're not relying on it.
+     */
+    methodToCall = (const Method*) -1;
+
+#if INTERP_TYPE == INTERP_DBG
+    if (debugIsMethodEntry) {
+        ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+                curMethod->name);
+        DUMP_REGS(curMethod, interpState->fp, false);
+    }
+#endif
+
+    switch (interpState->entryPoint) {
+    case kInterpEntryInstr:
+        /* just fall through to instruction loop or threaded kickstart */
+        break;
+    case kInterpEntryReturn:
+        CHECK_JIT_VOID();
+        goto returnFromMethod;
+    case kInterpEntryThrow:
+        goto exceptionThrown;
+    default:
+        dvmAbort();
+    }
+
+#ifdef THREADED_INTERP
+    FINISH(0);                  /* fetch and execute first instruction */
+#else
+    while (1) {
+        CHECK_DEBUG_AND_PROF(); /* service debugger and profiling */
+        CHECK_TRACKED_REFS();   /* check local reference tracking */
+
+        /* fetch the next 16 bits from the instruction stream */
+        inst = FETCH(0);
+
+        switch (INST_INST(inst)) {
+#endif
+
+/*--- start of opcodes ---*/
+
+/* File: c/OP_NOP.c */
+HANDLE_OPCODE(OP_NOP)
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+/* File: c/OP_MOVE.c */
+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.c */
+/* File: c/OP_MOVE_FROM16.c */
+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.c */
+/* File: c/OP_MOVE_16.c */
+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.c */
+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.c */
+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.c */
+/* File: c/OP_MOVE_RESULT.c */
+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.c */
+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.c */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+    ILOGV("|return-void");
+#ifndef NDEBUG
+    retval.j = 0xababababULL;    // placate valgrind
+#endif
+    GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.c */
+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.c */
+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.c */
+/* File: c/OP_RETURN.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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.c */
+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\n", obj, obj->clazz->descriptor);
+        EXPORT_PC();    /* need for precise GC, also WITH_MONITOR_TRACKING */
+        dvmLockObject(self, obj);
+#ifdef WITH_DEADLOCK_PREDICTION
+        if (dvmCheckException(self))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.c */
+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\n", 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.c */
+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)) {
+                dvmThrowExceptionWithClassMessage(
+                    "Ljava/lang/ClassCastException;", obj->clazz->descriptor);
+                GOTO_exceptionThrown();
+            }
+        }
+    }
+    FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.c */
+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.c */
+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.c */
+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();
+
+        /*
+         * 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 (!dvmDexGetResolvedClass(methodClassDex, ref)) {
+            /* Class initialization is still ongoing - abandon the trace */
+            ABORT_JIT_TSELECT();
+        }
+
+        /*
+         * Verifier now tests for interface/abstract class.
+         */
+        //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+        //    dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+        //        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.c */
+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) {
+            dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+            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.c */
+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.c */
+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.c */
+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 */
+            dvmThrowException("Ljava/lang/InternalError;",
+                              "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.c */
+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\n");
+        } else {
+            /* use the requested exception */
+            dvmSetException(self, obj);
+        }
+        GOTO_exceptionThrown();
+    }
+OP_END
+
+/* File: c/OP_GOTO.c */
+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(kInterpEntryInstr, (s1)vdst);
+    FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.c */
+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(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_GOTO_32.c */
+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(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.c */
+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, vsrc2);
+        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();
+            dvmThrowException("Ljava/lang/InternalError;", "bad packed switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)\n", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.c */
+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, vsrc2);
+        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();
+            dvmThrowException("Ljava/lang/InternalError;", "bad sparse switch");
+            GOTO_exceptionThrown();
+        }
+#endif
+        testVal = GET_REGISTER(vsrc1);
+
+        offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+        ILOGV("> branch taken (0x%04x)\n", offset);
+        if (offset <= 0)  /* uncommon */
+            PERIODIC_CHECKS(kInterpEntryInstr, offset);
+        FINISH(offset);
+    }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.c */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.c */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.c */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.c */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.c */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.c */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.c */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.c */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.c */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.c */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.c */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.c */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.c */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.c */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.c */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.c */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.c */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.c */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.c */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.c */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.c */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.c */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.c */
+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) {
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+                NULL);
+            GOTO_exceptionThrown();
+        }
+        obj = (Object*) GET_REGISTER(vdst);
+        if (obj != NULL) {
+            if (!checkForNull(obj))
+                GOTO_exceptionThrown();
+            if (!dvmCanPutArrayElement(obj->clazz, arrayObj->obj.clazz)) {
+                LOGV("Can't put a '%s'(%p) into array type='%s'(%p)\n",
+                    obj->clazz->descriptor, obj,
+                    arrayObj->obj.clazz->descriptor, arrayObj);
+                //dvmDumpClass(obj->clazz);
+                //dvmDumpClass(arrayObj->obj.clazz);
+                dvmThrowException("Ljava/lang/ArrayStoreException;", NULL);
+                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.c */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.c */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.c */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.c */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.c */
+HANDLE_IGET_X(OP_IGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.c */
+HANDLE_IGET_X(OP_IGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.c */
+HANDLE_IGET_X(OP_IGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.c */
+HANDLE_IGET_X(OP_IGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.c */
+HANDLE_IGET_X(OP_IGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.c */
+HANDLE_IGET_X(OP_IGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.c */
+HANDLE_IGET_X(OP_IGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.c */
+HANDLE_IPUT_X(OP_IPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.c */
+/*
+ * 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.c */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.c */
+HANDLE_IPUT_X(OP_IPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.c */
+HANDLE_IPUT_X(OP_IPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.c */
+HANDLE_IPUT_X(OP_IPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SGET.c */
+HANDLE_SGET_X(OP_SGET,                  "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.c */
+HANDLE_SGET_X(OP_SGET_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.c */
+HANDLE_SGET_X(OP_SGET_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.c */
+HANDLE_SGET_X(OP_SGET_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.c */
+HANDLE_SGET_X(OP_SGET_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.c */
+HANDLE_SGET_X(OP_SGET_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.c */
+HANDLE_SGET_X(OP_SGET_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.c */
+HANDLE_SPUT_X(OP_SPUT,                  "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE,             "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT,           "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.c */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN,          "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.c */
+HANDLE_SPUT_X(OP_SPUT_BYTE,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.c */
+HANDLE_SPUT_X(OP_SPUT_CHAR,             "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.c */
+HANDLE_SPUT_X(OP_SPUT_SHORT,            "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+    GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.c */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.c */
+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.c */
+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.c */
+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.c */
+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.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+    GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.c */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.c */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.c */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.c */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.c */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.c */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.c */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.c */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.c */
+HANDLE_NUMCONV(OP_INT_TO_LONG,          "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT,         "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE,        "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.c */
+HANDLE_NUMCONV(OP_LONG_TO_INT,          "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT,        "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE,       "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT,    "float-to-int",
+    float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG,   "float-to-long",
+    float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE,      "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT,   "double-to-int",
+    double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG,  "double-to-long",
+    double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT,      "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE,     "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR,     "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT,    "short", s2)    /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.c */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.c */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.c */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.c */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.c */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.c */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.c */
+HANDLE_OP_X_INT(OP_OR_INT,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.c */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.c */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.c */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.c */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.c */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.c */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.c */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.c */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.c */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.c */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.c */
+HANDLE_OP_X_LONG(OP_OR_LONG,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.c */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.c */
+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.c */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.c */
+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.c */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR,  "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.c */
+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.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.c */
+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.c */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.c */
+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.c */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16,  "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8,   "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.c */
+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.c */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8,   "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8,   "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8,   "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8,   "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8,    "or",  |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8,   "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8,   "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8,   "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8,  "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+    {
+        /*
+         * 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);
+        LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+            INST_REPLACE_OP(inst, originalOpCode));
+        inst = INST_REPLACE_OP(inst, originalOpCode);
+        FINISH_BKPT(originalOpCode);
+    }
+#else
+    LOGE("Breakpoint hit in non-debug interpreter\n");
+    dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.c */
+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.c */
+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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_EMPTY.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_EMPTY /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+#if INTERP_TYPE != INTERP_DBG
+    //LOGI("Ignoring empty\n");
+    FINISH(3);
+#else
+    if (!gDvm.debuggerActive) {
+        //LOGI("Skipping empty\n");
+        FINISH(3);      // don't want it to show up in profiler output
+    } else {
+        //LOGI("Running empty\n");
+        /* fall through to OP_INVOKE_DIRECT */
+        GOTO_invoke(invokeDirect, false);
+    }
+#endif
+OP_END
+
+/* File: c/OP_UNUSED_F1.c */
+HANDLE_OPCODE(OP_UNUSED_F1)
+OP_END
+
+/* File: c/OP_IGET_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK,          "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK,     "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK,   "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.c */
+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.c */
+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.c */
+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.c */
+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.c */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.c */
+HANDLE_OPCODE(OP_UNUSED_FF)
+    /*
+     * In portable interp, most unused opcodes will fall through to here.
+     */
+    LOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+    dvmAbort();
+    FINISH(1);
+OP_END
+
+/* File: c/gotoTargets.c */
+/*
+ * 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)
+    {
+        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)) {
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "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'\n", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            LOGE("non-int primitives not implemented\n");
+            dvmThrowException("Ljava/lang/InternalError;",
+                "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*) 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 = newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+    {
+        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\n");
+                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) && (INTERP_TYPE == INTERP_DBG)
+        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.
+             */
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+                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\n");
+                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.
+             */
+            dvmThrowException("Ljava/lang/NoSuchMethodError;",
+                baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+            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;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisClass;
+#endif
+
+        /*
+         * 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 (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        EXPORT_PC();
+
+        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\n");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    EXPORT_PC();
+
+    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\n");
+            GOTO_exceptionThrown();
+        }
+
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+            /* Class initialization is still ongoing */
+            ABORT_JIT_TSELECT();
+        }
+    }
+    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();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisPtr->clazz;
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s\n",
+            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) {
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < 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)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s\n",
+            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(kInterpEntryReturn, 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
+#if (INTERP_TYPE == INTERP_DBG)
+        TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = saveArea->prevFrame;
+        assert(fp != NULL);
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+            /* Let the Jit know the return is terminating normally */
+            CHECK_JIT_VOID();
+#endif
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 {
+            //LOGE("Unknown invoke instr %02x at %d\n",
+            //    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;
+
+        /*
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+        // Something threw during trace selection - abort the current trace
+        ABORT_JIT_TSELECT();
+#endif
+        /*
+         * 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);
+
+        LOGV("Handling exception %s at %s:%d\n",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+        /*
+         * Tell the debugger about it.
+         *
+         * 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 (gDvm.debuggerActive) {
+            void* catchFrame;
+            catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                        exception, true, &catchFrame);
+            dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+                catchRelPc, exception);
+        }
+#endif
+
+        /*
+         * 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*)&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
+            LOGD("Exception %s from %s:%d not caught locally\n",
+                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;
+            LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 */
+                LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+            //    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)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+            debugIsMethodEntry = true;              // profiling, debugging
+#endif
+            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 */
+#ifdef USE_INDIRECT_REF
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+            self->curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+            {
+                ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                        methodToCall->name, methodToCall->shorty);
+            }
+
+#if defined(WITH_JIT)
+            /* Allow the Jit to end any pending trace building */
+            CHECK_JIT_VOID();
+#endif
+
+            /*
+             * 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 (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->curFrame = fp;
+
+            /*
+             * 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)) {
+                LOGV("Exception thrown by/below native code\n");
+                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 {
+                //LOGE("Unknown invoke instr %02x at %d\n",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: portable/enddefs.c */
+/*--- end of opcodes ---*/
+
+#ifndef THREADED_INTERP
+        } // end of "switch"
+    } // end of "while"
+#endif
+
+bail:
+    ILOGD("|-- Leaving interpreter loop");      // note "curMethod" may be NULL
+
+    interpState->retval = retval;
+    return false;
+
+bail_switch:
+    /*
+     * The standard interpreter currently doesn't set or care about the
+     * "debugIsMethodEntry" value, so setting this is only of use if we're
+     * switching between two "debug" interpreters, which we never do.
+     *
+     * TODO: figure out if preserving this makes any sense.
+     */
+#if INTERP_TYPE == INTERP_DBG
+    interpState->debugIsMethodEntry = debugIsMethodEntry;
+#else
+    interpState->debugIsMethodEntry = false;
+#endif
+
+    /* export state changes */
+    interpState->method = curMethod;
+    interpState->pc = pc;
+    interpState->fp = fp;
+    /* debugTrackedRefStart doesn't change */
+    interpState->retval = retval;   /* need for _entryPoint=ret */
+    interpState->nextMode =
+        (INTERP_TYPE == INTERP_STD) ? INTERP_DBG : INTERP_STD;
+    LOGVV(" meth='%s.%s' pc=0x%x fp=%p\n",
+        curMethod->clazz->descriptor, curMethod->name,
+        pc - curMethod->insns, fp);
+    return true;
+}
+
diff --git a/vm/mterp/out/InterpC-x86-atom.c b/vm/mterp/out/InterpC-x86-atom.c
new file mode 100644
index 0000000..6d088f7
--- /dev/null
+++ b/vm/mterp/out/InterpC-x86-atom.c
@@ -0,0 +1,2326 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86-atom'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_IGET_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE,         "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+    {
+        /*
+         * 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);
+        LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+            INST_REPLACE_OP(inst, originalOpCode));
+        inst = INST_REPLACE_OP(inst, originalOpCode);
+        FINISH_BKPT(originalOpCode);
+    }
+#else
+    LOGE("Breakpoint hit in non-debug interpreter\n");
+    dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE,  "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/gotoTargets.c */
+/*
+ * 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)
+    {
+        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)) {
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "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'\n", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            LOGE("non-int primitives not implemented\n");
+            dvmThrowException("Ljava/lang/InternalError;",
+                "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*) 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 = newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+    {
+        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\n");
+                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) && (INTERP_TYPE == INTERP_DBG)
+        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.
+             */
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+                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\n");
+                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.
+             */
+            dvmThrowException("Ljava/lang/NoSuchMethodError;",
+                baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+            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;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisClass;
+#endif
+
+        /*
+         * 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 (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        EXPORT_PC();
+
+        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\n");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    EXPORT_PC();
+
+    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\n");
+            GOTO_exceptionThrown();
+        }
+
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+            /* Class initialization is still ongoing */
+            ABORT_JIT_TSELECT();
+        }
+    }
+    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();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisPtr->clazz;
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s\n",
+            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) {
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < 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)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s\n",
+            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(kInterpEntryReturn, 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
+#if (INTERP_TYPE == INTERP_DBG)
+        TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = saveArea->prevFrame;
+        assert(fp != NULL);
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+            /* Let the Jit know the return is terminating normally */
+            CHECK_JIT_VOID();
+#endif
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 {
+            //LOGE("Unknown invoke instr %02x at %d\n",
+            //    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;
+
+        /*
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+        // Something threw during trace selection - abort the current trace
+        ABORT_JIT_TSELECT();
+#endif
+        /*
+         * 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);
+
+        LOGV("Handling exception %s at %s:%d\n",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+        /*
+         * Tell the debugger about it.
+         *
+         * 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 (gDvm.debuggerActive) {
+            void* catchFrame;
+            catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                        exception, true, &catchFrame);
+            dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+                catchRelPc, exception);
+        }
+#endif
+
+        /*
+         * 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*)&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
+            LOGD("Exception %s from %s:%d not caught locally\n",
+                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;
+            LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 */
+                LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+            //    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)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+            debugIsMethodEntry = true;              // profiling, debugging
+#endif
+            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 */
+#ifdef USE_INDIRECT_REF
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+            self->curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+            {
+                ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                        methodToCall->name, methodToCall->shorty);
+            }
+
+#if defined(WITH_JIT)
+            /* Allow the Jit to end any pending trace building */
+            CHECK_JIT_VOID();
+#endif
+
+            /*
+             * 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 (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->curFrame = fp;
+
+            /*
+             * 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)) {
+                LOGV("Exception thrown by/below native code\n");
+                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 {
+                //LOGE("Unknown invoke instr %02x at %d\n",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.c */
+
+/* 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-x86.c b/vm/mterp/out/InterpC-x86.c
new file mode 100644
index 0000000..4882184
--- /dev/null
+++ b/vm/mterp/out/InterpC-x86.c
@@ -0,0 +1,2263 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * 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
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter.  If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP             /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS            /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 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 will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union.  The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy().  The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method.  Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR                   /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields.  Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * 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);     \
+            LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n",               \
+                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)                                                 \
+            LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n",                        \
+                self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+        else                                                                \
+            LOG(_level, LOG_TAG"i", "%-2d|####%s\n",                        \
+                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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    s8 val;
+    memcpy(&val, &ptr[idx], 8);
+    return val;
+#else
+    return *((s8*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &val, 8);
+#else
+    *((s8*) &ptr[idx]) = val;
+#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;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    double dval;
+    memcpy(&dval, &ptr[idx], 8);
+    return dval;
+#else
+    return *((double*) &ptr[idx]);
+#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];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+    memcpy(&ptr[idx], &dval, 8);
+#else
+    *((double*) &ptr[idx]) = dval;
+#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 ? \
+        putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# 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 ? \
+        putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#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 dvmThrowException(), 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)
+
+/*
+ * Determine if we need to switch to a different interpreter.  "_current"
+ * is either INTERP_STD or INTERP_DBG.  It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) (                                     \
+    (_current == INTERP_STD) ?                                              \
+        dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", 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();
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+    if (!dvmIsValidObject(obj)) {
+        LOGE("Invalid object %p\n", obj);
+        dvmAbort();
+    }
+#endif
+#ifndef NDEBUG
+    if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+        /* probable heap corruption */
+        LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+        dvmAbort();
+    }
+#endif
+    return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)                                      \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...)                                           \
+    void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) {              \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);                                                 \
+        const Method* methodToCall;                                         \
+        StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references.  (These are undefined down in "footer.c".)
+ */
+#define retval                  glue->retval
+#define pc                      glue->pc
+#define fp                      glue->fp
+#define curMethod               glue->method
+#define methodClassDex          glue->methodClassDex
+#define self                    glue->self
+#define debugTrackedRefStart    glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros.  Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void.  We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op)                                                  \
+    void dvmMterp_##_op(MterpGlue* glue) {                                  \
+        u2 ref, vsrc1, vsrc2, vdst;                                         \
+        u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) {                                                   \
+        ADJUST_PC(_offset);                                                 \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        return;                                                             \
+    }
+
+
+/*
+ * 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(glue);                                     \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_returnFromMethod()                                             \
+    do {                                                                    \
+        dvmMterp_returnFromMethod(glue);                                    \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange)                              \
+    do {                                                                    \
+        dvmMterp_##_target(glue, _methodCallRange);                         \
+        return;                                                             \
+    } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst)   \
+    do {                                                                    \
+        dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall,        \
+            _vsrc1, _vdst);                                                 \
+        return;                                                             \
+    } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp.  Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail()                                                         \
+    dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch()                                                  \
+    dvmMterpStdBail(glue, true);
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            glue->entryPoint = _entryPoint;                                 \
+            LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n",              \
+                self->threadId, (_entryPoint), (_pcadj));                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
+
+/* File: c/opcommon.c */
+/* 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\n", 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(kInterpEntryInstr, 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(kInterpEntryInstr, 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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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();                                                \
+                dvmThrowException("Ljava/lang/ArithmeticException;",        \
+                    "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) {                      \
+            LOGV("Invalid array access: %p %d (len=%d)\n",                  \
+                arrayObj, vsrc2, arrayObj->length);                         \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst,                                        \
+            ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]);            \
+        ILOGV("+ AGET[%d]=0x%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) {                      \
+            dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+                NULL);                                                      \
+            GOTO_exceptionThrown();                                         \
+        }                                                                   \
+        ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+        ((_type*) 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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_GET(&ifield->field);                                   \
+    }                                                                       \
+    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->field.name,                   \
+            (u8) GET_REGISTER##_regsize(vdst));                             \
+        UPDATE_FIELD_PUT(&ifield->field);                                   \
+    }                                                                       \
+    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.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield));    \
+        ILOGV("+ SGET '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_GET(&sfield->field);                                   \
+    }                                                                       \
+    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) {      \
+                ABORT_JIT_TSELECT();                                        \
+            }                                                               \
+        }                                                                   \
+        dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst));    \
+        ILOGV("+ SPUT '%s'=0x%08llx",                                       \
+            sfield->field.name, (u8)GET_REGISTER##_regsize(vdst));          \
+        UPDATE_FIELD_PUT(&sfield->field);                                   \
+    }                                                                       \
+    FINISH(2);
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE,    "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+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 INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
+/* File: c/gotoTargets.c */
+/*
+ * 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)
+    {
+        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)) {
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "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'\n", arrayClass->descriptor);
+        typeCh = arrayClass->descriptor[1];
+        if (typeCh == 'D' || typeCh == 'J') {
+            /* category 2 primitives not allowed */
+            dvmThrowException("Ljava/lang/RuntimeError;",
+                "bad filled array req");
+            GOTO_exceptionThrown();
+        } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+            /* TODO: requires multiple "fill in" loops with different widths */
+            LOGE("non-int primitives not implemented\n");
+            dvmThrowException("Ljava/lang/InternalError;",
+                "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*) 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 = newArray;
+    }
+    FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+    {
+        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\n");
+                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) && (INTERP_TYPE == INTERP_DBG)
+        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.
+             */
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+            baseMethod->clazz->descriptor, baseMethod->name,
+            (u4) baseMethod->methodIndex,
+            methodToCall->clazz->descriptor, methodToCall->name);
+        assert(methodToCall != NULL);
+
+#if 0
+        if (vsrc1 != methodToCall->insSize) {
+            LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+                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\n");
+                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.
+             */
+            dvmThrowException("Ljava/lang/NoSuchMethodError;",
+                baseMethod->name);
+            GOTO_exceptionThrown();
+        }
+        methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+            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;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisClass;
+#endif
+
+        /*
+         * 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 (methodToCall == NULL) {
+            assert(dvmCheckException(self));
+            GOTO_exceptionThrown();
+        }
+
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+    {
+        u2 thisReg;
+
+        vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+        ref = FETCH(1);             /* method ref */
+        vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+        EXPORT_PC();
+
+        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\n");     // should be impossible
+                GOTO_exceptionThrown();
+            }
+        }
+        GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+    }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+    vsrc1 = INST_AA(inst);      /* AA (count) or BA (count + arg 5) */
+    ref = FETCH(1);             /* method ref */
+    vdst = FETCH(2);            /* 4 regs -or- first reg */
+
+    EXPORT_PC();
+
+    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\n");
+            GOTO_exceptionThrown();
+        }
+
+        /*
+         * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+         * Since we use the portable interpreter to build the trace, this extra
+         * check is not needed for mterp.
+         */
+        if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+            /* Class initialization is still ongoing */
+            ABORT_JIT_TSELECT();
+        }
+    }
+    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();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+        callsiteClass = thisPtr->clazz;
+#endif
+
+        /*
+         * Combine the object we found with the vtable offset in the
+         * method.
+         */
+        assert(ref < thisPtr->clazz->vtableCount);
+        methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+        if (dvmIsAbstractMethod(methodToCall)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+
+        LOGVV("+++ virtual[%d]=%s.%s\n",
+            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) {
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(ref < 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)) {
+            dvmThrowException("Ljava/lang/AbstractMethodError;",
+                "abstract method not implemented");
+            GOTO_exceptionThrown();
+        }
+#else
+        assert(!dvmIsAbstractMethod(methodToCall) ||
+            methodToCall->nativeFunc != NULL);
+#endif
+        LOGVV("+++ super-virtual[%d]=%s.%s\n",
+            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(kInterpEntryReturn, 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
+#if (INTERP_TYPE == INTERP_DBG)
+        TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+        /* back up to previous frame and see if we hit a break */
+        fp = saveArea->prevFrame;
+        assert(fp != NULL);
+        if (dvmIsBreakFrame(fp)) {
+            /* bail without popping the method frame from stack */
+            LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+            /* Let the Jit know the return is terminating normally */
+            CHECK_JIT_VOID();
+#endif
+            GOTO_bail();
+        }
+
+        /* update thread FP, and reset local variables */
+        self->curFrame = fp;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 {
+            //LOGE("Unknown invoke instr %02x at %d\n",
+            //    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;
+
+        /*
+         * Since this is now an interpreter switch point, we must do it before
+         * we do anything at all.
+         */
+        PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+        // Something threw during trace selection - abort the current trace
+        ABORT_JIT_TSELECT();
+#endif
+        /*
+         * 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);
+
+        LOGV("Handling exception %s at %s:%d\n",
+            exception->clazz->descriptor, curMethod->name,
+            dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+        /*
+         * Tell the debugger about it.
+         *
+         * 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 (gDvm.debuggerActive) {
+            void* catchFrame;
+            catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+                        exception, true, &catchFrame);
+            dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+                catchRelPc, exception);
+        }
+#endif
+
+        /*
+         * 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*)&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
+            LOGD("Exception %s from %s:%d not caught locally\n",
+                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;
+            LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+                exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+                dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+                dvmGetMethodSourceFile(catchMethod),
+                dvmLineNumFromPC(catchMethod, catchRelPc));
+        }
+#endif
+
+        /*
+         * Adjust local variables to match self->curFrame and the
+         * updated PC.
+         */
+        //fp = (u4*) self->curFrame;
+        curMethod = SAVEAREA_FROM_FP(fp)->method;
+        //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 */
+                LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+                    self->interpStackStart, self->interpStackEnd, bottom,
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
+                assert(dvmCheckException(self));
+                GOTO_exceptionThrown();
+            }
+            //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+            //    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)
+        newSaveArea->returnAddr = 0;
+#endif
+        newSaveArea->method = methodToCall;
+
+        if (!dvmIsNativeMethod(methodToCall)) {
+            /*
+             * "Call" interpreted code.  Reposition the PC, update the
+             * frame pointer and other local state, and continue.
+             */
+            curMethod = methodToCall;
+            methodClassDex = curMethod->clazz->pDvmDex;
+            pc = methodToCall->insns;
+            fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+            debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+            debugIsMethodEntry = true;              // profiling, debugging
+#endif
+            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 */
+#ifdef USE_INDIRECT_REF
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+            newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+            self->curFrame = newFp;
+
+            DUMP_REGS(methodToCall, newFp, true);   // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+            {
+                ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+                        methodToCall->name, methodToCall->shorty);
+            }
+
+#if defined(WITH_JIT)
+            /* Allow the Jit to end any pending trace building */
+            CHECK_JIT_VOID();
+#endif
+
+            /*
+             * 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 (INTERP_TYPE == INTERP_DBG)
+            if (gDvm.debuggerActive) {
+                dvmDbgPostLocationEvent(methodToCall, -1,
+                    dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+            }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+            TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+            /* pop frame off */
+            dvmPopJniLocals(self, newSaveArea);
+            self->curFrame = fp;
+
+            /*
+             * 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)) {
+                LOGV("Exception thrown by/below native code\n");
+                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 {
+                //LOGE("Unknown invoke instr %02x at %d\n",
+                //    invokeInstr, (int) (pc - curMethod->insns));
+                assert(false);
+            }
+        }
+    }
+    assert(false);      // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
diff --git a/vm/mterp/portable/debug.c b/vm/mterp/portable/debug.c
new file mode 100644
index 0000000..1d06188
--- /dev/null
+++ b/vm/mterp/portable/debug.c
@@ -0,0 +1,239 @@
+/* code in here is only included in portable-debug interpreter */
+
+/*
+ * 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,
+    bool methodEntry, 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().
+     */
+    EXPORT_PC();
+
+    if (methodEntry)
+        eventFlags |= DBG_METHOD_ENTRY;
+
+    /*
+     * 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 (INST_INST(*pc) == OP_BREAKPOINT) {
+        LOGV("+++ breakpoint hit at %p\n", 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) {
+            LOGV("#####S %s\n", 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 inst = INST_INST(FETCH(0));
+    if (inst == OP_RETURN_VOID || inst == OP_RETURN || inst == OP_RETURN_WIDE ||
+        inst == OP_RETURN_OBJECT)
+    {
+        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 && !dvmIsValidObject(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);
+            LOGE("HEY: invalid 'this' ptr %p (%s.%s %s)\n", thisPtr,
+                method->clazz->descriptor, method->name, desc);
+            free(desc);
+            dvmAbort();
+        }
+        dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
+            eventFlags);
+    }
+}
+
+/*
+ * Perform some operations at the "top" of the interpreter loop.
+ * This stuff is required to support debugging and profiling.
+ *
+ * Using" __attribute__((noinline))" seems to do more harm than good.  This
+ * is best when inlined due to the large number of parameters, most of
+ * which are local vars in the main interp loop.
+ */
+static void checkDebugAndProf(const u2* pc, const u4* fp, Thread* self,
+    const Method* method, bool* pIsMethodEntry)
+{
+    /* check to see if we've run off end of 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 (/*gDvm.debuggerActive &&*/
+            strcmp(method->clazz->descriptor, cd) == 0 &&
+            strcmp(method->name, mn) == 0 &&
+            strcmp(method->shorty, sg) == 0)
+        {
+            LOGW("Reached %s.%s, enabling verbose mode\n",
+                method->clazz->descriptor, method->name);
+            android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
+            dumpRegs(method, fp, true);
+        }
+
+        if (!gDvm.debuggerActive)
+            *pIsMethodEntry = false;
+    }
+#endif
+
+    /*
+     * If the debugger is attached, check for events.  If the profiler is
+     * enabled, update that too.
+     *
+     * This code is executed for every instruction we interpret, so for
+     * performance we use a couple of #ifdef blocks instead of runtime tests.
+     */
+    bool isEntry = *pIsMethodEntry;
+    if (isEntry) {
+        *pIsMethodEntry = false;
+        TRACE_METHOD_ENTER(self, method);
+    }
+    if (gDvm.debuggerActive) {
+        updateDebugger(method, pc, fp, isEntry, 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).
+         */
+        int inst = *pc & 0xff;
+        gDvm.executedInstrCounts[inst]++;
+    }
+}
diff --git a/vm/mterp/portable/enddefs.c b/vm/mterp/portable/enddefs.c
new file mode 100644
index 0000000..30deedc
--- /dev/null
+++ b/vm/mterp/portable/enddefs.c
@@ -0,0 +1,40 @@
+/*--- end of opcodes ---*/
+
+#ifndef THREADED_INTERP
+        } // end of "switch"
+    } // end of "while"
+#endif
+
+bail:
+    ILOGD("|-- Leaving interpreter loop");      // note "curMethod" may be NULL
+
+    interpState->retval = retval;
+    return false;
+
+bail_switch:
+    /*
+     * The standard interpreter currently doesn't set or care about the
+     * "debugIsMethodEntry" value, so setting this is only of use if we're
+     * switching between two "debug" interpreters, which we never do.
+     *
+     * TODO: figure out if preserving this makes any sense.
+     */
+#if INTERP_TYPE == INTERP_DBG
+    interpState->debugIsMethodEntry = debugIsMethodEntry;
+#else
+    interpState->debugIsMethodEntry = false;
+#endif
+
+    /* export state changes */
+    interpState->method = curMethod;
+    interpState->pc = pc;
+    interpState->fp = fp;
+    /* debugTrackedRefStart doesn't change */
+    interpState->retval = retval;   /* need for _entryPoint=ret */
+    interpState->nextMode =
+        (INTERP_TYPE == INTERP_STD) ? INTERP_DBG : INTERP_STD;
+    LOGVV(" meth='%s.%s' pc=0x%x fp=%p\n",
+        curMethod->clazz->descriptor, curMethod->name,
+        pc - curMethod->insns, fp);
+    return true;
+}
diff --git a/vm/mterp/portable/entry.c b/vm/mterp/portable/entry.c
new file mode 100644
index 0000000..56649e7
--- /dev/null
+++ b/vm/mterp/portable/entry.c
@@ -0,0 +1,127 @@
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState)
+{
+#if defined(EASY_GDB)
+    StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->curFrame);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+    bool debugIsMethodEntry = false;
+    debugIsMethodEntry = interpState->debugIsMethodEntry;
+#endif
+#if defined(WITH_TRACKREF_CHECKS)
+    int debugTrackedRefStart = interpState->debugTrackedRefStart;
+#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 */
+    u2 ref;                     // 16-bit quantity fetched directly
+    u2 vsrc1, vsrc2, vdst;      // usually used for register indexes
+    /* method call setup */
+    const Method* methodToCall;
+    bool methodCallRange;
+
+
+#if defined(THREADED_INTERP)
+    /* static computed goto table */
+    DEFINE_GOTO_TABLE(handlerTable);
+#endif
+
+#if defined(WITH_JIT)
+#if 0
+    LOGD("*DebugInterp - entrypoint is %d, tgt is 0x%x, %s\n",
+         interpState->entryPoint,
+         interpState->pc,
+         interpState->method->name);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+    const ClassObject* callsiteClass = NULL;
+
+#if defined(WITH_SELF_VERIFICATION)
+    if (interpState->jitState != kJitSelfVerification) {
+        interpState->self->shadowSpace->jitExitState = kSVSIdle;
+    }
+#endif
+
+    /* Check to see if we've got a trace selection request. */
+    if (
+         /*
+          * Only perform dvmJitCheckTraceRequest if the entry point is
+          * EntryInstr and the jit state is either kJitTSelectRequest or
+          * kJitTSelectRequestHot. If debugger/profiler happens to be attached,
+          * dvmJitCheckTraceRequest will change the jitState to kJitDone but
+          * but stay in the dbg interpreter.
+          */
+         (interpState->entryPoint == kInterpEntryInstr) &&
+         (interpState->jitState == kJitTSelectRequest ||
+          interpState->jitState == kJitTSelectRequestHot) &&
+         dvmJitCheckTraceRequest(self, interpState)) {
+        interpState->nextMode = INTERP_STD;
+        //LOGD("Invalid trace request, exiting\n");
+        return true;
+    }
+#endif /* INTERP_TYPE == INTERP_DBG */
+#endif /* WITH_JIT */
+
+    /* copy state in */
+    curMethod = interpState->method;
+    pc = interpState->pc;
+    fp = interpState->fp;
+    retval = interpState->retval;   /* only need for kInterpEntryReturn? */
+
+    methodClassDex = curMethod->clazz->pDvmDex;
+
+    LOGVV("threadid=%d: entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n",
+        self->threadId, (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",
+        curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns,
+        fp, interpState->entryPoint);
+
+    /*
+     * DEBUG: scramble this to ensure we're not relying on it.
+     */
+    methodToCall = (const Method*) -1;
+
+#if INTERP_TYPE == INTERP_DBG
+    if (debugIsMethodEntry) {
+        ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+                curMethod->name);
+        DUMP_REGS(curMethod, interpState->fp, false);
+    }
+#endif
+
+    switch (interpState->entryPoint) {
+    case kInterpEntryInstr:
+        /* just fall through to instruction loop or threaded kickstart */
+        break;
+    case kInterpEntryReturn:
+        CHECK_JIT_VOID();
+        goto returnFromMethod;
+    case kInterpEntryThrow:
+        goto exceptionThrown;
+    default:
+        dvmAbort();
+    }
+
+#ifdef THREADED_INTERP
+    FINISH(0);                  /* fetch and execute first instruction */
+#else
+    while (1) {
+        CHECK_DEBUG_AND_PROF(); /* service debugger and profiling */
+        CHECK_TRACKED_REFS();   /* check local reference tracking */
+
+        /* fetch the next 16 bits from the instruction stream */
+        inst = FETCH(0);
+
+        switch (INST_INST(inst)) {
+#endif
+
+/*--- start of opcodes ---*/
diff --git a/vm/mterp/portable/portdbg.c b/vm/mterp/portable/portdbg.c
new file mode 100644
index 0000000..65349e9
--- /dev/null
+++ b/vm/mterp/portable/portdbg.c
@@ -0,0 +1,17 @@
+#define INTERP_FUNC_NAME dvmInterpretDbg
+#define INTERP_TYPE INTERP_DBG
+
+#define CHECK_DEBUG_AND_PROF() \
+    checkDebugAndProf(pc, fp, self, curMethod, &debugIsMethodEntry)
+
+#if defined(WITH_JIT)
+#define CHECK_JIT_BOOL() (dvmCheckJit(pc, self, interpState, callsiteClass,\
+                          methodToCall))
+#define CHECK_JIT_VOID() (dvmCheckJit(pc, self, interpState, callsiteClass,\
+                          methodToCall))
+#define ABORT_JIT_TSELECT() (dvmJitAbortTraceSelect(interpState))
+#else
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT(x) ((void)0)
+#endif
diff --git a/vm/mterp/portable/portstd.c b/vm/mterp/portable/portstd.c
new file mode 100644
index 0000000..f37c22b
--- /dev/null
+++ b/vm/mterp/portable/portstd.c
@@ -0,0 +1,8 @@
+#define INTERP_FUNC_NAME dvmInterpretStd
+#define INTERP_TYPE INTERP_STD
+
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
diff --git a/vm/mterp/portable/stubdefs.c b/vm/mterp/portable/stubdefs.c
new file mode 100644
index 0000000..b46bb3a
--- /dev/null
+++ b/vm/mterp/portable/stubdefs.c
@@ -0,0 +1,96 @@
+/*
+ * 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)
+
+/*
+ * 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".
+ *
+ * TODO: remove "switch" version.
+ */
+#ifdef THREADED_INTERP
+# define H(_op)             &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) {                                                  \
+        ADJUST_PC(_offset);                                                 \
+        inst = FETCH(0);                                                    \
+        CHECK_DEBUG_AND_PROF();                                             \
+        CHECK_TRACKED_REFS();                                               \
+        if (CHECK_JIT_BOOL()) GOTO_bail_switch();                           \
+        goto *handlerTable[INST_INST(inst)];                                \
+    }
+# define FINISH_BKPT(_opcode) {                                             \
+        goto *handlerTable[_opcode];                                        \
+    }
+#else
+# define HANDLE_OPCODE(_op) case _op:
+# define FINISH(_offset)    { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
+#endif
+
+#define OP_END
+
+#if defined(WITH_TRACKREF_CHECKS)
+# define CHECK_TRACKED_REFS() \
+    dvmInterpCheckTrackedRefs(self, curMethod, debugTrackedRefStart)
+#else
+# define CHECK_TRACKED_REFS() ((void)0)
+#endif
+
+
+/*
+ * 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;
+#define GOTO_bail_switch() goto bail_switch;
+
+/*
+ * 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(_entryPoint, _pcadj) {                              \
+        if (dvmCheckSuspendQuick(self)) {                                   \
+            EXPORT_PC();  /* need for precise GC */                         \
+            dvmCheckSuspendPending(self);                                   \
+        }                                                                   \
+        if (NEED_INTERP_SWITCH(INTERP_TYPE)) {                              \
+            ADJUST_PC(_pcadj);                                              \
+            interpState->entryPoint = _entryPoint;                          \
+            LOGVV("threadid=%d: switch to %s ep=%d adj=%d\n",               \
+                self->threadId,                                             \
+                (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",      \
+                (_entryPoint), (_pcadj));                                   \
+            GOTO_bail_switch();                                             \
+        }                                                                   \
+    }
diff --git a/vm/mterp/rebuild.sh b/vm/mterp/rebuild.sh
new file mode 100755
index 0000000..841546f
--- /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 portstd portdbg allstubs armv4t armv5te armv5te-vfp armv7-a armv7-a-neon x86 x86-atom; 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-portstd.S out/InterpAsm-portdbg.S
diff --git a/vm/mterp/x86-atom/OP_ADD_DOUBLE.S b/vm/mterp/x86-atom/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..22f3938
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_DOUBLE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_DOUBLE.S
+    */
+
+%include "x86-atom/binopWide.S" {"instr":"addsd   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..0b2bf4f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_DOUBLE_2ADDR.S
+    */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"addsd   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_FLOAT.S b/vm/mterp/x86-atom/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..aa3aa22
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_FLOAT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_FLOAT.S
+    */
+
+%include "x86-atom/binopF.S" {"instr":"addss     %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..3d62703
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_FLOAT_2ADDR.S
+    */
+
+%include "x86-atom/binopF2addr.S" {"instr":"addss     %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_INT.S b/vm/mterp/x86-atom/OP_ADD_INT.S
new file mode 100644
index 0000000..a423e75
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_INT.S
+    */
+
+%include "x86-atom/binop.S" {"instr":"addl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_ADD_INT_2ADDR.S b/vm/mterp/x86-atom/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..2a91f41
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_INT_2ADDR.S
+    */
+
+%include "x86-atom/binop2addr.S" {"instr":"addl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_ADD_INT_LIT16.S b/vm/mterp/x86-atom/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..72479ba
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_INT_LIT16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_INT_LIT16.S
+    */
+
+%include "x86-atom/binopLit16.S" {"instr":"addl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_ADD_INT_LIT8.S b/vm/mterp/x86-atom/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..eabd4b5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8.S" {"instr":"addl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_ADD_LONG.S b/vm/mterp/x86-atom/OP_ADD_LONG.S
new file mode 100644
index 0000000..7e31d35
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_LONG.S
+    */
+
+%include "x86-atom/binopWide.S" {"instr":"paddq   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..4c65a45
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ADD_LONG_2ADDR.S
+    */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"paddq   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_AGET.S b/vm/mterp/x86-atom/OP_AGET.S
new file mode 100644
index 0000000..73a27ab
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET.S
@@ -0,0 +1,53 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AGET.S
+    *
+    * Code: Generic 32-bit array "get" operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *
+    * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the value
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+%default { "mov":"l","scale":"4"}
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $$0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    lea         (%ecx, %edx, $scale), %ecx # %ecx<- &vBB[vCC]
+                                           # trying: lea (%ecx, %edx, scale), %ecx
+                                           # to reduce code size
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    mov$mov offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+                                                # doing this and the previous instr
+                                                # with one instr was not faster
+    SET_VREG    %edx  rINST             # vAA<- %edx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_AGET_BOOLEAN.S b/vm/mterp/x86-atom/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..1d28745
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_BOOLEAN.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AGET_BOOLEAN.S
+    */
+
+%include "x86-atom/OP_AGET.S" { "mov":"zbl", "scale":"1" }
diff --git a/vm/mterp/x86-atom/OP_AGET_BYTE.S b/vm/mterp/x86-atom/OP_AGET_BYTE.S
new file mode 100644
index 0000000..60e4266
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_BYTE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AGET_BYTE.S
+    */
+
+%include "x86-atom/OP_AGET.S" {  "mov":"sbl", "scale":"1" }
diff --git a/vm/mterp/x86-atom/OP_AGET_CHAR.S b/vm/mterp/x86-atom/OP_AGET_CHAR.S
new file mode 100644
index 0000000..114d02d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_CHAR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AGET_CHAR.S
+    */
+
+%include "x86-atom/OP_AGET.S" {  "mov":"zwl", "scale":"2" }
diff --git a/vm/mterp/x86-atom/OP_AGET_OBJECT.S b/vm/mterp/x86-atom/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..0ed02c3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_OBJECT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AGET_OBJECT.S
+    */
+
+%include "x86-atom/OP_AGET.S"
diff --git a/vm/mterp/x86-atom/OP_AGET_SHORT.S b/vm/mterp/x86-atom/OP_AGET_SHORT.S
new file mode 100644
index 0000000..3ddb9b9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_SHORT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AGET_SHORT.S
+    */
+
+%include "x86-atom/OP_AGET.S" {  "mov":"swl", "scale":"2" }
diff --git a/vm/mterp/x86-atom/OP_AGET_WIDE.S b/vm/mterp/x86-atom/OP_AGET_WIDE.S
new file mode 100644
index 0000000..db5a930
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_WIDE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_AGET_WIDE.S
+    *
+    * Code: 64-bit array get operation.
+    *
+    * For: aget-wide
+    *
+    * Description: Perform an array get operation at the identified index
+    *              of a given array; load the array value into the destination
+    *              register. vAA <- vBB[vCC].
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $$0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        offArrayObject_contents(%ecx, %edx, 8), %xmm0 # %xmm0<- vBB[vCC]
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %xmm0; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_AND_INT.S b/vm/mterp/x86-atom/OP_AND_INT.S
new file mode 100644
index 0000000..10d223b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AND_INT.S
+    */
+
+%include "x86-atom/binop.S" {"instr":"andl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_AND_INT_2ADDR.S b/vm/mterp/x86-atom/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..dcbd531
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AND_INT_2ADDR.S
+    */
+
+%include "x86-atom/binop2addr.S" {"instr":"andl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_AND_INT_LIT16.S b/vm/mterp/x86-atom/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..7e6493d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_INT_LIT16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AND_INT_LIT16.S
+    */
+
+%include "x86-atom/binopLit16.S" {"instr":"andl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_AND_INT_LIT8.S b/vm/mterp/x86-atom/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..511e3ae
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AND_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8.S" {"instr":"andl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_AND_LONG.S b/vm/mterp/x86-atom/OP_AND_LONG.S
new file mode 100644
index 0000000..e62e312
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AND_LONG.S
+    */
+
+%include "x86-atom/binopWide.S" {"instr":"pand   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_AND_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..90e77e6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_AND_LONG_2ADDR.S
+    */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"pand   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_APUT.S b/vm/mterp/x86-atom/OP_APUT.S
new file mode 100644
index 0000000..93b3866
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT.S
@@ -0,0 +1,50 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_APUT.S
+    *
+    * Code: Generic 32-bit array put operation.  Provides a "scale" variable
+    *       to specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       move performed also dependent on the type of the array element.
+    *       Provides a "value" register to specify the source of the move
+    *
+    * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+%default { "mov":"l","scale":"4", "value": "rINST"}
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $$0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         (%ecx, %edx, $scale), %ecx # %ecx<- &vBB[vCC]
+    GET_VREG    rINST                   # rINST<- vAA
+    mov$mov     $value, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_APUT_BOOLEAN.S b/vm/mterp/x86-atom/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..d9afd6d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_BOOLEAN.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_APUT_BOOLEAN.S
+    */
+
+%include "x86-atom/OP_APUT.S" { "mov":"b", "scale":"1", "value":"rINSTbl" }
diff --git a/vm/mterp/x86-atom/OP_APUT_BYTE.S b/vm/mterp/x86-atom/OP_APUT_BYTE.S
new file mode 100644
index 0000000..29cb708
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_BYTE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_APUT_BYTE.S
+    */
+
+%include "x86-atom/OP_APUT.S" { "mov":"b", "scale":"1", "value":"rINSTbl" }
diff --git a/vm/mterp/x86-atom/OP_APUT_CHAR.S b/vm/mterp/x86-atom/OP_APUT_CHAR.S
new file mode 100644
index 0000000..d43e540
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_CHAR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_APUT_CHAR.S
+    */
+
+%include "x86-atom/OP_APUT.S" { "mov":"w", "scale":"2", "value":"rINSTw" }
diff --git a/vm/mterp/x86-atom/OP_APUT_OBJECT.S b/vm/mterp/x86-atom/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..0e23d71
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_OBJECT.S
@@ -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.
+    */
+
+   /*
+    * File: OP_APUT_OBJECT.S
+    *
+    * Code: 32-bit array put operation.  Provides an "scale" variable
+    *       specify a scale value which depends on the width of the array
+    *       elements. Provides a "mov" variable which determines the type of
+    *       mov performed also dependent on the type of the array element.
+    *       Provides a "value" register to specify the source of the mov
+    *
+    * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %eax                    # %eax<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $$0, %eax               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%eax), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    GET_VREG    rINST                   # rINST<- vAA
+    lea         (%eax, %edx, 4), %edx   # %edx<- &vBB[vCC]
+    cmp         $$0, rINST              # check for null reference
+    je          .L${opcode}_skip_check  # reference is null so skip type check
+    jmp         .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    movl        %edx, sReg0             # save &vBB[vCC]
+    movl        %eax, sReg1             # save object head
+    movl        offObject_clazz(rINST), %edx # %edx<- obj->clazz
+    movl        %edx, -8(%esp)          # push parameter obj->clazz
+    movl        offObject_clazz(%eax), %eax # %eax<- arrayObj->clazz
+    movl        %eax, -4(%esp)          # push parameter arrayObj->clazz
+    lea         -8(%esp), %esp
+    call        dvmCanPutArrayElement   # test object type vs. array type
+                                        # call: ClassObject* elemClass, ClassObject* arrayClass)
+                                        # return: bool
+    lea         8(%esp), %esp
+    testl       %eax, %eax              # check for invalid array value
+    je          common_errArrayStore    # handle invalid array value
+    movl        sReg0, %edx             # restore &vBB[vCC]
+    movl        rINST, offArrayObject_contents(%edx)
+    movl        rGLUE, %eax
+    FFETCH_ADV  2, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    movl        offGlue_cardTable(%eax), %eax # get card table base
+    movl        sReg1, %edx             # restore object head
+    shrl        $$GC_CARD_SHIFT, %edx   # object head to card number
+    movb        %al, (%eax, %edx)       # mark card using object head
+    FGETOP_JMP  2, %ecx                 # jump to next instruction; getop, jmp
+.L${opcode}_skip_check:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl        rINST, offArrayObject_contents(%edx)
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_APUT_SHORT.S b/vm/mterp/x86-atom/OP_APUT_SHORT.S
new file mode 100644
index 0000000..daef0d8
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_SHORT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_APUT_SHORT.S
+    */
+
+%include "x86-atom/OP_APUT.S" { "mov":"w", "scale":"2", "value":"rINSTw" }
diff --git a/vm/mterp/x86-atom/OP_APUT_WIDE.S b/vm/mterp/x86-atom/OP_APUT_WIDE.S
new file mode 100644
index 0000000..b1b9e6a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_WIDE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_APUT_WIDE.S
+    *
+    * Code: 64-bit array put operation.
+    *
+    * For: aput-wide
+    *
+    * Description: Perform an array put operation from the value register;
+    *              store the value register at the identified index of a
+    *              given array. vBB[vCC] <- vAA.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    cmp         $$0, %ecx               # check for null array object
+    je          common_errNullObject    # handle null array object
+    cmp         offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+    jnc         common_errArrayIndex    # handle index >= length, bail
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vAA
+    movq        %xmm0, offArrayObject_contents(%ecx, %edx, 8) # vBB[vCC]<- %xmm0; value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_ARRAY_LENGTH.S b/vm/mterp/x86-atom/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..485f815
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ARRAY_LENGTH.S
@@ -0,0 +1,40 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_ARRAY_LENGTH.S
+    *
+    * Code: 32-bit array length operation.
+    *
+    * For: array-length
+    *
+    * Description: Store the length of the indicated array in the given
+    *              destination register. vB <- offArrayObject_length(vA)
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $$4, %eax               # %eax<- B
+    andl        $$15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB
+    cmp         $$0, %eax               # check for null array object
+    je          common_errNullObject    # handle null array object
+    FFETCH_ADV  1, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl        offArrayObject_length(%eax), %eax # %eax<- array length
+    movl        %eax, (rFP, rINST, 4)   # vA<- %eax; array length
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_BREAKPOINT.S b/vm/mterp/x86-atom/OP_BREAKPOINT.S
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_BREAKPOINT.S
diff --git a/vm/mterp/x86-atom/OP_CHECK_CAST.S b/vm/mterp/x86-atom/OP_CHECK_CAST.S
new file mode 100644
index 0000000..bbbdb0f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CHECK_CAST.S
@@ -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.
+    */
+
+   /*
+    * File: OP_CHECK_CAST.S
+    *
+    * Code: Checks to see if a cast is allowed. Uses no substitutions.
+    *
+    * For: check-cast
+    *
+    * Description: Throw if the reference in the given register cannot be
+    *              cast to the indicated type. The type must be a reference
+    *              type (not a primitive type).
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, type@BBBB
+    */
+
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %eax # %eax<- pDvmDex
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        offDvmDex_pResClasses(%eax), %eax # %eax<- pDvmDex->pResClasses
+    cmp         $$0, rINST              # check for null reference object
+    je          .L${opcode}_okay        # can always cast null object
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        (%eax, %ecx, 4), %ecx   # %ecx<- resolved class
+    cmp         $$0, %ecx               # check if classes is resolved before?
+    je          .L${opcode}_resolve     # resolve class
+    jmp         .L${opcode}_resolved    # continue
+%break
+
+.L${opcode}_resolved:
+    cmp         %ecx, offObject_clazz(rINST) # check for same class
+    jne         .L${opcode}_fullcheck   # not same class; do full check
+
+.L${opcode}_okay:
+    FINISH      2                       # jump to next instruction
+
+   /*
+    *  Trivial test failed, need to perform full check.
+    *  offObject_clazz(rINST) holds obj->clazz
+    *  %ecx holds class resolved from BBBB
+    *  rINST holds object
+    */
+
+.L${opcode}_fullcheck:
+    movl        offObject_clazz(rINST), %eax  # %eax<- obj->clazz
+    movl        %eax, -12(%esp)         # push parameter obj->clazz
+    movl        %ecx, -8(%esp)          # push parameter # push parameter resolved class
+    lea         -12(%esp), %esp
+    call        dvmInstanceofNonTrivial # call: (ClassObject* instance, ClassObject* clazz)
+                                        # return: int
+    lea         12(%esp), %esp
+    cmp         $$0, %eax               # failed?
+    jne         .L${opcode}_okay        # success
+
+   /*
+    * A cast has failed.  We need to throw a ClassCastException with the
+    * class of the object that failed to be cast.
+    */
+
+    EXPORT_PC                           # we will throw an exception
+    movl        $$.LstrClassCastExceptionPtr, -8(%esp) # push parameter message
+    movl        offObject_clazz(rINST), rINST # rINST<- obj->clazz
+    movl        offClassObject_descriptor(rINST), rINST # rINST<- obj->clazz->descriptor
+    movl        rINST, -4(%esp)         # push parameter obj->clazz->descriptor
+    lea         -8(%esp), %esp
+    call        dvmThrowExceptionWithClassMessage # call: (const char* exceptionDescriptor,
+                                                  #       const char* messageDescriptor, Object* cause)
+                                                  # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown
+
+   /*
+    * Resolution required.  This is the least-likely path.
+    *
+    *  rINST holds object
+    */
+
+.L${opcode}_resolve:
+    movl        offGlue_method(%edx), %eax # %eax<- glue->method
+    FETCH       1, %ecx                 # %ecx holds BBBB
+    EXPORT_PC                           # in case we throw an exception
+    movl        $$0, -8(%esp)           # push parameter false
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    movl        %ecx, -12(%esp)         # push parameter BBBB
+    movl        %eax, -16(%esp)         # push parameter glue->method>clazz
+    lea         -16(%esp), %esp
+    call        dvmResolveClass         # resolve ClassObject pointer
+                                        # call: (const ClassObject* referrer, u4 classIdx,
+                                        #        bool fromUnverifiedConstant)
+                                        # return ClassObject*
+    lea         16(%esp), %esp
+    cmp         $$0, %eax               # check for null pointer
+    je          common_exceptionThrown  # handle excpetion
+    movl        %eax, %ecx              # %ecx<- resolved class
+    jmp         .L${opcode}_resolved
+
+.LstrClassCastExceptionPtr:
+.asciz      "Ljava/lang/ClassCastException;"
diff --git a/vm/mterp/x86-atom/OP_CMPG_DOUBLE.S b/vm/mterp/x86-atom/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..87f8a3b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMPG_DOUBLE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CMPG_DOUBLE.S
+    */
+
+%include "x86-atom/OP_CMPL_FLOAT.S" { "nan":"$0x1", "sod":"d"}
diff --git a/vm/mterp/x86-atom/OP_CMPG_FLOAT.S b/vm/mterp/x86-atom/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..de42969
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMPG_FLOAT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CMPG_FLOAT.S
+    */
+
+%include "x86-atom/OP_CMPL_FLOAT.S" { "nan":"$0x1" }
diff --git a/vm/mterp/x86-atom/OP_CMPL_DOUBLE.S b/vm/mterp/x86-atom/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..2a603a4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMPL_DOUBLE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CMPL_DOUBLE.S
+    */
+
+%include "x86-atom/OP_CMPL_FLOAT.S" { "sod":"d" }
diff --git a/vm/mterp/x86-atom/OP_CMPL_FLOAT.S b/vm/mterp/x86-atom/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..5cb3ec7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMPL_FLOAT.S
@@ -0,0 +1,63 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CMPL_FLOAT.S
+    *
+    * Code: Provides a "nan" variable to specify the return value for
+    *       NaN. Provides a variable "sod" which appends a "s" or a "d"
+    *       to the move and comparison instructions, depending on if we
+    *       are working with a float or a double. For instructions
+    *       cmpx-float and cmpx-double, the x will be eiher a g or a l
+    *       to specify positive or negative bias for NaN.
+    *
+    * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+    *
+    * Description: Perform the indicated floating point or long comparison,
+    *              storing 0 if the two arguments are equal, 1 if the second
+    *              argument is larger, or -1 if the first argument is larger.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+%default { "nan":"$0xFFFFFFFF" , "sod":"s" }
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movs$sod    (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    comis$sod   (rFP, %edx, 4), %xmm0   # do comparison
+    ja          .L${opcode}_greater
+    jp          .L${opcode}_finalNan
+    jz          .L${opcode}_final
+
+.L${opcode}_less:
+    movl        $$0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+%break
+.L${opcode}_greater:
+    movl        $$0x1, (rFP, rINST, 4)  # vAA<- greater than
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.L${opcode}_final:
+    movl        $$0x0, (rFP, rINST, 4)  # vAA<- equal
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+.L${opcode}_finalNan:
+    movl        $nan, (rFP, rINST, 4)   # vAA<- NaN
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CMP_LONG.S b/vm/mterp/x86-atom/OP_CMP_LONG.S
new file mode 100644
index 0000000..cf021a3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMP_LONG.S
@@ -0,0 +1,55 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CMP_LONG.S
+    *
+    * Code: Compare floating point values. Uses no substitutions.
+    *
+    * For: cmp-long
+    *
+    * Description: Perform a long comparison, storing 0 if the two
+    *              arguments are equal, 1 if the second argument is larger
+    *              or -1 if the first argument is larger.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    movl        4(rFP, %ecx, 4), %eax   # %eax<- vBBhi
+    cmp         4(rFP, %edx, 4), %eax   # compare vCChi and vBBhi
+    jl          .L${opcode}_less
+    jg          .L${opcode}_greater
+    movl        (rFP, %ecx, 4), %eax    # %eax<- vBBlo
+    cmp         (rFP, %edx, 4), %eax    # compare vCClo and vBBlo
+    ja          .L${opcode}_greater
+    jne         .L${opcode}_less
+    jmp         .L${opcode}_final
+%break
+
+.L${opcode}_final:
+    movl        $$0x0, (rFP, rINST, 4)  # vAA<- equal
+    FINISH      2                       # jump to next instruction
+
+.L${opcode}_less:
+    movl        $$0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+    FINISH      2                       # jump to next instruction
+
+.L${opcode}_greater:
+    movl        $$0x1, (rFP, rINST, 4)  # vAA<- greater than
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_CONST.S b/vm/mterp/x86-atom/OP_CONST.S
new file mode 100644
index 0000000..7ff4c23
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST.S
@@ -0,0 +1,36 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CONST.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const
+    *
+    * Description: Move the given literal value into the specified register
+    *
+    * Format: AA|op BBBBlo BBBBhi (31i)
+    *
+    * Syntax: op vAA, #+BBBBBBBB
+    */
+
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    shl         $$16, %edx              # move BBBB to high bits
+    or          %edx, %ecx              # %ecx<- #+BBBBBBBB
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; literal
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_16.S b/vm/mterp/x86-atom/OP_CONST_16.S
new file mode 100644
index 0000000..f340696
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_16.S
@@ -0,0 +1,34 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CONST_16.S
+    *
+    * Code: Moves a literal to a register. Uses no substitutions.
+    *
+    * For: const/16
+    *
+    * Description: Move the given literal value (right-zero-extended to 32
+    *              bits) into the specified register
+    *
+    * Format: AA|op BBBB (21s)
+    *
+    * Syntax: op vAA, #+BBBB
+    */
+
+    FETCHs      1, %edx                 # %edx<- BBBB
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    SET_VREG    %edx rINST              # vAA<- BBBB; literal
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_4.S b/vm/mterp/x86-atom/OP_CONST_4.S
new file mode 100644
index 0000000..5396d72
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_4.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CONST_4.S
+    *
+    * Code: Moves a literal to a register. Uses no substitutions.
+    *
+    * For: const/4
+    *
+    * Description: Move the given literal value (right-zero-extended to 32
+    *              bits) into the specified register.
+    *
+    * Format: B|A|op (11n)
+    *
+    * Syntax: op vA, #+B
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    andl        $$15, rINST             # rINST<- A
+    shl         $$24, %edx              # %edx<- B000
+    sar         $$28, %edx              # %edx<- right-zero-extended B
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    SET_VREG    %edx, rINST             # vA<- %edx; literal
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_CLASS.S b/vm/mterp/x86-atom/OP_CONST_CLASS.S
new file mode 100644
index 0000000..bc6657c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_CLASS.S
@@ -0,0 +1,67 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CONST_CLASS.S
+    *
+    * Code: Move a class reference to a register. Uses no substitutions.
+    *
+    * For: const/class
+    *
+    * Description: Move a reference to the class specified
+    *              by the given index into the specified register.
+    *              In the case where the indicated type is primitive,
+    *              this will store a reference to the primitive type's
+    *              degenerate class.
+    *
+    * Format: AA|op BBBBlo BBBBhi (21c)
+    *
+    * Syntax: op vAA, field@BBBB
+    */
+
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        offGlue_methodClassDex(%edx), %eax # %eax<- pDvmDex
+    movl        offDvmDex_pResClasses(%eax), %eax # %eax<- pDvmDex->pResClasses
+    movl        (%eax, %ecx, 4), %eax   # %eax<- resolved class
+    cmp         $$0, %eax               # check if classes is resolved before?
+    je          .L${opcode}_resolve     # resolve class
+    SET_VREG    %eax, rINST             # vAA<- resolved class
+    FINISH      2                       # jump to next instruction
+%break
+
+   /*
+    * Continuation if the Class has not yet been resolved.
+    *  %ecx: BBBB (Class ref)
+    *  need: target register
+    */
+
+.L${opcode}_resolve:
+    EXPORT_PC
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        $$1, -4(%esp)           # push parameter true
+    movl        %ecx, -8(%esp)          # push parameter
+    movl        %edx, -12(%esp)         # push parameter glue->method->clazz
+    lea         -12(%esp), %esp
+    call        dvmResolveClass         # resolve ClassObject pointer
+                                        # class: (const ClassObject* referrer, u4 classIdx,
+                                        #         bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         12(%esp), %esp
+    cmp         $$0, %eax               # check for null pointer
+    je          common_exceptionThrown  # handle exception
+    SET_VREG    %eax, rINST             # vAA<- resolved class
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_CONST_HIGH16.S b/vm/mterp/x86-atom/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..f47d34b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_HIGH16.S
@@ -0,0 +1,35 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CONST_HIGH16.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const/high16
+    *
+    * Description: Move the given literal value (right-zero-extended to 32
+    *              bits) into the specified register
+    *
+    * Format: AA|op BBBB (21h)
+    *
+    * Syntax: op vAA, #+BBBB0000
+    */
+
+    FETCH       1, %ecx                 # %ecx<- 0000BBBB (zero-extended)
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    shl         $$16, %ecx              # %ecx<- BBBB0000
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; BBBB0000
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_STRING.S b/vm/mterp/x86-atom/OP_CONST_STRING.S
new file mode 100644
index 0000000..c958ed4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_STRING.S
@@ -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.
+    */
+
+   /*
+    * File: OP_CONST_STRING.S
+    *
+    * Code: Move a string reference to a register. Uses no substitutions.
+    *
+    * For: const/string
+    *
+    * Description: Move a referece to the string specified by the given
+    *              index into the specified register. vAA <- pResString[BBBB]
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %eax # %eax<- glue->methodClassDex
+    movl        offDvmDex_pResStrings(%eax), %eax # %eax<- glue->methodClassDex->pResStrings
+    movl        (%eax, %ecx, 4), %eax   # %eax<- pResStrings[BBBB]
+    cmp         $$0, %eax               # check if string is resolved
+    je          .L${opcode}_resolve     # resolve string reference
+    SET_VREG    %eax, rINST             # vAA<- %eax; pResString[BBBB]
+    FINISH      2                       # jump to next instruction
+
+%break
+
+
+   /*
+    * Continuation if the Class has not yet been resolved.
+    *  %ecx: BBBB (Class ref)
+    *  need: target register
+    */
+
+.L${opcode}_resolve:
+    EXPORT_PC
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %ecx, -4(%esp)          # push parameter class ref
+    movl        %edx, -8(%esp)          # push parameter glue->method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveString        # resolve string reference
+                                        # call: (const ClassObject* referrer, u4 stringIdx)
+                                        # return: StringObject*
+    lea         8(%esp), %esp
+    cmp         $$0, %eax               # check if resolved string failed
+    je          common_exceptionThrown  # resolve failed; exception thrown
+    SET_VREG    %eax, rINST             # vAA<- %eax; pResString[BBBB]
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_STRING_JUMBO.S b/vm/mterp/x86-atom/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..09d045d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_STRING_JUMBO.S
@@ -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.
+    */
+
+   /*
+    * File: OP_CONST_STRING_JUMBO.S
+    *
+    * Code: Move a string reference to a register. Uses no substitutions.
+    *
+    * For: const/string-jumbo
+    *
+    * Description: Move a reference to the string specified by the given
+    *              index into the specified register. vAA <- pResString[BBBB]
+    *
+    * Format: AA|op BBBBlo BBBBhi (31c)
+    *
+    * Syntax: op vAA, string@BBBBBBBB
+    */
+
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %eax # %eax<- glue->methodClassDex
+    movl        offDvmDex_pResStrings(%eax), %eax # %eax<- glue->methodClassDex->pResStrings
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    shl         $$16, %edx              # %edx<- prepare to create &BBBBBBBB
+    or          %edx, %ecx              # %ecx<- &BBBBBBBB
+    movl        (%eax, %ecx, 4), %eax   # %eax<- pResStrings[BBBB]
+    cmp         $$0, %eax               # check if string is resolved
+    je          .L${opcode}_resolve     # resolve string reference
+    SET_VREG    %eax, rINST             # vAA<- %eax; pResString[BBBB]
+    FINISH      3                       # jump to next instruction
+%break
+
+
+   /*
+    * Continuation if the Class has not yet been resolved.
+    *  %ecx: BBBB (Class ref)
+    *  need: target register
+    */
+.L${opcode}_resolve:
+    EXPORT_PC
+    movl        rGLUE, %edx             # get MterpGlue pointer
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx <- glue->method->clazz
+    movl        %ecx, -4(%esp)          # push parameter class ref
+    movl        %edx, -8(%esp)          # push parameter glue->method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveString        # resolve string reference
+                                        # call: (const ClassObject* referrer, u4 stringIdx)
+                                        # return: StringObject*
+    lea         8(%esp), %esp
+    cmp         $$0, %eax               # check if resolved string failed
+    je          common_exceptionThrown  # resolve failed; exception thrown
+    SET_VREG    %eax, rINST             # vAA<- %eax; pResString[BBBB]
+    FINISH      3                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_CONST_WIDE.S b/vm/mterp/x86-atom/OP_CONST_WIDE.S
new file mode 100644
index 0000000..1ce7c76
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_WIDE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_CONST_WIDE.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const-wide
+    *
+    * Description: Move the given literal value into the specified
+    *              register pair
+    *
+    * Format: AA|op BBBBlolo BBBBlohi BBBBhilo BBBBhihi (51l)
+    *
+    * Syntax: op vAA, #+BBBBBBBBBBBBBBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBBlolo
+    FETCH       2, %edx                 # %edx<- BBBBlohi
+    shl         $$16, %edx              # %edx<- prepare to create #+BBBBBBBBlo
+    or          %edx, %ecx              # %ecx<- #+BBBBBBBBlo
+    movl        %ecx, (rFP, rINST, 4)   # vAA <- #+BBBBBBBBlo
+    FETCH       3, %ecx                 # %ecx<- BBBBhilo
+    FETCH       4, %edx                 # %edx<- BBBBhihi
+    FFETCH_ADV  5, %eax                 # %eax<- next instruction hi; fetch, advance
+    shl         $$16, %edx              # %edx<- prepare to create #+BBBBBBBBhi
+    or          %edx, %ecx              # %ecx<- #+BBBBBBBBhi
+    movl        %ecx, 4(rFP, rINST, 4)  # vAA+1 <- #+BBBBBBBBlo
+    FGETOP_JMP  5, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_WIDE_16.S b/vm/mterp/x86-atom/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..c980f10
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_WIDE_16.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CONST_WIDE_16.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const-wide/16
+    *
+    * Description: Move the given literal value (sign-extended to 64 bits)
+    *              into the specified register-pair
+    *
+    * Format: AA|op BBBB (21s)
+    *
+    * Syntax: op vAA, #+BBBB
+    */
+
+    FETCHs      1, %ecx                 # %ecx<- ssssBBBB (sign-extended)
+    movl        %ecx, %edx              # %edx<- ssssBBBB (sign-extended)
+    sar         $$31, %ecx              # %ecx<- sign bit
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl        %edx, (rFP, rINST, 4)   # vAA<- ssssBBBB
+    movl        %ecx, 4(rFP, rINST, 4)  # vAA+1<- ssssssss
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_WIDE_32.S b/vm/mterp/x86-atom/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..92f8450
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_WIDE_32.S
@@ -0,0 +1,39 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CONST_WIDE_32.S
+    *
+    * Code: Move a literal to a register. Uses no substitutions.
+    *
+    * For: const-wide/32
+    *
+    * Description: Move the given literal value (sign-extended to 64 bits)
+    *              into the specified register-pair
+    *
+    * Format: AA|op BBBBlo BBBBhi (31i)
+    *
+    * Syntax: op vAA, #+BBBBBBBB
+    */
+
+    FETCH       1,  %edx                # %edx<- BBBBlo
+    FETCHs      2, %ecx                 # %ecx<- BBBBhi
+    shl         $$16, %ecx              # prepare to create #+BBBBBBBB
+    or          %ecx, %edx              # %edx<- %edx<- #+BBBBBBBB
+    sar         $$31, %ecx              # %ecx<- sign bit
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl        %edx, (rFP, rINST, 4)   # vAA<-  BBBBBBBB
+    movl        %ecx, 4(rFP, rINST, 4)  # vAA+1<- ssssssss
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_WIDE_HIGH16.S b/vm/mterp/x86-atom/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..5b4b4f1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,35 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_CONST_WIDE_HIGH16.S
+    *
+    * Code: Move a literal value to a register. Uses no substitutions.
+    *
+    * For: const-wide/high16
+    *
+    * Description: Move the given literal value (right-zero-extended to 64
+    *              bits) into the specified register
+    *
+    * Format: AA|op BBBB (21h)
+    *
+    * Syntax: op vAA, #+BBBB000000000000
+    */
+
+    FETCH       1, %ecx                 # %ecx<- 0000BBBB (zero-extended)
+    shl         $$16, %ecx              # rINST<- AA
+    movl        $$0, (rFP, rINST, 4)    # vAAlow<- 00000000
+    movl        %ecx, 4(rFP, rINST, 4)  # vAAhigh<- %ecx; BBBB0000
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DIV_DOUBLE.S b/vm/mterp/x86-atom/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..418a230
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_DOUBLE.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_DOUBLE.S
+    *
+    * Code: Divides doubles. Uses no substitutions.
+    *
+    * For: div-double
+    *
+    * Description: Divide operation on two source registers, storing
+    *              the result in a destination register
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    fldl        (rFP, %ecx, 4)          # floating point stack vBB
+    fdivl       (rFP, %edx, 4)          # divide double; vBB/vCC
+    fstpl       (rFP, rINST, 4)         # vAA<- result
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..7003e30
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_DOUBLE_2ADDR.S
+    *
+    * Code: Divides doubles. Uses no substitutions.
+    *
+    * For: div-double/2addr
+    *
+    * Description: Divide operation on two source registers, storing
+    *              the result in the first source reigster
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    andl        $$15, %edx              # %edx<- A
+    shr         $$4, rINST              # rINST<- B
+    fldl        (rFP, %edx, 4)          # %xmm0<- vA
+    fdivl       (rFP, rINST, 4)         # divide double; vA/vB
+    fstpl       (rFP, %edx, 4)          # vAA<- result
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DIV_FLOAT.S b/vm/mterp/x86-atom/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..a7aabd7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_FLOAT.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_FLOAT.S
+    *
+    * Code: Divides floats. Uses no substitutions.
+    *
+    * For: div-float
+    *
+    * Description: Divide operation on two source registers, storing
+    *              the result in a destiniation register
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    flds        (rFP, %eax, 4)          # floating point stack vBB
+    fdivs       (rFP, %ecx, 4)          # divide double; vBB/vCC
+    fstps       (rFP, rINST, 4)         # vAA<- result
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..471f30a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_FLOAT_2ADDR.S
+    *
+    * Code: Divides floats. Uses no substitutions.
+    *
+    * For: div-float/2addr
+    *
+    * Description: Divide operation on two source registers, storing
+    *              the result in the first source reigster
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $$15, %ecx              # %ecx<- A
+    shr         $$4, rINST              # rINST<- B
+    flds        (rFP, %ecx, 4)          # %xmm0<- vA
+    fdivs       (rFP, rINST, 4)         # divide double; vA/vB
+    fstps       (rFP, %ecx, 4)          # vAA<- result
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DIV_INT.S b/vm/mterp/x86-atom/OP_DIV_INT.S
new file mode 100644
index 0000000..c7f5b27
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_INT.S
+    */
+
+%include "x86-atom/binopD.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_INT_2ADDR.S b/vm/mterp/x86-atom/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..762d82f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_INT_2ADDR.S
+    */
+
+%include "x86-atom/binopD2addr.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_INT_LIT16.S b/vm/mterp/x86-atom/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..9c2ce55
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_INT_LIT16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_INT_LIT16.S
+    */
+
+%include "x86-atom/binopDLit16.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_INT_LIT8.S b/vm/mterp/x86-atom/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..ef0ea7b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_INT_LIT8.S
+    */
+
+%include "x86-atom/binopDLit8.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_LONG.S b/vm/mterp/x86-atom/OP_DIV_LONG.S
new file mode 100644
index 0000000..38ade6a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_LONG.S
+    */
+
+%include "x86-atom/binopDivRemLong.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..41e5430
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DIV_LONG_2ADDR.S
+    */
+
+%include "x86-atom/binopDivRemLong2Addr.S"
diff --git a/vm/mterp/x86-atom/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/x86-atom/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..516a2e6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,36 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DOUBLE_TO_FLOAT.S
+    *
+    * Code: Converts a double to a float. Uses no substitutions.
+    *
+    * For: double-to-float
+    *
+    * Description: Convert the source register (a double) to a float
+    *              and store the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %edx              # %edx<- A
+    fldl        (rFP, rINST, 4)         # load &vB
+    fstps       (rFP, %edx, 4)          # store float
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DOUBLE_TO_INT.S b/vm/mterp/x86-atom/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..f377762
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,68 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_DOUBLE_TO_INT.S
+    *
+    * Code: Converts a double to an integer. Uses no substitutions.
+    *
+    * For: double-to-int
+    *
+    * Description: Convert the source register (a double) to an integer
+    *              and store the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %edx              # %edx<- A
+    fldl        (rFP, rINST, 4)         # load &vB
+    fildl       .LintMax                # push max int value
+    fildl       .LintMin                # push min int value
+    fucomip     %st(2), %st(0)          # check for negInf
+    jae         .L${opcode}_negInf      # handle negInf
+    fucomip     %st(1), %st(0)          # check for posInf or NaN
+    jc          .L${opcode}_nanInf      # handle posInf or NaN
+    jmp         .L${opcode}_break       # do conversion
+%break
+
+.L${opcode}_break:
+    fnstcw      -2(%esp)                # save control word
+    orl         $$0xc00, -2(%esp)       # reset control
+    fldcw       -2(%esp)                # load control word
+    xorl        $$0xc00, -2(%esp)       # reset control
+    fistpl      (rFP, %edx, 4)          # move converted int
+    fldcw       -2(%esp)                # load saved control word
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_nanInf:
+    jnp         .L${opcode}_posInf
+    fstps       (rFP, %edx, 4)
+    movl        $$0x00000000,  (rFP, %edx, 4) # vA<- NaN
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_posInf:
+    fstps       (rFP, %edx, 4)
+    movl        $$0x7FFFFFFF,  (rFP, %edx, 4) # vA<- posInf
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_negInf:
+    fstps       (rFP, %edx, 4)
+    fstps       (rFP, %edx, 4)
+    movl        $$0x80000000,  (rFP, %edx, 4) # vA<- negInf
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DOUBLE_TO_LONG.S b/vm/mterp/x86-atom/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..2ce9bcc
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DOUBLE_TO_LONG.S
@@ -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.
+    */
+
+   /*
+    * File: OP_DOUBLE_TO_LONG.S
+    *
+    * Code: Converts a double to a long. Uses no substitutions.
+    *
+    * For: double-to-long
+    *
+    * Description: Convert the double in source register to a long
+    *              and store in the destintation register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %ecx<- BA
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %edx              # %ecx<- A
+    fldl        (rFP, rINST, 4)         # push vB to floating point stack
+    fildll      .LvaluePosInfLong       # push max int value
+    fildll      .LvalueNegInfLong       # push min int value
+    fucomip     %st(2), %st(0)          # check for negInf
+    jae         .L${opcode}_negInf      # handle negInf
+    fucomip     %st(1), %st(0)          # check for posInf or NaN
+    jc          .L${opcode}_nanInf      # handle posInf or NaN
+    jmp         .L${opcode}_break       # do conversion
+%break
+
+.L${opcode}_break:
+    fnstcw      -2(%esp)                # save control word
+    orl         $$0xc00, -2(%esp)       # reset control
+    fldcw       -2(%esp)                # load control word
+    xorl        $$0xc00, -2(%esp)       # reset control
+    fistpll     (rFP, %edx, 4)          # move converted int
+    fldcw       -2(%esp)                # load saved control word
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_nanInf:
+    jnp         .L${opcode}_posInf
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvalueNanLong, %xmm0   # %xmm0<- NaN
+    movq        %xmm0,  (rFP, %edx, 4)  # vA<- %xmm0; NaN
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_posInf:
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvaluePosInfLong, %xmm0 # %xmm0<- posInf
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; posInf
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_negInf:
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvalueNegInfLong, %xmm0 # %xmm0<- negInf
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; negInf
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_EXECUTE_INLINE.S b/vm/mterp/x86-atom/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..4f01cef
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_EXECUTE_INLINE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_EXECUTE_INLINE.S
+    *
+    * Code: Executes a "native inline" instruction. Uses no substitutions.
+    *
+    * For: execute-inline
+    *
+    * Description: Executes a "native inline" instruction. This instruction
+    *              is generated by the optimizer.
+    *
+    * Format:
+    *
+    * Syntax: vAA, {vC, vD, vE, vF}, inline@BBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        rGLUE, %eax             # %eax<- MterpGlue pointer
+    addl        $$offGlue_retval, %eax  # %eax<- &glue->retval
+    EXPORT_PC
+    shr         $$4, rINST              # rINST<- B
+    movl        %eax, -8(%esp)          # push parameter glue->retval
+    lea         -24(%esp), %esp
+    jmp         .L${opcode}_continue
+%break
+
+   /*
+    * Extract args, call function.
+    *  rINST = #of args (0-4)
+    *  %ecx = call index
+    */
+
+.L${opcode}_continue:
+    FETCH       2, %edx                 # %edx<- FEDC
+    cmp         $$1, rINST              # determine number of arguments
+    jl          0f                      # handle zero args
+    je          1f                      # handle one arg
+    cmp         $$3, rINST
+    jl          2f                      # handle two args
+    je          3f                      # handle three args
+4:
+    movl        %edx, rINST             # rINST<- FEDC
+    and         $$0xf000, rINST         # isolate F
+    shr         $$10, rINST
+    movl        (rFP, rINST), rINST     # rINST<- vF
+    movl        rINST, 12(%esp)         # push parameter vF
+3:
+    movl        %edx, rINST             # rINST<- FEDC
+    and         $$0x0f00, rINST         # isolate E
+    shr         $$6, rINST
+    movl        (rFP, rINST), rINST     # rINST<- vE
+    movl        rINST, 8(%esp)          # push parameter E
+2:
+    movl        %edx, rINST             # rINST<- FEDC
+    and         $$0x00f0, rINST         # isolate D
+    shr         $$2, rINST
+    movl        (rFP, rINST), rINST     # rINST<- vD
+    movl        rINST, 4(%esp)          # push parameter D
+1:
+    and         $$0x000f, %edx          # isolate C
+    movl        (rFP, %edx, 4), %edx    # rINST<- vC
+    movl        %edx, (%esp)            # push parameter C
+0:
+    shl         $$4, %ecx
+    movl        $$gDvmInlineOpsTable, %eax # %eax<- address for table of inline operations
+    call        *(%eax, %ecx)           # call function
+
+    cmp         $$0, %eax               # check boolean result of inline
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         24(%esp), %esp          # update stack pointer
+    je          common_exceptionThrown  # handle exception
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/x86-atom/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..cd5a048
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1,75 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_EXECUTE_INLINE_RANGE.S
+    *
+    * Code: Executes a "native inline" instruction. Uses no substitutions.
+    *
+    * For: execute-inline
+    *
+    * Description: Executes a "native inline" instruction. This instruction
+    *              is generated by the optimizer.
+    *
+    * Format:
+    *
+    * Syntax: AA, {vCCCC..v(CCCC+AA-1)}, inline@BBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    movl        rGLUE, %eax             # %eax<- MterpGlue pointer
+    addl        $$offGlue_retval, %eax  # %eax<- &glue->retval
+    EXPORT_PC
+    movl        %eax, -8(%esp)          # push parameter glue->retval
+    lea         -24(%esp), %esp
+    jmp         .L${opcode}_continue
+%break
+
+   /*
+    * Extract args, call function.
+    *  rINST = #of args (0-4)
+    *  %ecx = call index
+    */
+
+.L${opcode}_continue:
+    FETCH       2, %edx                 # %edx<- FEDC
+    cmp         $$1, rINST              # determine number of arguments
+    jl          0f                      # handle zero args
+    je          1f                      # handle one arg
+    cmp         $$3, rINST
+    jl          2f                      # handle two args
+    je          3f                      # handle three args
+4:
+    movl        12(rFP, %edx, 4), rINST     # rINST<- vF
+    movl        rINST, 12(%esp)         # push parameter vF
+3:
+    movl        8(rFP, %edx, 4), rINST     # rINST<- vE
+    movl        rINST, 8(%esp)          # push parameter E
+2:
+    movl        4(rFP, %edx, 4), rINST     # rINST<- vD
+    movl        rINST, 4(%esp)          # push parameter D
+1:
+    movl        (rFP, %edx, 4), %edx    # rINST<- vC
+    movl        %edx, (%esp)            # push parameter C
+0:
+    shl         $$4, %ecx
+    movl        $$gDvmInlineOpsTable, %eax # %eax<- address for table of inline operations
+    call        *(%eax, %ecx)           # call function
+
+    cmp         $$0, %eax               # check boolean result of inline
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    lea         24(%esp), %esp          # update stack pointer
+    je          common_exceptionThrown  # handle exception
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY.S b/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..f804f3e
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,173 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_FILLED_NEW_ARRAY.S
+    *
+    * Code: Constructs and fills an array with the given data. Provides
+    *
+    * For: float-to-int
+    *
+    * Description: Construct an array of the given type and size,
+    *              filling it with the supplied contents. The type
+    *              must be an array type. The array's contents
+    *              must be single-word. The constructed instance
+    *              is stored as a result in the same way that the
+    *              method invocation instructions store their results,
+    *              so the constructed instance must be moved to a
+    *              register with a subsequent move-result-object
+    *              instruction.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc) (range)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, vtaboff@CCCC
+    *         [B=4] op {vD, vE, vF, vG}, vtaboff@CCCC
+    *         [B=3] op {vD, vE, vF}, vtaboff@CCCC
+    *         [B=2] op {vD, vE}, vtaboff@CCCC
+    *         [B=1] op {vD}, vtaboff@CCCC
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB
+    *         op {vCCCC .. vNNNN}, type@BBBB
+    */
+
+%default { "isrange":"0" }
+
+    movl        rGLUE, %edx             # %edx<- MterpGlue pointer
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- glue->methodClassDex
+    movl        offDvmDex_pResClasses(%edx), %edx # %edx<- glue->methodClassDex->pResClasses
+    FETCH       1, %ecx                 # %ecx<- BBBB
+    EXPORT_PC
+    movl (%edx, %ecx, 4), %eax # %eax<- possibly resolved class
+    cmp         $$0, %eax               # %eax<- check if already resolved
+    jne         .L${opcode}_continue
+    jmp         .L${opcode}_break
+%break
+
+.L${opcode}_break:
+    movl        $$0, -8(%esp)           # push parameter false
+    movl        %ecx, -12(%esp)         # push parameter BBBB
+    movl        rGLUE, %edx             # %edx<- MterpGlue pointer
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -16(%esp)         # push parameter glue->method->clazz
+    lea         -16(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer, u4 classIdx,
+                                        #        bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         16(%esp), %esp
+    cmp         $$0, %eax               # check for null return
+    je          common_exceptionThrown  # handle exception
+
+   /*
+    * On entry:
+    *  %eax holds array class
+    *  rINST holds BA or AA
+    */
+
+.L${opcode}_continue:
+    movl        offClassObject_descriptor(%eax), %eax # %eax<- arrayClass->descriptor
+    movzbl      1(%eax), %eax           # %eax<- descriptor[1]
+    cmpb        $$'I', %al             # check if array of ints
+    je          1f
+    cmpb        $$'L', %al
+    je          1f
+    cmpb        $$'[', %al
+    jne         .L${opcode}_notimpl     # jump to not implemented
+1:
+    movl        %eax, sReg0             # save type
+    movl        rINST, -12(%esp)        # push parameter length
+    movl        %eax, -16(%esp)         # push parameter descriptor[1]
+    movl        $$ALLOC_DONT_TRACK, -8(%esp) # push parameter to allocate flags
+    .if         (!$isrange)
+    shrl        $$4, -12(%esp)          # parameter length is B
+    .endif
+    lea         -16(%esp), %esp
+    call        dvmAllocPrimitiveArray  # call: (char type, size_t length, int allocFlags)
+                                        # return: ArrayObject*
+    lea         16(%esp), %esp
+    cmp         $$0, %eax               # check for null return
+    je          common_exceptionThrown  # handle exception
+
+    FETCH       2, %edx                 # %edx<- FEDC or CCCC
+    movl        rGLUE, %ecx             # %ecx<- MterpGlue pointer
+    movl        %eax, offGlue_retval(%ecx) # retval<- new array
+    lea         offArrayObject_contents(%eax), %eax # %eax<- newArray->contents
+    subl        $$1, -12(%esp)          # length--; check for negative
+    js          2f                      # if length was zero, finish
+
+   /*
+    * copy values from registers into the array
+    * %eax=array, %edx=CCCC/FEDC, -12(%esp)=length (from AA or B), rINST=AA/BA
+    */
+
+    .if         $isrange
+    lea         (rFP, %edx, 4), %ecx    # %ecx<- &fpp[CCCC]
+1:
+    movl        (%ecx), %edx            # %edx<- %ecx++
+    lea         4(%ecx), %ecx           # %ecx++
+    movl        %edx, (%eax)            # *contents<- vX
+    lea         4(%eax), %eax           # %eax++; contents++
+    subl        $$1, -12(%esp)          # length--
+    jns         1b                      # or continue at 2
+    .else
+    cmp         $$4, -12(%esp)          # check length
+    jne         1f                      # has four args
+    and         $$15, rINST             # rINST<- A
+    GET_VREG    rINST                   # rINST<- vA
+    subl        $$1, -12(%esp)          # count--
+    movl        rINST, 16(%eax)         # contents[4]<- vA
+1:
+    movl        %edx, %ecx              # %ecx<- %edx; ecx for temp
+    andl        $$15, %ecx              # %ecx<- G/F/E/D
+    GET_VREG    %ecx                    # %ecx<- vG/vF/vE/vD
+    shr         $$4, %edx               # %edx<- put next reg in low 4
+    subl        $$1, -12(%esp)          # count--
+    movl        %ecx, (%eax)            # *contents<- vX
+    lea         4(%eax), %eax           # %eax++; contents++
+    jns         1b                      # or continue at 2
+    .endif
+2:
+    cmpb        $$'I', sReg0            # check for int array
+    je          3f
+    movl        rGLUE, %ecx             # %ecx<- MterpGlue pointer
+    movl        offGlue_retval(%ecx), %eax # Object head
+    movl        offGlue_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
+3:
+    FINISH      3                       # jump to next instruction
+
+   /*
+    * Throw an exception to indicate this mode of filled-new-array
+    * has not been implemented.
+    */
+
+.L${opcode}_notimpl:
+    movl        $$.LstrInternalError, -8(%esp)
+    movl        $$.LstrFilledNewArrayNotImpl, -4(%esp)
+    lea         -8(%esp), %esp
+    call        dvmThrowException # call: (const char* exceptionDescriptor,
+                                  #        const char* msg)
+                                  # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown
+
+.if         (!$isrange)                 # define in one or the other, not both
+.LstrFilledNewArrayNotImpl:
+.asciz      "filled-new-array only implemented for 'int'"
+.LstrInternalError:
+.asciz  "Ljava/lang/InternalError;"
+.endif
diff --git a/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..fd8b0c5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_FILLED_NEW_ARRAY_RANGE.S
+    */
+
+%include "x86-atom/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/x86-atom/OP_FILL_ARRAY_DATA.S b/vm/mterp/x86-atom/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..de808d9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,46 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_FILL_ARRAY_DATA.S
+    *
+    * Code: Fills an array with given data. Uses no substitutions.
+    *
+    * For: fill-array-data
+    *
+    * Description: Fill the given array with the idicated data. The reference
+    *              must be an array of primitives, and the data table must
+    *              match it in type and size
+    *
+    * Format: AA|op BBBBlo BBBBhi (31t)
+    *
+    * Syntax: op vAA, +BBBBBBBB
+    */
+
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    shl         $$16, %edx              # prepare to create +BBBBBBBB
+    or          %ecx, %edx              # %edx<- +BBBBBBBB
+    lea         (rPC, %edx, 2), %edx    # %edx<- PC + +BBBBBBBB; array data location
+    EXPORT_PC
+    push        %edx
+    push        (rFP, rINST, 4)
+    call        dvmInterpHandleFillArrayData # call: (ArrayObject* arrayObject, const u2* arrayData)
+                                             # return: bool
+    FFETCH_ADV  3, %edx                 # %edx<- next instruction hi; fetch, advance
+    cmp         $$0, %eax
+    lea         8(%esp), %esp
+    je          common_exceptionThrown
+    FGETOP_JMP  3, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/x86-atom/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..91866a4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,36 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_FLOAT_TO_DOUBLE.S
+    *
+    * Code: Converts a float to a double. Uses no substitutions.
+    *
+    * For: float-to-double
+    *
+    * Description: Convert the float in source register to a double
+    *              and store the result in the destintation register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %edx              # %edx<- A
+    flds        (rFP, rINST, 4)         # load float
+    fstpl       (rFP, %edx, 4)          # store double
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_FLOAT_TO_INT.S b/vm/mterp/x86-atom/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..615f187
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FLOAT_TO_INT.S
@@ -0,0 +1,68 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_FLOAT_TO_INT.S
+    *
+    * Code: Converts a float to a int. Uses no substitutions.
+    *
+    * For: float-to-int
+    *
+    * Description: Convert the float in source register to a int
+    *              and store the result in the destintation register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %edx              # %edx<- A
+    flds        (rFP, rINST, 4)         # push vB to floating point stack
+    fildl       .LintMax                # push max int value
+    fildl       .LintMin                # push min int value
+    fucomip     %st(2), %st(0)          # check for negInf
+    jae         .L${opcode}_negInf      # handle negInf
+    fucomip     %st(1), %st(0)          # check for posInf or NaN
+    jc          .L${opcode}_nanInf      # handle posInf or NaN
+    jmp         .L${opcode}_break       # do conversion
+%break
+
+.L${opcode}_break:
+    fnstcw      -2(%esp)                # save control word
+    orl         $$0xc00, -2(%esp)       # reset control
+    fldcw       -2(%esp)                # load control word
+    xorl        $$0xc00, -2(%esp)       # reset control
+    fistpl      (rFP, %edx, 4)          # move converted int
+    fldcw       -2(%esp)                # load saved control word
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_nanInf:
+    jnp         .L${opcode}_posInf      # handle posInf
+    fstps       (rFP, %edx, 4)          # pop floating point stack
+    movl        $$0x00000000,  (rFP, %edx, 4) # vA<- NaN
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_posInf:
+    fstps       (rFP, %edx, 4)          # pop floating point stack
+    movl        $$0x7FFFFFFF,  (rFP, %edx, 4) # vA<- posInf
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_negInf:
+    fstps       (rFP, %edx, 4)          # pop floating point stack
+    fstps       (rFP, %edx, 4)          # pop floating point stack
+    movl        $$0x80000000, (rFP, %edx, 4) # vA<- negInf
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_FLOAT_TO_LONG.S b/vm/mterp/x86-atom/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..9a50b78
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FLOAT_TO_LONG.S
@@ -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.
+    */
+
+   /*
+    * File: OP_FLOAT_TO_LONG.S
+    *
+    * Code: Converts a float to a long. Uses no substitutions.
+    *
+    * For: float-to-long
+    *
+    * Description: Convert the float in source register to a long
+    *              and store the result in the destintation register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %edx              # %edx<- A
+    flds        (rFP, rINST, 4)         # push vB to floating point stack
+    fildll      .LvaluePosInfLong       # push max int value
+    fildll      .LvalueNegInfLong       # push min int value
+    fucomip     %st(2), %st(0)          # check for negInf
+    jae         .L${opcode}_negInf      # handle negInf
+    fucomip     %st(1), %st(0)          # check for posInf or NaN
+    jc          .L${opcode}_nanInf      # handle posInf or NaN
+    jmp         .L${opcode}_break       # do conversion
+%break
+
+.L${opcode}_break:
+    fnstcw      -2(%esp)                # save control word
+    orl         $$0xc00, -2(%esp)       # update control
+    fldcw       -2(%esp)                # load control word
+    xorl        $$0xc00, -2(%esp)       # reset control
+    fistpll     (rFP, %edx, 4)          # move converted int
+    fldcw       -2(%esp)                # load saved control word
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_nanInf:
+    jnp         .L${opcode}_posInf
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvalueNanLong, %xmm0   # %xmm0<- NaN
+    movq        %xmm0,  (rFP, %edx, 4)  # vA<- %xmm0; NaN
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_posInf:
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvaluePosInfLong, %xmm0 # %xmm0<- posInf
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; posInf
+    FINISH      1                       # jump to next instruction
+
+.L${opcode}_negInf:
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        .LvalueNegInfLong, %xmm0 # %xmm0<- negInf
+    fstpl       (rFP, %edx, 4)          # move converted int
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; negInf
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_GOTO.S b/vm/mterp/x86-atom/OP_GOTO.S
new file mode 100644
index 0000000..7bd9956
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_GOTO.S
@@ -0,0 +1,36 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_GOTO.S
+    *
+    * Code: Do an unconditional branch. Uses no substitutions.
+    *
+    * For: goto
+    *
+    * Description: Performs an unconditionally jump to the indicated instruction.
+    *              The branch uses an 8-bit offset that cannot be zero.
+    *
+    * Format: AA|op (10t)
+    *
+    * Syntax: op +AA
+    */
+
+LOP_GOTO.S:
+
+    movsbl      rINSTbl, %edx           # %edx<- +AA
+    shl         $$1, %edx               # %edx is shifted for byte offset
+    js          common_periodicChecks_backwardBranch  # do check on backwards branch
+    FINISH_RB   %edx, %ecx              # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_GOTO_16.S b/vm/mterp/x86-atom/OP_GOTO_16.S
new file mode 100644
index 0000000..931d215
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_GOTO_16.S
@@ -0,0 +1,34 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_GOTO_16.S
+    *
+    * Code: Do an unconditional branch. Uses no substitutions.
+    *
+    * For: goto/16
+    *
+    * Description: Performs an unconditionally jump to the indicated instruction.
+    *              The branch uses a 16-bit offset that cannot be zero.
+    *
+    * Format: ØØ|op AAAA (20t)
+    *
+    * Syntax: op +AAAA
+    */
+
+    FETCHs      1, %edx                 # %edx<- ssssAAAA (sign-extended)
+    shl         $$1, %edx               # %edx is doubled to get the byte offset
+    js          common_periodicChecks_backwardBranch  # do check on backwards branch
+    FINISH_RB   %edx, %ecx              # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_GOTO_32.S b/vm/mterp/x86-atom/OP_GOTO_32.S
new file mode 100644
index 0000000..d00c3a4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_GOTO_32.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_GOTO_32.S
+    *
+    * Code: Do an unconditional branch. Uses no substitutions.
+    *
+    * For: goto/32
+    *
+    * Description:  Performs an unconditionally jump to the indicated instruction.
+    *               The branch uses a 32-bit offset that can be zero.
+    *
+    * Format: ØØ|op AAAAlo AAAAhi (30t)
+    *
+    * Syntax: op +AAAAAAAA
+    */
+
+    FETCH       1, %edx                 # %edx<- AAAAlo
+    FETCH       2, %ecx                 # %ecx<- AAAAhi
+    shl         $$16, %ecx              # prepare to create +AAAAAAAA
+    or          %ecx, %edx              # %edx<- +AAAAAAAA
+    shl         $$1, %edx               # %edx is doubled to get the byte offset
+    jle          common_periodicChecks_backwardBranch  # do check on backwards branch
+    FINISH_RB   %edx, %ecx              # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_IF_EQ.S b/vm/mterp/x86-atom/OP_IF_EQ.S
new file mode 100644
index 0000000..61781a0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_EQ.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_EQ.S
+    */
+
+%include "x86-atom/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86-atom/OP_IF_EQZ.S b/vm/mterp/x86-atom/OP_IF_EQZ.S
new file mode 100644
index 0000000..2f7c140
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_EQZ.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_EQZ.S
+    */
+
+%include "x86-atom/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86-atom/OP_IF_GE.S b/vm/mterp/x86-atom/OP_IF_GE.S
new file mode 100644
index 0000000..e90a1e5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_GE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_GE.S
+    */
+
+%include "x86-atom/bincmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86-atom/OP_IF_GEZ.S b/vm/mterp/x86-atom/OP_IF_GEZ.S
new file mode 100644
index 0000000..8ee71a8
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_GEZ.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_GEZ.S
+    */
+
+%include "x86-atom/zcmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86-atom/OP_IF_GT.S b/vm/mterp/x86-atom/OP_IF_GT.S
new file mode 100644
index 0000000..7f19db9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_GT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_GT.S
+    */
+
+%include "x86-atom/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86-atom/OP_IF_GTZ.S b/vm/mterp/x86-atom/OP_IF_GTZ.S
new file mode 100644
index 0000000..3f8039f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_GTZ.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_GTZ.S
+    */
+
+%include "x86-atom/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86-atom/OP_IF_LE.S b/vm/mterp/x86-atom/OP_IF_LE.S
new file mode 100644
index 0000000..287bd0d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_LE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_LE.S
+    */
+
+%include "x86-atom/bincmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86-atom/OP_IF_LEZ.S b/vm/mterp/x86-atom/OP_IF_LEZ.S
new file mode 100644
index 0000000..b7d31d1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_LEZ.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_LEZ.S
+    */
+
+%include "x86-atom/zcmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86-atom/OP_IF_LT.S b/vm/mterp/x86-atom/OP_IF_LT.S
new file mode 100644
index 0000000..7e58e18
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_LT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_LT.S
+    */
+
+%include "x86-atom/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86-atom/OP_IF_LTZ.S b/vm/mterp/x86-atom/OP_IF_LTZ.S
new file mode 100644
index 0000000..0a3e56b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_LTZ.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_LTZ.S
+    */
+
+%include "x86-atom/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86-atom/OP_IF_NE.S b/vm/mterp/x86-atom/OP_IF_NE.S
new file mode 100644
index 0000000..929bd05
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_NE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_NE.S
+    */
+
+%include "x86-atom/bincmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86-atom/OP_IF_NEZ.S b/vm/mterp/x86-atom/OP_IF_NEZ.S
new file mode 100644
index 0000000..07f2c87
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_NEZ.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IF_NEZ.S
+    */
+
+%include "x86-atom/zcmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86-atom/OP_IGET.S b/vm/mterp/x86-atom/OP_IGET.S
new file mode 100644
index 0000000..e3a72f7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET.S
@@ -0,0 +1,80 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IGET.S
+    *
+    * Code: Generic 32-bit instance field "get" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iget's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+    *      iget-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+%default { "mov":"l" }
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $$0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .L${opcode}_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %edx # %edx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    jmp         .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $$0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # not resolved; handle exception
+
+    /*
+     *  %eax holds resolved field
+     */
+
+.L${opcode}_finish2:
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $$4, %ecx               # %ecx<- B
+    and         $$15, rINST             # rINST<- A
+
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $$0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    mov$mov     (%ecx, %edx), %edx      # %edx<- object field
+    SET_VREG    %edx, rINST             # vA<- %edx; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IGET_BOOLEAN.S b/vm/mterp/x86-atom/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..12100f9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_BOOLEAN.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IGET_BOOLEAN.S
+    */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_BYTE.S b/vm/mterp/x86-atom/OP_IGET_BYTE.S
new file mode 100644
index 0000000..6d6b870
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_BYTE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IGET_BYTE.S
+    */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_CHAR.S b/vm/mterp/x86-atom/OP_IGET_CHAR.S
new file mode 100644
index 0000000..8f285d7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_CHAR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IGET_CHAR.S
+    */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_OBJECT.S b/vm/mterp/x86-atom/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..369e1b9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_OBJECT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IGET_OBJECT.S
+    */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_OBJECT_QUICK.S b/vm/mterp/x86-atom/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..36b7f0e
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IGET_OBJECT_QUICK.S
+    */
+
+%include "x86-atom/OP_IGET_QUICK.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/x86-atom/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..5de2fa3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_QUICK.S b/vm/mterp/x86-atom/OP_IGET_QUICK.S
new file mode 100644
index 0000000..8ec86ec
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_QUICK.S
@@ -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.
+    */
+
+   /*
+    * File: OP_IGET_QUICK.S
+    *
+    * Code: Optimization for iget
+    *
+    * For: iget-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $$4, %eax               # %eax<- B
+    and         $$15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB; object to operate on
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    cmp         $$0, %eax               # check if object is null
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %edx                 # %eax<- next instruction hi; fetch, advance
+    movl        (%ecx, %eax), %eax      # %eax<- object field
+    SET_VREG    %eax, rINST             # fp[A]<- %eax
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IGET_SHORT.S b/vm/mterp/x86-atom/OP_IGET_SHORT.S
new file mode 100644
index 0000000..968b815
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_SHORT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IGET_SHORT.S
+    */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_VOLATILE.S b/vm/mterp/x86-atom/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..5de2fa3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_WIDE.S b/vm/mterp/x86-atom/OP_IGET_WIDE.S
new file mode 100644
index 0000000..370b0b0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_WIDE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_IGET_WIDE.S
+    *
+    * Code: 64 bit instance field "get" operation. Uses no substitutions.
+    *
+    * For: iget-wide
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    * Format:  B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rGLUE, %eax             # %eax<- MterpGlue pointer
+    movl        offGlue_methodClassDex(%eax), %ecx # %ecx<- pDvmDex
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- CCCC
+    FETCH       1, %edx                 # %edx<- pDvmDex->pResFields
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved InstField ptr
+    cmp         $$0, %ecx               # check for null ptr; resolved InstField ptr
+    jne         .L${opcode}_finish
+    movl        offGlue_method(%eax), %ecx # %ecx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%ecx), %ecx # %ecx<- method->clazz
+    movl        %ecx, -8(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -4(%esp)          # push parameter method->clazz
+    jmp         .L${opcode}_finish2
+%break
+
+.L${opcode}_finish2:
+    lea         -8(%esp), %esp
+    call        dvmResolveInstField     # resolve InstField ptr
+                                        # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    cmp         $$0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    movl        %eax, %ecx              # %ecx<- %eax; %ecx expected to hold field
+    je          common_exceptionThrown
+
+   /*
+    *  %ecx holds resolved field
+    */
+
+.L${opcode}_finish:
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB
+    cmp         $$0, %edx               # check for null object
+    je          common_errNullObject
+    movl        offInstField_byteOffset(%ecx), %ecx # %ecx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (%ecx, %edx), %xmm0     # %xmm0<- object field
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- %xmm0; object field
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IGET_WIDE_QUICK.S b/vm/mterp/x86-atom/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..08a57f6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_WIDE_QUICK.S
@@ -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.
+    */
+
+   /*
+    * File: OP_IGET_WIDE_QUICK.S
+    *
+    * Code: Optimization for iget
+    *
+    * For: iget/wide-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB; object to operate on
+    cmp         $$0, %edx               # check if object is null
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    je          common_errNullObject    # handle null object
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    movq        (%ecx, %edx), %xmm0     # %xmm0<- object field
+    movq        %xmm0, (rFP, rINST, 4)  # fp[A]<- %xmm0
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_INSTANCE_OF.S b/vm/mterp/x86-atom/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..4dde31c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INSTANCE_OF.S
@@ -0,0 +1,121 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INSTANCE_OF.S
+    *
+    * Code: Checks if object is instance of a class. Uses no substitutions.
+    *
+    * For: instance-of
+    *
+    * Description: Store in the given destination register 1 if the indicated
+    *              reference is an instance of the given type, or 0 if not.
+    *              The type must be a reference type (not a primitive type).
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    GET_VREG    %edx                    # %edx<- vB
+    cmp         $$0, %edx               # check for null object
+    je          .L${opcode}_store       # null object
+    jmp         .L${opcode}_break
+%break
+
+.L${opcode}_break:
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- CCCC
+    movl        offDvmDex_pResClasses(%ecx), %ecx # %ecx<- pDvmDex->pResClasses
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved class
+    movl        offObject_clazz(%edx), %edx # %edx<- obj->clazz
+    cmp         $$0, %ecx               # check if already resovled
+    je          .L${opcode}_resolve     # not resolved before, so resolve now
+
+.L${opcode}_resolved:
+    cmp         %ecx, %edx              # check if same class
+    je          .L${opcode}_trivial     # yes, finish
+    jmp         .L${opcode}_fullcheck   # no, do full check
+
+   /*
+    * The trivial test failed, we need to perform a full check.
+    * %edx holds obj->clazz
+    * %ecx holds class resolved from BBBB
+    */
+
+.L${opcode}_fullcheck:
+    movl        %edx, -8(%esp)          # push parameter obj->clazz
+    movl        %ecx, -4(%esp)          # push parameter resolved class
+    lea         -8(%esp), %esp
+    call        dvmInstanceofNonTrivial # perform full check
+                                        # call: (ClassObject* instance, ClassObject* clazz)
+                                        # return: int
+    andl        $$15, rINST             # rINST<- A
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    lea         8(%esp), %esp
+    SET_VREG    %eax, rINST             # vA<- r0
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
+
+   /*
+    * %edx holds boolean result
+    */
+
+.L${opcode}_store:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    andl        $$15, rINST             # rINST<- A
+    SET_VREG    %edx, rINST             # vA<- r0
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+   /*
+    * Trivial test succeeded, save and bail.
+    */
+
+.L${opcode}_trivial:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    andl        $$15, rINST             # rINST<- A
+    SET_VREG    $$1, rINST              # vA<- r0
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+
+   /*
+    * Resolution required.  This is the least-likely path.
+    * %eax holds BBBB
+    */
+
+.L${opcode}_resolve:
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    EXPORT_PC
+    movl        offGlue_method(%ecx), %ecx # %ecx<- glue->method
+    movl        offMethod_clazz(%ecx), %ecx # %ecx<- glue->method->clazz
+    movl        %ecx, -12(%esp)         # push parameter glue->method->clazz
+    movl        %eax, -8(%esp)          # push parameter CCCC; type index
+    movl        $$1, -4(%esp)           # push parameter true
+    lea         -12(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer, u4 classIdx,
+                                        #        bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         12(%esp), %esp
+    cmp         $$0, %eax               # check for null
+    je          common_exceptionThrown  # handle exception
+    movl        rINST, %edx             # %edx<- BA+
+    shr         $$4, %edx               # %edx<- B
+    movl        %eax, %ecx              # need class in %ecx
+    GET_VREG    %edx                    # %edx<- vB
+    movl        offObject_clazz(%edx), %edx # %edx<- obj->clazz
+    jmp         .L${opcode}_resolved    # clazz resolved, continue
diff --git a/vm/mterp/x86-atom/OP_INT_TO_BYTE.S b/vm/mterp/x86-atom/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..27cafe9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_BYTE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INT_TO_BYTE.S
+    */
+
+%include "x86-atom/unop.S" { "preinstr":"sal $24, %ecx", "instr":"sar $24, %ecx" }
diff --git a/vm/mterp/x86-atom/OP_INT_TO_CHAR.S b/vm/mterp/x86-atom/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..a28602d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_CHAR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INT_TO_CHAR.S
+    */
+
+%include "x86-atom/unop.S" {"preinstr":"sal $16, %ecx", "instr":"shr $16, %ecx" }
diff --git a/vm/mterp/x86-atom/OP_INT_TO_DOUBLE.S b/vm/mterp/x86-atom/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..cd16eea
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INT_TO_DOUBLE.S
+    *
+    * Code: Convert an int to a double. Uses no substitutions.
+    *
+    * For: int-to-double
+    *
+    * Description: Converts an int in the source register, to a double, and
+    *              stores the result in the destination register. vA<- (double) vB
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %eax             # %eax<- BA+
+    shr         $$4, %eax               # %eax<- B
+    andl        $$15, rINST             # rINST<- A
+    cvtsi2sd    (rFP, %eax, 4), %xmm0   # %xmm0<- vB
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- %xmm0; (double) vB
+    FFETCH_ADV  1, %edx                 # %edx<- next instruction hi; fetch, advance
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_INT_TO_FLOAT.S b/vm/mterp/x86-atom/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..52ce729
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_FLOAT.S
@@ -0,0 +1,36 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INT_TO_FLOAT.S
+    *
+    * Code: Convert an int to a float. Uses no substitutions.
+    *
+    * For: int-to-float
+    *
+    * Description: Convert an int in the source register, to a float, and
+    *              stores the result in the destintation register. vA<- (float) vB
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %eax             # %eax<- BA+
+    shr         $$4, %eax               # %eax<- B
+    andl        $$15,  rINST            # rINST<- A
+    cvtsi2ss    (rFP,%eax,4), %xmm0     # %xmm0<- vB
+    movss       %xmm0, (rFP, rINST, 4)  # vA<- %xmm0
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_INT_TO_LONG.S b/vm/mterp/x86-atom/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..1bc125b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_LONG.S
@@ -0,0 +1,40 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INT_TO_LONG.S
+    *
+    * Code:  Convert an int to a long. Uses no substitutions.
+    *
+    * For:
+    *
+    * Description: Convert an int in the source register, to a long, and
+    *              stores the result in the destintation register. vA<- (long) vB
+    *
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %eax             # %eax<- BA+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $$4, %eax               # %eax<- B
+    andl        $$15, %ecx              # %ecx<- A
+    GET_VREG    %eax                    # %eax<- vB
+    cdq                                 # %edx:%eax<- sign-extend of %eax
+    movl        %eax, (rFP, %ecx, 4)    # vA<- lo part
+    movl        %edx, 4(rFP, %ecx, 4)   # vA+1<- hi part
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_INT_TO_SHORT.S b/vm/mterp/x86-atom/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..f2b0b87
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_SHORT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INT_TO_SHORT.S
+    */
+
+%include "x86-atom/unop.S" { "preinstr":"sal $16, %ecx", "instr":"sar $16, %ecx" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_DIRECT.S b/vm/mterp/x86-atom/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..78b6c06
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_DIRECT.S
@@ -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.
+    */
+
+   /*
+    * File: OP_INVOKE_DIRECT.S
+    *
+    * Code: Call a non-static direct method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_direct that allows up to 255 arguments.
+    *
+    * For: invoke-direct, invoke-direct/range
+    *
+    * Description: invoke-direct is used to invoke a non-static direct method;
+    *              an instance method that is non-overridable, for example,
+    *              either a private instance method or a constructor.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved method to call
+    .if         (!$isrange)
+    andl        $$15, %edx              # %edx<- D if not range
+    .endif
+    EXPORT_PC                           # must export for invoke
+    movl        %edx, -4(%esp)          # save "this" pointer register
+    cmp         $$0, %ecx               # check if already resolved
+    GET_VREG    %edx                    # %edx<- "this" pointer
+    je          .L${opcode}_resolve     # handle resolve
+
+.L${opcode}_finish:
+    cmp         $$0, %edx               # check for null "this"
+    jne         common_invokeMethod${routine} # invoke method common code
+    jmp         common_errNullObject
+%break
+
+   /*
+    * %eax = reference (BBBB or CCCC)
+    * -4(%esp) = "this" register
+    */
+
+.L${opcode}_resolve:
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        $$METHOD_DIRECT, -8(%esp) # push parameter method type
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        %eax, -12(%esp)         # push parameter reference
+    lea         -16(%esp), %esp
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, (%esp)            # push parameter clazz
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         16(%esp), %esp
+    cmp         $$0, %eax               # check for null method return
+    movl        -4(%esp), %edx          # get "this" pointer register
+    GET_VREG    %edx                    # get "this" pointer
+    je          common_exceptionThrown  # null pointer; handle exception
+    cmp         $$0, %edx               # check for null "this"
+    movl        %eax, %ecx              # %ecx<- method
+    jne         common_invokeMethod${routine} # invoke method common code
+    jmp         common_errNullObject    # handle null object
diff --git a/vm/mterp/x86-atom/OP_INVOKE_DIRECT_EMPTY.S b/vm/mterp/x86-atom/OP_INVOKE_DIRECT_EMPTY.S
new file mode 100644
index 0000000..85c0418
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_DIRECT_EMPTY.S
@@ -0,0 +1,26 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_DIRECT_EMPTY.S
+    *
+    * Code: Used as a no-op. Uses no substitutions.
+    *
+    * For: invoke-direct-empty
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    */
+
+    FINISH 3
diff --git a/vm/mterp/x86-atom/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..3ad26e1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_DIRECT_RANGE.S
+    */
+
+%include "x86-atom/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_INTERFACE.S b/vm/mterp/x86-atom/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..cbd7b31
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,76 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_INTERFACE.S
+    *
+    * Code: Call at method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_interface that allows up to 255 arguments.
+    *
+    * For: invoke-interface, invoke-interface-range
+    *
+    * Description: invoke-interface is used to invoke an interface method; on an
+    *              object whose concrete class isn't known, using a method_id that
+    *              refers to an interface.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        %ecx, -12(%esp)         # push argument method index
+    .if         (!$isrange)
+    and         $$15, %edx              # %edx<- D if not range
+    .endif
+    EXPORT_PC                           # must export for invoke
+    GET_VREG    %edx                    # %edx<- first arg "this pointer"
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+    movl        %eax, -4(%esp)          # push parameter class
+    cmp         $$0, %edx               # check for null object
+    je          common_errNullObject    # handle null object
+    jmp         .L${opcode}_break
+%break
+.L${opcode}_break:
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_method(%ecx), %ecx # %ecx<- glue->method
+    movl        %ecx, -8(%esp)          # push parameter method
+    movl        offObject_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -16(%esp)         # push parameter
+    lea         -16(%esp), %esp
+    call        dvmFindInterfaceMethodInCache # call: (ClassObject* thisClass, u4 methodIdx,
+                                              #       const Method* method, DvmDex* methodClassDex)
+                                              # return: Method*
+    lea         16(%esp), %esp
+    cmp         $$0, %eax               # check if find failed
+    je          common_exceptionThrown  # handle exception
+    movl        %eax, %ecx              # %ecx<- method
+    jmp         common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..b323ba0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_INTERFACE_RANGE.S
+    */
+
+%include "x86-atom/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_STATIC.S b/vm/mterp/x86-atom/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..30b6d8c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_STATIC.S
@@ -0,0 +1,70 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_STATIC.S
+    *
+    * Code: Call static direct method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_static that allows up to 255 arguments.
+    *
+    * For: invoke-static, invoke-static/range
+    *
+    * Description: invoke-static is used to invoke static direct method.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+%default { "routine":"NoRange" }
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %edx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %edx<- pDvmDex->pResMethods
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved method to call
+    cmp         $$0, %ecx               # check if already resolved
+    EXPORT_PC                           # must export for invoke
+    jne         common_invokeMethod${routine} # invoke method common code
+    jmp         .L${opcode}_break
+%break
+
+.L${opcode}_break:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    movl        $$METHOD_STATIC, -4(%esp) # resolver method type
+    movl        %eax, -8(%esp)          # push parameter method index
+    movl        offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+    movl        %edx, -12(%esp)         # push parameter method
+    lea         -12(%esp), %esp
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         12(%esp), %esp
+    cmp         $$0, %eax               # check for null method
+    je          common_exceptionThrown
+    movl        %eax, %ecx              # %ecx<- method
+    jmp         common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..ce39e13
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_STATIC_RANGE.S
+    */
+
+%include "x86-atom/OP_INVOKE_STATIC.S" { "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_SUPER.S b/vm/mterp/x86-atom/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..539bea1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_SUPER.S
@@ -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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER.S
+    *
+    * Code: Call super method.
+    *
+    * For: invoke-super, invoke-super/range
+    *
+    * Description: invoke-super is used to invoke the closest superclass's virtual
+    *              method (as opposed to the one with the same method_id in the
+    *              calling class).
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    FETCH       2, %eax                 # %eax<- GFED or CCCC
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+    .if         (!$isrange)
+    and         $$15, %eax              # %eax<- D if not range
+    .endif
+    FETCH       1, %edx                 # %edx<- method index
+    movl        offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+    cmp         $$0, (rFP, %eax, 4)     # check for null object
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved base method
+    je          common_errNullObject    # handle null object
+    jmp         .L${opcode}_continue2
+%break
+
+.L${opcode}_continue2:
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_method(%eax), %eax # %eax<- glue->method
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    EXPORT_PC                           # must export for invoke
+    cmp         $$0, %ecx               # check if already resolved
+    jne         .L${opcode}_continue
+    jmp         .L${opcode}_resolve     # handle resolve
+
+   /*
+    *  %ecx = resolved base method
+    *  %eax = method->clazz
+    */
+
+.L${opcode}_continue:
+    movl        offClassObject_super(%eax), %edx # %edx<- glue->method->clazz->super
+    movzwl      offMethod_methodIndex(%ecx), %ecx # %ecx<-  baseMethod->methodIndex
+    cmp          offClassObject_vtableCount(%edx), %ecx # compare vtableCount with methodIndex
+    EXPORT_PC                           # must export for invoke
+    jnc         .L${opcode}_nsm         # handle method not present
+    movl        offClassObject_vtable(%edx), %edx # %edx<- glue->method->clazz->super->vtable
+    movl        (%edx, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethod${routine} # invoke method common code
+
+.L${opcode}_resolve:
+    movl        %eax, -12(%esp)         # push parameter clazz
+    movl        %edx, -8(%esp)          # push parameter method index
+    movl        $$METHOD_VIRTUAL, -4(%esp) # push parameter method type
+    lea         -12(%esp), %esp
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         12(%esp), %esp
+    movl        %eax, %ecx              # %ecx<- method
+    cmp         $$0, %ecx               # check for null method return
+    movl        -12(%esp), %eax         # %eax<- glue->method->clazz
+    jne         .L${opcode}_continue
+    jmp         common_exceptionThrown  # null pointer; handle exception
+
+   /*
+    * Throw a NoSuchMethodError with the method name as the message.
+    * %ecx = resolved base method
+    */
+
+.L${opcode}_nsm:
+    movl        offMethod_name(%ecx), %edx # %edx<- method name
+    jmp         common_errNoSuchMethod
diff --git a/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..55c7e94
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,40 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER_QUICK.S
+    *
+    * Code: Optimization for invoke-super and invoke-super/range
+    *
+    * For: invoke-super/quick, invoke-super/quick-range
+    */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_method(%ecx), %eax # %eax<- glue->method
+    .if         (!$isrange)
+    and         $$15, %edx              #  %edx<- D if not range
+    .endif
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    movl        offClassObject_super(%eax), %eax # %eax<- glue->method->clazz->super
+    EXPORT_PC                           # must export for invoke
+    movl        offClassObject_vtable(%eax), %eax # %edx<- glue->method->clazz->super->vtable
+    cmp         $$0, (rFP, %edx, 4)     # check for null object
+    movl        (%eax, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    je          common_errNullObject    # handle null object
+    jmp         common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..9e9f311
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER_QUICK_RANGE.S
+    */
+
+%include "x86-atom/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..6e77c02
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_SUPER_RANGE.S
+    */
+
+%include "x86-atom/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL.S b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..46c9265
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,93 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL.S
+    *
+    * Code: Call a virtual method. Provides an "isrange" variable and
+    *       a "routine" variable to specify this is the "range" version of
+    *       invoke_direct that allows up to 255 arguments.
+    *
+    * For: invoke-virtual, invoke-virtual/range
+    *
+    * Description: invoke-virtual is used to invoke a normal virtual method;
+    *              a method that is not static or final, and is not a constructor.
+    *
+    * Format: B|A|op CCCC G|F|E|D (35c)
+    *         AA|op BBBB CCCC (3rc)
+    *
+    * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+    *         [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+    *         [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+    *         [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+    *         [B=2] op {vD, vE}, kind@CCCC (35c)
+    *         [B=1] op {vD}, kind@CCCC (35c)
+    *         [B=0] op {}, kind@CCCC (35c)
+    *
+    *         op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+    *         op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+    *                                              and C determines the first register)
+    */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    EXPORT_PC                           # must export pc for invoke
+    movl        offGlue_methodClassDex(%eax), %eax # %eax<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- method index
+    movl        offDvmDex_pResMethods(%eax), %eax # %eax<- pDvmDex->pResMethods
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    .if         (!$isrange)
+    and         $$15, %edx              # %edx<- D if not range
+    .endif
+    cmp         $$0, (%eax, %ecx, 4)    # check if already resolved
+    je          .L${opcode}_break
+    movl        (%eax, %ecx, 4), %eax   # %eax<- resolved base method
+    jmp         .L${opcode}_continue
+%break
+
+.L${opcode}_break:
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        %edx, -4(%esp)          # save "this" pointer register
+    movl        offGlue_method(%eax), %eax # %eax<- glue->method
+    movl        $$METHOD_VIRTUAL, -8(%esp) # push parameter method type
+    movl        %ecx, -12(%esp)         # push paramter method index
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    lea         -16(%esp), %esp
+    movl        %eax, (%esp)            # push parameter clazz
+    call        dvmResolveMethod        # call: (const ClassObject* referrer,
+                                        #       u4 methodIdx, MethodType methodType)
+                                        # return: Method*
+    lea         16(%esp), %esp
+    cmp         $$0, %eax               # check for null method return
+    movl        -4(%esp), %edx          # get "this" pointer register
+    jne         .L${opcode}_continue
+    jmp         common_exceptionThrown  # null pointer; handle exception
+
+   /*
+    * At this point:
+    *  %eax = resolved base method
+    *  %edx = D or CCCC (index of first arg, which is the "this" ptr)
+    */
+
+.L${opcode}_continue:
+    GET_VREG    %edx                    # %edx<- "this" ptr
+    movzwl      offMethod_methodIndex(%eax), %eax # %eax<- baseMethod->methodIndex
+    cmp         $$0, %edx               # %edx<- check for null "this"
+    je          common_errNullObject    # handle null object
+    movl        offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+    movl        offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+    movl        (%edx, %eax, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..16a4e40
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK.S
@@ -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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL_QUICK.S
+    *
+    * Code: Optimization for invoke-virtual and invoke-virtual/range
+    *
+    * For: invoke-virtual/quick, invoke-virtual/quick-range
+    */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+    FETCH       2, %edx                 # %edx<- GFED or CCCC
+    .if (!$isrange)
+    and         $$15, %edx              # %edx<- D if not range
+    .endif
+    FETCH       1, %ecx                 # %ecx<- method index
+    GET_VREG    %edx                    # %edx<- "this" ptr
+    cmp         $$0, %edx               # %edx<- check for null "this"
+    EXPORT_PC                           # must export pc for invoke
+    je          common_errNullObject
+    movl        offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+    movl        offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+    movl        (%edx, %ecx, 4), %ecx   # %ecx<- vtable[methodIndex]
+    jmp         common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..888bcc0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL_QUICK_RANGE.S
+    */
+
+%include "x86-atom/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..d548a22
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_INVOKE_VIRTUAL_RANGE.S
+    */
+
+%include "x86-atom/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_IPUT.S b/vm/mterp/x86-atom/OP_IPUT.S
new file mode 100644
index 0000000..4c029be
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT.S
@@ -0,0 +1,76 @@
+   /* 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.
+    */
+
+    /*
+    * File: OP_IPUT.S
+    *
+    * Code: Generic 32-bit instance field "put" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iput's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+    *      iput-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+%default { "mov":"l" }
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $$0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .L${opcode}_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    jmp         .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    lea         -8(%esp), %esp
+    movl        %edx, (%esp)            # push parameter method->clazz
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    lea         8(%esp), %esp
+    cmp         $$0, %eax               # check if resolved
+    jne         .L${opcode}_finish2
+    jmp         common_exceptionThrown  # not resolved; handle exception
+
+.L${opcode}_finish2:
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $$4, %ecx               # %ecx<- B
+    and         $$15, rINST             # rINST<- A
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $$0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    mov$mov     rINST, (%edx, %ecx)     # object field<- vA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_BOOLEAN.S b/vm/mterp/x86-atom/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..46c2932
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IPUT_BOOLEAN.S
+    */
+
+%include "x86-atom/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_BYTE.S b/vm/mterp/x86-atom/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..d23f492
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_BYTE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IPUT_BYTE.S
+    */
+
+%include "x86-atom/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_CHAR.S b/vm/mterp/x86-atom/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..d645fae
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_CHAR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IPUT_CHAR.S
+    */
+
+%include "x86-atom/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_OBJECT.S b/vm/mterp/x86-atom/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..302cf44
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_OBJECT.S
@@ -0,0 +1,81 @@
+   /* 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.
+    */
+
+    /*
+    * File: OP_IPUT.S
+    *
+    * Code: Generic 32-bit instance field "put" operation. Provides a
+    *       "mov" variable which determines the type of mov performed.
+    *       Currently, none of the iput's use this variable - may want
+    *       to change this, but seems ok for now.
+    *
+    * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+    *      iput-short
+    *
+    * Description: Perform the object instance field "get" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    movl        offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+    cmp         $$0, (%edx, %ecx, 4)    # check for null ptr; resolved InstField ptr
+    movl        (%edx, %ecx, 4), %eax   # %eax<- resolved InstField ptr
+    jne         .L${opcode}_finish2
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    jmp         .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    movl        offGlue_method(%edx), %edx # %edx<- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %ecx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    lea         -8(%esp), %esp
+    movl        %edx, (%esp)            # push parameter method->clazz
+    call        dvmResolveInstField     # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: InstField*
+    lea         8(%esp), %esp
+    cmp         $$0, %eax               # check if resolved
+    jne         .L${opcode}_finish2
+    jmp         common_exceptionThrown  # not resolved; handle exception
+
+.L${opcode}_finish2:
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $$4, %ecx               # %ecx<- B
+    and         $$15, rINST             # rINST<- A
+    GET_VREG    %ecx                    # %ecx<- vB
+    cmp         $$0, %ecx               # check for null object
+    je          common_errNullObject    # handle null object
+    movl        offInstField_byteOffset(%eax), %edx # %edx<- field offset
+    GET_VREG    rINST                   # rINST<- vA
+    movl        rINST, (%edx, %ecx)     # object field<- vA
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl     rGLUE, %eax             # get glue
+    movl        offGlue_cardTable(%eax), %eax # get card table base
+    testl       rINST, rINST            # test if we stored a null value
+    je          1f                     # skip card mark if null stored
+    shrl        $$GC_CARD_SHIFT, %ecx   # set obeject head to card number
+    movb        %al, (%eax, %ecx)
+1:
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/x86-atom/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..eee88e9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_OBJECT_QUICK.S
@@ -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.
+    */
+
+   /*
+    * File: OP_IPUT_QUICK.S
+    * Code: Optimization for iput
+    *
+    * For: iput-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $$4, %eax               # %eax<- B
+    and         $$15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB; object to operate on
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    cmp         $$0, %eax               # check if object is null
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl        rINST, (%eax, %ecx)     # object field<- vA
+    testl       rINST, rINST            # did we write a null object
+    je          1f
+    movl        rGLUE, %ecx             # get glue
+    movl        offGlue_cardTable(%ecx), %ecx # get card table base
+    shrl        $$GC_CARD_SHIFT, %eax   # get gc card index
+    movb        %cl, (%eax, %ecx)       # mark gc card in table
+1:
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/x86-atom/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..4b024d0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_IPUT_OBJECT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_QUICK.S b/vm/mterp/x86-atom/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..572291e
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_QUICK.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IPUT_QUICK.S
+    * Code: Optimization for iput
+    *
+    * For: iput-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $$4, %eax               # %eax<- B
+    and         $$15, rINST             # rINST<- A
+    GET_VREG    %eax                    # %eax<- vB; object to operate on
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    cmp         $$0, %eax               # check if object is null
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vA
+    movl        rINST, (%eax, %ecx)     # object field<- vA
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_SHORT.S b/vm/mterp/x86-atom/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..9836283
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_SHORT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_IPUT_SHORT.S
+    */
+
+%include "x86-atom/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_VOLATILE.S b/vm/mterp/x86-atom/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..475a0c5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_WIDE.S b/vm/mterp/x86-atom/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..1686219
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_WIDE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_IPUT_WIDE.S
+    *
+    * Code: 64 bit instance field "put" operation. Uses no substitutions.
+    *
+    * For: iget-wide
+    *
+    * Description: Perform the object instance field "put" operation
+    *              with the identified field; load the instance value into
+    *              the value register.
+    *
+    * Format:  B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rGLUE, %eax             # %eax<- MterpGlue pointer
+    movl        offGlue_methodClassDex(%eax), %ecx # %ecx<- pDvmDex
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- CCCC
+    FETCH       1, %edx                 # %edx<- pDvmDex->pResFields
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved InstField ptr
+    cmp         $$0, %ecx               # check for null ptr; resolved InstField ptr
+    jne         .L${opcode}_finish
+    movl        offGlue_method(%eax), %ecx # %ecx <- current method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        offMethod_clazz(%ecx), %ecx # %ecx<- method->clazz
+    movl        %ecx, -8(%esp)          # push parameter CCCC; field ref
+    movl        %edx, -4(%esp)          # push parameter method->clazz
+    jmp         .L${opcode}_finish2
+%break
+
+.L${opcode}_finish2:
+    lea         -8(%esp), %esp
+    call        dvmResolveInstField     # resolve InstField ptr
+    cmp         $$0, %eax               # check if resolved
+    lea         8(%esp), %esp
+    movl        %eax, %ecx              # %ecx<- %eax; %ecx expected to hold field
+    jne         .L${opcode}_finish
+    jmp         common_exceptionThrown
+
+   /*
+    * Currently:
+    *  %ecx holds resolved field
+    *  %edx does not hold object yet
+    */
+
+.L${opcode}_finish:
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB
+    cmp         $$0, %edx               # check for null object
+    je          common_errNullObject
+    movl        offInstField_byteOffset(%ecx), %ecx # %ecx<- field offset
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vA
+    movq        %xmm0, (%ecx, %edx)     # object field<- %xmm0; vA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_WIDE_QUICK.S b/vm/mterp/x86-atom/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..5880231
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_WIDE_QUICK.S
@@ -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.
+    */
+
+   /*
+    * File: OP_IPUT_WIDE_QUICK.S
+    *
+    * Code: Optimization for iput
+    *
+    * For: iput/wide-quick
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, offset@CCCC
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB; object to operate on
+    cmp         $$0, %edx               # check if object is null
+    FETCH       1, %ecx                 # %ecx<- CCCC; field byte offset
+    je          common_errNullObject    # handle null object
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- fp[A]
+    movq        %xmm0, (%edx, %ecx)     # object field<- %xmm0; fp[A]
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_LONG_TO_DOUBLE.S b/vm/mterp/x86-atom/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..5705e25
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_LONG_TO_DOUBLE.S
+    *
+    * Code: Convert a long to a dobule. Uses no substitutions.
+    *
+    * For: long-to-double
+    *
+    * Description: Converts a long in the source register to a double, and
+    *              stores the result in the destination register. vA<- (double) vB
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    fildll      (rFP, rINST, 4)         # FPU<- vB
+    fstpl       (rFP, %ecx, 4)          # vA<- FPU; (double) vB
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_LONG_TO_FLOAT.S b/vm/mterp/x86-atom/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..1bb8779
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_LONG_TO_FLOAT.S
+    *
+    * Code: Convert a long to a float. Uses no substitutions.
+    *
+    * For: int-to-float
+    *
+    * Description: Converts a float in the source register, to a float, and
+    *              stores the result in the destination register. vA<- (double) vB
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    fildll      (rFP, rINST, 4)         # FPU<- vB
+    fstps       (rFP, %ecx, 4)          # vA<- FPU; (float) vB
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_LONG_TO_INT.S b/vm/mterp/x86-atom/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..0984bc0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_LONG_TO_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_LONG_TO_INT.S
+    */
+
+%include "x86-atom/OP_MOVE.S"
diff --git a/vm/mterp/x86-atom/OP_MONITOR_ENTER.S b/vm/mterp/x86-atom/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..d3fada3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MONITOR_ENTER.S
@@ -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.
+    */
+
+   /*
+    * File: OP_MONITOR_ENTER.S
+    *
+    * Code: Aquire a monitor
+    *
+    * For: monitor-enter
+    *
+    * Description: Aquire a monitor for the indicated object.
+    *
+    *
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    GET_VREG    rINST                   # rINST<- vAA
+    cmp         $$0, rINST              # check for null object
+    movl        offGlue_self(%eax), %eax # %eax<- glue->self
+#ifdef WITH_MONITOR_TRACKING
+    EXPORT_PC   # export PC so we can grab stack trace
+#endif
+    je          common_errNullObject    # handle null object
+#    jmp         .L${opcode}_finish
+#%break
+#.L${opcode}_finish:
+    movl        rINST, -4(%esp)         # push parameter reference
+    movl        %eax, -8(%esp)          # push parameter
+    lea         -8(%esp), %esp
+    call        dvmLockObject           # call: (struct Thread* self,
+                                        #       struct Object* obj)
+                                        # return: void
+    FFETCH_ADV  1, %edx                 # %edx<- next instruction hi; fetch, advance
+    lea         8(%esp), %esp
+#ifdef WITH_DEADLOCK_PREDICTION
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_self(%eax), %eax # %eax<- glue->self
+    movl        offThread_exception(%eax), %eax # %eax<- glue->self->exception
+    cmp         $$0, %eax               # check for exception
+    jne         common_exceptionThrown  # handle exception
+#endif
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MONITOR_EXIT.S b/vm/mterp/x86-atom/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..37738d5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MONITOR_EXIT.S
@@ -0,0 +1,46 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MONITOR_EXIT.S
+    *
+    * Code: Release a monitor
+    *
+    * For: monitor-exit
+    *
+    * Description: Release a monitor for the indicated object. If this instruction needs
+    *              to throw an execption, it must do so as if the pc has already
+    *              advanced pased the instruction.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    EXPORT_PC                           # export the pc
+    GET_VREG    rINST                   # rINST<- vAA
+    cmp         $$0, rINST              # rINST<- check for null object
+    je          common_errNullObject    # handle null object
+    push        rINST                   # push parameter object
+    push        offGlue_self(%eax)      # push parameter self
+    call        dvmUnlockObject         # call: (struct Thread* self,
+                                        #       struct Object* obj)
+                                        # return: bool
+    FINISH_FETCH_ADVANCE 1, %edx        # advance pc before exception
+    cmp         $$0, %eax               # check for success
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # handle exception
+    FINISH_JMP  %edx                    # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_MOVE.S b/vm/mterp/x86-atom/OP_MOVE.S
new file mode 100644
index 0000000..9982ced
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_MOVE.S
+    *
+    * Code: Copies contents from one register to another. Uses no
+    *       substitutions.
+    *
+    * For: move, move-object, long-to-int
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              vA<- vB; fp[A]<- fp[B]
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vB
+    SET_VREG    rINST, %ecx             # vA<- vB; %edx
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_16.S b/vm/mterp/x86-atom/OP_MOVE_16.S
new file mode 100644
index 0000000..013a11b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_16.S
@@ -0,0 +1,36 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_16.S
+    *
+    * Code: Copies contents from one register to another
+    *
+    * For: move/16, move-object/16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              fp[A]<- fp[B]
+    *
+    * Format: ØØ|op AAAA BBBB (32x)
+    *
+    * Syntax: op vAAAA, vBBBB
+    */
+
+    FETCH       2, %edx                 # %edx<- BBBB
+    FETCH       1, %ecx                 # %ecx<- AAAA
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    SET_VREG    %edx, %ecx              # vA<- vB; %edx
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_EXCEPTION.S b/vm/mterp/x86-atom/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..76e700d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_EXCEPTION.S
@@ -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.
+    */
+
+   /*
+    * File: OP_MOVE_EXCEPTION.S
+    *
+    * Code: Moves an exception to a register
+    *
+    * For: move-exception
+    *
+    * Description: Save a just-caught exception into the given register. This
+    *              instruction is only valid as the first instruction of an
+    *              exception handler.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_self(%eax), %ecx # %ecx<- glue->self
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    movl        offThread_exception(%ecx), %edx # %edx<- glue->self->exception
+    movl        $$0, offThread_exception(%ecx) # clear exception
+    SET_VREG    %edx, rINST             # vAA<- glue->self->exception
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_FROM16.S b/vm/mterp/x86-atom/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..55a6e96
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_FROM16.S
@@ -0,0 +1,35 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_FROM16.S
+    *
+    * Code: Copies contents from one register to another
+    *
+    * For: move/from16, move-object/from16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *              vA<- vB; fp[A]<- fp[B]
+    *
+    * Format: AA|op BBBB (22x)
+    *
+    * Syntax: op vAA, vBBBB
+    */
+
+    FETCH       1, %edx                 # %edx<- BBBB
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    SET_VREG    %edx, rINST             # vA<- vB; %edx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_OBJECT.S b/vm/mterp/x86-atom/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..1cd22cb
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_OBJECT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_OBJECT.S
+    */
+
+%include "x86-atom/OP_MOVE.S"
diff --git a/vm/mterp/x86-atom/OP_MOVE_OBJECT_16.S b/vm/mterp/x86-atom/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..a61162c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_OBJECT_16.S
+    */
+
+%include "x86-atom/OP_MOVE_16.S"
diff --git a/vm/mterp/x86-atom/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/x86-atom/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..bfca7da
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_OBJECT_FROM16.S
+    */
+
+%include "x86-atom/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/x86-atom/OP_MOVE_RESULT.S b/vm/mterp/x86-atom/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..1d13bf5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_RESULT.S
@@ -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.
+    */
+
+   /*
+    * File: OP_MOVE_RESULT.S
+    *
+    * Code: Copies a return value to a register
+    *
+    * For: move-result, move-result-object
+    *
+    * Description: Move the single-word non-object result of the most
+    *              recent method invocation into the indicated register. This
+    *              must be done as the instruction immediately after a
+    *              method invocation whose (single-word, non-object) result
+    *              is not to be ignored; anywhere else is invalid.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    FFETCH_ADV  1, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    movl        offGlue_retval(%eax), %edx # %edx<- glue->retval
+    SET_VREG    %edx, rINST             # vA<- glue->retval
+    FGETOP_JMP  1, %ecx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/x86-atom/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..6d1fa75
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_RESULT_OBJECT.S
+    */
+
+%include "x86-atom/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/x86-atom/OP_MOVE_RESULT_WIDE.S b/vm/mterp/x86-atom/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..8f15264
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_RESULT_WIDE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_MOVE_RESULT_WIDE.S
+    *
+    * Code: Copies a return value to a register
+    *
+    * For: move-result-wide
+    *
+    * Description: Move the double-word non-object result of the most
+    *              recent method invocation into the indicated register. This
+    *              must be done as the instruction immediately after a
+    *              method invocation whose (single-word, non-object) result
+    *              is not to be ignored; anywhere else is invalid.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movq        offGlue_retval(%eax), %xmm0 # %xmm0<- glue->retval
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- glue->retval
+    FFETCH_ADV  1, %edx                 # %edx<- next instruction hi; fetch, advance
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_WIDE.S b/vm/mterp/x86-atom/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..909243b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_WIDE.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_WIDE.S
+    *
+    * Code: Copies contents from one register to another. Uses no
+    *       substitutions.
+    *
+    * For: move-wide
+    *
+    * Description: Copies contents from one non-object register to another.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA+
+    shr         $$4, %edx               # %edx<- B
+    and         $$15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vB
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- vB
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_WIDE_16.S b/vm/mterp/x86-atom/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..af266fe
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_WIDE_16.S
@@ -0,0 +1,36 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_WIDE_16.S
+    *
+    * Code: Copies contents from one register to another. Uses no
+    *       substitutions.
+    *
+    * For: move-wide/16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *
+    * Format: ØØ|op AAAA BBBB (32x)
+    *
+    * Syntax: op vAAAA, vBBBB
+    */
+
+    FETCH       2, %edx                 # %edx<- BBBB
+    FETCH       1, %ecx                 # %ecx<- AAAA
+    FFETCH_ADV  3, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vB
+    movq        %xmm0, (rFP, %ecx, 4)   # vA<- vB; %xmm0
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_WIDE_FROM16.S b/vm/mterp/x86-atom/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..4056b34
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,34 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MOVE_WIDE_FROM16.S
+    *
+    * Code: Copies contents from one register to another
+    *
+    * For: move-wide/from16
+    *
+    * Description: Copies contents from one non-object register to another.
+    *
+    * Format: AA|op BBBB (22x)
+    *
+    * Syntax: op vAA, vBBBB
+    */
+
+    FETCH       1, %edx                 # %edx<- BBBB
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vB
+    movq        %xmm0, (rFP, rINST, 4)  # vA<- vB
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MUL_DOUBLE.S b/vm/mterp/x86-atom/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..cecbf05
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_DOUBLE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MUL_DOUBLE.S
+    */
+
+%include "x86-atom/binopWide.S" {"instr":"mulsd   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..adc61d6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MUL_DOUBLE_2ADDR.S
+    */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"mulsd   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_MUL_FLOAT.S b/vm/mterp/x86-atom/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..34eba58
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_FLOAT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MUL_FLOAT.S
+    */
+
+%include "x86-atom/binopF.S" {"instr":"mulss %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..dbd615d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MUL_FLOAT_2ADDR.S
+    */
+
+%include "x86-atom/binopF2addr.S" {"instr":"mulss %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_MUL_INT.S b/vm/mterp/x86-atom/OP_MUL_INT.S
new file mode 100644
index 0000000..8f5dac5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MUL_INT.S
+    */
+
+%include "x86-atom/binop.S" {"instr":"imul     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_MUL_INT_2ADDR.S b/vm/mterp/x86-atom/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..b544df7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MUL_INT_2ADDR.S
+    */
+
+%include "x86-atom/binop2addr.S" {"instr":"imul     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_MUL_INT_LIT16.S b/vm/mterp/x86-atom/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..241531f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_INT_LIT16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MUL_INT_LIT16.S
+    */
+
+%include "x86-atom/binopLit16.S" {"instr":"imul     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_MUL_INT_LIT8.S b/vm/mterp/x86-atom/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..efcffe1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_MUL_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8.S" {"instr":"imul     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_MUL_LONG.S b/vm/mterp/x86-atom/OP_MUL_LONG.S
new file mode 100644
index 0000000..85cccf2
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_LONG.S
@@ -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.
+    */
+
+   /*
+    * File: OP_MUL_LONG.S
+    *
+    * Code: 64-bit integer multiply
+    *
+    * For: mul-long
+    *
+    * Description: Multiply two source registers and store the
+    *              result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+   /*
+    * 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.
+    */
+
+    movl        rINST, -4(%esp)         # -4(%esp)<- AA+
+    FETCH_BB    1, rINST                # rINST<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    jmp         .L${opcode}_finish
+%break
+
+   /*
+    * X = (rFP, rINST, 4)
+    * W = 4(rFP, rINST, 4)
+    * Z = (rFP, %edx, 4)
+    * Y = 4(rFP, %edx, 4)
+    */
+
+.L${opcode}_finish:
+    movl        4(rFP, rINST, 4), %ecx  # %ecx<- W
+    imull       (rFP, %edx, 4),  %ecx   # %ecx<- WxZ
+    mov         4(rFP, %edx, 4), %eax   # %ecx<- Y
+    imull       (rFP, rINST, 4), %eax   # %eax<- XxY
+    addl        %eax, %ecx              # %ecx<- (WZ + XY)
+    movl        (rFP, %edx, 4), %eax    # %eax<- Z
+    mull        (rFP, rINST, 4)         # %edx:eax<- XZ
+    movzbl      -4(%esp), rINST         # rINST<- AA
+    addl        %edx, %ecx              # %ecx<- carry + (WZ + XY)
+    movl        %ecx, 4(rFP, rINST, 4)  # vAA+1<- results hi
+    movl        %eax, (rFP, rINST, 4)   # vAA<- results lo
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_MUL_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..d6b8c16
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_LONG_2ADDR.S
@@ -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.
+    */
+
+   /*
+    * File: OP_MUL_LONG_2ADDR.S
+    *
+    * Code:  64-bit integer multiply
+    *
+    * For: mul-long/2addr
+    *
+    * Description: Multiply two sources registers and store the result
+    *              in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+   /*
+    * 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.
+    */
+
+    movl        rINST, %edx             # %edx<- BA+
+    shr         $$4, rINST              # rINST<- B
+    andl        $$15, %edx              # %edx<- A
+    movl        %edx, sReg0             # sReg0<- A
+    jmp         .L${opcode}_finish
+%break
+
+   /*
+    * X = (rFP, rINST, 4)
+    * W = 4(rFP, rINST, 4)
+    * Z = (rFP, %edx, 4)
+    * Y = 4(rFP, %edx, 4)
+    */
+
+.L${opcode}_finish:
+    movl        4(rFP, rINST, 4), %ecx  # %ecx<- W
+    imull       (rFP, %edx, 4), %ecx    # %ecx<- WxZ
+    movl                4(rFP, %edx, 4), %eax   # %eax<- Y
+    imull       (rFP, rINST, 4), %eax   # %eax<- X*Y
+    addl        %eax, %ecx              # %ecx<- (WZ + XY)
+    movl        (rFP, %edx, 4), %eax    # %eax<- Z
+    mull        (rFP, rINST, 4)         # %edx:eax<- XZ
+    addl        %edx, %ecx              # %ecx<- carry + (WZ + XY)
+    movl        sReg0, %edx             # %edx<- A
+    movl        %ecx, 4(rFP, %edx, 4)   # vA+1<- results hi
+    movl        %eax, (rFP, %edx, 4)    # vA<- results lo
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_NEG_DOUBLE.S b/vm/mterp/x86-atom/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..b6fb070
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEG_DOUBLE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_NEG_DOUBLE.S
+    */
+
+%include "x86-atom/unopWide.S" { "preinstr":"movq .LdoubNeg, %xmm1", "instr":"pxor %xmm1, %xmm0" }
diff --git a/vm/mterp/x86-atom/OP_NEG_FLOAT.S b/vm/mterp/x86-atom/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..418fc0a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEG_FLOAT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_NEG_FLOAT.S
+    */
+
+%include "x86-atom/unop.S" { "instr":"addl      $0x80000000, %ecx" }
diff --git a/vm/mterp/x86-atom/OP_NEG_INT.S b/vm/mterp/x86-atom/OP_NEG_INT.S
new file mode 100644
index 0000000..68acb89
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEG_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_NEG_INT.S
+    */
+
+%include "x86-atom/unop.S" {"instr":"neg        %ecx"}
diff --git a/vm/mterp/x86-atom/OP_NEG_LONG.S b/vm/mterp/x86-atom/OP_NEG_LONG.S
new file mode 100644
index 0000000..3f500bb
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEG_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_NEG_LONG.S
+    */
+
+%include "x86-atom/unopWide.S" {"preinstr":"xorps %xmm1, %xmm1", "instr":"psubq %xmm0, %xmm1", "result":"%xmm1"}
diff --git a/vm/mterp/x86-atom/OP_NEW_ARRAY.S b/vm/mterp/x86-atom/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..a6d5fd3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEW_ARRAY.S
@@ -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.
+    */
+
+   /*
+    * File: OP_NEW_ARRAY.S
+    *
+    * Code: Create a new array. Uses no substitutions.
+    *
+    * For: new-array
+    *
+    * Description: Construct a new array of the indicated type and size.
+    *              The type must be an array type.
+    *
+    * Format: B|A|op CCCC (22c)
+    *
+    * Syntax: op vA, vB, type@CCCC
+    *         op vA, vB, field@CCCC
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    movl        offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+    FETCH       1, %ecx                 # %ecx<- CCCC
+    GET_VREG    %edx                    # %edx<- vB
+    movl        offDvmDex_pResClasses(%eax), %eax # %eax<- glue->pDvmDex->pResClasses
+    cmp         $$0, %edx               # check for negative length
+    movl        (%eax, %ecx, 4), %eax   # %eax<- resolved class
+    js          common_errNegativeArraySize # handle negative array length
+    cmp         $$0, %eax               # check for null
+    EXPORT_PC                           # required for resolve
+    jne         .L${opcode}_finish      # already resovled so continue
+    jmp         .L${opcode}_resolve     # need to resolve
+%break
+
+   /*
+    * Resolve class.  (This is an uncommon case.)
+    *
+    *  %edx holds array length
+    *  %ecx holds class ref CCCC
+    */
+
+.L${opcode}_resolve:
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_method(%eax), %eax # %eax<- glue->method
+    movl        %edx, -4(%esp)          # save length
+    movl        $$0, -8(%esp)           # push parameter false
+    movl        %ecx, -12(%esp)         # push parameter class ref
+    movl        offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+    movl        %eax, -16(%esp)         # push parameter clazz
+    lea         -16(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer,
+                                        #       u4 classIdx, bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    cmp         $$0, %eax               # check for failure
+    lea         16(%esp), %esp
+    je          common_exceptionThrown  # handle exception
+    movl        -4(%esp), %edx          # %edx<- length
+
+   /*
+    * Finish allocation.
+    *
+    *  %eax holds class
+    *  %edx holds array length
+    */
+
+.L${opcode}_finish:
+    movl        %eax, -12(%esp)         # push parameter class
+    movl        %edx, -8(%esp)          # push parameter length
+    movl        $$ALLOC_DONT_TRACK, -4(%esp)
+    lea         -12(%esp), %esp
+    call        dvmAllocArrayByClass    # call: (ClassObject* arrayClass,
+                                        # size_t length, int allocFlags)
+                                        # return: ArrayObject*
+    and         $$15, rINST             # rINST<- A
+    cmp         $$0, %eax               # check for allocation failure
+    lea         12(%esp), %esp
+    je          common_exceptionThrown  # handle exception
+    SET_VREG    %eax, rINST             # vA<- pArray
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_NEW_INSTANCE.S b/vm/mterp/x86-atom/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..d65afb7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEW_INSTANCE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_NEW_INSTANCE.S
+    *
+    * Code: Create a new instance of a given type. Uses no substitutions.
+    *
+    * For: new-instance
+    *
+    * Description: Construct a new instance of the indicated type,
+    *              storing a reference to it in the destination.
+    *              The type must refer to a non-array class.
+    *
+    *
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, type@BBBB
+    *         op vAA, field@BBBB
+    *         op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_methodClassDex(%ecx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %edx                 # %edx<- BBBB
+    movl        offDvmDex_pResClasses(%ecx), %ecx # %ecx<- glue->pDvmDex->pResClasses
+    movl        (%ecx, %edx, 4), %edx   # %edx<- vB
+    EXPORT_PC                           # required for resolve
+    cmp         $$0, %edx               # check for null
+    je          .L${opcode}_resolve     # need to resolve
+
+   /*
+    *  %edx holds class object
+    */
+
+.L${opcode}_resolved:
+    movzbl      offClassObject_status(%edx), %eax # %eax<- class status
+    cmp         $$CLASS_INITIALIZED, %eax # check if class is initialized
+    jne         .L${opcode}_needinit    # initialize class
+
+   /*
+    *  %edx holds class object
+    */
+
+.L${opcode}_initialized:
+    testl       $$(ACC_INTERFACE|ACC_ABSTRACT), offClassObject_accessFlags(%edx)
+    mov         $$ALLOC_DONT_TRACK, %eax # %eax<- flag for alloc call
+    je          .L${opcode}_finish      # continue
+    jmp         .L${opcode}_abstract    # handle abstract or interface
+
+   /*
+    *  %edx holds class object
+    *  %eax holds flags for alloc call
+    */
+
+%break
+.balign 32
+.L${opcode}_finish:
+    movl        %edx, -8(%esp)          # push parameter object
+    movl        %eax, -4(%esp)          # push parameter flags
+    lea         -8(%esp), %esp
+    call        dvmAllocObject          # call: (ClassObject* clazz, int flags)
+                                        # return: Object*
+    cmp         $$0, %eax               # check for failure
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # handle exception
+    SET_VREG    %eax, rINST             # vAA<- pObject
+    FINISH      2                       # jump to next instruction
+
+   /*
+    * Class initialization required.
+    *
+    *  %edx holds class object
+    */
+
+.L${opcode}_needinit:
+    movl        %edx, -4(%esp)          # push parameter object
+    lea         -4(%esp), %esp
+    call        dvmInitClass            # call: (ClassObject* clazz)
+                                        # return: bool
+    lea         4(%esp), %esp
+    cmp         $$0, %eax               # check for failure
+    movl        -4(%esp), %edx          # %edx<- object
+    je          common_exceptionThrown  # handle exception
+    testl       $$(ACC_INTERFACE|ACC_ABSTRACT), offClassObject_accessFlags(%edx)
+    mov         $$ALLOC_DONT_TRACK, %eax # %eax<- flag for alloc call
+    je          .L${opcode}_finish      # continue
+    jmp         .L${opcode}_abstract    # handle abstract or interface
+
+   /*
+    * Resolution required.  This is the least-likely path.
+    *
+    *  BBBB in %eax
+    */
+
+.L${opcode}_resolve:
+
+
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offGlue_method(%ecx), %ecx # %ecx<- glue->method
+    movl        offMethod_clazz(%ecx), %ecx # %ecx<- glue->method->clazz
+    movl        %ecx, -12(%esp)         # push parameter clazz
+    movl        $$0, -4(%esp)           # push parameter false
+    movl        %eax, -8(%esp)          # push parameter BBBB
+    lea         -12(%esp), %esp
+    call        dvmResolveClass         # call: (const ClassObject* referrer,
+                                        #       u4 classIdx, bool fromUnverifiedConstant)
+                                        # return: ClassObject*
+    lea         12(%esp), %esp
+    movl        %eax, %edx              # %edx<- pObject
+    cmp         $$0, %edx               # check for failure
+    jne         .L${opcode}_resolved    # continue
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * We can't instantiate an abstract class or interface, so throw an
+    * InstantiationError with the class descriptor as the message.
+    *
+    *  %edx holds class object
+    */
+
+.L${opcode}_abstract:
+    movl        offClassObject_descriptor(%edx), %ecx # %ecx<- descriptor
+    movl        %ecx, -4(%esp)          # push parameter descriptor
+    movl        $$.LstrInstantiationErrorPtr, -8(%esp) # push parameter message
+    lea         -8(%esp), %esp
+    call        dvmThrowExceptionWithClassMessage # call: (const char* exceptionDescriptor,
+                                                  #        const char* messageDescriptor)
+                                                  # return: void
+    jmp         common_exceptionThrown  # handle exception
+
+.LstrInstantiationErrorPtr:
+.asciz      "Ljava/lang/InstantiationError;"
diff --git a/vm/mterp/x86-atom/OP_NOP.S b/vm/mterp/x86-atom/OP_NOP.S
new file mode 100644
index 0000000..9911da3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NOP.S
@@ -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.
+    */
+
+   /*
+    * File: OP_NOP.S
+    *
+    * Code: Use a cycle. Uses no substitutions.
+    *
+    * For: nop
+    *
+    * Description: No operation. Use a cycle
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    FINISH      1                       # jump to next instruction
+
+#ifdef ASSIST_DEBUGGER
+
+   /*
+    * insert fake function header to help gdb find the stack frame
+    */
+
+    .type       dalvik_inst, %function
+dalvik_inst:
+    MTERP_ENTRY
+#endif
diff --git a/vm/mterp/x86-atom/OP_NOT_INT.S b/vm/mterp/x86-atom/OP_NOT_INT.S
new file mode 100644
index 0000000..b82e5b6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NOT_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_NOT_INT.S
+    */
+
+%include "x86-atom/unop.S" {"instr":"not        %ecx"}
diff --git a/vm/mterp/x86-atom/OP_NOT_LONG.S b/vm/mterp/x86-atom/OP_NOT_LONG.S
new file mode 100644
index 0000000..98ff80b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NOT_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_NOT_LONG.S
+    */
+
+%include "x86-atom/unopWide.S" {"instr":"pandn  0xFFFFFFFF, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_OR_INT.S b/vm/mterp/x86-atom/OP_OR_INT.S
new file mode 100644
index 0000000..0ece38c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_OR_INT.S
+    */
+
+%include "x86-atom/binop.S" {"instr":"or %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_OR_INT_2ADDR.S b/vm/mterp/x86-atom/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..693e099
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_OR_INT_2ADDR.S
+    */
+
+%include "x86-atom/binop2addr.S" {"instr":"or %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_OR_INT_LIT16.S b/vm/mterp/x86-atom/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..5c63867
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_INT_LIT16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_OR_INT_LIT16.S
+    */
+
+%include "x86-atom/binopLit16.S" {"instr":"or %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_OR_INT_LIT8.S b/vm/mterp/x86-atom/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..aacd6c3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_OR_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8.S" {"instr":"or %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_OR_LONG.S b/vm/mterp/x86-atom/OP_OR_LONG.S
new file mode 100644
index 0000000..f698e54
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_OR_LONG.S
+    */
+
+%include "x86-atom/binopWide.S" {"instr":"por %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_OR_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..12a88ec
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_OR_LONG_2ADDR.S
+    */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"por %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_PACKED_SWITCH.S b/vm/mterp/x86-atom/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..debac02
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_PACKED_SWITCH.S
@@ -0,0 +1,52 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_PACKED_SWITCH.S
+    *
+    * Code: Jump to a new instruction using a jump table
+    *
+    * For: packed-switch, sparse-switch
+    *
+    * Description: Jump to a new instruction based on the value in the given
+    *              register, using a table of offsets corresponding to each
+    *              value in a particular integral range, or fall through to
+    *              the next instruction if there is no match.
+    *
+    * Format: AA|op BBBBlo BBBBhi (31t)
+    *
+    * Syntax: op vAA, +BBBBBBBB
+    */
+
+%default { "func":"dvmInterpHandlePackedSwitch" }
+
+    FETCH       1, %ecx                 # %ecx<- BBBBlo
+    FETCH       2, %edx                 # %edx<- BBBBhi
+    shl         $$16, %edx              # prepare to create +BBBBBBBB
+    or          %edx, %ecx              # %ecx<- +BBBBBBBB
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, -4(%esp)         # push parameter vAA
+    lea         (rPC, %ecx, 2), %ecx    # %ecx<- PC + +BBBBBBBB*2
+    movl        %ecx, -8(%esp)          # push parameter PC + +BBBBBBBB*2
+    lea         -8(%esp), %esp
+    call        $func                   # call code-unit branch offset
+    shl         $$1, %eax               # shift for byte offset
+    movl        %eax, %edx              # %edx<- offset
+    lea         8(%esp), %esp
+    jle         common_periodicChecks_backwardBranch  # do backward branch
+    jmp         .L${opcode}_finish
+%break
+.L${opcode}_finish:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_DOUBLE.S b/vm/mterp/x86-atom/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..aa7d332
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_DOUBLE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_REM_DOUBLE.S
+    *
+    * Code: Computes the remainder of a division. Performs no substitutions.
+    *
+    * For: rem-double
+    *
+    * Description: Calls fmod to compute the remainder of the result of dividing a
+    *              source register by a second, and stores the result in a
+    *              destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    movl        (rFP, %ecx, 4), %eax    # %eax<- vBBlo
+    movl        %eax, -16(%esp)         # push parameter double lo
+    movl        4(rFP, %ecx, 4), %eax   # %eax<- vBBhi
+    movl        %eax, -12(%esp)         # push parameter double hi
+    movl        (rFP, %edx, 4), %eax    # %eax<- vCClo
+    movl        %eax, -8(%esp)          # push parameter double lo
+    movl        4(rFP, %edx, 4), %eax   # %eax<- vCChi
+    movl        %eax, -4(%esp)          # push parameter double hi
+    lea         -16(%esp), %esp
+    jmp         .L${opcode}_break
+%break
+
+.L${opcode}_break:
+    call        fmod                    # call: (long double x, long double y)
+                                        # return: double
+    lea         16(%esp), %esp
+    fstpl       (rFP, rINST, 4)         # vAA<- remainder; return of fmod
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..434c878
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,52 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_REM_DOUBLE_2ADDR.S
+    *
+    * Code: Computes the remainder of a division. Performs no substitutions.
+    *
+    * For: rem-double/2addr
+    *
+    * Description: Calls fmod to compute the remainder of the result of dividing a
+    *              source register by a second, and stores the result in the first
+    *              source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    and         $$15, rINST             # rINST<- A
+    shr         $$4, %edx               # %edx<- B
+    movl        (rFP, rINST, 4), %eax   # %eax<- vAlo
+    movl        %eax, -20(%esp)         # push parameter vAAlo
+    movl        4(rFP, rINST, 4), %eax  # %eax<- vAhi
+    movl        %eax, -16(%esp)         # push parameter vAAhi
+    movl        (rFP, %edx, 4), %eax    # %eax<- vBlo
+    movl        %eax, -12(%esp)         # push parameter vBBlo
+    movl        4(rFP, %edx, 4), %eax   # %eax<- vBhi
+    movl        %eax, -8(%esp)          # push parameter vBBhi
+    lea         -20(%esp), %esp
+    jmp         .L${opcode}_break
+%break
+
+.L${opcode}_break:
+    call        fmod                    # call: (long double x, long double y)
+                                        # return: double
+    lea         20(%esp), %esp
+    fstpl       (rFP, rINST, 4)         # vAA<- remainder; return of fmod
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_FLOAT.S b/vm/mterp/x86-atom/OP_REM_FLOAT.S
new file mode 100644
index 0000000..de5e161
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_FLOAT.S
@@ -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.
+    */
+
+   /*
+    * File: OP_REM_FLOAT.S
+    *
+    * Code: Computes the remainder of a division. Performs no substitutions.
+    *
+    * For: rem-float
+    *
+    * Description: Calls fmod to compute the remainder of the result of dividing a
+    *              source register by a second, and stores the result in a
+    *              destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    movl        %ecx, -8(%esp)          # push parameter float
+    movl        %edx, -4(%esp)          # push parameter float
+    lea         -8(%esp), %esp
+    call        fmodf                   # call: (float x, float y)
+                                        # return: float
+    lea         8(%esp), %esp
+    fstps       (rFP, rINST, 4)         # vAA<- remainder; return of fmod
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..5ff5af5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_FLOAT_2ADDR.S
@@ -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.
+    */
+
+   /*
+    * File: OP_REM_FLOAT_2ADDR.S
+    *
+    * Code: Computes the remainder of a division. Performs no substitutions.
+    *
+    * For: rem-float/2addr
+    *
+    * Description: Calls fmod to compute the remainder of the result of dividing a
+    *              source register by a second, and stores the result in the first
+    *              source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- A
+    GET_VREG    %edx                    # %edx<- vB
+    movl        (rFP, rINST, 4), %ecx   # %ecx<- vA
+    movl        %ecx, -8(%esp)          # push parameter vA
+    movl        %edx, -4(%esp)          # push parameter vB
+    lea         -8(%esp), %esp
+    call        fmodf                   # call: (float x, float y)
+                                        # return: float
+    lea         8(%esp), %esp
+    fstps       (rFP, rINST, 4)
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_INT.S b/vm/mterp/x86-atom/OP_REM_INT.S
new file mode 100644
index 0000000..5f62d66
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_REM_INT.S
+    */
+
+%include "x86-atom/binopD.S" {"div":"0"}
diff --git a/vm/mterp/x86-atom/OP_REM_INT_2ADDR.S b/vm/mterp/x86-atom/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..369ea5c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_REM_INT_2ADDR.S
+    */
+
+%include "x86-atom/binopD2addr.S" {"div":"0"}
diff --git a/vm/mterp/x86-atom/OP_REM_INT_LIT16.S b/vm/mterp/x86-atom/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..0c9afa3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_INT_LIT16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_REM_INT_LIT16.S
+    */
+
+%include "x86-atom/binopDLit16.S" {"div":"0"}
diff --git a/vm/mterp/x86-atom/OP_REM_INT_LIT8.S b/vm/mterp/x86-atom/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..6578c7c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_REM_INT_LIT8.S
+    */
+
+%include "x86-atom/binopDLit8.S" {"div":"0"}
diff --git a/vm/mterp/x86-atom/OP_REM_LONG.S b/vm/mterp/x86-atom/OP_REM_LONG.S
new file mode 100644
index 0000000..3e3b200
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_REM_LONG.S
+    */
+
+%include "x86-atom/binopDivRemLong.S" {"func":"__moddi3"}
diff --git a/vm/mterp/x86-atom/OP_REM_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..f494caf
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_REM_LONG_2ADDR.S
+    */
+
+%include "x86-atom/binopDivRemLong2Addr.S" {"func":"__moddi3"}
diff --git a/vm/mterp/x86-atom/OP_RETURN.S b/vm/mterp/x86-atom/OP_RETURN.S
new file mode 100644
index 0000000..48d7e34
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_RETURN.S
+    */
+
+%include "x86-atom/OP_RETURN_COMMON.S"
diff --git a/vm/mterp/x86-atom/OP_RETURN_COMMON.S b/vm/mterp/x86-atom/OP_RETURN_COMMON.S
new file mode 100644
index 0000000..d58a16c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN_COMMON.S
@@ -0,0 +1,34 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_RETURN_COMMON.S
+    *
+    * Code: Return a 32-bit value. Uses no substitutions.
+    *
+    * For: return, return-object
+    *
+    * Description: Copies the return value into the "glue"
+    *              structure, then jumps to the return handler.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offGlue_retval(%edx) # glue->retval<- vAA
+    jmp         common_returnFromMethod # jump to common return code
diff --git a/vm/mterp/x86-atom/OP_RETURN_OBJECT.S b/vm/mterp/x86-atom/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..3b9c10c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN_OBJECT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_RETURN_OBJECT.S
+    */
+
+%include "x86-atom/OP_RETURN_COMMON.S"
diff --git a/vm/mterp/x86-atom/OP_RETURN_VOID.S b/vm/mterp/x86-atom/OP_RETURN_VOID.S
new file mode 100644
index 0000000..4d8c92b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN_VOID.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_RETURN_VOID.S
+    */
+
+    jmp         common_returnFromMethod
diff --git a/vm/mterp/x86-atom/OP_RETURN_WIDE.S b/vm/mterp/x86-atom/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..8069e85
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN_WIDE.S
@@ -0,0 +1,34 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_RETURN_WIDE.S
+    *
+    * Code: Return a 64-bit value. Uses no substitutions.
+    *
+    * For: return-wide
+    *
+    * Description: Copies the return value into the "glue"
+    *              structure, then jumps to the return handler.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vAA
+    movq        %xmm0, offGlue_retval(%edx)# glue->retval<- vAA
+    jmp         common_returnFromMethod # jump to common return code
diff --git a/vm/mterp/x86-atom/OP_RSUB_INT.S b/vm/mterp/x86-atom/OP_RSUB_INT.S
new file mode 100644
index 0000000..87498f9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RSUB_INT.S
@@ -0,0 +1,39 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_RSUB_INT.S
+    *
+    * Code: 32-bit reverse-subtraction. Uses no substitutions.
+    *
+    * For: rsub-int
+    *
+    * Description: Perform a reverse subtraction on a register and a
+    *              signed extended 16-bit literal value and store the
+    *              result in a destination register.
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $$4, %ecx               # %ecx<- B
+    andl        $$15, rINST             # rINST<- A
+    FETCHs      1, %edx                 # %edx<- +CCCC, sign-extended literal
+    GET_VREG    %ecx                    # %ecx<- vB
+    subl        %ecx, %edx              # %edx<- +CCCC sub vB
+    SET_VREG    %edx, rINST             # vA<- %edx; result
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_RSUB_INT_LIT8.S b/vm/mterp/x86-atom/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..d6114dd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,36 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_RSUB_INT_LIT8.S
+    *
+    * Code: 32-bit reverse-subtraction. Uses no substitutions.
+    *
+    * For: rsub-int/lit8
+    *
+    * Description: Perform a reverse subtraction on a register and a
+    *              signed extended 8-bit literal value.
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CCs   1, %edx                 # %edx<- +CC, sign-extended literal
+    GET_VREG    %ecx                    # %ecx<- vBB
+    sub         %ecx, %edx              # %edx<- +CC sub vBB
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SGET.S b/vm/mterp/x86-atom/OP_SGET.S
new file mode 100644
index 0000000..914b4dc
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET.S
@@ -0,0 +1,60 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SGET.S
+    *
+    * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; load the field value
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $$0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .L${opcode}_resolve
+    jmp         .L${opcode}_finish
+%break
+
+.L${opcode}_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $$0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    mov         %eax, %ecx              # %ecx<- result
+
+.L${opcode}_finish:
+    FFETCH_ADV  2, %edx                 # %edx<- next instruction hi; fetch, advance
+    movl offStaticField_value(%ecx), %eax # %eax<- field value
+    SET_VREG    %eax, rINST             # vAA<- field value
+    FGETOP_JMP  2, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_SGET_BOOLEAN.S b/vm/mterp/x86-atom/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..8e383b4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_BOOLEAN.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SGET_BOOLEAN.S
+    */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_BYTE.S b/vm/mterp/x86-atom/OP_SGET_BYTE.S
new file mode 100644
index 0000000..c86fc20
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_BYTE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SGET_BYTE.S
+    */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_CHAR.S b/vm/mterp/x86-atom/OP_SGET_CHAR.S
new file mode 100644
index 0000000..0a3cffd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_CHAR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SGET_CHAR.S
+    */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_OBJECT.S b/vm/mterp/x86-atom/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..5145f14
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_OBJECT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SGET_OBJECT.S
+    */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/x86-atom/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..5f64fb5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_SHORT.S b/vm/mterp/x86-atom/OP_SGET_SHORT.S
new file mode 100644
index 0000000..77064b6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_SHORT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SGET_SHORT.S
+    */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_VOLATILE.S b/vm/mterp/x86-atom/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..5f64fb5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_WIDE.S b/vm/mterp/x86-atom/OP_SGET_WIDE.S
new file mode 100644
index 0000000..3ef6916
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_WIDE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_SGET_WIDE.S
+    *
+    * Code: 64-bit static field "get" operation. Uses no substitutions.
+    *
+    * For: sget-wide
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field, loading or storing
+    *              into the value register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_methodClassDex(%eax), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %edx                 # %edx<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $$0, (%ecx, %edx, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .L${opcode}_resolve
+
+.L${opcode}_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        offStaticField_value(%ecx), %xmm0 # %xmm0<- field value
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- field value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+%break
+
+   /*
+    * Continuation if the field has not yet been resolved.
+    *  %edx: BBBB field ref
+    */
+
+.L${opcode}_resolve:
+    movl        offGlue_method(%eax), %eax # %eax <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %edx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%eax), %eax # %eax<- method->clazz
+    movl        %eax, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    lea         8(%esp), %esp
+    cmp         $$0, %eax               # check if initalization failed
+    movl        %eax, %ecx              # %ecx<- result
+    jne         .L${opcode}_finish      # success, continue
+    jmp         common_exceptionThrown  # failed; handle exception
diff --git a/vm/mterp/x86-atom/OP_SHL_INT.S b/vm/mterp/x86-atom/OP_SHL_INT.S
new file mode 100644
index 0000000..13e4a11
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHL_INT.S
+    */
+
+%include "x86-atom/binopS.S" {"instr":"sal     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHL_INT_2ADDR.S b/vm/mterp/x86-atom/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..a27e09a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHL_INT_2ADDR.S
+    */
+
+%include "x86-atom/binopS2addr.S" {"instr":"sal     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHL_INT_LIT8.S b/vm/mterp/x86-atom/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..5141e5c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHL_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8S.S" {"instr":"sal     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHL_LONG.S b/vm/mterp/x86-atom/OP_SHL_LONG.S
new file mode 100644
index 0000000..cef558c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_LONG.S
@@ -0,0 +1,40 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHL_LONG.S
+    *
+    * Code: Performs a shift left long. Uses no substitutions.
+    *
+    * For: shl-long
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where one is the shift amount and the other is the value to shift.
+    *              Store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_CC    1, %eax                 # %eax<- CC
+    FETCH_BB    1, %edx                 # %edx<- BB
+    movq        .LshiftMask, %xmm2      # %xmm2<- mask for the shift bits
+    movss       (rFP, %eax, 4), %xmm0   # %xmm0<- vCC
+    pand        %xmm2, %xmm0            # %xmm0<- masked shift bits
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vBB
+    psllq       %xmm0, %xmm1            # %xmm1<- shifted vBB
+    movq        %xmm1, (rFP, rINST, 4)  # vAA<- shifted vBB
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SHL_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..28dfaf2
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_LONG_2ADDR.S
@@ -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.
+    */
+
+   /*
+    * File: OP_SHL_LONG_2ADDR.S
+    *
+    * Code: Performs a shift left long. Uses no substitutions.
+    *
+    * For: shl-long/2addr
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where the fist is the value to shift and the second is the
+    *              shift amount. Store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- A
+    movss       (rFP, %edx, 4),  %xmm0  # %xmm0<- vB
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vA
+    movq        .LshiftMask, %xmm2      # %xmm2<- mask for the shift bits
+    pand        %xmm2, %xmm0            # %xmm0<- masked shift bits
+    psllq       %xmm0, %xmm1            # %xmm1<- shifted vA
+    movq        %xmm1, (rFP, rINST, 4)  # vA<- shifted vA
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SHR_INT.S b/vm/mterp/x86-atom/OP_SHR_INT.S
new file mode 100644
index 0000000..e7fd28b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHR_INT.S
+    */
+
+%include "x86-atom/binopS.S" {"instr":"sar     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHR_INT_2ADDR.S b/vm/mterp/x86-atom/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..0d0b461
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHR_INT_2ADDR.S
+    */
+
+%include "x86-atom/binopS2addr.S" {"instr":"sar     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHR_INT_LIT8.S b/vm/mterp/x86-atom/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..3467bf5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHR_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8S.S" {"instr":"sar     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHR_LONG.S b/vm/mterp/x86-atom/OP_SHR_LONG.S
new file mode 100644
index 0000000..be893ef
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_LONG.S
@@ -0,0 +1,53 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHR_LONG.S
+    *
+    * Code: Performs a shift right long
+    *
+    * For: shl-long
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where one is the shift amount and the other is the value to shift.
+    *              Store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CC    1, %eax                 # %eax<- CC
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vBB
+    movss       (rFP, %eax, 4), %xmm0   # %xmm0<- vCC
+    movq        .LshiftMask, %xmm2
+    pand        %xmm2, %xmm0            # %xmm0<- masked for the shift bits
+    psrlq       %xmm0, %xmm1            # %xmm1<- shifted vBB
+    cmpl        $$0, 4(rFP, %edx, 4)    # check if we need to consider sign
+    jl          .L${opcode}_finish      # consider sign
+    jmp         .L${opcode}_final       # sign is fine, finish
+%break
+
+.L${opcode}_finish:
+    movq        .Lvalue64, %xmm3        # %xmm3<- 64
+    psubq       %xmm0, %xmm3            # %xmm3<- 64 - shift amount
+    movq        .L64bits, %xmm4         # %xmm4<- lower 64 bits set
+    psllq       %xmm3, %xmm4            # %xmm4<- correct mask for sign bits
+    por         %xmm4, %xmm1            # %xmm1<- signed and shifted vBB
+
+.L${opcode}_final:
+    movq        %xmm1, (rFP, rINST, 4)  # vAA<- shifted vBB
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SHR_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..38aefcf
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,54 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SHR_LONG_2ADDR.S
+    *
+    * Code: Performs a shift left long
+    *
+    * For: shl-long/2addr
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where the fist is the value to shift and the second is the
+    *              shift amount. Store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- BA
+    movss       (rFP, %edx, 4),  %xmm0  # %xmm0<- vB
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vA
+    movq        .LshiftMask, %xmm2
+    pand        %xmm2, %xmm0            # %xmm0<- masked for the shift bits
+    psrlq       %xmm0, %xmm1            # %xmm1<- shifted vBB
+    cmpl        $$0, 4(rFP, rINST, 4)   # check if we need to consider sign
+    jl          .L${opcode}_finish      # consider sign
+    jmp         .L${opcode}_final       # sign is fine, finish
+%break
+
+.L${opcode}_finish:
+    movq        .Lvalue64, %xmm3        # %xmm3<- 64
+    psubq       %xmm0, %xmm3            # %xmm3<- 64 - shift amount
+    movq        .L64bits, %xmm4         # %xmm4<- lower 64 bits set
+    psllq       %xmm3, %xmm4            # %xmm4<- correct mask for sign bits
+    por         %xmm4, %xmm1            # %xmm1<- signed and shifted vBB
+
+.L${opcode}_final:
+    movq        %xmm1, (rFP, rINST, 4)  # vAA<- shifted vBB
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SPARSE_SWITCH.S b/vm/mterp/x86-atom/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..8020d1a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPARSE_SWITCH.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SPARSE_SWITCH.S
+    */
+
+%include "x86-atom/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/x86-atom/OP_SPUT.S b/vm/mterp/x86-atom/OP_SPUT.S
new file mode 100644
index 0000000..55715a7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT.S
@@ -0,0 +1,60 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SPUT.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $$0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .L${opcode}_resolve
+    jmp         .L${opcode}_finish
+%break
+
+.L${opcode}_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $$0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    movl        %eax, %ecx              # %ecx<- result
+
+.L${opcode}_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vAA
+    movl        rINST, offStaticField_value(%ecx) # field value<- vAA
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_SPUT_BOOLEAN.S b/vm/mterp/x86-atom/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..9bb64f8
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SPUT_BOOLEAN.S
+    */
+
+%include "x86-atom/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_BYTE.S b/vm/mterp/x86-atom/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..1d4f016
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_BYTE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SPUT_BYTE.S
+    */
+
+%include "x86-atom/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_CHAR.S b/vm/mterp/x86-atom/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..58300ef
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_CHAR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SPUT_CHAR.S
+    */
+
+%include "x86-atom/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_OBJECT.S b/vm/mterp/x86-atom/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..88ebaf7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_OBJECT.S
@@ -0,0 +1,70 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SPUT_OBJECT.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+    FETCH       1, %eax                 # %eax<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $$0, (%ecx, %eax, 4)    # check for null ptr; resolved StaticField
+    movl        (%ecx, %eax, 4), %ecx   # %ecx<- resolved StaticField
+    je          .L${opcode}_resolve
+    jmp         .L${opcode}_finish
+%break
+
+.L${opcode}_resolve:
+    movl        offGlue_method(%edx), %edx # %edx <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %eax, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    movl        %edx, -8(%esp)          # push parameter method->clazz
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    cmp         $$0, %eax               # check if initalization failed
+    lea         8(%esp), %esp
+    je          common_exceptionThrown  # failed; handle exception
+    movl        %eax, %ecx              # %ecx<- result
+
+.L${opcode}_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    rINST                   # rINST<- vAA
+
+
+    movl        rINST, offStaticField_value(%ecx) # field value<- vAA
+    testl       rINST, rINST            # stored null object ptr?
+    je          1f
+    movl        rGLUE, %edx             # get glue
+    movl        offField_clazz(%ecx), %ecx # ecx<- field->clazz
+    movl        offGlue_cardTable(%edx), %edx # get card table base
+    shrl        $$GC_CARD_SHIFT, %ecx   # head to card number
+    movb        %dl, (%edx, %ecx)       # mark card
+1:
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/x86-atom/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..b368e31
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_SPUT_OBJECT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_SHORT.S b/vm/mterp/x86-atom/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..1ecc562
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_SHORT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SPUT_SHORT.S
+    */
+
+%include "x86-atom/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_VOLATILE.S b/vm/mterp/x86-atom/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..7ee4140
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_WIDE.S b/vm/mterp/x86-atom/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..7d661cf
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_WIDE.S
@@ -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.
+    */
+
+   /*
+    * File: OP_SPUT_WIDE.S
+    *
+    * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+    *
+    * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+    *
+    * Description: Perform the identified object static field operation
+    *              with the identified static field; store the field value
+    *              register.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, string@BBBB
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_methodClassDex(%eax), %ecx # %ecx<- glue->pDvmDex
+    FETCH       1, %edx                 # %edx<- BBBB
+    movl        offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+    cmp         $$0, (%ecx, %edx, 4)    # check for null ptr; resolved StaticField ptr
+    movl        (%ecx, %edx, 4), %ecx   # %ecx<- resolved StaticField ptr
+    je          .L${opcode}_resolve
+
+.L${opcode}_finish:
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vAA
+    movq        %xmm0, offStaticField_value(%ecx) # field value<- field value
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
+%break
+
+   /*
+    * Continuation if the field has not yet been resolved.
+    *  %edx: BBBB field ref
+    */
+
+.L${opcode}_resolve:
+    movl        offGlue_method(%eax), %eax # %eax <- glue->method
+    EXPORT_PC                           # in case an exception is thrown
+    movl        %edx, -4(%esp)          # push parameter CCCC; field ref
+    movl        offMethod_clazz(%eax), %eax # %eax<- method->clazz
+    movl        %eax, -8(%esp)
+    lea         -8(%esp), %esp
+    call        dvmResolveStaticField   # call: (const ClassObject* referrer, u4 ifieldIdx)
+                                        # return: StaticField*
+    lea         8(%esp), %esp
+    cmp         $$0, %eax               # check if initalization failed
+    movl        %eax, %ecx              # %ecx<- result
+    jne         .L${opcode}_finish      # success, continue
+    jmp         common_exceptionThrown  # failed; handle exception
diff --git a/vm/mterp/x86-atom/OP_SUB_DOUBLE.S b/vm/mterp/x86-atom/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..5f2dbb6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_DOUBLE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_DOUBLE.S
+    */
+
+%include "x86-atom/binopWide.S" {"instr":"subsd   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..cd6f12a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_DOUBLE_2ADDR.S
+    */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"subsd   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_FLOAT.S b/vm/mterp/x86-atom/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..eb79d79
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_FLOAT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_FLOAT.S
+    */
+
+%include "x86-atom/binopF.S" {"instr":"subss     %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..77f23f1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_FLOAT_2ADDR.S
+    */
+
+%include "x86-atom/binopF2addr.S" {"instr":"subss     %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_INT.S b/vm/mterp/x86-atom/OP_SUB_INT.S
new file mode 100644
index 0000000..8d342cd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_INT.S
+    */
+
+%include "x86-atom/binop.S" {"instr":"subl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_SUB_INT_2ADDR.S b/vm/mterp/x86-atom/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..4d295a4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_INT_2ADDR.S
+    */
+
+%include "x86-atom/binop2addr.S" {"instr":"subl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_SUB_INT_LIT8.S b/vm/mterp/x86-atom/OP_SUB_INT_LIT8.S
new file mode 100644
index 0000000..8bf0902
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8.S" {"instr":"subl     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_SUB_LONG.S b/vm/mterp/x86-atom/OP_SUB_LONG.S
new file mode 100644
index 0000000..84e25d0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_LONG.S
+    */
+
+%include "x86-atom/binopWide.S" {"instr":"psubq    %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..ca6a2ad
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_SUB_LONG_2ADDR.S
+    */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"psubq    %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_THROW.S b/vm/mterp/x86-atom/OP_THROW.S
new file mode 100644
index 0000000..120b1e9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_THROW.S
@@ -0,0 +1,37 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_THROW.S
+    *
+    * Code: Throw an exception
+    *
+    * For: throw
+    *
+    * Description: Throw an exception object in the current thread.
+    *
+    * Format: AA|op (11x)
+    *
+    * Syntax: op vAA
+    */
+
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    EXPORT_PC                           # export the pc
+    GET_VREG    rINST                   # rINST<- vAA
+    cmp         $$0, rINST              # check for null
+    movl        offGlue_self(%eax), %ecx # %ecx<- glue->self
+    je          common_errNullObject    # handle null object
+    movl        rINST, offThread_exception(%ecx) # thread->exception<- object
+    jmp         common_exceptionThrown  # handle exception
diff --git a/vm/mterp/x86-atom/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/x86-atom/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..f920b50
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_THROW_VERIFICATION_ERROR.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.
+    */
+
+   /*
+    * File: OP_THROW_VERIFICATION_ERROR.S
+    *
+    * Code:
+    *
+    * For: throw-verification-error
+    *
+    * Description: Throws an exception for an error discovered during verification.
+    *              The exception is indicated by AA with details provided by BBBB.
+    *
+    * Format: AA|op BBBB (21c)
+    *
+    * Syntax: op vAA, ref@BBBB
+    */
+
+    movl        rGLUE, %edx                  # %edx<- pMterpGlue
+    movl        offGlue_method(%edx), %ecx   # %ecx<- glue->method
+    EXPORT_PC                                # in case an exception is thrown
+    FETCH       1, %eax                      # %eax<- BBBB
+    movl        %eax, -4(%esp)               # push parameter BBBB; ref
+    movl        rINST, -8(%esp)              # push parameter AA
+    movl        %ecx, -12(%esp)              # push parameter glue->method
+    lea         -12(%esp), %esp
+    call        dvmThrowVerificationError    # call: (const Method* method, int kind, int ref)
+    jmp         common_exceptionThrown       # failed; handle exception
diff --git a/vm/mterp/x86-atom/OP_UNUSED_3E.S b/vm/mterp/x86-atom/OP_UNUSED_3E.S
new file mode 100644
index 0000000..d91d469
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_3E.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_3E.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_3F.S b/vm/mterp/x86-atom/OP_UNUSED_3F.S
new file mode 100644
index 0000000..84cc69d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_3F.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_3F.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_40.S b/vm/mterp/x86-atom/OP_UNUSED_40.S
new file mode 100644
index 0000000..e0853da
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_40.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_40.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_41.S b/vm/mterp/x86-atom/OP_UNUSED_41.S
new file mode 100644
index 0000000..a30fbe3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_41.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_41.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_42.S b/vm/mterp/x86-atom/OP_UNUSED_42.S
new file mode 100644
index 0000000..64c5648
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_42.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_42.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_43.S b/vm/mterp/x86-atom/OP_UNUSED_43.S
new file mode 100644
index 0000000..4a6120b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_43.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_43.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_73.S b/vm/mterp/x86-atom/OP_UNUSED_73.S
new file mode 100644
index 0000000..9808865
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_73.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_73.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_79.S b/vm/mterp/x86-atom/OP_UNUSED_79.S
new file mode 100644
index 0000000..6ebd8ff
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_79.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_79.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_7A.S b/vm/mterp/x86-atom/OP_UNUSED_7A.S
new file mode 100644
index 0000000..79a22d0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_7A.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_7A.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E3.S b/vm/mterp/x86-atom/OP_UNUSED_E3.S
new file mode 100644
index 0000000..2921274
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E3.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_E3.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E4.S b/vm/mterp/x86-atom/OP_UNUSED_E4.S
new file mode 100644
index 0000000..69eb419
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E4.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_E4.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E5.S b/vm/mterp/x86-atom/OP_UNUSED_E5.S
new file mode 100644
index 0000000..2172369
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E5.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_E5.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E6.S b/vm/mterp/x86-atom/OP_UNUSED_E6.S
new file mode 100644
index 0000000..1464cbd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E6.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_E6.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E7.S b/vm/mterp/x86-atom/OP_UNUSED_E7.S
new file mode 100644
index 0000000..67029e4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E7.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_E7.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_F1.S b/vm/mterp/x86-atom/OP_UNUSED_F1.S
new file mode 100644
index 0000000..b6c264a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_F1.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_F1.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_FC.S b/vm/mterp/x86-atom/OP_UNUSED_FC.S
new file mode 100644
index 0000000..24b104c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_FC.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_FC.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_FD.S b/vm/mterp/x86-atom/OP_UNUSED_FD.S
new file mode 100644
index 0000000..b3cc6fb
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_FD.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_FD.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_FE.S b/vm/mterp/x86-atom/OP_UNUSED_FE.S
new file mode 100644
index 0000000..435624f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_FE.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_FE.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_FF.S b/vm/mterp/x86-atom/OP_UNUSED_FF.S
new file mode 100644
index 0000000..e831696
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_FF.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_UNUSED_FF.S
+    */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_USHR_INT.S b/vm/mterp/x86-atom/OP_USHR_INT.S
new file mode 100644
index 0000000..a1d91c6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_USHR_INT.S
+    */
+
+%include "x86-atom/binopS.S" {"instr":"shr     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_USHR_INT_2ADDR.S b/vm/mterp/x86-atom/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..9ee9c66
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_USHR_INT_2ADDR.S
+    */
+
+%include "x86-atom/binopS2addr.S" {"instr":"shr     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_USHR_INT_LIT8.S b/vm/mterp/x86-atom/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..5a54df7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_USHR_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8S.S" {"instr":"shr     %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_USHR_LONG.S b/vm/mterp/x86-atom/OP_USHR_LONG.S
new file mode 100644
index 0000000..1c404f0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_LONG.S
@@ -0,0 +1,39 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_USHR_LONG.S
+    *
+    * Code: Performs an unsigned shift right long operation. Uses no substitutions.
+    *
+    * For: ushr-long
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where one is the shift amount and the other is the value to shift.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_CC    1, %eax                 # %eax<- CC
+    FETCH_BB    1, %edx                 # %edx<- BB
+    movsd        .LshiftMask, %xmm2     # %xmm2<- mask for the shift bits
+    movss       (rFP, %eax, 4), %xmm0   # %xmm0<- vCC
+    pand        %xmm2, %xmm0            # %xmm0<- masked shift bits
+    movsd       (rFP, %edx, 4), %xmm1   # %xmm1<- vBB
+    psrlq       %xmm0, %xmm1            # %xmm1<- shifted vBB
+    movsd       %xmm1, (rFP, rINST, 4)  # vAA<- shifted vBB
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_USHR_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..31cb5bc
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_LONG_2ADDR.S
@@ -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.
+    */
+
+   /*
+    * File: OP_USHR_LONG_2ADDR.S
+    *
+    * Code: Performs an unsigned shift right long operation. Uses no substiutions.
+    *
+    * For: ushr-long/2addr
+    *
+    * Description: Perform a binary shift operation using two source registers
+    *              where the fist is the value to shift and the second is the
+    *              shift amount. Store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- A
+    movq        .LshiftMask, %xmm2      # %xmm2<- mask for the shift bits
+    movss       (rFP, %edx, 4),  %xmm0  # %xmm0<- vB
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vA
+    pand        %xmm2, %xmm0            # %xmm0<- masked shift bits
+    psrlq       %xmm0, %xmm1            # %xmm1<- shifted vA
+    movq        %xmm1, (rFP, rINST, 4)  # vA<- shifted vA
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_XOR_INT.S b/vm/mterp/x86-atom/OP_XOR_INT.S
new file mode 100644
index 0000000..d36b83f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_INT.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_XOR_INT.S
+    */
+
+%include "x86-atom/binop.S" {"instr":"xor     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_XOR_INT_2ADDR.S b/vm/mterp/x86-atom/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..0bab865
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_XOR_INT_2ADDR.S
+    */
+
+%include "x86-atom/binop2addr.S" {"instr":"xor     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_XOR_INT_LIT16.S b/vm/mterp/x86-atom/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..a26bcc5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_INT_LIT16.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_XOR_INT_LIT16.S
+    */
+
+%include "x86-atom/binopLit16.S" {"instr":"xor     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_XOR_INT_LIT8.S b/vm/mterp/x86-atom/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..9a3c8e3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_INT_LIT8.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_XOR_INT_LIT8.S
+    */
+
+%include "x86-atom/binopLit8.S" {"instr":"xor     %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_XOR_LONG.S b/vm/mterp/x86-atom/OP_XOR_LONG.S
new file mode 100644
index 0000000..58a8384
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_LONG.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_XOR_LONG.S
+    */
+
+%include "x86-atom/binopWide.S" {"instr":"pxor   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_XOR_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..6b42cbd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+   /* 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.
+    */
+
+   /*
+    * File: OP_XOR_LONG_2ADDR.S
+    */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"pxor   %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/TODO.txt b/vm/mterp/x86-atom/TODO.txt
new file mode 100644
index 0000000..72d8855
--- /dev/null
+++ b/vm/mterp/x86-atom/TODO.txt
@@ -0,0 +1,4 @@
+Items requiring attention:
+
+(lo) Implement OP_BREAKPOINT
+(lo) Implement OP_*_VOLATILE (12 instructions)
diff --git a/vm/mterp/x86-atom/bincmp.S b/vm/mterp/x86-atom/bincmp.S
new file mode 100644
index 0000000..a8fbed5
--- /dev/null
+++ b/vm/mterp/x86-atom/bincmp.S
@@ -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.
+    */
+
+   /*
+    * File: bincmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform.
+    *
+    * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+    *
+    * Description: Branch to the given destination if the comparison
+    *              test between the given registers values is true.
+    *
+    * Format: B|A|op CCCC (22t)
+    *
+    * Syntax: op vA, vB, +CCCC
+    */
+
+    movl        rINST,  %eax            # %eax<- BA
+    andl        $$15, rINST             # rINST<- A
+    shr         $$4, %eax               # %eax<- B
+    GET_VREG    rINST                   # rINST<- vA
+    movl        $$4, %edx               # %edx<- 4
+    cmp         (rFP, %eax, 4), rINST   # compare vA and vB
+    j${revcmp}  1f                      # goto next instruction if reverse
+                                        # comparison is true
+    FETCHs      1, %edx                 # %edx<- +CCCC, Branch offset
+    sal         $$1, %edx
+    js          common_periodicChecks_backwardBranch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
diff --git a/vm/mterp/x86-atom/binop.S b/vm/mterp/x86-atom/binop.S
new file mode 100644
index 0000000..1706b72
--- /dev/null
+++ b/vm/mterp/x86-atom/binop.S
@@ -0,0 +1,39 @@
+   /* 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.
+    */
+
+   /*
+    * File: binop.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    GET_VREG    %edx                    # %edx<- vCC
+    $instr                              # %ecx<- vBB op vCC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binop2addr.S b/vm/mterp/x86-atom/binop2addr.S
new file mode 100644
index 0000000..b26b25a
--- /dev/null
+++ b/vm/mterp/x86-atom/binop2addr.S
@@ -0,0 +1,45 @@
+   /* 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.
+    */
+
+   /*
+    * File: binop2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%ecx = %ecx op %edx".
+    *
+    * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+    *      sub-int/2addr, xor-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    andl        $$15, rINST             # rINST<- A
+    movl        rINST, %ecx             # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vB
+    GET_VREG    %ecx                    # %ecx<- vA
+    $instr                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
+
+
diff --git a/vm/mterp/x86-atom/binopD.S b/vm/mterp/x86-atom/binopD.S
new file mode 100644
index 0000000..20a2e9a
--- /dev/null
+++ b/vm/mterp/x86-atom/binopD.S
@@ -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.
+    */
+
+   /*
+    * File: binopD.S
+    *
+    * Code: 32-bit integer divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int, rem-int
+    *
+    * Description: Perform a binary operation on two source
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+%default {"div":"1"}
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    GET_VREG    %eax                    # %eax<- vBB
+    GET_VREG    %ecx                    # %ecx<- vCC
+    cmp         $$0, %ecx               # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    cmpl        $$-1, %ecx              # handle -1 special case divide error
+    jne         .L${opcode}_noerror
+    cmpl        $$0x80000000,%eax       # handle min int special case divide error
+    je         .L${opcode}_break
+.L${opcode}_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    .if  $div
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .L${opcode}_break2
+%break
+.L${opcode}_break:
+    .if  $div
+    movl        $$0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $$0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.L${opcode}_break2:
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/binopD2addr.S b/vm/mterp/x86-atom/binopD2addr.S
new file mode 100644
index 0000000..392b46b
--- /dev/null
+++ b/vm/mterp/x86-atom/binopD2addr.S
@@ -0,0 +1,68 @@
+   /* 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.
+    */
+
+   /*
+    * File: binopD2addr.S
+    *
+    * Code: 32-bit "/2addr" integer divde operation. If "div"
+    *       is set, the code returns the quotient, else it returns
+    *       the remainder. Also, a divide-by-zero check is done.
+    *
+    * For: div-int/2addr, rem-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+%default {"div":"1"}
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $$15, rINST             # rINST<- A, to be used as dest
+    movl        rINST, %eax             # %eax<- A
+    shr         $$4, %ecx               # %ecx<- B
+    GET_VREG    %eax                    # %eax<- vA
+    GET_VREG    %ecx                    # %edx<- vB
+    cmp         $$0, %ecx               # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    cmpl        $$-1, %ecx              # handle -1 special case divide error
+    jne         .L${opcode}_noerror
+    cmpl        $$0x80000000,%eax       # handle min int special case divide error
+    je         .L${opcode}_break
+.L${opcode}_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+     .if  $div
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .L${opcode}_break2
+    #FFETCH_ADV 1, %edx  # %ecx<- next instruction hi; fetch, advance
+    #FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
+%break
+.L${opcode}_break:
+    .if  $div
+    movl        $$0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $$0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.L${opcode}_break2:
+    FFETCH_ADV  1, %edx                 # %ecx<- next instruction hi; fetch, advance
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
+
diff --git a/vm/mterp/x86-atom/binopDLit16.S b/vm/mterp/x86-atom/binopDLit16.S
new file mode 100644
index 0000000..3e67d0a
--- /dev/null
+++ b/vm/mterp/x86-atom/binopDLit16.S
@@ -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.
+    */
+
+   /*
+    * File: binopDLit16.S
+    *
+    * Code: 32-bit "lit16" divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int/lit16, rem-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+%default {"div":"1"}
+
+    movl        rINST, %eax             # %eax<- BA
+    shr         $$4, %eax               # %eax<- B
+    FETCHs      1, %ecx                 # %ecx<- +CCCC, sign-extended literal
+    cmp         $$0, %ecx               # check for divide by zero
+    GET_VREG    %eax                    # %eax<- vB
+    je          common_errDivideByZero  # handle divide by zero
+    andl        $$15, rINST             # rINST<- A
+    cmpl        $$-1, %ecx              # handle -1 special case divide error
+    jne         .L${opcode}_noerror
+    cmpl        $$0x80000000,%eax       # handle min int special case divide error
+    je         .L${opcode}_break
+.L${opcode}_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    #FFETCH_ADV 2, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    .if  $div
+    SET_VREG    %eax rINST              # vA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vA<- %edx (remainder)
+    .endif
+    jmp         .L${opcode}_break2
+
+%break
+.L${opcode}_break:
+    .if  $div
+    movl        $$0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $$0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+.L${opcode}_break2:
+
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/binopDLit8.S b/vm/mterp/x86-atom/binopDLit8.S
new file mode 100644
index 0000000..f197714
--- /dev/null
+++ b/vm/mterp/x86-atom/binopDLit8.S
@@ -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.
+    */
+
+   /*
+    * File: binopDLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. If "div" is set, the code
+    *       returns the quotient, else it returns the remainder.
+    *       Also, a divide-by-zero check is done.
+    *
+    * For: div-int/lit8, rem-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signe extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+%default {"div":"1"}
+
+    FETCH_BB    1, %eax                 # %eax<- BB
+    FETCH_CCs   1, %ecx                 # %ecx<- +CC, sign-extended literal
+    cmp         $$0, %ecx               # check for divide by zero
+    GET_VREG    %eax                    # %eax<- vBB
+    je          common_errDivideByZero  # handle divide by zero
+
+    cmpl        $$-1, %ecx              # handle -1 special case divide error
+    jne         .L${opcode}_noerror
+    cmpl        $$0x80000000,%eax       # handle min int special case divide error
+    je         .L${opcode}_break
+.L${opcode}_noerror:
+    cdq                                 # sign-extend %eax to %edx
+    idiv        %ecx                    # divide %edx:%eax by %ecx
+    #FFETCH_ADV 2, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    .if  $div
+    SET_VREG    %eax rINST              # vAA<- %eax (quotient)
+    .else
+    SET_VREG    %edx rINST              # vAA<- %edx (remainder)
+    .endif
+    jmp         .L${opcode}_break2
+%break
+.L${opcode}_break:
+    .if  $div
+    movl        $$0x80000000, (rFP, rINST, 4) # vAA<- min int
+    .else
+    movl        $$0, (rFP, rINST, 4)    # vAA<- 0
+    .endif
+
+.L${opcode}_break2:
+    FINISH      2                       # jump to next instruction
+    #FGETOP_JMP 2, %ecx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopDivRemLong.S b/vm/mterp/x86-atom/binopDivRemLong.S
new file mode 100644
index 0000000..93ed4f8
--- /dev/null
+++ b/vm/mterp/x86-atom/binopDivRemLong.S
@@ -0,0 +1,52 @@
+   /* 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.
+    */
+
+   /*
+    * File: binopDivRemLong.S
+    *
+    * Code: 64-bit long divide operation. Variable
+    *       "func" defines the function called to do the operation.
+    *
+    * For: div-long, rem-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+%default {"func":"__divdi3"}
+
+    FETCH_CC    1, %edx                 # %edx<- CC
+    movl        (rFP, %edx, 4), %eax    # %eax<- vCC
+    movl        4(rFP, %edx, 4), %ecx   # %ecx<- vCC+1
+    movl        %eax, -8(%esp)          # push arg vCC
+    or          %ecx, %eax              # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    FETCH_BB    1, %edx                 # %edx<- BB
+    movl        %ecx, -4(%esp)          # push arg vCC+1
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vBB,vBB+1
+    jmp         .L${opcode}_finish
+%break
+.L${opcode}_finish:
+    movq        %xmm0, -16(%esp)        # push arg vBB,vBB+1
+    lea         -16(%esp), %esp
+    call        $func                   # call func
+    lea         16(%esp), %esp
+    movl        %eax, (rFP, rINST, 4)   # vAA<- return low
+    movl        %edx, 4(rFP, rINST, 4)  # vAA+1<- return high
+    FINISH      2                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/binopDivRemLong2Addr.S b/vm/mterp/x86-atom/binopDivRemLong2Addr.S
new file mode 100644
index 0000000..aa427de
--- /dev/null
+++ b/vm/mterp/x86-atom/binopDivRemLong2Addr.S
@@ -0,0 +1,54 @@
+   /* 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.
+    */
+
+   /*
+    * File: binopDivRemLong2Addr.S
+    *
+    * Code: 64-bit "/2addr" long divide operation. Variable
+    *       "func" defines the function called to do the operation.
+    *
+    * For: div-long/2addr, rem-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register.
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+%default {"func":"__divdi3"}
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, %edx               # %edx<- B
+    and         $$15, rINST             # rINST<- A
+    movl        (rFP, %edx, 4), %eax    # %eax<- vB
+    movl        %eax, -12(%esp)         # push arg vB
+    movl        4(rFP, %edx, 4), %ecx   # %ecx<- vB+1
+    or          %ecx, %eax              # check for divide by zero
+    je          common_errDivideByZero  # handle divide by zero
+    movl        %ecx, -8(%esp)          # push arg vB+1
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vA,vA+1
+    jmp         .L${opcode}_break
+%break
+.L${opcode}_break:
+    movq        %xmm0, -20(%esp)        # push arg vA, vA+1
+    lea         -20(%esp), %esp
+    call        $func                   # call func
+    lea         20(%esp), %esp
+    movl        %eax, (rFP, rINST, 4)   # vA<- return low
+    movl        %edx, 4(rFP, rINST, 4)  # vA<- return high
+    FFETCH_ADV  1, %ecx                 # %ecx<- next instruction hi; fetch, advance
+    FGETOP_JMP 1, %ecx                  # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopF.S b/vm/mterp/x86-atom/binopF.S
new file mode 100644
index 0000000..0c07b97
--- /dev/null
+++ b/vm/mterp/x86-atom/binopF.S
@@ -0,0 +1,39 @@
+   /* 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.
+    */
+
+   /*
+    * File: binopF.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-float, mul-float, sub-float
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movss       (rFP, %ecx, 4), %xmm0   # %xmm0<-vBB
+    movss       (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    $instr                              # %xmm0<- vBB op vCC
+    movss       %xmm0, (rFP, rINST, 4)  # vAA<- %xmm0; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopF2addr.S b/vm/mterp/x86-atom/binopF2addr.S
new file mode 100644
index 0000000..135ca0c
--- /dev/null
+++ b/vm/mterp/x86-atom/binopF2addr.S
@@ -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.
+    */
+
+   /*
+    * File: binopF2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0 = %xmm0 op %xmm1".
+    *
+    * For: add-float/2addr, mul-float/2addr, sub-float/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    andl        $$15, %ecx              # %ecx<- A
+    shr         $$4, rINST              # rINST<- B
+    FFETCH_ADV  1, %edx                 # %ecx<- next instruction hi; fetch, advance
+    movss       (rFP, %ecx, 4), %xmm0   # %xmm0<- vA
+    movss       (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    $instr                              # %xmm0<- vA op vB
+    movss       %xmm0, (rFP, %ecx, 4)   # vA<- %xmm0; result
+    FGETOP_JMP  1, %edx                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopLit16.S b/vm/mterp/x86-atom/binopLit16.S
new file mode 100644
index 0000000..4972b4d
--- /dev/null
+++ b/vm/mterp/x86-atom/binopLit16.S
@@ -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.
+    */
+
+   /*
+    * File: binopLit16.S
+    *
+    * Code: 32-bit "lit16" operation. Provides an "instr" line to
+    *       specify an instruction that performs "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+    *      xor-int/lit16
+    *
+    * Description: Perform a binary operation on a register and a
+    *              sign extended 16-bit literal value and store the
+    *              result in a destination register.
+    *
+    * Format: B|A|op CCCC (22s)
+    *
+    * Syntax: op vA, vB, #+CCCC
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $$4, %ecx               # %ecx<- B
+    andl        $$15, rINST             # rINST<- A
+    FETCHs      1, %edx                 # %edx<- +CCCC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    $instr                              # %ecx<- vA op vB
+    SET_VREG    %ecx, rINST             # vA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopLit8.S b/vm/mterp/x86-atom/binopLit8.S
new file mode 100644
index 0000000..239e443
--- /dev/null
+++ b/vm/mterp/x86-atom/binopLit8.S
@@ -0,0 +1,40 @@
+   /* 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.
+    */
+
+   /*
+    * File: binopLit8.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs  "%ecx = %ecx op %edx"
+    *
+    *
+    * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+    *      xor-int/lit8
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CCs   1, %edx                 # %edx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vBB
+    $instr                              # %ecx<- vBB op +CC
+    SET_VREG    %ecx, rINST             # vAA<- %ecx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopLit8S.S b/vm/mterp/x86-atom/binopLit8S.S
new file mode 100644
index 0000000..c0360aa
--- /dev/null
+++ b/vm/mterp/x86-atom/binopLit8S.S
@@ -0,0 +1,40 @@
+   /* 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.
+    */
+
+   /*
+    * File: binopLit8S.S
+    *
+    * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+    *       to specify an instruction that performs "%edx = %edx op %cl"
+    *
+    *
+    * For: shl-int/lit8, shr-int/lit8, ushr-int/lit8
+    *
+    *
+    * Description: Perform a binary operation on a register and a
+    *              signed extended 8-bit literal value
+    *
+    * Format: AA|op CC|BB (22b)
+    *
+    * Syntax: op vAA, vBB, #+CC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CCs   1, %ecx                 # %ecx<- +CC, sign-extended literal
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vBB
+    $instr                              # %edx<- vBB op +CC
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopS.S b/vm/mterp/x86-atom/binopS.S
new file mode 100644
index 0000000..b09f5c2
--- /dev/null
+++ b/vm/mterp/x86-atom/binopS.S
@@ -0,0 +1,39 @@
+   /* 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.
+    */
+
+   /*
+    * File: binopS.S
+    *
+    * Code: Generic 32-bit binary operation.  Provides an "instr" line to
+    *       specify an instruction that performs "%edx = %edx op %cl"
+    *
+    * For: shl-int, shr-int, ushr-int
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %edx                 # %edx<- BB
+    FETCH_CC    1, %ecx                 # %ecx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %edx                    # %edx<- vBB
+    GET_VREG    %ecx                    # %ecx<- vCC
+    $instr                              # %edx<- vBB op +CC
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopS2addr.S b/vm/mterp/x86-atom/binopS2addr.S
new file mode 100644
index 0000000..0c3c29a
--- /dev/null
+++ b/vm/mterp/x86-atom/binopS2addr.S
@@ -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.
+    */
+
+   /*
+    * File: binopS2addr.S
+    *
+    * Code: Generic 32-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%edx = %edx op %cl".
+    *
+    * For: shl-int/2addr, shr-int/2addr, ushr-int/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %ecx             # %ecx<- BA
+    shr         $$4, %ecx               # %ecx<- B
+    andl        $$15, rINST             # rINST<- A
+    movl        rINST, %edx             # %edx<- A
+    FFETCH_ADV  1, %eax                 # %ecx<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    GET_VREG    %edx                    # %edx<- vA
+    $instr                              # %edx<- vA op vB
+    SET_VREG    %edx, rINST             # vAA<- %edx; result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopWide.S b/vm/mterp/x86-atom/binopWide.S
new file mode 100644
index 0000000..3cd3e52
--- /dev/null
+++ b/vm/mterp/x86-atom/binopWide.S
@@ -0,0 +1,40 @@
+   /* 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.
+    */
+
+   /*
+    * File: binopWide.S
+    *
+    * Code: Generic 64-bit binary operation.  Provides an "instr" variable to
+    *       specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+    *
+    * For: add-double, add-long, and-long, mul-double, or-long,
+    *      sub-double, sub-long, xor-long
+    *
+    * Description: Perform a binary operation on two source registers
+    *              and store the result in a destination register.
+    *
+    * Format: AA|op CC|BB (23x)
+    *
+    * Syntax: op vAA, vBB, vCC
+    */
+
+    FETCH_BB    1, %ecx                 # %ecx<- BB
+    FETCH_CC    1, %edx                 # %edx<- CC
+    FFETCH_ADV  2, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, %ecx, 4), %xmm0   # %xmm0<- vBB
+    movq        (rFP, %edx, 4), %xmm1   # %xmm1<- vCC
+    $instr                              # %xmm0<- vBB op vCC
+    movq        %xmm0, (rFP, rINST, 4)  # vAA<- %ecx
+    FGETOP_JMP  2, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopWide2addr.S b/vm/mterp/x86-atom/binopWide2addr.S
new file mode 100644
index 0000000..f9aa29c
--- /dev/null
+++ b/vm/mterp/x86-atom/binopWide2addr.S
@@ -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.
+    */
+
+   /*
+    * File: binopWide2addr.S
+    *
+    * Code: Generic 64-bit "/2addr" binary operation.  Provides an
+    *       "instr" line to specify an instruction that performs
+    *       "%xmm0= %xmm0 op %xmm1".
+    *
+    * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+    *      or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+    *
+    * Description: Perform a binary operation on two sources registers
+    *              and store the result in the first source register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+    movl        rINST, %edx             # %edx<- BA
+    shr         $$4, rINST              # rINST<- B
+    andl        $$15, %edx              # %edx<- A
+    movq        (rFP, rINST, 4), %xmm1  # %xmm1<- vB
+    movq        (rFP, %edx, 4), %xmm0   # %xmm0<- vA
+    $instr                              # %xmm0<- vA op vB
+    movq        %xmm0, (rFP, %edx, 4)   # vA<- %xmm0; result
+    FINISH      1                       # jump to next instruction
diff --git a/vm/mterp/x86-atom/entry.S b/vm/mterp/x86-atom/entry.S
new file mode 100644
index 0000000..9d7f61e
--- /dev/null
+++ b/vm/mterp/x86-atom/entry.S
@@ -0,0 +1,384 @@
+   /* 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.
+    */
+
+   /*
+    * File: entry.S
+    */
+
+#define ASSIST_DEBUGGER 1
+    .text
+    .align      2
+    .global     dvmMterpStdRun
+    .type       dvmMterpStdRun, %function
+
+   /*
+    * Save registers, initialize sp and fp.
+    * On entry:
+    *     bool MterpGlue(glue *)
+    */
+
+    .macro      MTERP_ENTRY
+    movl        4(%esp), %ecx           # get first argument
+    movl        %ebp, -4(%esp)          # save caller base pointer
+    movl        %ebx, -8(%esp)          # save %ebx
+    movl        %esi, -12(%esp)         # save %esi
+    movl        %edi, -16(%esp)         # save %edi
+    lea         -40(%esp), %ebp         # set callee base pointer
+    lea         -40(%esp), %esp         # set callee stack pointer
+    .endm
+
+   /*
+    * Restore registers.
+    * This function returns a boolean "changeInterp" value.
+    * The return value is from dvmMterpStdBail().
+    */
+
+    .macro      MTERP_EXIT
+    lea         40(%esp), %esp          # correct stack pointer
+    movl        -16(%esp), %edi         # restore %edi
+    movl        -12(%esp), %esi         # restore %esi
+    movl        -8(%esp), %ebx          # restore %ebx
+    movl        -4(%esp), %ebp          # restore caller base pointer
+    ret                                 # return
+    .endm
+
+   /*
+    * DvmMterpStdRun entry point: save stack pointer, setup memory locations, get
+    * entry point, start executing instructions.
+    */
+
+dvmMterpStdRun:
+    MTERP_ENTRY
+    movl        %ecx, rGLUE             # save value for pMterpGlue
+    movl        offGlue_pc(%ecx), rPC   # get program counter
+    cmp         $$kInterpEntryInstr, offGlue_entryPoint(%ecx) # check instruction
+    movl        offGlue_fp(%ecx), rFP   # get frame pointer
+    movl        %esp, offGlue_bailPtr(%ecx) # save SP for eventual return
+    FFETCH      %edx                    # %edx<- opcode
+    jne         .Lnot_instr             # no, handle it
+    FGETOP_JMPa %edx                    # start executing the instruction at rPC
+
+   /*
+    * Not an instruction. Are we returning from a method?
+    */
+
+.Lnot_instr:
+    cmpl        $$kInterpEntryReturn, offGlue_entryPoint(%ecx)
+    je          common_returnFromMethod
+
+   /*
+    * No, are we throwing an exception?
+    */
+
+.Lnot_return:
+    cmpl        $$kInterpEntryThrow, offGlue_entryPoint(%ecx)
+    je          common_exceptionThrown
+
+   /*
+    * No, then we must abort.
+    */
+
+.Lbad_arg:
+    pushl       offGlue_entryPoint(%ecx)
+    movl        $$.LstrBadEntryPoint, -4(%esp)
+    lea         -4(%esp), %esp
+    call        printf
+    lea         8(%esp), %esp
+    call        dvmAbort                # call (void)
+
+   /*
+    * Restore the stack pointer and PC from the save point established on entry and
+    * return to whoever called dvmMterpStdRun.
+    *
+    * On entry:
+    *  4(%esp) MterpGlue* glue
+    *  8(%esp) bool changeInterp
+    */
+
+    .global     dvmMterpStdBail
+    .type       dvmMterpStdBail, %function
+
+dvmMterpStdBail:
+    movl        4(%esp), %ecx           # get first argument
+    movl        8(%esp), %eax           # get second argument
+    movl        offGlue_bailPtr(%ecx), %esp # sp <- saved SP
+    MTERP_EXIT
+
+   /*
+    * String references.
+    */
+
+.LstrBadEntryPoint:
+    .asciz "Bad entry point %d\n"
+
+
+dvmAsmInstructionJmpTable = .LdvmAsmInstructionJmpTable
+.LdvmAsmInstructionJmpTable:
+.long .L_OP_NOP
+.long .L_OP_MOVE
+.long .L_OP_MOVE_FROM16
+.long .L_OP_MOVE_16
+.long .L_OP_MOVE_WIDE
+.long .L_OP_MOVE_WIDE_FROM16
+.long .L_OP_MOVE_WIDE_16
+.long .L_OP_MOVE_OBJECT
+.long .L_OP_MOVE_OBJECT_FROM16
+.long .L_OP_MOVE_OBJECT_16
+.long .L_OP_MOVE_RESULT
+.long .L_OP_MOVE_RESULT_WIDE
+.long .L_OP_MOVE_RESULT_OBJECT
+.long .L_OP_MOVE_EXCEPTION
+.long .L_OP_RETURN_VOID
+.long .L_OP_RETURN
+.long .L_OP_RETURN_WIDE
+.long .L_OP_RETURN_OBJECT
+.long .L_OP_CONST_4
+.long .L_OP_CONST_16
+.long .L_OP_CONST
+.long .L_OP_CONST_HIGH16
+.long .L_OP_CONST_WIDE_16
+.long .L_OP_CONST_WIDE_32
+.long .L_OP_CONST_WIDE
+.long .L_OP_CONST_WIDE_HIGH16
+.long .L_OP_CONST_STRING
+.long .L_OP_CONST_STRING_JUMBO
+.long .L_OP_CONST_CLASS
+.long .L_OP_MONITOR_ENTER
+.long .L_OP_MONITOR_EXIT
+.long .L_OP_CHECK_CAST
+.long .L_OP_INSTANCE_OF
+.long .L_OP_ARRAY_LENGTH
+.long .L_OP_NEW_INSTANCE
+.long .L_OP_NEW_ARRAY
+.long .L_OP_FILLED_NEW_ARRAY
+.long .L_OP_FILLED_NEW_ARRAY_RANGE
+.long .L_OP_FILL_ARRAY_DATA
+.long .L_OP_THROW
+.long .L_OP_GOTO
+.long .L_OP_GOTO_16
+.long .L_OP_GOTO_32
+.long .L_OP_PACKED_SWITCH
+.long .L_OP_SPARSE_SWITCH
+.long .L_OP_CMPL_FLOAT
+.long .L_OP_CMPG_FLOAT
+.long .L_OP_CMPL_DOUBLE
+.long .L_OP_CMPG_DOUBLE
+.long .L_OP_CMP_LONG
+.long .L_OP_IF_EQ
+.long .L_OP_IF_NE
+.long .L_OP_IF_LT
+.long .L_OP_IF_GE
+.long .L_OP_IF_GT
+.long .L_OP_IF_LE
+.long .L_OP_IF_EQZ
+.long .L_OP_IF_NEZ
+.long .L_OP_IF_LTZ
+.long .L_OP_IF_GEZ
+.long .L_OP_IF_GTZ
+.long .L_OP_IF_LEZ
+.long .L_OP_UNUSED_3E
+.long .L_OP_UNUSED_3F
+.long .L_OP_UNUSED_40
+.long .L_OP_UNUSED_41
+.long .L_OP_UNUSED_42
+.long .L_OP_UNUSED_43
+.long .L_OP_AGET
+.long .L_OP_AGET_WIDE
+.long .L_OP_AGET_OBJECT
+.long .L_OP_AGET_BOOLEAN
+.long .L_OP_AGET_BYTE
+.long .L_OP_AGET_CHAR
+.long .L_OP_AGET_SHORT
+.long .L_OP_APUT
+.long .L_OP_APUT_WIDE
+.long .L_OP_APUT_OBJECT
+.long .L_OP_APUT_BOOLEAN
+.long .L_OP_APUT_BYTE
+.long .L_OP_APUT_CHAR
+.long .L_OP_APUT_SHORT
+.long .L_OP_IGET
+.long .L_OP_IGET_WIDE
+.long .L_OP_IGET_OBJECT
+.long .L_OP_IGET_BOOLEAN
+.long .L_OP_IGET_BYTE
+.long .L_OP_IGET_CHAR
+.long .L_OP_IGET_SHORT
+.long .L_OP_IPUT
+.long .L_OP_IPUT_WIDE
+.long .L_OP_IPUT_OBJECT
+.long .L_OP_IPUT_BOOLEAN
+.long .L_OP_IPUT_BYTE
+.long .L_OP_IPUT_CHAR
+.long .L_OP_IPUT_SHORT
+.long .L_OP_SGET
+.long .L_OP_SGET_WIDE
+.long .L_OP_SGET_OBJECT
+.long .L_OP_SGET_BOOLEAN
+.long .L_OP_SGET_BYTE
+.long .L_OP_SGET_CHAR
+.long .L_OP_SGET_SHORT
+.long .L_OP_SPUT
+.long .L_OP_SPUT_WIDE
+.long .L_OP_SPUT_OBJECT
+.long .L_OP_SPUT_BOOLEAN
+.long .L_OP_SPUT_BYTE
+.long .L_OP_SPUT_CHAR
+.long .L_OP_SPUT_SHORT
+.long .L_OP_INVOKE_VIRTUAL
+.long .L_OP_INVOKE_SUPER
+.long .L_OP_INVOKE_DIRECT
+.long .L_OP_INVOKE_STATIC
+.long .L_OP_INVOKE_INTERFACE
+.long .L_OP_UNUSED_73
+.long .L_OP_INVOKE_VIRTUAL_RANGE
+.long .L_OP_INVOKE_SUPER_RANGE
+.long .L_OP_INVOKE_DIRECT_RANGE
+.long .L_OP_INVOKE_STATIC_RANGE
+.long .L_OP_INVOKE_INTERFACE_RANGE
+.long .L_OP_UNUSED_79
+.long .L_OP_UNUSED_7A
+.long .L_OP_NEG_INT
+.long .L_OP_NOT_INT
+.long .L_OP_NEG_LONG
+.long .L_OP_NOT_LONG
+.long .L_OP_NEG_FLOAT
+.long .L_OP_NEG_DOUBLE
+.long .L_OP_INT_TO_LONG
+.long .L_OP_INT_TO_FLOAT
+.long .L_OP_INT_TO_DOUBLE
+.long .L_OP_LONG_TO_INT
+.long .L_OP_LONG_TO_FLOAT
+.long .L_OP_LONG_TO_DOUBLE
+.long .L_OP_FLOAT_TO_INT
+.long .L_OP_FLOAT_TO_LONG
+.long .L_OP_FLOAT_TO_DOUBLE
+.long .L_OP_DOUBLE_TO_INT
+.long .L_OP_DOUBLE_TO_LONG
+.long .L_OP_DOUBLE_TO_FLOAT
+.long .L_OP_INT_TO_BYTE
+.long .L_OP_INT_TO_CHAR
+.long .L_OP_INT_TO_SHORT
+.long .L_OP_ADD_INT
+.long .L_OP_SUB_INT
+.long .L_OP_MUL_INT
+.long .L_OP_DIV_INT
+.long .L_OP_REM_INT
+.long .L_OP_AND_INT
+.long .L_OP_OR_INT
+.long .L_OP_XOR_INT
+.long .L_OP_SHL_INT
+.long .L_OP_SHR_INT
+.long .L_OP_USHR_INT
+.long .L_OP_ADD_LONG
+.long .L_OP_SUB_LONG
+.long .L_OP_MUL_LONG
+.long .L_OP_DIV_LONG
+.long .L_OP_REM_LONG
+.long .L_OP_AND_LONG
+.long .L_OP_OR_LONG
+.long .L_OP_XOR_LONG
+.long .L_OP_SHL_LONG
+.long .L_OP_SHR_LONG
+.long .L_OP_USHR_LONG
+.long .L_OP_ADD_FLOAT
+.long .L_OP_SUB_FLOAT
+.long .L_OP_MUL_FLOAT
+.long .L_OP_DIV_FLOAT
+.long .L_OP_REM_FLOAT
+.long .L_OP_ADD_DOUBLE
+.long .L_OP_SUB_DOUBLE
+.long .L_OP_MUL_DOUBLE
+.long .L_OP_DIV_DOUBLE
+.long .L_OP_REM_DOUBLE
+.long .L_OP_ADD_INT_2ADDR
+.long .L_OP_SUB_INT_2ADDR
+.long .L_OP_MUL_INT_2ADDR
+.long .L_OP_DIV_INT_2ADDR
+.long .L_OP_REM_INT_2ADDR
+.long .L_OP_AND_INT_2ADDR
+.long .L_OP_OR_INT_2ADDR
+.long .L_OP_XOR_INT_2ADDR
+.long .L_OP_SHL_INT_2ADDR
+.long .L_OP_SHR_INT_2ADDR
+.long .L_OP_USHR_INT_2ADDR
+.long .L_OP_ADD_LONG_2ADDR
+.long .L_OP_SUB_LONG_2ADDR
+.long .L_OP_MUL_LONG_2ADDR
+.long .L_OP_DIV_LONG_2ADDR
+.long .L_OP_REM_LONG_2ADDR
+.long .L_OP_AND_LONG_2ADDR
+.long .L_OP_OR_LONG_2ADDR
+.long .L_OP_XOR_LONG_2ADDR
+.long .L_OP_SHL_LONG_2ADDR
+.long .L_OP_SHR_LONG_2ADDR
+.long .L_OP_USHR_LONG_2ADDR
+.long .L_OP_ADD_FLOAT_2ADDR
+.long .L_OP_SUB_FLOAT_2ADDR
+.long .L_OP_MUL_FLOAT_2ADDR
+.long .L_OP_DIV_FLOAT_2ADDR
+.long .L_OP_REM_FLOAT_2ADDR
+.long .L_OP_ADD_DOUBLE_2ADDR
+.long .L_OP_SUB_DOUBLE_2ADDR
+.long .L_OP_MUL_DOUBLE_2ADDR
+.long .L_OP_DIV_DOUBLE_2ADDR
+.long .L_OP_REM_DOUBLE_2ADDR
+.long .L_OP_ADD_INT_LIT16
+.long .L_OP_RSUB_INT
+.long .L_OP_MUL_INT_LIT16
+.long .L_OP_DIV_INT_LIT16
+.long .L_OP_REM_INT_LIT16
+.long .L_OP_AND_INT_LIT16
+.long .L_OP_OR_INT_LIT16
+.long .L_OP_XOR_INT_LIT16
+.long .L_OP_ADD_INT_LIT8
+.long .L_OP_RSUB_INT_LIT8
+.long .L_OP_MUL_INT_LIT8
+.long .L_OP_DIV_INT_LIT8
+.long .L_OP_REM_INT_LIT8
+.long .L_OP_AND_INT_LIT8
+.long .L_OP_OR_INT_LIT8
+.long .L_OP_XOR_INT_LIT8
+.long .L_OP_SHL_INT_LIT8
+.long .L_OP_SHR_INT_LIT8
+.long .L_OP_USHR_INT_LIT8
+.long .L_OP_IGET_VOLATILE
+.long .L_OP_IPUT_VOLATILE
+.long .L_OP_SGET_VOLATILE
+.long .L_OP_SPUT_VOLATILE
+.long .L_OP_IGET_OBJECT_VOLATILE
+.long .L_OP_IGET_WIDE_VOLATILE
+.long .L_OP_IPUT_WIDE_VOLATILE
+.long .L_OP_SGET_WIDE_VOLATILE
+.long .L_OP_SPUT_WIDE_VOLATILE
+.long .L_OP_BREAKPOINT
+.long .L_OP_THROW_VERIFICATION_ERROR
+.long .L_OP_EXECUTE_INLINE
+.long .L_OP_EXECUTE_INLINE_RANGE
+.long .L_OP_INVOKE_DIRECT_EMPTY
+.long .L_OP_UNUSED_F1
+.long .L_OP_IGET_QUICK
+.long .L_OP_IGET_WIDE_QUICK
+.long .L_OP_IGET_OBJECT_QUICK
+.long .L_OP_IPUT_QUICK
+.long .L_OP_IPUT_WIDE_QUICK
+.long .L_OP_IPUT_OBJECT_QUICK
+.long .L_OP_INVOKE_VIRTUAL_QUICK
+.long .L_OP_INVOKE_VIRTUAL_QUICK_RANGE
+.long .L_OP_INVOKE_SUPER_QUICK
+.long .L_OP_INVOKE_SUPER_QUICK_RANGE
+.long .L_OP_IPUT_OBJECT_VOLATILE
+.long .L_OP_SGET_OBJECT_VOLATILE
+.long .L_OP_SPUT_OBJECT_VOLATILE
+.long .L_OP_UNUSED_FF
diff --git a/vm/mterp/x86-atom/footer.S b/vm/mterp/x86-atom/footer.S
new file mode 100644
index 0000000..cb9970d
--- /dev/null
+++ b/vm/mterp/x86-atom/footer.S
@@ -0,0 +1,662 @@
+   /* 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.
+    */
+
+   /*
+    * File: footer.S
+    */
+
+    .text
+    .align      2
+
+   /*
+    * Check to see if the thread needs to be suspended or debugger/profiler
+    * activity has begun.
+    *
+    * On entry:
+    *  %ecx is reentry type, e.g. kInterpEntryInstr
+    *  %edx is PC adjustment in bytes
+    */
+
+common_periodicChecks:
+    movl        %edx, -8(%esp)          # save pc adjustments
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        %ebx, -4(%esp)          # save %ebx to the stack
+    movl        offGlue_pSelfSuspendCount(%edx), %ebx # %ebx<- pSuspendCount (int)
+4:
+    movl        offGlue_pDebuggerActive(%edx), %eax # %eax<- pDebuggerActive
+    testl       %eax, %eax
+    je          5f
+    movzbl        (%eax), %eax            # %eax<- get debuggerActive (boolean)
+5:
+    cmp         $$0, (%ebx)             # check if suspend is pending
+    jne         2f                      # handle suspend
+    movl        offGlue_pActiveProfilers(%edx), %ebx # %ebx<- activeProfilers (int)
+    orl         (%ebx), %eax            # %eax<- merge activeProfilers and debuggerActive
+    movl        -8(%esp), %edx          # %edx<- restore %edx
+    jne         3f                      # debugger or profiler active; switch interp
+    movl        -4(%esp), %ebx          # %ebx<- restore %ebx
+    ret                                 # return
+2:                                      # check suspended
+    EXPORT_PC
+    movl        offGlue_self(%edx), %eax # %eax<- glue->self
+    movl        %eax, -12(%esp)         # push parameter boolean
+    lea         -12(%esp), %esp
+    call        dvmCheckSuspendPending  # call: (Thread* self)
+                                        # return: bool
+    movl        4(%esp), %edx           # %edx<- restore %edx
+    movl        8(%esp), %ebx           # %ebx<- restore %ebx
+    lea         12(%esp), %esp
+    ret
+3:                                      # debugger/profiler enabled, bail out
+    leal        (rPC, %edx, 2), rPC     # adjust pc to show target
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movb        $$kInterpEntryInstr, offGlue_entryPoint(%ecx)
+    movl        $$1, %edx               # switch interpreter
+    jmp         common_gotoBail         # bail
+
+   /*
+    * Check to see if the thread needs to be suspended or debugger/profiler
+    * activity has begun. With this variant, the reentry type is hard coded
+    * as kInterpEntryInstr.
+    *
+    * On entry:
+    *  %edx is PC adjustment in bytes
+    */
+
+common_periodicChecks_backwardBranch:
+    EXPORT_PC
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_pSelfSuspendCount(%ecx), rINST # %ebx<- pSuspendCount (int)
+4:
+    movl        offGlue_pDebuggerActive(%ecx), %eax # %eax<- pDebuggerActive
+    testl       %eax, %eax              # test for NULL pointer
+    je          5f
+    movzbl      (%eax), %eax            # %eax<- get debuggerActive count
+5:
+    cmp         $$0, (rINST)            # check if suspend is pending
+    jne         2f                      # handle suspend
+    movl        offGlue_pActiveProfilers(%ecx), rINST # %edx<- activeProfilers (int)
+    orl          (rINST), %eax           # %eax<- merge activeProfilers and debuggerActive
+    jne         3f                      # debugger or profiler active; switch interp
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+2:                                      # check suspended
+    movl        offGlue_self(%ecx), %eax# %eax<- glue->self
+    movl        %edx, rINST
+    movl        %eax, -12(%esp)         # push parameter boolean
+    lea         -12(%esp), %esp
+    call        dvmCheckSuspendPending  # call: (Thread* self)
+                                        # return: bool
+    movl        rINST, %edx             # %edx<- restore %edx
+    lea         12(%esp), %esp
+    FINISH_RB   %edx, %ecx
+3:                                      # debugger/profiler enabled, bail out
+    leal        (rPC, %edx, 2), rPC     # adjust pc to show target
+    movb        $$kInterpEntryInstr, offGlue_entryPoint(%ecx)
+    movl        $$1, %edx               # switch interpreter
+    jmp         common_gotoBail         # bail
+
+   /*
+    * The equivalent of "goto bail", this calls through the "bail handler".
+    * State registers will be saved to the "glue" area before bailing.
+    *
+    * On entry:
+    *  %edx is "bool changeInterp", indicating if we want to switch to the
+    *     other interpreter or just bail all the way out
+    */
+
+common_gotoBail:
+    SAVE_PC_FP_TO_GLUE %ecx             # save program counter and frame pointer
+
+   /*
+    * Inlined dvmMterpStdBail
+    */
+
+    lea         40(%ebp), %esp
+    movl        %edx, %eax
+    movl        24(%ebp), %edi
+    movl        28(%ebp), %esi
+    movl        32(%ebp), %ebx
+    movl        36(%ebp), %ebp
+    ret
+
+   /*
+    * Common code for method invocation with range.
+    *
+    * On entry:
+    *  %ecx is "Method* methodToCall", the method we're trying to call
+    */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+
+   /*
+    * prepare to copy args to "outs" area of current frame
+    */
+
+    SAVEAREA_FROM_FP %eax               # %eax<- &outs; &StackSaveArea
+    test        rINST, rINST            # test for no args
+    movl        rINST, sReg0            # sReg0<- AA
+    jz          .LinvokeArgsDone        # no args; jump to args done
+    FETCH       2, %edx                 # %edx<- CCCC
+
+   /*
+    * %ecx=methodToCall, %edx=CCCC, sReg0=count, %eax=&outs (&stackSaveArea)
+    * (very few methods have > 10 args; could unroll for common cases)
+    */
+
+    movl        %ebx, sReg1             # sReg1<- save %ebx
+    lea         (rFP, %edx, 4), %edx    # %edx<- &vCCCC
+    shll        $$2, sReg0              # sReg0<- offset
+    subl        sReg0, %eax             # %eax<- update &outs
+    shrl        $$2, sReg0              # sReg0<- offset
+1:
+    movl        (%edx), %ebx            # %ebx<- vCCCC
+    lea         4(%edx), %edx           # %edx<- &vCCCC++
+    subl        $$1, sReg0              # sReg<- sReg--
+    movl        %ebx, (%eax)            # *outs<- vCCCC
+    lea         4(%eax), %eax           # outs++
+    jne         1b                      # loop if count (sReg0) not zero
+    movl        sReg1, %ebx             # %ebx<- restore %ebx
+    jmp         .LinvokeArgsDone        # continue
+
+   /*
+    * %ecx is "Method* methodToCall", the method we're trying to call
+    * prepare to copy args to "outs" area of current frame
+    */
+
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    movl        rINST, sReg0            # sReg0<- BA
+    shrl        $$4, sReg0              # sReg0<- B
+    je          .LinvokeArgsDone        # no args; jump to args done
+    SAVEAREA_FROM_FP %eax               # %eax<- &outs; &StackSaveArea
+    FETCH       2, %edx                 # %edx<- GFED
+
+   /*
+    * %ecx=methodToCall, %edx=GFED, sReg0=count, %eax=outs
+    */
+
+.LinvokeNonRange:
+    cmp         $$2, sReg0              # compare sReg0 to 2
+    movl        %edx, sReg1             # sReg1<- GFED
+    jl          1f                      # handle 1 arg
+    je          2f                      # handle 2 args
+    cmp         $$4, sReg0              # compare sReg0 to 4
+    jl          3f                      # handle 3 args
+    je          4f                      # handle 4 args
+5:
+    andl        $$15, rINST             # rINST<- A
+    lea         -4(%eax), %eax          # %eax<- update &outs; &outs--
+    movl        (rFP, rINST, 4), %edx   # %edx<- vA
+    movl        %edx, (%eax)            # *outs<- vA
+    movl        sReg1, %edx             # %edx<- GFED
+4:
+    shr         $$12, %edx              # %edx<- G
+    lea         -4(%eax), %eax          # %eax<- update &outs; &outs--
+    movl        (rFP, %edx, 4), %edx    # %edx<- vG
+    movl        %edx, (%eax)            # *outs<- vG
+    movl        sReg1, %edx             # %edx<- GFED
+3:
+    and         $$0x0f00, %edx          # %edx<- 0F00
+    shr         $$6, %edx               # %edx<- F at correct offset
+    lea         -4(%eax), %eax          # %eax<- update &outs; &outs--
+    movl        (rFP, %edx), %edx       # %edx<- vF
+    movl        %edx, (%eax)            # *outs<- vF
+    movl        sReg1, %edx             # %edx<- GFED
+2:
+    and         $$0x00f0, %edx          # %edx<- 00E0
+    shr         $$2, %edx               # %edx<- E at correct offset
+    lea         -4(%eax), %eax          # %eax<- update &outs; &outs--
+    movl        (rFP, %edx), %edx       # %edx<- vE
+    movl        %edx, (%eax)            # *outs<- vE
+    movl        sReg1, %edx             # %edx<- GFED
+1:
+    and         $$0x000f, %edx          # %edx<- 000D
+    movl        (rFP, %edx, 4), %edx    # %edx<- vD
+    movl        %edx, -4(%eax)          # *--outs<- vD
+0:
+
+   /*
+    * %ecx is "Method* methodToCall", the method we're trying to call
+    * find space for the new stack frame, check for overflow
+    */
+
+.LinvokeArgsDone:
+    movzwl      offMethod_registersSize(%ecx), %eax # %eax<- methodToCall->regsSize
+    movzwl      offMethod_outsSize(%ecx), %edx # %edx<- methodToCall->outsSize
+    movl        %ecx, sReg0             # sReg<- methodToCall
+    shl         $$2, %eax               # %eax<- update offset
+    SAVEAREA_FROM_FP %ecx               # %ecx<- &outs; &StackSaveArea
+    subl        %eax, %ecx              # %ecx<- newFP; (old savearea - regsSize)
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        %ecx, sReg1             # sReg1<- &outs
+    subl        $$sizeofStackSaveArea, %ecx # %ecx<- newSaveArea (stack save area using newFP)
+    movl        offGlue_interpStackEnd(%eax), %eax # %eax<- glue->interpStackEnd
+    movl        %eax, sReg2             # sReg2<- glue->interpStackEnd
+    shl         $$2, %edx               # %edx<- update offset for outsSize
+    movl        %ecx, %eax              # %eax<- newSaveArea
+    sub         %edx, %ecx              # %ecx<- bottom; (newSaveArea - outsSize)
+    cmp         sReg2, %ecx             # compare interpStackEnd and bottom
+    movl        sReg0, %ecx             # %ecx<- restore methodToCall
+    jl          .LstackOverflow         # handle frame overflow
+
+   /*
+    * set up newSaveArea
+    */
+
+#ifdef EASY_GDB
+    SAVEAREA_FROM_FP %edx               # %edx<- &outs; &StackSaveArea
+    movl        %edx, offStackSaveArea_prevSave(%eax) # newSaveArea->prevSave<- &outs
+#endif
+    movl        rFP, offStackSaveArea_prevFrame(%eax) # newSaveArea->prevFrame<- rFP
+    movl        rPC, offStackSaveArea_savedPc(%eax) # newSaveArea->savedPc<- rPC
+    testl       $$ACC_NATIVE, offMethod_accessFlags(%ecx) # check for native call
+    movl        %ecx, offStackSaveArea_method(%eax) # newSaveArea->method<- method to call
+    jne         .LinvokeNative          # handle native call
+
+   /*
+    * Update "glue" values for the new method
+    * %ecx=methodToCall, sReg1=newFp
+    */
+
+    movl        offMethod_clazz(%ecx), %edx # %edx<- method->clazz
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        %ecx, offGlue_method(%eax) # glue->method<- methodToCall
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        offMethod_insns(%ecx), rPC # rPC<- methodToCall->insns
+    movl        %edx, offGlue_methodClassDex(%eax) # glue->methodClassDex<- method->clazz->pDvmDex
+    movl        offGlue_self(%eax), %ecx # %ecx<- glue->self
+    movl        sReg1, rFP              # rFP<- newFP
+    movl        rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP
+    FINISH_A                            # jump to methodToCall->insns
+
+   /*
+    * Prep for the native call
+    * %ecx=methodToCall, sReg1=newFP, %eax=newSaveArea
+    */
+
+.LinvokeNative:
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        %ecx, -20(%esp)         # push parameter methodToCall
+    movl        offGlue_self(%edx), %edx # %edx<- glue->self
+    movl        offThread_jniLocal_topCookie(%edx), %ecx # %ecx<- glue->self->thread->refNext
+    movl        %ecx, offStackSaveArea_localRefCookie(%eax) # newSaveArea->localRefCookie<- refNext
+    movl        %eax, -4(%esp)          # save newSaveArea
+    movl        sReg1, %eax             # %eax<- newFP
+    movl        %eax, offThread_curFrame(%edx) # glue->self->curFrame<- newFP
+    movl        %edx, -8(%esp)          # save glue->self
+    movl        %edx, -16(%esp)         # push parameter glue->self
+    movl        rGLUE, %edx             # %edx<- pMterpGlue
+    movl        -20(%esp), %ecx         # %ecx<- methodToCall
+    lea         offGlue_retval(%edx), %edx # %edx<- &retval
+    movl        %edx, -24(%esp)         # push parameter pMterpGlue
+    movl        %eax, -28(%esp)         # push parameter newFP
+    lea         -28(%esp), %esp
+
+#ifdef ASSIST_DEBUGGER
+    jmp         .Lskip
+    .type       dalvik_mterp, %function
+dalvik_mterp:
+    MTERP_ENTRY
+.Lskip:
+#endif
+    call        *offMethod_nativeFunc(%ecx) # call methodToCall->nativeFunc
+    lea         28(%esp), %esp
+    movl        -4(%esp), %edx          # %edx<- newSaveArea
+    movl        -8(%esp), %ecx          # %ecx<- glue->self
+    movl        offStackSaveArea_localRefCookie(%edx), %eax  # %eax<- newSaveArea->localRefCookie
+    FFETCH_ADV  3, %edx                 # %edx<- next instruction hi; fetch, advance
+    cmp         $$0, offThread_exception(%ecx) # check for exception
+    movl        rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP
+    movl        %eax, offThread_jniLocal_topCookie(%ecx) # glue->self<- newSaveArea->localRefCookie
+    jne         common_exceptionThrown  # handle exception
+    FGETOP_JMP  3, %edx                 # jump to next instruction; getop, jmp
+
+.LstackOverflow:
+    movl        %ecx, -4(%esp)          # push method to call
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offGlue_self(%ecx), %ecx # %ecx<- glue->self
+    movl        %ecx, -8(%esp)          # push parameter self
+    lea         -8(%esp), %esp
+    call        dvmHandleStackOverflow  # call: (Thread* self, Method *meth)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+#ifdef ASSIST_DEBUGGER
+#endif
+
+   /*
+    * Common code for handling a return instruction.
+    *
+    * This does not return.
+    */
+
+common_returnFromMethod:
+.LreturnNew:
+
+   /*
+    * Inline common periodic checks
+    */
+
+    movl        rGLUE, rINST            # %ecx<- pMterpGlue
+    movl        offGlue_pSelfSuspendCount(rINST), %edx # %ebx<- pSuspendCount (int)
+    movl        offGlue_pDebuggerActive(rINST), %eax # %eax<- pDebuggerActive
+    movl        (%eax), %eax            # %eax<- get debuggerActive (boolean)
+    and         $$7, %eax               # %eax<- mask for boolean (just how many bits does it take?)
+    cmp         $$0, (%edx)             # check if suspend is pending
+    jne         2f                      # handle suspend
+    movl        offGlue_pActiveProfilers(rINST), %edx # %edx<- activeProfilers (int)
+    or          (%edx), %eax            # %eax<- merge activeProfilers and debuggerActive
+    cmp         $$0, %eax               # check for debuggerActive
+    jne         3f                      # debugger or profiler active; switch interp
+    jmp         4f
+2:                                      # check suspended
+    movl        offGlue_self(rINST), %eax# %eax<- glue->self
+    movl        %eax, -12(%esp)         # push parameter boolean
+    lea         -12(%esp), %esp
+    call        dvmCheckSuspendPending  # call: (Thread* self)
+                                        # return: bool
+    lea         12(%esp), %esp
+    jmp         4f
+3:                                      # debugger/profiler enabled, bail out
+    movl        $$kInterpEntryInstr, offGlue_entryPoint(rINST) # glue->entryPoint<- reentry type
+    movl        $$1, %edx               # switch to interp<- true
+    jmp         common_gotoBail         # bail
+
+
+   /*
+    * Get save area; rGLUE is %ebx, rFP is %eax
+    */
+4:
+    SAVEAREA_FROM_FP %ecx               # %ecx<- saveArea(old)
+    movl        offStackSaveArea_prevFrame(%ecx), rFP # rFP<- saveArea->PrevFrame
+    movl        (offStackSaveArea_method - sizeofStackSaveArea)(rFP), %edx # %edx<- method we are returning to
+    cmpl        $$0, %edx               # check for break frame
+    je          common_gotoBail         # bail if break frame
+    movl        offStackSaveArea_savedPc(%ecx), rPC # rPC<- saveAreaOld->savedPc
+    movl        offGlue_self(rINST), %ecx # %eax<- glue->self
+    movl        %edx, offGlue_method(rINST) # glue->method<- newSave->method
+    movl        offMethod_clazz(%edx), %edx # %edx<- method->clazz
+    FFETCH_ADV  3, %eax                 # %ecx<- next instruction hi; fetch, advance
+    movl        rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        %edx, offGlue_methodClassDex(rINST) # glue->pDvmDex<- method->clazz->pDvmDex
+    FGETOP_JMP  3, %eax                 # jump to next instruction; getop, jmp
+
+   /*
+    * Handle thrown an exception. 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.
+    */
+
+common_exceptionThrown:
+.LexceptionNew:
+    movl        $$kInterpEntryThrow, %ecx # %ecx<- reentry type
+    movl        $$0, %edx               # %edx<- pc adjustment
+    call        common_periodicChecks
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_self(%eax), %edx # %edx<- glue->self
+    movl        offThread_exception(%edx), %ecx # %ecx<- pMterpGlue->self->exception
+    movl        %edx, -4(%esp)          # push parameter self
+    movl        %ecx, -8(%esp)          # push parameter obj
+    lea         -8(%esp), %esp
+    call        dvmAddTrackedAlloc      # don't allow the exception to be GC'd
+                                        # call: (Object* obj, Thread* self)
+                                        # return: void
+    movl        4(%esp), %edx           # %edx<- glue->self
+    movl        $$0, offThread_exception(%edx) # glue->self->exception<- NULL
+
+   /*
+    * set up args and a local for &fp
+    */
+
+    movl        rFP, -4(%esp)           # move fp to stack
+    lea         -4(%esp), %esp          # update %esp
+    movl        %esp, -4(%esp)          # push parameter 4<- &fp
+    movl        $$0, -8(%esp)           # push parameter 3<- false
+    movl        4(%esp), %edx
+    movl        %edx, -12(%esp)         # push parameter 2<- glue->self->exception
+    movl        rGLUE, %eax             # %eax<- pMterpGlue
+    movl        offGlue_method(%eax), %edx # %edx<- glue->method
+    movl        offMethod_insns(%edx), %edx # %edx<- glue->method->insns
+    movl        rPC, %ecx               # %ecx<- rPC
+    subl        %edx, %ecx              # %ecx<- pc - glue->method->insns
+    sar         $$1, %ecx               # %ecx<- adjust %ecx for offset
+    movl        %ecx, -16(%esp)         # push parameter 1<- glue->method->insns
+    movl        8(%esp), %edx
+    movl        %edx, -20(%esp)         # push parameter 0<- glue->self
+    lea         -20(%esp), %esp
+
+   /*
+    * call dvmFindCatchBlock, %eax gets catchRelPc (a code-unit offset)
+    */
+
+    call        dvmFindCatchBlock       # call: (Thread* self, int relPc, Object* exception,
+                                        #      bool doUnroll, void** newFrame)
+                                        # return: int
+    lea         32(%esp), %esp
+    movl        -12(%esp), rFP          # rFP<- updated rFP
+    cmp         $$0, %eax               # check for catchRelPc < 0
+    jl          .LnotCaughtLocally      # handle not caught locally
+
+   /*
+    * fix stack overflow if necessary
+    */
+
+    movl        -4(%esp), %ecx          # %ecx<- glue->self
+    cmp         $$0, offThread_stackOverflowed(%ecx)
+    je          1f
+    movl        %eax, -4(%esp)          # save %eax for later
+    movl        %ecx, -12(%esp)         # push parameter 2 glue->self
+    lea         -12(%esp), %esp
+    call        dvmCleanupStackOverflow # call: (Thread* self, Object* exception)
+                                        # return: void
+    lea         12(%esp), %esp
+    movl        -4(%esp), %eax          # %eax<- restore %eax
+    jmp         2f
+1:
+    movl        %ecx, -12(%esp)         # push parameter 2 glue->self
+2:
+
+   /*
+    * adjust locals to match self->curFrame and updated PC
+    *
+    */
+
+    SAVEAREA_FROM_FP %edx               # %edx<- get newSaveArea
+    movl        rGLUE, %ecx             # %ecx<- pMterpGlue
+    movl        offStackSaveArea_method(%edx), rPC # rPC<- newMethod
+    movl        rPC, offGlue_method(%ecx) # glue->method<- newMethod
+    movl        offMethod_clazz(rPC), %edx # %edx<- method->clazz
+    movl        offMethod_insns(rPC), rPC # rPC<- method->insns
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    lea         (rPC, %eax, 2), rPC     # rPC<- method->insns + catchRelPc
+    movl        %edx, offGlue_methodClassDex(%ecx) # glue->pDvmDex<- method->clazz->pDvmDex
+    movl        -8(%esp), %eax
+    movl        %eax, -16(%esp)         # push parameter 1 obj
+    lea         -16(%esp), %esp
+    call        dvmReleaseTrackedAlloc  # call: (Object* obj, Thread* self)
+                                        # return: void
+    lea         16(%esp), %esp
+    FINISH_FETCH %eax
+    cmp         $$OP_MOVE_EXCEPTION, %eax # is it a move exception
+    jne         1f
+    movl        -12(%esp), %edx         # %edx<- glue->self
+    movl        -8(%esp), %ecx          # %ecx<- exception
+    movl        %ecx, offThread_exception(%edx) # restore the exception
+1:
+    FINISH_JMP  %eax
+
+   /*
+    * -8(%esp) = exception, -4(%esp) = self
+    */
+
+.LnotCaughtLocally:
+    movl        -4(%esp), %edx          # %edx<- glue->self
+    movzb       offThread_stackOverflowed(%edx), %eax # %eax<- self->stackOverflowed
+    cmp         $$0, %eax               # check for stack overflow;
+                                        # maybe should use cmpb
+    je          1f                      #
+    movl        %edx, -12(%esp)         # push parameter 1 glue->self
+    lea         -12(%esp), %esp
+    call        dvmCleanupStackOverflow # call: (Thread* self, Object* exception)
+                                        # return: void
+    lea         12(%esp), %esp
+
+   /*
+    * Release the exception
+    * -8(%esp) = exception, -4(%esp) = self
+    */
+1:
+    movl        -8(%esp), %ecx          # %ecx<- exception
+    movl        -4(%esp), %edx          # %edx<- glue->self
+    movl        %ecx, offThread_exception(%edx) # glue->self<- exception
+    lea         -8(%esp), %esp
+    call        dvmReleaseTrackedAlloc  # call: (Object* obj, Thread* self)
+                                        # return: void
+    lea         8(%esp), %esp
+    movl        $$0, %edx               # switch to interp<- false
+    jmp         common_gotoBail         # bail
+
+   /*
+    * After returning from a "glued" function, pull out the updated
+    * values and start executing at the next instruction.
+    */
+
+common_resumeAfterGlueCall:
+    LOAD_PC_FP_FROM_GLUE                # pull rPC and rFP out of glue
+    FINISH_A                            # jump to next instruction
+
+   /*
+    * For debugging, cause an immediate fault.
+    */
+
+common_abort:
+    jmp         .LdeadFood
+
+.LdeadFood:
+.int 0xdeadf00d
+
+   /*
+    * Invalid array index.
+    */
+
+common_errArrayIndex:
+    EXPORT_PC
+    movl        $$.LstrArrayIndexException, -8(%esp) # push parameter description
+    movl        $$0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Invalid array value.
+    */
+
+common_errArrayStore:
+    EXPORT_PC
+    movl        $$.LstrArrayStoreException, -8(%esp) # push parameter description
+    movl        $$0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Integer divide or mod by zero.
+    */
+
+common_errDivideByZero:
+    EXPORT_PC
+    movl        $$.LstrArithmeticException, -8(%esp) # push parameter description
+    movl        $$.LstrDivideByZero, -4(%esp) # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Attempt to allocate an array with a negative size.
+    */
+
+common_errNegativeArraySize:
+    EXPORT_PC
+    movl        $$.LstrNegativeArraySizeException, -8(%esp) # push parameter description
+    movl        $$0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Invocation of a non-existent method.
+    */
+
+common_errNoSuchMethod:
+    EXPORT_PC
+    movl        $$.LstrNoSuchMethodError, -8(%esp) # push parameter description
+    movl        $$0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * Unexpected null object.
+    */
+
+common_errNullObject:
+    EXPORT_PC
+    movl        $$.LstrNullPointerException, -8(%esp) # push parameter description
+    movl        $$0, -4(%esp)           # push parameter msg paramter
+    lea         -8(%esp), %esp
+    call        dvmThrowException       # call: (const char* exceptionDescriptor, const char* msg)
+                                        # return: void
+    lea         8(%esp), %esp
+    jmp         common_exceptionThrown  # handle exception
+
+   /*
+    * String references
+    */
+
+    .align 4
+    .section .rodata
+.LstrArithmeticException:
+    .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+    .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+    .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+    .asciz "divide by zero"
+.LstrInstantiationError:
+    .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+    .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+    .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+    .asciz "Ljava/lang/NullPointerException;"
+.LstrExceptionNotCaughtLocally:
+    .asciz "Exception %s from %s:%d not caught locally\n"
diff --git a/vm/mterp/x86-atom/header.S b/vm/mterp/x86-atom/header.S
new file mode 100644
index 0000000..28c19d6
--- /dev/null
+++ b/vm/mterp/x86-atom/header.S
@@ -0,0 +1,433 @@
+   /* 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.
+    */
+
+   /*
+    * File: header.S
+    */
+
+   /*
+    * IA32 calling convention and general notes:
+    *
+    * EAX, ECX, EDX - general purpose scratch registers (caller-saved);
+    *
+    * The stack (%esp) - used to pass arguments to functions
+    *
+    * EAX - holds the first 4 bytes of a return
+    * EDX - holds the second 4 bytes of a return
+    *
+    * EBX, ESI, EDI, EBP - are callee saved
+    *
+    * CS, DS, SS - are segment registers
+    * ES, FS, GS - are segment registers. We will try to avoid using these registers
+    *
+    * The stack is "full descending". Only the arguments that do not fit    * in the first two arg registers are placed on the stack.
+    * "%esp" points to the first stacked argument (i.e. the 3rd arg).
+    */
+
+   /*
+    * Mterp and IA32 notes
+    *
+    * mem          nick      purpose
+    * (%ebp)       rGLUE     InterpState base pointer (A.K.A. MterpGlue Pointer)
+    * %esi         rPC       interpreted program counter, used for fetching
+    *                        instructions
+    * %ebx         rINST     first 16-bit code unit of current instruction
+    * %edi         rFP       interpreted frame pointer, used for accessing
+    *                        locals and args
+    */
+
+   /*
+    * Includes
+    */
+
+#include "../common/asm-constants.h"
+
+   /*
+    * Reserved registers
+    */
+
+#define rGLUE  (%ebp)
+#define rINST   %ebx
+#define rINSTbl  %bl
+#define rINSTbh  %bh
+#define rINSTw  %bx
+#define rPC     %esi
+#define rFP     %edi
+
+   /*
+    * Temporary register used when finishing an opcode
+    */
+
+#define rFinish %edx
+
+   /*
+    * Stack locations used for temporary data. For convenience.
+    */
+
+#define sReg0    4(%ebp)
+#define sReg1    8(%ebp)
+#define sReg2   12(%ebp)
+#define sReg3   16(%ebp)
+
+   /*
+    * Save the PC and FP to the glue struct
+    */
+
+    .macro      SAVE_PC_FP_TO_GLUE _reg
+    movl        rGLUE, \_reg
+    movl        rPC, offGlue_pc(\_reg)
+    movl        rFP, offGlue_fp(\_reg)
+    .endm
+
+   /*
+    * Restore the PC and FP from the glue struct
+    */
+
+    .macro      LOAD_PC_FP_FROM_GLUE
+    movl        rGLUE, rFP
+    movl        offGlue_pc(rFP), rPC
+    movl        offGlue_fp(rFP), rFP
+    .endm
+
+   /*
+    * "Export" the PC to the stack frame, f/b/o future exception objects. This must
+    * be done *before* something calls dvmThrowException.
+    *
+    * 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
+
+   /*
+    * Given a frame pointer, find the stack save area.
+    * In C this is "((StackSaveArea*)(_fp) -1)".
+    */
+
+    .macro      SAVEAREA_FROM_FP  _reg
+    lea         -sizeofStackSaveArea(rFP), \_reg
+    .endm
+
+   /*
+    * Get the 32-bit value from a dalvik register.
+    */
+
+    .macro      GET_VREG _vreg
+    movl        (rFP,\_vreg, 4), \_vreg
+    .endm
+
+   /*
+    * Set the 32-bit value from a dalvik register.
+    */
+
+    .macro      SET_VREG _reg _vreg
+    movl        \_reg, (rFP,\_vreg, 4)
+    .endm
+
+   /*
+    * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+    */
+
+    .macro      FETCH_INST
+    movzwl      (rPC), rINST
+    .endm
+
+   /*
+    * Fetch the next instruction from the specified offset. Advances rPC
+    * to point to the next instruction. "_count" is in 16-bit code units.
+    *
+    * 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())
+    */
+
+    .macro      FETCH_ADVANCE_INST _count
+    add         $$(\_count*2), rPC
+    movzwl      (rPC), rINST
+    .endm
+
+   /*
+    * 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.
+    */
+
+    .macro      FETCH_ADVANCE_INST_RB _reg
+    addl        \_reg, rPC
+    movzwl      (rPC), rINST
+    .endm
+
+   /*
+    * 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.
+    * For example, given instruction of format: AA|op BBBB, it
+    * fetches BBBB.
+    */
+
+    .macro      FETCH _count _reg
+    movzwl      (\_count*2)(rPC), \_reg
+    .endm
+
+   /*
+    * 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.
+    * This variant treats the value as signed.
+    */
+
+    .macro      FETCHs _count _reg
+    movswl      (\_count*2)(rPC), \_reg
+    .endm
+
+   /*
+    * Fetch the first byte from an offset past the current PC. The
+    * "_count" value is in 16-bit code units. Does not advance rPC.
+    * For example, given instruction of format: AA|op CC|BB, it
+    * fetches BB.
+    */
+
+    .macro      FETCH_BB _count _reg
+    movzbl      (\_count*2)(rPC), \_reg
+    .endm
+
+    /*
+    * Fetch the second byte from an offset past the current PC. The
+    * "_count" value is in 16-bit code units. Does not advance rPC.
+    * For example, given instruction of format: AA|op CC|BB, it
+    * fetches CC.
+    */
+
+    .macro      FETCH_CC _count _reg
+    movzbl      (\_count*2 + 1)(rPC), \_reg
+    .endm
+
+   /*
+    * Fetch the second byte from an offset past the current PC. The
+    * "_count" value is in 16-bit code units. Does not advance rPC.
+    * This variant treats the value as signed.
+    */
+
+    .macro      FETCH_CCs _count _reg
+    movsbl      (\_count*2 + 1)(rPC), \_reg
+    .endm
+
+
+   /*
+    * 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).
+    */
+
+    .macro      FETCH_B _reg  _count  _byte
+    movzbl      (\_count*2+\_byte)(rPC), \_reg
+    .endm
+
+   /*
+    * Put the instruction's opcode field into the specified register.
+    */
+
+    .macro      GET_INST_OPCODE _reg
+    movzbl      rINSTbl, \_reg
+    .endm
+
+   /*
+    * Begin executing the opcode in _reg.
+    */
+
+    .macro      GOTO_OPCODE _reg
+    shl         $$${handler_size_bits}, \_reg
+    addl        $$dvmAsmInstructionStart,\_reg
+    jmp         *\_reg
+    .endm
+
+
+
+   /*
+    * Macros pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+    * by using a jump table. _rFinish should must be the same register for
+    * both macros.
+    */
+
+    .macro      FFETCH _rFinish
+    movzbl      (rPC), \_rFinish
+    .endm
+
+    .macro      FGETOP_JMPa _rFinish
+    movzbl      1(rPC), rINST
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .endm
+
+   /*
+    * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+    * by using a jump table. _rFinish and _count should must be the same register for
+    * both macros.
+    */
+
+    .macro      FFETCH_ADV _count _rFinish
+    movzbl      (\_count*2)(rPC), \_rFinish
+    .endm
+
+    .macro      FGETOP_JMP _count _rFinish
+    movzbl      (\_count*2 + 1)(rPC), rINST
+    addl        $$(\_count*2), rPC
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .endm
+
+   /*
+    * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+    * by using a jump table. _rFinish and _reg should must be the same register for
+    * both macros.
+    */
+
+    .macro      FFETCH_ADV_RB _reg _rFinish
+    movzbl      (\_reg, rPC), \_rFinish
+    .endm
+
+    .macro      FGETOP_RB_JMP _reg _rFinish
+    movzbl      1(\_reg, rPC), rINST
+    addl        \_reg, rPC
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_INST, GET_INST_OPCODE using
+    * a jump table. This macro should be called before FINISH_JMP where
+    * rFinish should be the same register containing the opcode value.
+    * This is an attempt to split up FINISH in order to reduce or remove
+    * potential stalls due to the wait for rFINISH.
+    */
+
+    .macro      FINISH_FETCH _rFinish
+    movzbl      (rPC), \_rFinish
+    movzbl      1(rPC), rINST
+    .endm
+
+
+   /*
+    * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE using
+    * a jump table. This macro should be called before FINISH_JMP where
+    * rFinish should be the same register containing the opcode value.
+    * This is an attempt to split up FINISH in order to reduce or remove
+    * potential stalls due to the wait for rFINISH.
+    */
+
+    .macro      FINISH_FETCH_ADVANCE _count _rFinish
+    movzbl      (\_count*2)(rPC), \_rFinish
+    movzbl      (\_count*2 + 1)(rPC), rINST
+    addl        $$(\_count*2), rPC
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE using
+    * a jump table. This macro should be called before FINISH_JMP where
+    * rFinish should be the same register containing the opcode value.
+    * This is an attempt to split up FINISH in order to reduce or remove
+    * potential stalls due to the wait for rFINISH.
+    */
+
+    .macro      FINISH_FETCH_ADVANCE_RB _reg _rFinish
+    movzbl      (\_reg, rPC), \_rFinish
+    movzbl      1(\_reg, rPC), rINST
+    addl        \_reg, rPC
+    .endm
+
+   /*
+    * Attempts to speed up GOTO_OPCODE using a jump table. This macro should
+    * be called after a FINISH_FETCH* instruction where rFinish should be the
+    * same register containing the opcode value. This is an attempt to split up
+    * FINISH in order to reduce or remove potential stalls due to the wait for rFINISH.
+    */
+
+    .macro      FINISH_JMP _rFinish
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_INST, GET_INST_OPCODE, GOTO_OPCODE by using
+    * a jump table. Uses a single macro - but it should be faster if we
+    * split up the fetch for rFinish and the jump using rFinish.
+    */
+
+    .macro      FINISH_A
+    movzbl      (rPC), rFinish
+    movzbl      1(rPC), rINST
+    jmp         *dvmAsmInstructionJmpTable(,rFinish, 4)
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE,
+    * GOTO_OPCODE by using a jump table. Uses a single macro -
+    * but it should be faster if we split up the fetch for rFinish
+    * and the jump using rFinish.
+    */
+
+    .macro      FINISH _count
+    movzbl      (\_count*2)(rPC), rFinish
+    movzbl      (\_count*2 + 1)(rPC), rINST
+    addl        $$(\_count*2), rPC
+    jmp         *dvmAsmInstructionJmpTable(,rFinish, 4)
+    .endm
+
+   /*
+    * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE,
+    * GOTO_OPCODE by using a jump table. Uses a single macro -
+    * but it should be faster if we split up the fetch for rFinish
+    * and the jump using rFinish.
+    */
+
+    .macro      FINISH_RB _reg _rFinish
+    movzbl      (\_reg, rPC), \_rFinish
+    movzbl      1(\_reg, rPC), rINST
+    addl        \_reg, rPC
+    jmp         *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+    .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
diff --git a/vm/mterp/x86-atom/stub.S b/vm/mterp/x86-atom/stub.S
new file mode 100644
index 0000000..54f3f54
--- /dev/null
+++ b/vm/mterp/x86-atom/stub.S
@@ -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.
+    */
+
+   /*
+    * File: stub.S
+    */
+
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_${opcode}      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
diff --git a/vm/mterp/x86-atom/unop.S b/vm/mterp/x86-atom/unop.S
new file mode 100644
index 0000000..00f9f8d
--- /dev/null
+++ b/vm/mterp/x86-atom/unop.S
@@ -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.
+    */
+
+   /*
+    * File: unop.S
+    *
+    * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%ecx = op %edx".
+    *
+    * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+%default {"preinstr":"", "instr":""}
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $$4, %ecx               # %ecx<- B
+    and         $$15, rINST             # rINST<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    GET_VREG    %ecx                    # %ecx<- vB
+    $preinstr                           # do operation part 1
+    $instr                              # do operation part 2
+    SET_VREG    %ecx, rINST             # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/unopWide.S b/vm/mterp/x86-atom/unopWide.S
new file mode 100644
index 0000000..3790a2c
--- /dev/null
+++ b/vm/mterp/x86-atom/unopWide.S
@@ -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.
+    */
+
+   /*
+    * File: unopWide.S
+    *
+    * Code: Generic 64-bit unary operation. Provide an "instr" variable and a
+    *       preinstr variable that together specify an instruction that
+    *       performs, for example, "%xmm0 = op %xmm1".
+    *
+    * For:  neg-double, neg-long, not-long
+    *
+    * Description: Perform the identified unary operation on the source
+    *              register, storing the result in the destination register
+    *
+    * Format: B|A|op (12x)
+    *
+    * Syntax: op vA, vB
+    */
+
+%default {"preinstr":"","result":"%xmm0"}
+
+    movl        rINST, %ecx             # %ecx<- BA+
+    shr         $$4, rINST              # rINST<- B
+    and         $$15, %ecx              # %ecx<- A
+    FFETCH_ADV  1, %eax                 # %eax<- next instruction hi; fetch, advance
+    movq        (rFP, rINST, 4), %xmm0  # %xmm0<- vB
+    $preinstr                           # do operation part 1
+    $instr                              # do operation part 2
+    movq        $result, (rFP, %ecx, 4) # vA<- result
+    FGETOP_JMP  1, %eax                 # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/unused.S b/vm/mterp/x86-atom/unused.S
new file mode 100644
index 0000000..8267709
--- /dev/null
+++ b/vm/mterp/x86-atom/unused.S
@@ -0,0 +1,30 @@
+   /* 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.
+    */
+
+   /*
+    * File: unused.S
+    *
+    * Code: Common code for unused bytecodes. Uses no subtitutions.
+    *
+    * For: all unused bytecodes
+    *
+    * Description: aborts if executed.
+    *
+    * Format: ØØ|op (10x)
+    *
+    * Syntax: op
+    */
+
+    call        common_abort
diff --git a/vm/mterp/x86-atom/zcmp.S b/vm/mterp/x86-atom/zcmp.S
new file mode 100644
index 0000000..6614dba
--- /dev/null
+++ b/vm/mterp/x86-atom/zcmp.S
@@ -0,0 +1,53 @@
+   /* 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.
+    */
+
+   /*
+    * File: zcmp.S
+    *
+    * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+    *       variable to specify the reverse comparison to perform
+    *
+    * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+    *
+    * Description: Branch to the given destination if the given register's
+    *              value compares with 0 as specified.
+    *
+    * Format: AA|op BBBB (21t)
+    *
+    * Syntax: op vAA, +BBBB
+    */
+
+    cmp         $$0, (rFP, rINST, 4)    # compare vAA with zero
+    j${revcmp}  ${opcode}_2f                    # goto next instruction or branch
+    FETCHs      1, %edx                 # %edx<- BBBB; branch offset
+    sal         $$1, %edx               # %edx<- adjust byte offset
+
+   /*
+    * Inline common_backwardBranch
+    */
+
+    js          common_periodicChecks_backwardBranch  # jump on backwards branch
+1:
+    FINISH_RB   %edx, %ecx              # jump to next instruction
+
+   /*
+    * FINISH code
+    */
+
+${opcode}_2f:
+    movzbl      4(rPC), %edx            # grab the next opcode
+    movzbl      5(rPC), rINST           # update the instruction
+    addl        $$4, rPC                # update the program counter
+    jmp         *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
diff --git a/vm/mterp/x86/OP_ADD_DOUBLE.S b/vm/mterp/x86/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..9fded29
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"faddl","load":"fldl","store":"fstpl"}
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..81e55ea
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"faddl","load":"fldl","store":"fstpl"}
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..861cf01
--- /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),rPC", "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..775802f
--- /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_FULL,4)","instr2":"adcl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/OP_AGET.S b/vm/mterp/x86/OP_AGET.S
new file mode 100644
index 0000000..fe87037
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET.S
@@ -0,0 +1,23 @@
+%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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    $load     offArrayObject_contents(%eax,%ecx,$shift),%eax
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..c050156
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_WIDE.S
@@ -0,0 +1,27 @@
+%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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%ecx,%ecx)                 # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jb        .L${opcode}_finish        # index < length, OK
+    jmp       common_errArrayIndex      # index >= length, bail
+%break
+
+.L${opcode}_finish:
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    movl      (%eax),%ecx
+    movl      4(%eax),%eax
+    SET_VREG_WORD(%ecx,rINST_FULL,0)
+    SET_VREG_WORD(%eax,rINST_FULL,1)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..0feaead
--- /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),rPC", "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..47d99d9
--- /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_FULL,4)","instr2":"andl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/OP_APUT.S b/vm/mterp/x86/OP_APUT.S
new file mode 100644
index 0000000..b9a660f
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT.S
@@ -0,0 +1,23 @@
+%default { "reg":"%ecx", "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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%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
+    leal      offArrayObject_contents(%eax,%ecx,$shift),%eax
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    $store     $reg,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_APUT_BOOLEAN.S b/vm/mterp/x86/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..14f8c7f
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" {"reg":"%cl", "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..d92225f
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"%cl", "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..d466007
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"%cx", "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..3cd9e0d
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_OBJECT.S
@@ -0,0 +1,56 @@
+%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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%ecx,%ecx)                 # ecs<- vCC (requested index)
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- vAA
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jb        .L${opcode}_continue
+    jmp       common_errArrayIndex      # index >= length, bail
+%break
+
+    /* On entry:
+     *   eax<- array object
+     *   ecx<- index
+     *   rINST_FULL<- vAA
+     */
+.L${opcode}_continue:
+    leal      offArrayObject_contents(%eax,%ecx,4),%ecx
+    testl     rINST_FULL,rINST_FULL     # storing null reference?
+    je        .L${opcode}_skip_check
+    SPILL(rPC)
+    SPILL_TMP(%ecx)
+    movl      %eax,LOCAL0_OFFSET(%ebp)   # save copy of object head
+    movl      offObject_clazz(%eax),%eax # eax<- arrayObj->clazz
+    movl      offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+    movl      %eax,OUT_ARG1(%esp)
+    movl      %ecx,OUT_ARG0(%esp)
+    call      dvmCanPutArrayElement     # test object type vs. array type
+    UNSPILL(rPC)
+    UNSPILL_TMP(%ecx)
+    testl     %eax,%eax
+    GET_GLUE(%eax)
+    je        common_errArrayStore
+    movl      offGlue_cardTable(%eax),%eax   # get card table base
+    movl      rINST_FULL,(%ecx)
+    movl      LOCAL0_OFFSET(%ebp),%ecx       # recover object head
+    FETCH_INST_WORD(2)
+    shrl      $$GC_CARD_SHIFT,%ecx           # object head to card number
+    movb      %al,(%eax,%ecx)                # mark card using object head
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+.L${opcode}_skip_check:
+    movl      rINST_FULL,(%ecx)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_APUT_SHORT.S b/vm/mterp/x86/OP_APUT_SHORT.S
new file mode 100644
index 0000000..d466007
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"%cx", "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..658ca7c
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_WIDE.S
@@ -0,0 +1,27 @@
+%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
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
+    GET_VREG(%ecx,%ecx)                 # ecs<- vCC (requested index)
+    testl     %eax,%eax                 # null array object?
+    je        common_errNullObject      # bail if so
+    cmpl      offArrayObject_length(%eax),%ecx
+    jb        .L${opcode}_finish        # index < length, OK
+    jmp       common_errArrayIndex      # index >= length, bail
+%break
+
+.L${opcode}_finish:
+    leal      offArrayObject_contents(%eax,%ecx,8),%eax
+    GET_VREG_WORD(%ecx,rINST_FULL,0)
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1)
+    movl      rINST_FULL,4(%eax)
+    FETCH_INST_WORD(2)
+    movl      %ecx,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_ARRAY_LENGTH.S b/vm/mterp/x86/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..8d2a5a2
--- /dev/null
+++ b/vm/mterp/x86/OP_ARRAY_LENGTH.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /*
+     * Return the length of an array.
+     */
+   movzbl   rINST_HI,%eax              # eax<- BA
+   sarl     $$12,rINST_FULL            # rINST_FULL<- B
+   GET_VREG(%ecx,rINST_FULL)           # ecx<- vB (object ref)
+   andb     $$0xf,%al                  # eax<- A
+   testl    %ecx,%ecx                  # is null?
+   je       common_errNullObject
+   FETCH_INST_WORD(1)
+   movl     offArrayObject_length(%ecx),%ecx
+   ADVANCE_PC(1)
+   SET_VREG(%ecx,%eax)
+   GOTO_NEXT
diff --git a/vm/mterp/x86/OP_BREAKPOINT.S b/vm/mterp/x86/OP_BREAKPOINT.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_BREAKPOINT.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_CHECK_CAST.S b/vm/mterp/x86/OP_CHECK_CAST.S
new file mode 100644
index 0000000..b9d651c
--- /dev/null
+++ b/vm/mterp/x86/OP_CHECK_CAST.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 a cast from one class to another is allowed.
+     */
+    /* check-cast vAA, class@BBBB */
+    GET_GLUE(%ecx)
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- vAA (object)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    testl     rINST_FULL,rINST_FULL     # 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_FULL),%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_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+%break
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  ecx holds obj->clazz
+     *  eax holds class resolved from BBBB
+     *  rINST_FULL holds object
+     */
+.L${opcode}_fullcheck:
+    movl    %eax,OUT_ARG1(%esp)
+    movl    %ecx,OUT_ARG0(%esp)
+    SPILL(rPC)
+    call    dvmInstanceofNonTrivial     # eax<- boolean result
+    UNSPILL(rPC)
+    testl   %eax,%eax                   # failed?
+    jne     .L${opcode}_okay            # no, success
+
+    # A cast has failed.  We need to throw a ClassCastException with the
+    # class of the object that failed to be cast.
+    EXPORT_PC()
+    movl    offObject_clazz(rINST_FULL),%ecx  # ecx<- obj->clazz
+    movl    $$.LstrClassCastException,%eax
+    movl    offClassObject_descriptor(%ecx),%ecx
+    movl    %eax,OUT_ARG0(%esp)     # arg0<- message
+    movl    %ecx,OUT_ARG1(%esp)     # arg1<- obj->clazz->descriptor
+    SPILL(rPC)
+    call    dvmThrowExceptionWithClassMessage
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+    /*
+     * Resolution required.  This is the least-likely path, and we're
+     * going to have to recreate some data.
+     *
+     *  rINST_FULL holds object
+     */
+.L${opcode}_resolve:
+    GET_GLUE(%ecx)
+    EXPORT_PC()
+    movzwl  2(rPC),%eax                # eax<- BBBB
+    movl    offGlue_method(%ecx),%ecx  # ecx<- glue->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(rPC)
+    call    dvmResolveClass            # eax<- resolved ClassObject ptr
+    UNSPILL(rPC)
+    testl   %eax,%eax                  # got null?
+    je      common_exceptionThrown     # yes, handle exception
+    movl    offObject_clazz(rINST_FULL),%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..0eb1ac2
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPG_DOUBLE.S
@@ -0,0 +1,36 @@
+%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
+    movzbl   rINST_HI,rINST_FULL
+    xorl     %ecx,%ecx
+    fucompp     # z if equal, p set if NaN, c set if st0 < st1
+    fnstsw   %ax
+    sahf
+    movl      rINST_FULL,%eax
+    FETCH_INST_WORD(2)
+    jp       .L${opcode}_isNaN
+    je       .L${opcode}_finish
+    sbbl     %ecx,%ecx
+    jb       .L${opcode}_finish
+    incl     %ecx
+.L${opcode}_finish:
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+%break
+
+.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..3b1afbc
--- /dev/null
+++ b/vm/mterp/x86/OP_CMP_LONG.S
@@ -0,0 +1,37 @@
+%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.
+     */
+    /* cmp-long vAA, vBB, vCC */
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rPC)
+    movzbl    3(rPC),rPC               # rPC<- CC
+    GET_VREG_WORD(%eax,%ecx,1)         # eax<- v[BB+1]
+    GET_VREG_WORD(%ecx,%ecx,0)         # ecx<- v[BB+0]
+    movzbl    rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    cmpl      4(rFP,rPC,4),%eax
+    jl        .L${opcode}_smaller
+    jg        .L${opcode}_bigger
+    sub       (rFP,rPC,4),%ecx
+    ja        .L${opcode}_bigger
+    jb        .L${opcode}_smaller
+    UNSPILL(rPC)
+    jmp       .L${opcode}_finish
+%break
+
+.L${opcode}_bigger:
+    UNSPILL(rPC)
+    movl      $$1,%ecx
+    jmp       .L${opcode}_finish
+.L${opcode}_smaller:
+    UNSPILL(rPC)
+    movl      $$-1,%ecx
+.L${opcode}_finish:
+    SET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST.S b/vm/mterp/x86/OP_CONST.S
new file mode 100644
index 0000000..80d547a
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    /* const vAA, #+BBBBbbbb */
+    movzbl    rINST_HI,%ecx           # ecx<- AA
+    movl      2(rPC),%eax               # grab all 32 bits at once
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    SET_VREG(%eax,%ecx)                 # vAA<- eax
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_16.S b/vm/mterp/x86/OP_CONST_16.S
new file mode 100644
index 0000000..d45f1c1
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+    /* const/16 vAA, #+BBBB */
+    movswl  2(rPC),%ecx                # ecx<- ssssBBBB
+    movzx   rINST_HI,%eax              # eax<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%ecx,%eax)                # vAA<- ssssBBBB
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_4.S b/vm/mterp/x86/OP_CONST_4.S
new file mode 100644
index 0000000..499f7c3
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* const/4 vA, #+B */
+    movsx   rINST_HI,%eax              # eax<-ssssssBx
+    movl    $$0xf,%ecx
+    andl    %eax,%ecx                  # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    sarl    $$4,%eax
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_CLASS.S b/vm/mterp/x86/OP_CONST_CLASS.S
new file mode 100644
index 0000000..fa37917
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_CLASS.S
@@ -0,0 +1,41 @@
+
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+    /* const/class vAA, Class@BBBB */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+    movzbl    rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- dvmDex->pResClasses
+    movl      (%ecx,%eax,4),%eax       # eax<- rResClasses[BBBB]
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    SET_VREG(%eax,%ecx)                # vAA<- rResClasses[BBBB]
+    ADVANCE_PC(2)
+    GOTO_NEXT
+%break
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    GET_GLUE(%eax)
+    movl     %ecx,rINST_FULL           # rINST_FULL<- AA
+    EXPORT_PC()
+    movl     offGlue_method(%eax),%eax # eax<- glue->method
+    movl     $$1,OUT_ARG2(%esp)        # true
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    SPILL(rPC)
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    call     dvmResolveClass           # go resolve
+    UNSPILL(rPC)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_HIGH16.S b/vm/mterp/x86/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..9e9fa8a
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_HIGH16.S
@@ -0,0 +1,9 @@
+%verify "executed"
+    /* const/high16 vAA, #+BBBB0000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    movzbl     rINST_HI,%ecx              # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    sall       $$16,%eax                  # eax<- BBBB0000
+    SET_VREG(%eax,%ecx)                   # vAA<- eax
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_STRING.S b/vm/mterp/x86/OP_CONST_STRING.S
new file mode 100644
index 0000000..96e5dcd
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_STRING.S
@@ -0,0 +1,40 @@
+
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBB */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+    movzbl    rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    SET_VREG(%eax,%ecx)                # vAA<- rResString[BBBB]
+    ADVANCE_PC(2)
+    GOTO_NEXT
+%break
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    GET_GLUE(%eax)
+    movl     %ecx,rINST_FULL           # rINST_FULL<- AA
+    EXPORT_PC()
+    movl     offGlue_method(%eax),%eax # eax<- glue->method
+    movzwl   2(rPC),%ecx               # ecx<- BBBB
+    movl     offMethod_clazz(%eax),%eax
+    SPILL(rPC)
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rPC)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..45316f9
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,40 @@
+
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+    /* const/string vAA, String@BBBBBBBB */
+    GET_GLUE(%ecx)
+    movl      2(rPC),%eax              # eax<- BBBBBBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+    movzbl    rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    movl      offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+    movl      (%ecx,%eax,4),%eax       # eax<- rResString[BBBB]
+    movl      rINST_FULL,%ecx
+    FETCH_INST_WORD(3)
+    testl     %eax,%eax                # resolved yet?
+    je        .L${opcode}_resolve
+    SET_VREG(%eax,%ecx)                # vAA<- rResString[BBBB]
+    ADVANCE_PC(3)
+    GOTO_NEXT
+%break
+
+/* This is the less common path, so we'll redo some work
+   here rather than force spills on the common path */
+.L${opcode}_resolve:
+    GET_GLUE(%eax)
+    movl     %ecx,rINST_FULL           # rINST_FULL<- AA
+    EXPORT_PC()
+    movl     offGlue_method(%eax),%eax # eax<- glue->method
+    movl     2(rPC),%ecx               # ecx<- BBBBBBBB
+    movl     offMethod_clazz(%eax),%eax
+    SPILL(rPC)
+    movl     %ecx,OUT_ARG1(%esp)
+    movl     %eax,OUT_ARG0(%esp)
+    call     dvmResolveString          # go resolve
+    UNSPILL(rPC)
+    testl    %eax,%eax                 # failed?
+    je       common_exceptionThrown
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_WIDE.S b/vm/mterp/x86/OP_CONST_WIDE.S
new file mode 100644
index 0000000..aa582b8
--- /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    rINST_HI,%ecx       # ecx <- AA
+    movl      6(rPC),rINST_FULL   # rINST_FULL<- msw
+    leal      (rFP,%ecx,4),%ecx   # dst addr
+    movl      rINST_FULL,4(%ecx)
+    FETCH_INST_WORD(5)
+    movl      %eax,(%ecx)
+    ADVANCE_PC(5)
+    GOTO_NEXT
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..03270dc
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* const-wide/16 vAA, #+BBBB */
+    movswl    2(rPC),%eax               # eax<- ssssBBBB
+    SPILL(rPC)
+    movzbl    rINST_HI,%ecx             # ecx<- AA
+    FETCH_INST_WORD(2)
+    cltd                                # rPC:eax<- ssssssssssssBBBB
+    SET_VREG_WORD(rPC,%ecx,1)           # store msw
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)          # store lsw
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..19246c7
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_32.S
@@ -0,0 +1,12 @@
+%verify "executed"
+    /* const-wide/32 vAA, #+BBBBbbbb */
+    movl     2(rPC),%eax               # eax<- BBBBbbbb
+    SPILL(rPC)
+    movzbl    rINST_HI,%ecx             # ecx<- AA
+    FETCH_INST_WORD(3)
+    cltd                                # rPC:eax<- ssssssssssssBBBB
+    SET_VREG_WORD(rPC,%ecx,1)           # store msw
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)          # store lsw
+    ADVANCE_PC(3)
+    GOTO_NEXT
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..1d726f4
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* const-wide/high16 vAA, #+BBBB000000000000 */
+    movzwl     2(rPC),%eax                # eax<- 0000BBBB
+    movzbl     rINST_HI,%ecx              # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    sall       $$16,%eax                  # eax<- BBBB0000
+    SET_VREG_WORD(%eax,%ecx,1)            # v[AA+1]<- eax
+    xorl       %eax,%eax
+    SET_VREG_WORD(%eax,%ecx,0)            # v[AA+0]<- eax
+    GOTO_NEXT
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..f324be3
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_LONG.S
@@ -0,0 +1,52 @@
+%verify "executed"
+%default {"routine":"__divdi3","special":"$0x80000000"}
+    /* div vAA, vBB, vCC */
+    movzbl    3(rPC),%eax              # eax<- CC
+    movzbl    2(rPC),%ecx              # ecx<- BB
+    SPILL(rPC)
+    GET_VREG_WORD(rPC,%eax,0)
+    GET_VREG_WORD(%eax,%eax,1)
+    movl     rPC,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(rPC,%ecx,0)
+    GET_VREG_WORD(%ecx,%ecx,1)
+.L${opcode}_notSpecial1:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rPC,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    jmp      .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+    call     $routine
+.L${opcode}_finish:
+    movzbl   rINST_HI,%ecx
+    SET_VREG_WORD(rPC,%ecx,1)
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+.L${opcode}_check_zero:
+    testl   rPC,rPC
+    jne     .L${opcode}_notSpecial
+    UNSPILL(rPC)
+    jmp     common_errDivideByZero
+.L${opcode}_check_neg1:
+    testl   rPC,%eax
+    jne     .L${opcode}_notSpecial
+    GET_VREG_WORD(rPC,%ecx,0)
+    GET_VREG_WORD(%ecx,%ecx,1)
+    testl    rPC,rPC
+    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,%edx
+    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..128ede0
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,54 @@
+%verify "executed"
+%default {"routine":"__divdi3","special":"$0x80000000"}
+    /* div/2addr vA, vB */
+    movzbl    rINST_HI,%eax
+    shrl      $$4,%eax                  # eax<- B
+    movzbl    rINST_HI,rINST_FULL
+    andb      $$0xf,rINST_LO            # rINST_FULL<- A
+    SPILL(rPC)
+    GET_VREG_WORD(rPC,%eax,0)
+    GET_VREG_WORD(%eax,%eax,1)
+    movl     rPC,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(rPC,rINST_FULL,0)
+    GET_VREG_WORD(%ecx,rINST_FULL,1)
+.L${opcode}_notSpecial1:
+    jmp      .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+    movl     %eax,OUT_ARG3(%esp)
+    movl     rPC,OUT_ARG0(%esp)
+    movl     %ecx,OUT_ARG1(%esp)
+    call     $routine
+.L${opcode}_finish:
+    movl     rINST_FULL,%ecx
+    SET_VREG_WORD(rPC,%ecx,1)
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+.L${opcode}_check_zero:
+    testl   rPC,rPC
+    jne     .L${opcode}_notSpecial
+    UNSPILL(rPC)
+    jmp     common_errDivideByZero
+.L${opcode}_check_neg1:
+    testl   rPC,%eax
+    jne     .L${opcode}_notSpecial
+    GET_VREG_WORD(rPC,rINST_FULL,0)
+    GET_VREG_WORD(%ecx,rINST_FULL,1)
+    testl    rPC,rPC
+    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,%edx
+    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..1eca6e2
--- /dev/null
+++ b/vm/mterp/x86/OP_EXECUTE_INLINE.S
@@ -0,0 +1,66 @@
+%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)
+     *
+     */
+    /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+    GET_GLUE(%ecx)
+    EXPORT_PC()
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    leal      offGlue_retval(%ecx),%ecx # ecx<- & glue->retval
+    movl      %ecx,OUT_ARG4(%esp)
+    sarl      $$12,rINST_FULL           # rINST_FULL<- arg count (0-4)
+    SPILL(rPC)
+    call      .L${opcode}_continue      # make call; will return after
+    UNSPILL(rPC)
+    testl     %eax,%eax                 # successful?
+    FETCH_INST_WORD(3)
+    je        common_exceptionThrown    # no, handle exception
+    ADVANCE_PC(3)
+    GOTO_NEXT
+%break
+
+.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),rPC
+
+    movl      $$0xf,%ecx
+    andl      rPC,%ecx
+    GET_VREG(%ecx,%ecx)
+    sarl      $$4,rPC
+    movl      %ecx,4+OUT_ARG0(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rPC,%ecx
+    GET_VREG(%ecx,%ecx)
+    sarl      $$4,rPC
+    movl      %ecx,4+OUT_ARG1(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rPC,%ecx
+    GET_VREG(%ecx,%ecx)
+    sarl      $$4,rPC
+    movl      %ecx,4+OUT_ARG2(%esp)
+
+    movl      $$0xf,%ecx
+    andl      rPC,%ecx
+    GET_VREG(%ecx,%ecx)
+    sarl      $$4,rPC
+    movl      %ecx,4+OUT_ARG3(%esp)
+
+    sall      $$4,%eax      # index *= sizeof(table entry)
+    jmp       *gDvmInlineOpsTable(%eax)
+    # will return to caller of .L${opcode}_continue
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..55b1f84
--- /dev/null
+++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,142 @@
+%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 */
+    GET_GLUE(%eax)
+    movzbl  rINST_HI,rINST_FULL               # rINST_FULL<- AA or BA
+    movl    offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+    movzwl  2(rPC),%ecx                       # ecx<- BBBB
+    movl    offDvmDex_pResClasses(%eax),%eax  # eax<- pDvmDex->pResClasses
+    SPILL(rPC)
+    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
+    GET_GLUE(%eax)
+    movl    $$0,OUT_ARG2(%esp)                # arg2<- false
+    movl    %ecx,OUT_ARG1(%esp)               # arg1<- BBBB
+    movl    offGlue_method(%eax),%eax         # eax<- glue->method
+    jmp     .L${opcode}_more
+%break
+
+.L${opcode}_more:
+    movl    offMethod_clazz(%eax),%eax        # eax<- method->clazz
+    movl    %eax,OUT_ARG0(%esp)               # arg0<- clazz
+    call    dvmResolveClass                   # eax<- call(clazz,ref,flag)
+    UNSPILL(rPC)
+    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_FULL holds AA or BB [r10]
+     *    ecx is scratch
+     *    rPC is valid, but has been spilled
+     */
+.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
+    GET_GLUE(%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,offGlue_retval+4(%eax)           # save type
+    .if      (!$isrange)
+    SPILL_TMP(rINST_FULL)                         # save copy, need "B" later
+    sarl    $$4,rINST_FULL
+    .endif
+    movl    rINST_FULL,OUT_ARG1(%esp)             # arg1<- A or AA (length)
+    call    dvmAllocArrayByClass                  # eax<- call(arrayClass, length, flags)
+    UNSPILL(rPC)
+    GET_GLUE(%ecx)
+    testl   %eax,%eax                             # alloc successful?
+    je      common_exceptionThrown                # no, handle exception
+    movl    %eax,offGlue_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_FULL is length
+ *     ecx is FEDC or CCCC
+ *     TMP_SPILL is BA
+ *     rPC is valid, but spilled
+ *  We now need to copy values from registers into the array
+ */
+
+    .if $isrange
+    # set up src pointer
+    SPILL(rFP)     # esi
+    SPILL(rIBASE)   # edi
+    movl    %eax,%edi         # set up dst ptr
+    leal    (rFP,%ecx,4),%esi # set up src ptr
+    movl    rINST_FULL,%ecx   # load count register
+    FETCH_INST_WORD(3)
+    rep
+    movsd
+    GET_GLUE(%ecx)
+    UNSPILL(rIBASE)
+    movl    offGlue_retval+4(%ecx),%eax      # eax<- type
+    UNSPILL(rFP)
+    .else
+    testl  rINST_FULL,rINST_FULL
+    je     4f
+    UNSPILL_TMP(rPC)
+    andl   $$0x0f,rPC            # rPC<- 0000000A
+    sall   $$16,rPC              # rPC<- 000A0000
+    orl    %ecx,rPC              # rpc<- 000AFEDC
+3:
+    movl   $$0xf,%ecx
+    andl   rPC,%ecx           # ecx<- next reg to load
+    GET_VREG(%ecx,%ecx)
+    shrl   $$4,rPC
+    leal   4(%eax),%eax
+    movl   %ecx,-4(%eax)
+    sub    $$1,rINST_FULL
+    jne    3b
+4:
+    GET_GLUE(%ecx)
+    UNSPILL(rPC)
+    movl    offGlue_retval+4(%ecx),%eax      # eax<- type
+    FETCH_INST_WORD(3)
+    .endif
+
+    cmpb    $$'I',%al                        # Int array?
+    je      5f                               # skip card mark if so
+    movl    offGlue_retval(%ecx),%eax        # eax<- object head
+    movl    offGlue_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:
+    ADVANCE_PC(3)
+    GOTO_NEXT
+
+
+    /*
+     * Throw an exception indicating that we have not implemented this
+     * mode of filled-new-array.
+     */
+.L${opcode}_notimpl:
+    movl    $$.LstrInternalError,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    $$.LstrFilledNewArrayNotImpl,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    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..9db60ac
--- /dev/null
+++ b/vm/mterp/x86/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* fill-array-data vAA, +BBBBBBBB */
+    movl    2(rPC),%ecx                # ecx<- BBBBbbbb
+    movzbl  rINST_HI,rINST_FULL        # rINST_FULL<- AA
+    leal    (rPC,%ecx,2),%ecx          # ecx<- PC + BBBBbbbb*2
+    GET_VREG(%eax,rINST_FULL)
+    SPILL(rPC)
+    EXPORT_PC()
+    movl    %eax,OUT_ARG0(%esp)
+    movl    %ecx,OUT_ARG1(%esp)
+    call    dvmInterpHandleFillArrayData
+    UNSPILL(rPC)
+    FETCH_INST_WORD(3)
+    testl   %eax,%eax                   # exception thrown?
+    je      common_exceptionThrown
+    ADVANCE_PC(3)
+    GOTO_NEXT
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..5d530ec
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO.S
@@ -0,0 +1,16 @@
+%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 */
+    movsbl  rINST_HI,rINST_FULL         # ebx<- ssssssAA
+    testl   rINST_FULL,rINST_FULL       # test for <0
+    js      common_backwardBranch
+    movl    rINST_FULL,%eax
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    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..3feb33b
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO_16.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "forward and backward"
+    /*
+     * Unconditional branch, 16-bit offset.
+     *
+     * The branch distance is a signed code-unit offset
+     */
+    /* goto/16 +AAAA */
+    movswl  2(rPC),rINST_FULL           # rINST_FULL<- ssssAAAA
+    testl   rINST_FULL,rINST_FULL       # test for <0
+    js      common_backwardBranch
+    movl    rINST_FULL,%eax
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    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..6720bd6
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO_32.S
@@ -0,0 +1,18 @@
+%verify "executed"
+%verify "forward, backward, self"
+    /*
+     * Unconditional branch, 32-bit offset.
+     *
+     * The branch distance is a signed code-unit 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 */
+    movl    2(rPC),rINST_FULL           # rINST_FULL<- AAAAAAAA
+    cmpl    $$0,rINST_FULL              # test for <= 0
+    jle     common_backwardBranch
+    movl    rINST_FULL,%eax
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    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..cd8033f
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET.S
@@ -0,0 +1,64 @@
+%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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $$4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $$0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    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_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    $load   (%ecx,%eax,1),%ecx                   # ecx<- obj.field (8/16/32 bits)
+    movl    rINST_FULL,%eax                      # eax<- A
+    FETCH_INST_WORD(2)
+    SET_VREG(%ecx,%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..88d0725
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-quick, iget-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG(%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
+    movzbl    rINST_HI,%ecx
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    andb      $$0xf,%cl                 # rINST_FULL<- A
+    SET_VREG  (%eax,%ecx)               # fp[A]<- result
+    GOTO_NEXT
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..77a5994
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_WIDE.S
@@ -0,0 +1,64 @@
+%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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $$4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $$0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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
+    GET_GLUE(rIBASE)
+    jmp     .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    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_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    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_FULL,0)
+    SET_VREG_WORD(%eax,rINST_FULL,1)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..4747284
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,20 @@
+%verify "executed"
+%verify "null object"
+    /* For: iget-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG(%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
+    movzbl    rINST_HI,rINST_FULL
+    andb      $$0xf,rINST_LO            # rINST_FULL<- A
+    SET_VREG_WORD(%ecx,rINST_FULL,0)    # v[A+0]<- lsw
+    SET_VREG_WORD(%eax,rINST_FULL,1)    # v[A+1]<- msw
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_INSTANCE_OF.S b/vm/mterp/x86/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..71b92d3
--- /dev/null
+++ b/vm/mterp/x86/OP_INSTANCE_OF.S
@@ -0,0 +1,102 @@
+%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 */
+    movzbl  rINST_HI,%eax               # eax<- BA
+    sarl    $$4,%eax                    # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB (obj)
+    GET_GLUE(%ecx)
+    testl   %eax,%eax                   # object null?
+    movl    offGlue_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    SPILL(rPC)
+    je      .L${opcode}_store           # null obj, not instance, store it
+    movzwl  2(rPC),rPC                  # rPC<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    movl    (%ecx,rPC,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
+    jmp     .L${opcode}_fullcheck       # no, do full check
+%break
+
+    /*
+     * Trivial test failed, need to perform full check.  This is common.
+     *  eax holds obj->clazz
+     *  ecx holds class resolved from BBBB
+     *  rINST_HI has BA
+     *  rPC already spilled
+     */
+.L${opcode}_fullcheck:
+    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_HI holds BA
+     */
+.L${opcode}_store:
+    UNSPILL(rPC)
+    movzbl  rINST_HI,%ecx               # ecx<- BA
+    FETCH_INST_WORD(2)
+    andb    $$0xf,%cl                   # ecl<- A
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)                 # vA<- eax
+    GOTO_NEXT
+
+    /*
+     * Trivial test succeeded, save and bail.
+     *  r9 holds A
+     */
+.L${opcode}_trivial:
+    UNSPILL(rPC)
+    movzbl  rINST_HI,%ecx               # ecx<- BA
+    FETCH_INST_WORD(2)
+    andb    $$0xf,%cl                   # ecl<- A
+    ADVANCE_PC(2)
+    movl    $$1,%eax
+    SET_VREG(%eax,%ecx)                  # vA<- true
+    GOTO_NEXT
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     *  rPC holds BBBB
+     *  rINST_HI holds BA
+     */
+.L${opcode}_resolve:
+    movl    rPC,OUT_ARG1(%esp)          # arg1<- BBBB
+    GET_GLUE(%ecx)
+    UNSPILL(rPC)
+    movl    offGlue_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
+    UNSPILL(rPC)
+    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
+    movzbl  rINST_HI,%eax               # eax<- BA
+    sarl    $$4,%eax                    # eax<- B
+    GET_VREG(%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..13f7483
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_LONG.S
@@ -0,0 +1,14 @@
+%verify "executed"
+    /* int to long vA, vB */
+    movzbl  rINST_HI,%ecx               # ecx<- +A
+    sarl    $$12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    SPILL(rPC)                          # will step on edx later
+    andb    $$0xf,%cl                   # ecx<- A
+    cltd                                # edx:eax<- sssssssBBBBBBBB
+    SET_VREG_WORD(%edx,%ecx,1)          # v[A+1]<- edx/rPC
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,%ecx,0)          # v[A+0]<- %eax
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..f423dc3
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT.S
@@ -0,0 +1,58 @@
+%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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax              # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    SPILL(rPC)
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movzwl    4(rPC),rPC               # rPC<- GFED or CCCC
+    movl      (%ecx,%eax,4),%eax       # eax<- resolved methodToCall
+    .if       (!$isrange)
+    andl      $$0xf,rPC                # rPC<- D (or stays CCCC)
+    .endif
+    testl     %eax,%eax                # already resolved?
+    GET_VREG(%ecx,rPC)                 # ecx<- "this" ptr
+    je        .L${opcode}_resolve      # not resolved, do it now
+.L${opcode}_finish:
+    UNSPILL(rPC)
+    testl     %ecx,%ecx                # null "this"?
+    jne       common_invokeMethod${routine}  # no, continue on
+    jmp       common_errNullObject
+%break
+
+    /*
+     * 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_TMP(%ecx)
+     GET_GLUE(%ecx)
+     UNSPILL(rPC)
+     movl     offGlue_method(%ecx),%ecx  # ecx<- glue->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_TMP(%ecx)
+     testl    %eax,%eax
+     jne      .L${opcode}_finish
+     UNSPILL(rPC)
+     jmp      common_exceptionThrown
diff --git a/vm/mterp/x86/OP_INVOKE_DIRECT_EMPTY.S b/vm/mterp/x86/OP_INVOKE_DIRECT_EMPTY.S
new file mode 100644
index 0000000..cec8b0a
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT_EMPTY.S
@@ -0,0 +1,7 @@
+%verify "executed"
+    /*
+     * invoke-direct-empty is a no-op in a "standard" interpreter.
+     */
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    GOTO_NEXT
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..ff48ab8
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,38 @@
+%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
+    GET_GLUE(%ecx)
+    .if        (!$isrange)
+    andl       $$0xf,%eax               # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG(%eax,%eax)                 # eax<- "this"
+    EXPORT_PC()
+    testl      %eax,%eax                # null this?
+    je         common_errNullObject     # yes, fail
+    movl       offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+    movl       %eax,OUT_ARG0(%esp)                 # arg0<- class
+    movl       offGlue_methodClassDex(%ecx),%eax   # eax<- methodClassDex
+    movl       offGlue_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
+    SPILL(rPC)
+    jmp        .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+    call       dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+    UNSPILL(rPC)
+    testl      %eax,%eax
+    je         common_exceptionThrown
+    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_STATIC.S b/vm/mterp/x86/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..a8a8d77
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_STATIC.S
@@ -0,0 +1,36 @@
+%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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    movl      offDvmDex_pResMethods(%ecx),%ecx  # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%eax        # eax<- resolved methodToCall
+    testl     %eax,%eax
+    jne       common_invokeMethod${routine}
+    GET_GLUE(%ecx)
+    movl      offGlue_method(%ecx),%ecx # ecx<- glue->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
+    jmp       .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+    movl      $$METHOD_STATIC,%eax
+    movl      %eax,OUT_ARG2(%esp)       # arg2<- flags
+    SPILL(rPC)
+    call      dvmResolveMethod          # call(clazz,ref,flags)
+    UNSPILL(rPC)
+    testl     %eax,%eax                 # got null?
+    jne       common_invokeMethod${routine}
+    jmp       common_exceptionThrown
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..013fc01
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER.S
@@ -0,0 +1,72 @@
+%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 */
+    GET_GLUE(rINST_FULL)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(rINST_FULL),%ecx # ecx<- pDvmDex
+    EXPORT_PC()
+    movl      offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved baseMethod
+    movl      offGlue_method(rINST_FULL),%eax # eax<- method
+    movzwl    4(rPC),rINST_FULL         # rINST_FULL<- GFED or CCCC
+    .if       (!$isrange)
+    andl      $$0xf,rINST_FULL          # rINST_FULL<- D (or stays CCCC)
+    .endif
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- "this" ptr
+    testl     rINST_FULL,rINST_FULL     # null "this"?
+    je        common_errNullObject      # yes, throw
+    movl      offMethod_clazz(%eax),%eax # eax<- method->clazz
+    testl     %ecx,%ecx                 # already resolved?
+    jne       .L${opcode}_continue      # yes - go on
+    jmp       .L${opcode}_resolve
+%break
+
+    /*
+     * 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),%ecx  # ecx<- baseMthod->methodIndex
+    cmpl    offClassObject_vtableCount(%eax),%ecx # compare(methodIndex,vtableCount)
+    jae     .L${opcode}_nsm           # method not present in superclass
+    movl    offClassObject_vtable(%eax),%eax   # eax<- ...clazz->super->vtable
+    movl    (%eax,%ecx,4),%eax        # eax<- vtable[methodIndex]
+    jmp     common_invokeMethod${routine}
+
+
+    /* At this point:
+     * ecx = null (needs to be resolved base method)
+     * eax = method->clazz
+    */
+.L${opcode}_resolve:
+    SPILL_TMP(%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
+    SPILL(rPC)
+    call    dvmResolveMethod            # eax<- call(clazz, ref, flags)
+    UNSPILL(rPC)
+    testl   %eax,%eax                   # got null?
+    movl    %eax,%ecx                   # ecx<- resolved base method
+    UNSPILL_TMP(%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
+    mov     %eax,OUT_ARG1(%esp)
+    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..96c662a
--- /dev/null
+++ b/vm/mterp/x86/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 */
+    GET_GLUE(%ecx)
+    movzwl    4(rPC),%eax               # eax<- GFED or CCCC
+    movl      offGlue_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(%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
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+    EXPORT_PC()
+    movl      (%ecx,%eax,4),%eax        # eax<- super->vtable[BBBB]
+    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..85fcf83
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,55 @@
+
+%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 */
+    GET_GLUE(%eax)
+    movzwl    2(rPC),%ecx                 # ecx<- BBBB
+    movl      offGlue_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
+    GET_GLUE(%eax)
+    movl      %ecx,OUT_ARG1(%esp)         # arg1<- ref
+    movl      offGlue_method(%eax),%eax   # eax<- glue->method
+    SPILL(rPC)
+    jmp       .L${opcode}_more
+%break
+
+
+.L${opcode}_more:
+    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)
+    UNSPILL(rPC)
+    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(%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),%ecx  # ecx<- thisPtr->clazz
+    movl      offClassObject_vtable(%ecx),%ecx # ecx<- thisPtr->clazz->vtable
+    movl      (%ecx,%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..1ba93eb
--- /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),%eax               # eax<- FEDC or CCCC
+    movzwl    2(rPC),%ecx               # ecx<- BBBB
+    .if     (!$isrange)
+    andl      $$0xf,%eax                # eax<- C (or stays CCCC)
+    .endif
+    GET_VREG(%eax,%eax)                 # eax<- vC ("this" ptr)
+    testl     %eax,%eax                 # null?
+    je        common_errNullObject      # yep, throw exception
+    movl      offObject_clazz(%eax),%eax # eax<- thisPtr->clazz
+    movl      offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+    EXPORT_PC()                         # might throw later - get ready
+    movl      (%eax,%ecx,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..78d9edb
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT.S
@@ -0,0 +1,64 @@
+
+%default { "store":"movl", "reg":"rINST_FULL", "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_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $$4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $$0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    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_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    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_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IPUT_BOOLEAN.S b/vm/mterp/x86/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..5072a68
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movb","reg":"rINST_LO", "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..3ad2a4b
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movb", "reg":"rINST_LO", "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..c7a7478
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movw", "reg":"rINST", "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..87ce915
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT.S
@@ -0,0 +1,70 @@
+%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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $$4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $$0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    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_FULL holds A
+     */
+    GET_VREG(rINST_FULL,rINST_FULL)              # rINST_FULL<- v[A]
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    testl   %ecx,%ecx                            # object null?
+    je      common_errNullObject                 # object was null
+    movl    rINST_FULL,(%ecx,%eax)          # obj.field <- v[A](8/16/32 bits)
+    GET_GLUE(%eax)
+    testl   rINST_FULL,rINST_FULL                # stored a NULL?
+    movl    offGlue_cardTable(%eax),%eax         # get card table base
+    FETCH_INST_WORD(2)
+    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:
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..3537628
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,28 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-object-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG(%ecx,%ecx)                 # vB (object we're operating on)
+    movzbl    rINST_HI,rINST_FULL
+    andb      $$0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                 # is object null?
+    je        common_errNullObject
+    movl      rINST_FULL,(%ecx,%eax,1)
+    GET_GLUE(%eax)
+    jmp       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    testl     rINST_FULL,rINST_FULL         # did we store null?
+    FETCH_INST_WORD(2)
+    movl      offGlue_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:
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..e62ec00
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG(%ecx,%ecx)                 # vB (object we're operating on)
+    movzbl    rINST_HI,rINST_FULL
+    andb      $$0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG(rINST_FULL,rINST_FULL)     # rINST_FULL<- v[A]
+    movzwl    2(rPC),%eax               # eax<- field byte offset
+    testl     %ecx,%ecx                  # is object null?
+    je        common_errNullObject
+    movl      rINST_FULL,(%ecx,%eax,1)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IPUT_SHORT.S b/vm/mterp/x86/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..4b20b46
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movw", "reg":"rINST", "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..53f8212
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_WIDE.S
@@ -0,0 +1,64 @@
+%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 */
+    GET_GLUE(%ecx)
+    SPILL(rIBASE)                                 # need another reg
+    movzwl  2(rPC),rIBASE                         # rIBASE<- 0000CCCC
+    movl    offGlue_methodClassDex(%ecx),%eax     # eax<- DvmDex
+    movzbl  rINST_HI,%ecx                         # ecx<- BA
+    sarl    $$4,%ecx                              # ecx<- B
+    movl    offDvmDex_pResFields(%eax),%eax       # eax<- pDvmDex->pResFields
+    movzbl  rINST_HI,rINST_FULL                   # rINST_FULL<- BA
+    andb    $$0xf,rINST_LO                        # rINST_FULL<- A
+    GET_VREG(%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)
+    GET_GLUE(rIBASE)
+    jmp     .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+    EXPORT_PC()
+    SPILL(rPC)
+    movl    offGlue_method(rIBASE),rPC            # rPC<- current method
+    UNSPILL(rIBASE)
+    movl    offMethod_clazz(rPC),rPC              # rPC<- method->clazz
+    SPILL_TMP(%ecx)                               # save object pointer across call
+    movl    rPC,OUT_ARG0(%esp)                    # pass in method->clazz
+    call    dvmResolveInstField                   #  ... to dvmResolveInstField
+    UNSPILL_TMP(%ecx)
+    UNSPILL(rPC)
+    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_FULL holds A
+     */
+    movl    offInstField_byteOffset(%eax),%eax   # eax<- byte offset of field
+    UNSPILL(rIBASE)
+    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_FULL,0)             # ecx<- lsw
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1)       # rINST_FULL<- msw
+    movl    rINST_FULL,4(%eax)
+    FETCH_INST_WORD(2)
+    movl    %ecx,(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..d2a1ae4
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,20 @@
+%verify "executed"
+%verify "null object"
+    /* For: iput-wide-quick */
+    /* op vA, vB, offset@CCCC */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    sarl      $$4,%ecx                  # ecx<- B
+    GET_VREG(%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
+    movzbl    rINST_HI,rINST_FULL
+    andb      $$0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG_WORD(%eax,rINST_FULL,0)    # eax<- lsw
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- msw
+    movl      %eax,(%ecx)
+    movl      rINST_FULL,4(%ecx)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..548f71f
--- /dev/null
+++ b/vm/mterp/x86/OP_MONITOR_ENTER.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "exception for null object"
+    /*
+     * Synchronize on an object.
+     */
+    /* monitor-enter vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)           # eax<- vAA
+    movl    offGlue_self(%ecx),%ecx     # ecx<- glue->self
+    FETCH_INST_WORD(1)
+    testl   %eax,%eax                   # null object?
+    EXPORT_PC()                         # need for precise GC, MONITOR_TRACKING
+    jne     .L${opcode}_continue
+    jmp     common_errNullObject
+%break
+
+.L${opcode}_continue:
+    SPILL(rPC)                          # have to - caller save
+    movl    %ecx,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    call    dvmLockObject               # dvmLockObject(self,object)
+    UNSPILL(rPC)
+#ifdef WITH_DEADLOCK_PREDICTION
+    GET_GLUE(%ecx)
+    movl    offGlueSelf(%ecx),%ecx      # ecx<- glue->self
+    movl    offThread_exception(%ecx),%eax
+    testl   %eax,%eax
+    jne     common_exceptionThrown
+#endif
+    ADVANCE_PC(1)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MONITOR_EXIT.S b/vm/mterp/x86/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..788b7a7
--- /dev/null
+++ b/vm/mterp/x86/OP_MONITOR_EXIT.S
@@ -0,0 +1,35 @@
+%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 */
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)
+    GET_GLUE(%ecx)
+    EXPORT_PC()
+    testl   %eax,%eax                   # null object?
+    je      .L${opcode}_errNullObject   # go if so
+    movl    offGlue_self(%ecx),%ecx     # ecx<- glue->self
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    movl    %ecx,OUT_ARG0(%esp)
+    jmp     .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+    call    dvmUnlockObject             # unlock(self,obj)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(1)
+    testl   %eax,%eax                   # success?
+    ADVANCE_PC(1)
+    je      common_exceptionThrown      # no, exception pending
+    GOTO_NEXT
+.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..f0d070d
--- /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 rINST_HI,%eax         # eax<- BA
+    andb   $$0xf,%al             # eax<- A
+    shrl   $$12,rINST_FULL       # rINST_FULL<- B
+    GET_VREG(%ecx,rINST_FULL)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG(%ecx,%eax)          # fp[A]<-fp[B]
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_16.S b/vm/mterp/x86/OP_MOVE_16.S
new file mode 100644
index 0000000..9a0e4ee
--- /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(%ecx,%ecx)
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    SET_VREG(%ecx,%eax)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_EXCEPTION.S b/vm/mterp/x86/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..07a32c9
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* move-exception vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL        # rINST_FULL<- AA
+    movl    offGlue_self(%ecx),%ecx    # ecx<- glue->self
+    movl    offThread_exception(%ecx),%eax # eax<- dvmGetException bypass
+    SET_VREG(%eax,rINST_FULL)          # fp[AA]<- exception object
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    movl    $$0,offThread_exception(%ecx) # dvmClearException bypass
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_FROM16.S b/vm/mterp/x86/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..35d3147
--- /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    rINST_HI,%eax            # eax <= AA
+    movw     2(rPC),rINST             # rINST <= BBBB
+    GET_VREG (%ecx,rINST_FULL)        # ecx<- fp[BBBB]
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG (%ecx,%eax)              # fp[AA]<- ecx]
+    GOTO_NEXT
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..160aec6
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT.S
@@ -0,0 +1,10 @@
+%verify "executed"
+    /* for: move-result, move-result-object */
+    /* op vAA */
+    GET_GLUE(%eax)                         # eax<- rGLUE
+    movzx    rINST_HI,%ecx                 # ecx<- AA
+    movl     offGlue_retval(%eax),%eax     # eax<- glue->retval.l
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG (%eax,%ecx)                   # fp[AA]<- retval.l
+    GOTO_NEXT
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..905037f
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+    /* move-result-wide vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    movl    offGlue_retval(%ecx),%eax
+    movl    4+offGlue_retval(%ecx),%ecx
+    SET_VREG_WORD(%eax,rINST_FULL,0)    # v[AA+0] <- eax
+    SET_VREG_WORD(%ecx,rINST_FULL,1)    # v[AA+1] <- ecx
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_WIDE.S b/vm/mterp/x86/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..022cc6e
--- /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    rINST_HI,%ecx                # ecx <- BA
+    sarl      $$12,rINST_FULL              # rinst_FULL<- B
+    GET_VREG_WORD(%eax,rINST_FULL,0)       # eax<- v[B+0]
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- v[B+1]
+    andb      $$0xf,%cl                    # ecx <- A
+    SET_VREG_WORD(rINST_FULL,%ecx,1)       # v[A+1]<- rINST_FULL
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    SET_VREG_WORD(%eax,%ecx,0)             # v[A+0]<- eax
+    GOTO_NEXT
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..d7be1d1
--- /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_FULL,%ecx,0) # rINST_WORD<- v[BBBB+0]
+    GET_VREG_WORD(%ecx,%ecx,1)       # ecx<- v[BBBB+1]
+    SET_VREG_WORD(rINST_FULL,%eax,0) # v[AAAA+0]<- rINST_FULL
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    SET_VREG_WORD(%ecx,%eax,1)       # v[AAAA+1]<- ecx
+    GOTO_NEXT
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..cbc13d2
--- /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    rINST_HI,%eax            # eax<- AAAA
+    GET_VREG_WORD(rINST_FULL,%ecx,0)   # rINST_FULL<- v[BBBB+0]
+    GET_VREG_WORD(%ecx,%ecx,1)         # ecx<- v[BBBB+1]
+    SET_VREG_WORD(rINST_FULL,%eax,0)   # v[AAAA+0]<- rINST_FULL
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG_WORD(%ecx,%eax,1)         # v[AAAA+1]<- eax
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MUL_DOUBLE.S b/vm/mterp/x86/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..59a2079
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fmull","load":"fldl","store":"fstpl"}
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..45a2fa3
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fmull","load":"fldl","store":"fstpl"}
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..b859672
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /*
+     * 32-bit binary multiplication.
+     */
+    /* mul vAA, vBB, vCC */
+    movzbl   2(rPC),%eax            # eax<- BB
+    movzbl   3(rPC),%ecx            # ecx<- CC
+    SPILL(rPC)
+    GET_VREG(%eax,%eax)             # eax<- vBB
+    imull    (rFP,%ecx,4),%eax      # trashes rPC/edx
+    UNSPILL(rPC)
+    movzbl   rINST_HI,%ecx          # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
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..823ab64
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* mul vA, vB */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $$12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    andb    $$0xf,%cl                   # ecx<- A
+    SPILL(rPC)
+    imull   (rFP,%ecx,4),%eax
+    UNSPILL(rPC)
+    SET_VREG(%eax,%ecx)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..f562425
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_LIT16.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /* mul/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST_FULL, ssssCCCC in ecx, vB in eax */
+    movzbl   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $$4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    SPILL(rPC)
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $$0xf,rINST_LO             # rINST_FULL<- A
+    imull     %ecx,%eax                 # trashes rPC
+    UNSPILL(rPC)
+    SET_VREG(%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..2cf11b3
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_LIT8.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /* mul/lit8 vAA, vBB, #+CC */
+    movzbl    2(rPC),%eax              # eax<- BB
+    movsbl    3(rPC),%ecx              # ecx<- ssssssCC
+    SPILL(rPC)
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    imull     %ecx,%eax                # trashes rPC
+    UNSPILL(rPC)
+    SET_VREG  (%eax,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MUL_LONG.S b/vm/mterp/x86/OP_MUL_LONG.S
new file mode 100644
index 0000000..fd151e0
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_LONG.S
@@ -0,0 +1,43 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply.
+     *
+     * We could definately use more free registers for
+     * this code.  We must spill rPC (edx) because it
+     * is used by imul.  We'll also spill rINST (ebx),
+     * giving us eax, ebc, ecx and edx as computational
+     * temps.  On top of that, we'll spill rIBASE (edi)
+     * for use as the vB pointer and rFP (esi) 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(rPC)
+    SPILL(rIBASE)
+    SPILL(rFP)
+    SPILL(rINST_FULL)
+    leal      (rFP,%eax,4),rIBASE      # rIBASE<- &v[B]
+    leal      (rFP,%ecx,4),rFP         # rFP<- &v[C]
+    movl      4(rIBASE),%ecx      # ecx<- Bmsw
+    imull     (rFP),%ecx          # ecx<- (Bmsw*Clsw)
+    movl      4(rFP),%eax         # eax<- Cmsw
+    imull     (rIBASE),%eax       # eax<- (Cmsw*Blsw)
+    addl      %eax,%ecx           # ecx<- (Bmsw*Clsw)+(Cmsw*Blsw)
+    movl      (rFP),%eax          # eax<- Clsw
+    mull      (rIBASE)            # eax<- (Clsw*Alsw)
+    UNSPILL(rINST_FULL)
+    UNSPILL(rFP)
+    jmp       .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+    leal      (%ecx,%edx),%edx    # full result now in %edx:%eax
+    movzbl    rINST_HI,%ecx       # ecx<- A
+    movl      %edx,4(rFP,%ecx,4)  # v[B+1]<- %edx
+    UNSPILL(rPC)                  # restore rPC/%edx
+    FETCH_INST_WORD(2)
+    UNSPILL(rIBASE)
+    movl      %eax,(rFP,%ecx,4)   # v[B]<- %eax
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..5651dfe
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,41 @@
+%verify "executed"
+    /*
+     * Signed 64-bit integer multiply, 2-addr version
+     *
+     * We could definately use more free registers for
+     * this code.  We must spill rPC (edx) because it
+     * is used by imul.  We'll also spill rINST (ebx),
+     * giving us eax, ebc, ecx and edx as computational
+     * temps.  On top of that, we'll spill rIBASE (edi)
+     * for use as the vA pointer and rFP (esi) for use
+     * as the vB pointer.  Yuck.
+     */
+    /* mul-long/2addr vA, vB */
+    movzbl    rINST_HI,%eax            # eax<- BA
+    andb      $$0xf,%al                # eax<- A
+    sarl      $$12,rINST_FULL          # rINST_FULL<- B
+    SPILL(rPC)
+    SPILL(rIBASE)
+    SPILL(rFP)
+    leal      (rFP,%eax,4),rIBASE      # rIBASE<- &v[A]
+    leal      (rFP,rINST_FULL,4),rFP   # rFP<- &v[B]
+    movl      4(rIBASE),%ecx      # ecx<- Amsw
+    imull     (rFP),%ecx          # ecx<- (Amsw*Blsw)
+    movl      4(rFP),%eax         # eax<- Bmsw
+    imull     (rIBASE),%eax       # eax<- (Bmsw*Alsw)
+    addl      %eax,%ecx           # ecx<- (Amsw*Blsw)+(Bmsw*Alsw)
+    movl      (rFP),%eax          # eax<- Blsw
+    mull      (rIBASE)            # eax<- (Blsw*Alsw)
+    jmp       .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+    leal      (%ecx,%edx),%edx    # full result now in %edx:%eax
+    movl      %edx,4(rIBASE)      # v[A+1]<- %edx
+    UNSPILL(rPC)                  # restore rPC/%edx
+    FETCH_INST_WORD(1)
+    movl      %eax,(rIBASE)       # v[A]<- %eax
+    UNSPILL(rFP)
+    UNSPILL(rIBASE)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..18bd275
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_LONG.S
@@ -0,0 +1,16 @@
+%verify "executed"
+    /* unop vA, vB */
+    movzbl    rINST_HI,%ecx            # ecx<- BA
+    sarl      $$4,%ecx                 # ecx<- B
+    movzbl    rINST_HI,rINST_FULL      # ecx<- BA
+    andb      $$0xf,rINST_LO           # rINST_FULL<- 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_FULL,0)   # v[A+0]<- eax
+    SET_VREG_WORD(%ecx,rINST_FULL,1)   # v[A+1]<- ecx
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_NEW_ARRAY.S b/vm/mterp/x86/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..74d72ed
--- /dev/null
+++ b/vm/mterp/x86/OP_NEW_ARRAY.S
@@ -0,0 +1,72 @@
+%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_GLUE(%ecx)
+    EXPORT_PC()
+    movl    offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+    movzwl  2(rPC),%eax                       # eax<- CCCC
+    movl    offDvmDex_pResClasses(%ecx),%ecx  # ecx<- pDvmDex->pResClasses
+    movl    (%ecx,%eax,4),%ecx                # ecx<- resolved class
+    movzbl  rINST_HI,%eax
+    sarl    $$4,%eax                          # eax<- B
+    GET_VREG(%eax,%eax)                       # eax<- vB (array length)
+    movzbl  rINST_HI,rINST_FULL
+    andb    $$0xf,rINST_LO                    # rINST_FULL<- A
+    testl   %eax,%eax
+    js      common_errNegativeArraySize       # bail
+    testl   %ecx,%ecx                         # already resolved?
+    jne     .L${opcode}_finish                # yes, fast path
+    jmp     .L${opcode}_resolve               # resolve now
+%break
+
+    /*
+     * Resolve class.  (This is an uncommon case.)
+     *  ecx holds class (null here)
+     *  eax holds array length (vB)
+     */
+.L${opcode}_resolve:
+    GET_GLUE(%ecx)
+    SPILL_TMP(%eax)                    # save array length
+    movl    offGlue_method(%ecx),%ecx  # ecx<- glue->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)
+    SPILL(rPC)
+    call    dvmResolveClass            # eax<- call(clazz,ref,flag)
+    UNSPILL(rPC)
+    movl    %eax,%ecx
+    UNSPILL_TMP(%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)
+    SPILL(rPC)
+    call    dvmAllocArrayByClass    # eax<- call(clazz,length,flags)
+    UNSPILL(rPC)
+    testl   %eax,%eax               # failed?
+    je      common_exceptionThrown  # yup - go handle
+    movl    rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_NEW_INSTANCE.S b/vm/mterp/x86/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..e11e518
--- /dev/null
+++ b/vm/mterp/x86/OP_NEW_INSTANCE.S
@@ -0,0 +1,93 @@
+%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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax               # eax<- BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- pDvmDex
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    movl      offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+    EXPORT_PC()
+    movl      (%ecx,%eax,4),%ecx        # ecx<- resolved class
+    SPILL(rPC)
+    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)
+    je        .L${opcode}_initialized
+    jmp       .L${opcode}_needinit
+%break
+
+.L${opcode}_initialized:  # on entry, ecx<- class
+    /* TODO: remove test for interface/abstract, now done in verifier */
+    testl     $$(ACC_INTERFACE|ACC_ABSTRACT),offClassObject_accessFlags(%ecx)
+    movl      $$ALLOC_DONT_TRACK,OUT_ARG1(%esp)
+    jne       .L${opcode}_abstract
+.L${opcode}_finish: # ecx=class
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmAllocObject             # eax<- new object
+    UNSPILL(rPC)
+    movl     rINST_FULL,%ecx
+    FETCH_INST_WORD(2)
+    testl    %eax,%eax                  # success?
+    je       common_exceptionThrown     # no, bail out
+    SET_VREG(%eax,%ecx)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+    /*
+     * Class initialization required.
+     *
+     *  ecx holds class object
+     */
+.L${opcode}_needinit:
+    SPILL_TMP(%ecx)                     # save object
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmInitClass                # initialize class
+    UNSPILL_TMP(%ecx)                   # restore object
+    testl   %eax,%eax                   # success?
+    jne     .L${opcode}_initialized     # success, continue
+    UNSPILL(rPC)                        # failed, restore PC
+    jmp     common_exceptionThrown      # go deal with init exception
+
+    /*
+     * Resolution required.  This is the least-likely path.
+     *
+     */
+.L${opcode}_resolve:
+    GET_GLUE(%ecx)
+    movzwl  2(rPC),%eax
+    movl    offGlue_method(%ecx),%ecx   # ecx<- glue->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
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown      # no, handle exception
+
+    /*
+     * TODO: remove this
+     * We can't instantiate an abstract class or interface, so throw an
+     * InstantiationError with the class descriptor as the message.
+     *
+     *  ecx holds class object
+     */
+.L${opcode}_abstract:
+    movl    offClassObject_descriptor(%ecx),%eax
+    movl    $$.LstrInstantiationError,OUT_ARG0(%esp)
+    movl    %eax,OUT_ARG1(%esp)
+    call    dvmThrowExceptionWithClassMessage
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
diff --git a/vm/mterp/x86/OP_NOP.S b/vm/mterp/x86/OP_NOP.S
new file mode 100644
index 0000000..17e3589
--- /dev/null
+++ b/vm/mterp/x86/OP_NOP.S
@@ -0,0 +1,4 @@
+%verify "executed"
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..3eca120
--- /dev/null
+++ b/vm/mterp/x86/OP_NOT_LONG.S
@@ -0,0 +1,15 @@
+%verify "executed"
+    /* unop vA, vB */
+    movzbl    rINST_HI,%ecx            # ecx<- BA
+    sarl      $$4,%ecx                 # ecx<- B
+    movzbl    rINST_HI,rINST_FULL      # ecx<- BA
+    andb      $$0xf,rINST_LO           # rINST_FULL<- 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_FULL,0)   # v[A+0]<- eax
+    SET_VREG_WORD(%ecx,rINST_FULL,1)   # v[A+1]<- ecx
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..b14555b
--- /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),rPC", "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..d1e78b2
--- /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_FULL,4)","instr2":"orl %ecx,4(rFP,rINST_FULL,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..089fc55
--- /dev/null
+++ b/vm/mterp/x86/OP_PACKED_SWITCH.S
@@ -0,0 +1,27 @@
+%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 */
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    movl    2(rPC),%ecx                 # ecx<- BBBBbbbb
+    GET_VREG(%eax,rINST_FULL)           # 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
+    SPILL(rPC)
+    call    $func
+    UNSPILL(rPC)
+    testl   %eax,%eax
+    movl    %eax,rINST_FULL             # set up word offset
+    jle     common_backwardBranch       # check on special actions
+    ADVANCE_PC_INDEXED(rINST_FULL)
+    FETCH_INST()
+    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..809ac0a
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_DOUBLE.S
@@ -0,0 +1,17 @@
+%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
+    movzbl   rINST_HI,%ecx          # ecx<- AA
+    FETCH_INST_WORD(2)
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC(2)
+    fstpl    (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
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..b199e6e
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* rem_float/2addr vA, vB */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $$12,rINST_FULL             # rINST_FULL<- B
+    fldl     (rFP,rINST_FULL,4)         # vBB to fp stack
+    andb    $$0xf,%cl                   # ecx<- A
+    fldl     (rFP,%ecx,4)               # vAA to fp stack
+    FETCH_INST_WORD(1)
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC(1)
+    fstpl    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_FLOAT.S b/vm/mterp/x86/OP_REM_FLOAT.S
new file mode 100644
index 0000000..d78bc9a
--- /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   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC(2)
+    fstps    (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
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..fd1742b
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,17 @@
+%verify "executed"
+    /* rem_float/2addr vA, vB */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $$12,rINST_FULL             # rINST_FULL<- B
+    flds     (rFP,rINST_FULL,4)         # vBB to fp stack
+    andb    $$0xf,%cl                   # ecx<- A
+    flds     (rFP,%ecx,4)               # vAA to fp stack
+    FETCH_INST_WORD(1)
+1:
+    fprem
+    fstsw     %ax
+    sahf
+    jp        1b
+    fstp      %st(1)
+    ADVANCE_PC(1)
+    fstps    (rFP,%ecx,4)               # %st to vA
+    GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_INT.S b/vm/mterp/x86/OP_REM_INT.S
new file mode 100644
index 0000000..601b383
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv.S" {"result":"%edx","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..cfb60bd
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv2addr.S" {"result":"%edx","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..a5fdb1e
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit16.S" {"result":"%edx","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..9d06fcd
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit8.S" {"result":"%edx","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..5d6a9a0
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /*
+     * Return a 32-bit value.  Copies the return value into the "glue"
+     * structure, then jumps to the return handler.
+     *
+     * for: return, return-object
+     */
+    /* op vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL         # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)           # eax<- vAA
+    movl    %eax,offGlue_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..a2fd636
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+    /*
+     * Return a 64-bit value.  Copies the return value into the "glue"
+     * structure, then jumps to the return handler.
+     */
+    /* return-wide vAA */
+    GET_GLUE(%ecx)
+    movzbl  rINST_HI,rINST_FULL            # rINST_FULL<- AA
+    GET_VREG_WORD(%eax,rINST_FULL,0)       # eax<- v[AA+0]
+    GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- v[AA+1]
+    movl    %eax,offGlue_retval(%ecx)
+    movl    rINST_FULL,4+offGlue_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..f05eae7
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET.S
@@ -0,0 +1,43 @@
+%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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
+%break
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .L${opcode}_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
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..5c038a7
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_WIDE.S
@@ -0,0 +1,44 @@
+%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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,rINST_FULL                # rINST_FULL<- AA
+    SET_VREG_WORD(%ecx,rINST_FULL,0)
+    SET_VREG_WORD(%eax,rINST_FULL,1)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+%break
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .L${opcode}_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
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..2d06200
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_LONG.S
@@ -0,0 +1,37 @@
+%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 edx */
+    /* rINST gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rPC)                          # spill edx
+    GET_VREG_WORD(%edx,%eax,1)          # ecx<- v[BB+1]
+    GET_VREG  (%ecx,%ecx)               # ecx<- vCC
+    GET_VREG_WORD(%eax,%eax,0)          # eax<- v[BB+0]
+    shldl     %eax,%edx
+    sall      %cl,%eax
+    testb     $$32,%cl
+    je        2f
+    movl      %eax,%edx
+    xorl      %eax,%eax
+2:
+    movzbl    rINST_HI,%ecx
+    SET_VREG_WORD(%edx,%ecx,1)         # v[AA+1]<- %edx
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    jmp       .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+    SET_VREG_WORD(%eax,%ecx,0)         # v[AA+0]<- %eax
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..b98ed92
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,35 @@
+%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 edx */
+    /* rINST gets AA */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    movzbl    rINST_HI,rINST_FULL       # rINST_HI<- BA
+    andb      $$0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG_WORD(%eax,rINST_FULL,0)    # eax<- v[AA+0]
+    sarl      $$4,%ecx                  # ecx<- B
+    SPILL(rPC)
+    GET_VREG_WORD(%edx,rINST_FULL,1)    # edx<- v[AA+1]
+    GET_VREG(%ecx,%ecx)                 # ecx<- vBB
+    shldl     %eax,%edx
+    sall      %cl,%eax
+    testb     $$32,%cl
+    je        2f
+    movl      %eax,%edx
+    xorl      %eax,%eax
+2:
+    SET_VREG_WORD(%edx,rINST_FULL,1)   # v[AA+1]<- edx
+    UNSPILL(rPC)
+    jmp       .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+    SET_VREG_WORD(%eax,rINST_FULL,0)  # v[AA+0]<- eax
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..bccae8e
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_LONG.S
@@ -0,0 +1,38 @@
+%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 edx */
+    /* rINST gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rPC)                          # spill edx
+    GET_VREG_WORD(%edx,%eax,1)          # edx<- v[BB+1]
+    GET_VREG  (%ecx,%ecx)               # ecx<- vCC
+    GET_VREG_WORD(%eax,%eax,0)          # eax<- v[BB+0]
+    shrdl     %edx,%eax
+    sarl      %cl,%edx
+    testb     $$32,%cl
+    je        2f
+    movl      %edx,%eax
+    sarl      $$31,%edx
+2:
+    movzbl    rINST_HI,%ecx
+    SET_VREG_WORD(%edx,%ecx,1)         # v[AA+1]<- edx
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    jmp       .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+    SET_VREG_WORD(%eax,%ecx,0)         # v[AA+0]<- eax
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..fba1f25
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,35 @@
+%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 edx */
+    /* rINST gets AA */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    movzbl    rINST_HI,rINST_FULL       # rINST_HI<- BA
+    andb      $$0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG_WORD(%eax,rINST_FULL,0)    # eax<- v[AA+0]
+    sarl      $$4,%ecx                  # ecx<- B
+    SPILL(rPC)
+    GET_VREG_WORD(%edx,rINST_FULL,1)    # edx<- v[AA+1]
+    GET_VREG(%ecx,%ecx)                 # ecx<- vBB
+    shrdl     %edx,%eax
+    sarl      %cl,%edx
+    testb     $$32,%cl
+    je        2f
+    movl      %edx,%eax
+    sarl      $$31,%edx
+2:
+    SET_VREG_WORD(%edx,rINST_FULL,1)   # v[AA+1]<- edx
+    UNSPILL(rPC)
+    jmp       .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+    SET_VREG_WORD(%eax,rINST_FULL,0)  # v[AA+0]<- eax
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..440dcfb
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT.S
@@ -0,0 +1,43 @@
+%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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    FETCH_INST_WORD(2)
+    movl      %ecx,offStaticField_value(%eax)
+    ADVANCE_PC(2)
+    GOTO_NEXT
+%break
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .L${opcode}_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
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..13fa860
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_OBJECT.S
@@ -0,0 +1,50 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+    /*
+     * SPUT object handler.
+     */
+    /* op vAA, field@BBBB */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG(%ecx,%ecx)
+    jmp       .L${opcode}_continue
+%break
+
+
+.L${opcode}_continue:
+    movl      %ecx,offStaticField_value(%eax)    # do the store
+    testl     %ecx,%ecx                          # stored null object ptr?
+    FETCH_INST_WORD(2)
+    je        1f                                 # skip card mark if null
+    GET_GLUE(%ecx)
+    movl      offField_clazz(%eax),%eax          # eax<- field->clazz
+    movl      offGlue_cardTable(%ecx),%ecx       # get card table base
+    shrl      $$GC_CARD_SHIFT,%eax               # head to card number
+    movb      %cl,(%ecx,%eax)                    # mark card
+1:
+    ADVANCE_PC(2)
+    GOTO_NEXT
+
+.L${opcode}_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .L${opcode}_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
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..5a48c2e
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_WIDE.S
@@ -0,0 +1,45 @@
+%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 */
+    GET_GLUE(%ecx)
+    movzwl    2(rPC),%eax                        # eax<- field ref BBBB
+    movl      offGlue_methodClassDex(%ecx),%ecx  # ecx<- DvmDex
+    movl      offDvmDex_pResFields(%ecx),%ecx    # ecx<- dvmDex->pResFields
+    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
+    movzbl    rINST_HI,%ecx                      # ecx<- AA
+    GET_VREG_WORD(rINST_FULL,%ecx,0)             # rINST_FULL<- lsw
+    GET_VREG_WORD(%ecx,%ecx,1)                   # ecx<- msw
+    movl      rINST_FULL,offStaticField_value(%eax)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    movl      %ecx,4+offStaticField_value(%eax)
+    GOTO_NEXT
+%break
+
+    /*
+     * Go resolve the field
+     */
+.L${opcode}_resolve:
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                        # eax<- field ref BBBB
+    movl     offGlue_method(%ecx),%ecx          # ecx<- current method
+    EXPORT_PC()                                 # could throw, need to export
+    movl     offMethod_clazz(%ecx),%ecx         # ecx<- method->clazz
+    SPILL(rPC)
+    movl     %eax,OUT_ARG1(%esp)
+    movl     %ecx,OUT_ARG0(%esp)
+    call     dvmResolveStaticField              # eax<- resolved StaticField ptr
+    UNSPILL(rPC)
+    testl    %eax,%eax
+    jne      .L${opcode}_finish                 # success, continue
+    jmp      common_exceptionThrown             # no, handle exception
diff --git a/vm/mterp/x86/OP_SUB_DOUBLE.S b/vm/mterp/x86/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..224f3a1
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fsubl","load":"fldl","store":"fstpl"}
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..991c380
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fsubl","load":"fldl","store":"fstpl"}
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..6eda7bb
--- /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),rPC", "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..94bf0d6
--- /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_FULL,4)","instr2":"sbbl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/OP_THROW.S b/vm/mterp/x86/OP_THROW.S
new file mode 100644
index 0000000..d7e1574
--- /dev/null
+++ b/vm/mterp/x86/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_GLUE(%ecx)
+    EXPORT_PC()
+    movzbl   rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    GET_VREG(%eax,rINST_FULL)          # eax<- exception object
+    movl     offGlue_self(%ecx),%ecx   # ecx<- glue->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..e492e2d
--- /dev/null
+++ b/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,19 @@
+%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 */
+    GET_GLUE(%ecx)
+    movzwl   2(rPC),%eax                     # eax<- BBBB
+    movl     offGlue_method(%ecx),%ecx       # ecx<- glue->method
+    EXPORT_PC()
+    movzbl   rINST_HI,rINST_FULL             # rINST_FULL<- AA
+    movl     %eax,OUT_ARG2(%esp)             # arg2<- BBBB
+    movl     rINST_FULL,OUT_ARG1(%esp)       # arg1<- AA
+    movl     %ecx,OUT_ARG0(%esp)             # arg0<- method
+    SPILL(rPC)
+    call     dvmThrowVerificationError       # call(method, kind, ref)
+    UNSPILL(rPC)
+    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_E3.S b/vm/mterp/x86/OP_UNUSED_E3.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E3.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E4.S b/vm/mterp/x86/OP_UNUSED_E4.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E4.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E5.S b/vm/mterp/x86/OP_UNUSED_E5.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E5.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E6.S b/vm/mterp/x86/OP_UNUSED_E6.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E6.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E7.S b/vm/mterp/x86/OP_UNUSED_E7.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E7.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_F1.S b/vm/mterp/x86/OP_UNUSED_F1.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_F1.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FC.S b/vm/mterp/x86/OP_UNUSED_FC.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FC.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FD.S b/vm/mterp/x86/OP_UNUSED_FD.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FD.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FE.S b/vm/mterp/x86/OP_UNUSED_FE.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FE.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..5e4c89b
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_LONG.S
@@ -0,0 +1,38 @@
+%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 edx */
+    /* rINST gets AA */
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rPC)                          # spill edx
+    GET_VREG_WORD(%edx,%eax,1)          # edx<- v[BB+1]
+    GET_VREG  (%ecx,%ecx)               # ecx<- vCC
+    GET_VREG_WORD(%eax,%eax,0)          # eax<- v[BB+0]
+    shrdl     %edx,%eax
+    shrl      %cl,%edx
+    testb     $$32,%cl
+    je        2f
+    movl      %edx,%eax
+    xorl      %edx,%edx
+2:
+    movzbl    rINST_HI,%ecx
+    SET_VREG_WORD(%edx,%ecx,1)         # v[BB+1]<- edx
+    UNSPILL(rPC)
+    jmp       .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+    SET_VREG_WORD(%eax,%ecx,0)        # v[BB+0]<- eax
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
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..b2555e9
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,35 @@
+%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 edx */
+    /* rINST gets AA */
+    movzbl    rINST_HI,%ecx             # ecx<- BA
+    movzbl    rINST_HI,rINST_FULL       # rINST_HI<- BA
+    andb      $$0xf,rINST_LO            # rINST_FULL<- A
+    GET_VREG_WORD(%eax,rINST_FULL,0)    # eax<- v[AA+0]
+    sarl      $$4,%ecx                  # ecx<- B
+    SPILL(rPC)
+    GET_VREG_WORD(%edx,rINST_FULL,1)    # edx<- v[AA+1]
+    GET_VREG(%ecx,%ecx)                 # ecx<- vBB
+    shrdl     %edx,%eax
+    shrl      %cl,%edx
+    testb     $$32,%cl
+    je        2f
+    movl      %edx,%eax
+    xorl      %edx,%edx
+2:
+    SET_VREG_WORD(%edx,rINST_FULL,1)   # v[AA+1]<- edx
+    UNSPILL(rPC)
+    jmp       .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+    SET_VREG_WORD(%eax,rINST_FULL,0)   # v[AA+0]<- eax
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..ebeb126
--- /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),rPC", "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..2b127b1
--- /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_FULL,4)","instr2":"xorl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/bincmp.S b/vm/mterp/x86/bincmp.S
new file mode 100644
index 0000000..26956b4
--- /dev/null
+++ b/vm/mterp/x86/bincmp.S
@@ -0,0 +1,26 @@
+%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    rINST_HI,%ecx              # ecx <- A+
+    andb     $$0xf,%cl                  # ecx <- A
+    GET_VREG(%eax,%ecx)                 # eax <- vA
+    sarl     $$12,rINST_FULL            # rINST_FULL<- B
+    cmpl     (rFP,rINST_FULL,4),%eax    # compare (vA, vB)
+    movswl   2(rPC),rINST_FULL          # Get signed branch offset
+    movl     $$2,%eax                   # assume not taken
+    j${revcmp}   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
+    GOTO_NEXT
diff --git a/vm/mterp/x86/bindiv.S b/vm/mterp/x86/bindiv.S
new file mode 100644
index 0000000..6aca1a4
--- /dev/null
+++ b/vm/mterp/x86/bindiv.S
@@ -0,0 +1,32 @@
+
+%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(%eax,%eax)             # eax<- vBB
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    SPILL(rPC)
+    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
+    jmp      .L${opcode}_finish_div
+
+%break
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+.L${opcode}_finish_div:
+    movzbl   rINST_HI,%ecx         # ecl<- AA
+    SET_VREG($result,%ecx)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/bindiv2addr.S b/vm/mterp/x86/bindiv2addr.S
new file mode 100644
index 0000000..efa76b5
--- /dev/null
+++ b/vm/mterp/x86/bindiv2addr.S
@@ -0,0 +1,32 @@
+%default {"result":"","special":""}
+    /*
+     * 32-bit binary div/rem operation.  Handles special case of op0=minint and
+     * op1=-1.
+     */
+    /* div/rem/2addr vA, vB */
+    movzx    rINST_HI,%ecx          # eax<- BA
+    sarl     $$4,%ecx               # ecx<- B
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    movzbl   rINST_HI,rINST_FULL    # rINST_FULL<- BA
+    andb     $$0xf,rINST_LO         # rINST_FULL<- A
+    GET_VREG(%eax,rINST_FULL)       # eax<- vBB
+    SPILL(rPC)
+    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
+    jmp      .L${opcode}_finish_div2addr
+
+%break
+.L${opcode}_continue_div2addr:
+    cltd
+    idivl   %ecx
+.L${opcode}_finish_div2addr:
+    SET_VREG($result,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/bindivLit16.S b/vm/mterp/x86/bindivLit16.S
new file mode 100644
index 0000000..3806830
--- /dev/null
+++ b/vm/mterp/x86/bindivLit16.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/lit16 vA, vB, #+CCCC */
+    /* Need A in rINST_FULL, ssssCCCC in ecx, vB in eax */
+    movzbl   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $$4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $$0xf,rINST_LO             # rINST_FULL<- A
+    SPILL(rPC)
+    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
+    jmp      .L${opcode}_finish_div
+
+%break
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+.L${opcode}_finish_div:
+    SET_VREG($result,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/bindivLit8.S b/vm/mterp/x86/bindivLit8.S
new file mode 100644
index 0000000..6d616ba
--- /dev/null
+++ b/vm/mterp/x86/bindivLit8.S
@@ -0,0 +1,30 @@
+%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
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    SPILL(rPC)
+    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
+    jmp      .L${opcode}_finish_div
+
+%break
+.L${opcode}_continue_div:
+    cltd
+    idivl   %ecx
+.L${opcode}_finish_div:
+    SET_VREG($result,rINST_FULL)
+    UNSPILL(rPC)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binflop.S b/vm/mterp/x86/binflop.S
new file mode 100644
index 0000000..233799c
--- /dev/null
+++ b/vm/mterp/x86/binflop.S
@@ -0,0 +1,15 @@
+    /*
+     * 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
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    $store   (rFP,%ecx,4)           # %st to vAA
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binflop2addr.S b/vm/mterp/x86/binflop2addr.S
new file mode 100644
index 0000000..4b78e11
--- /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   rINST_HI,%ecx               # ecx<- A+
+    andb    $$0xf,%cl                   # ecx<- A
+    $load    (rFP,%ecx,4)               # vAA to fp stack
+    sarl    $$12,rINST_FULL             # rINST_FULL<- B
+    $instr   (rFP,rINST_FULL,4)           # ex: faddp
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    $store    (rFP,%ecx,4)              # %st to vA
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binop.S b/vm/mterp/x86/binop.S
new file mode 100644
index 0000000..00d9118
--- /dev/null
+++ b/vm/mterp/x86/binop.S
@@ -0,0 +1,20 @@
+%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(%eax,%eax)             # eax<- vBB
+    $instr              # ex: addl    (rFP,%ecx,4),%eax
+    movzbl   rINST_HI,%ecx         # ecx<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG($result,%ecx)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binop1.S b/vm/mterp/x86/binop1.S
new file mode 100644
index 0000000..e932103
--- /dev/null
+++ b/vm/mterp/x86/binop1.S
@@ -0,0 +1,16 @@
+%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(%eax,%eax)             # eax<- vBB
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    $instr                          # ex: addl    %ecx,%eax
+    movzbl   rINST_HI,$tmp          # tmp<- AA
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    SET_VREG($result,$tmp)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binop2addr.S b/vm/mterp/x86/binop2addr.S
new file mode 100644
index 0000000..0600aa3
--- /dev/null
+++ b/vm/mterp/x86/binop2addr.S
@@ -0,0 +1,24 @@
+%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 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 */
+    movzx   rINST_HI,%ecx               # ecx<- A+
+    sarl    $$12,rINST_FULL             # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)           # eax<- vB
+    FETCH_INST_WORD(1)
+    andb    $$0xf,%cl                   # ecx<- A
+    $instr                              # for ex: addl   %eax,(rFP,%ecx,4)
+    ADVANCE_PC(1)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binopLit16.S b/vm/mterp/x86/binopLit16.S
new file mode 100644
index 0000000..762068f
--- /dev/null
+++ b/vm/mterp/x86/binopLit16.S
@@ -0,0 +1,22 @@
+%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   rINST_HI,%eax              # eax<- 000000BA
+    sarl     $$4,%eax                   # eax<- B
+    GET_VREG(%eax,%eax)                 # eax<- vB
+    movswl   2(rPC),%ecx                # ecx<- ssssCCCC
+    movzbl   rINST_HI,rINST_FULL        # rINST_FULL<- BA
+    andb     $$0xf,rINST_LO             # rINST_FULL<- A
+    $instr                              # for example: addl %ecx, %eax
+    SET_VREG($result,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binopLit8.S b/vm/mterp/x86/binopLit8.S
new file mode 100644
index 0000000..73a146c
--- /dev/null
+++ b/vm/mterp/x86/binopLit8.S
@@ -0,0 +1,21 @@
+%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
+    movzx     rINST_HI,rINST_FULL      # rINST_FULL<- AA
+    GET_VREG  (%eax,%eax)              # eax<- rBB
+    $instr                             # ex: addl %ecx,%eax
+    SET_VREG  ($result,rINST_FULL)
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binopWide.S b/vm/mterp/x86/binopWide.S
new file mode 100644
index 0000000..592e45f
--- /dev/null
+++ b/vm/mterp/x86/binopWide.S
@@ -0,0 +1,19 @@
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop vAA, vBB, vCC */
+
+    movzbl    2(rPC),%eax               # eax<- BB
+    movzbl    3(rPC),%ecx               # ecx<- CC
+    SPILL(rPC)
+    GET_VREG_WORD(rPC,%eax,0)           # rPC<- v[BB+0]
+    GET_VREG_WORD(%eax,%eax,1)          # eax<- v[BB+1]
+    $instr1         # ex: addl   (rFP,%ecx,4),rPC
+    $instr2         # ex: adcl   4(rFP,%ecx,4),%eax
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
+    SET_VREG_WORD(rPC,rINST_FULL,0)     # v[AA+0] <- rPC
+    UNSPILL(rPC)
+    SET_VREG_WORD(%eax,rINST_FULL,1)    # v[AA+1] <- eax
+    FETCH_INST_WORD(2)
+    ADVANCE_PC(2)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/binopWide2addr.S b/vm/mterp/x86/binopWide2addr.S
new file mode 100644
index 0000000..5d378bf
--- /dev/null
+++ b/vm/mterp/x86/binopWide2addr.S
@@ -0,0 +1,15 @@
+    /*
+     * Generic 64-bit binary operation.
+     */
+    /* binop/2addr vA, vB */
+    movzbl    rINST_HI,%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]
+    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- BA
+    andb      $$0xF,rINST_LO            # rINST_FULL<- A
+    $instr1         # example: addl   %eax,(rFP,rINST_FULL,4)
+    $instr2         # example: adcl   %ecx,4(rFP,rINST_FULL,4)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/cvtfp_int.S b/vm/mterp/x86/cvtfp_int.S
new file mode 100644
index 0000000..f4f36b8
--- /dev/null
+++ b/vm/mterp/x86/cvtfp_int.S
@@ -0,0 +1,63 @@
+%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    rINST_HI,%ecx           # ecx<- A+
+    sarl      $$12,rINST_FULL         # rINST_FULL<- B
+    .if $srcdouble
+    fldl     (rFP,rINST_FULL,4)       # %st0<- vB
+    .else
+    flds     (rFP,rINST_FULL,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
+    FETCH_INST_WORD(1)
+    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
+    jmp      .L${opcode}_continue
+%break
+
+
+.L${opcode}_continue:
+    .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:
+    ADVANCE_PC(1)
+    GOTO_NEXT
+
+.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..ff310fe
--- /dev/null
+++ b/vm/mterp/x86/entry.S
@@ -0,0 +1,111 @@
+/*
+ * 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(MterpGlue* glue)
+ *
+ * Interpreter entry point.  Returns changeInterp.
+ *
+ */
+dvmMterpStdRun:
+    push    %ebp
+    movl    %esp,%ebp
+    push    %edi
+    push    %esi
+    push    %ebx
+
+/* at this point, stack is misaligned by 1 word
+   We're allocating spill space for 6 words, plus
+   outgoing argument (5 words) and local variables
+   (4 words) - 15 words or 60 bytes total. See
+   diagram in header.S
+*/
+    subl   $$60,%esp
+
+/* Set up "named" registers */
+    movl    IN_ARG0(%ebp),%ecx
+    movl    %ecx,rGLUE_SPILL(%ebp)
+    LOAD_PC_FROM_GLUE(%ecx)
+    LOAD_FP_FROM_GLUE(%ecx)
+    movl    $$dvmAsmInstructionStart,rIBASE
+
+/* Remember %esp for future "longjmp" */
+    movl    %esp,offGlue_bailPtr(%ecx)
+
+/* How to start? */
+    movb    offGlue_entryPoint(%ecx),%al
+
+/* Normal start? */
+    cmpb    $$kInterpEntryInstr,%al
+    jne     .Lnot_instr
+
+   /* Normal case: start executing the instruction at rPC */
+    FETCH_INST()
+    GOTO_NEXT
+
+.Lnot_instr:
+    /* Reset to normal case */
+    movb   $$kInterpEntryInstr,offGlue_entryPoint(%ecx)
+    cmpb   $$kInterpEntryReturn,%al
+    je     common_returnFromMethod
+    cmpb   $$kInterpEntryThrow,%al
+    je     common_exceptionThrown
+    movzx  %al,%eax
+    movl   %eax,OUT_ARG1(%esp)
+    movl   $$.LstrBadEntryPoint,OUT_ARG0(%esp)
+    call   printf
+    call   dvmAbort
+    /* Not reached */
+
+
+    .global dvmMterpStdBail
+    .type   dvmMterpStdBail, %function
+/*
+ * void dvmMterpStdBail(MterpGlue* glue, 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)  MterpGlue* glue
+ *  esp+8 (arg1)  bool changeInterp
+ */
+dvmMterpStdBail:
+    movl    4(%esp),%ecx                 # grab glue
+    movl    8(%esp),%eax                 # changeInterp to return reg
+    movl    offGlue_bailPtr(%ecx),%esp   # Stack back to normal
+    addl    $$60,%esp                    # Strip dvmMterpStdRun's frame
+    pop     %ebx
+    pop     %esi
+    pop     %edi
+    pop     %ebp
+    ret                                  # return to dvmMterpStdRun's caller
+
+
+/*
+ * 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..d43a662
--- /dev/null
+++ b/vm/mterp/x86/footer.S
@@ -0,0 +1,535 @@
+/*
+ * 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.
+ */
+
+/*
+ * Common code when a backwards branch is taken
+ *
+ * On entry:
+ *   ebx (a.k.a. rINST_FULL) -> PC adjustment in 16-bit words
+ */
+common_backwardBranch:
+    GET_GLUE(%ecx)
+    call   common_periodicChecks  # Note: expects rPC to be preserved
+    ADVANCE_PC_INDEXED(rINST_FULL)
+    FETCH_INST()
+    GOTO_NEXT
+
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ *   eax = Method* methodToCall
+ *   rINST trashed, must reload
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+
+   /*
+    * prepare to copy args to "outs" area of current frame
+    */
+
+    movzbl      1(rPC),rINST_FULL       # rINST_FULL<- AA
+    movzwl      4(rPC), %ecx            # %ecx<- CCCC
+    SPILL(rPC)
+    SAVEAREA_FROM_FP(%edx,rFP)          # %edx<- &StackSaveArea
+    test        rINST_FULL, rINST_FULL
+    movl        rINST_FULL, 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)
+    */
+
+    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
+    */
+
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+    movzbl      1(rPC),rINST_FULL       # rINST_FULL<- BA
+    SPILL(rPC)
+    movl        rINST_FULL, 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,rFP)          # %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_FULL        # rINST<- A
+    lea         -4(%edx), %edx          # %edx<- update &outs; &outs--
+    movl        (rFP, rINST_FULL, 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,rFP)          # %eax<- &StackSaveArea
+    subl        %edx, %eax              # %eax<- newFP; (old savearea - regsSize)
+    GET_GLUE(%edx)                      # %edx<- pMterpGlue
+    movl        %eax, LOCAL1_OFFSET(%ebp)       # LOCAL1_OFFSET(%ebp)<- &outs
+    subl        $$sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
+    movl        offGlue_interpStackEnd(%edx), %edx # %edx<- glue->interpStackEnd
+    movl        %edx, LOCAL2_OFFSET(%ebp)       # LOCAL2_OFFSET<- glue->interpStackEnd
+    shl         $$2, %ecx               # %ecx<- update offset for outsSize
+    movl        %eax, %edx              # %edx<- newSaveArea
+    sub         %ecx, %eax              # %eax<- bottom; (newSaveArea - outsSize)
+    cmp         LOCAL2_OFFSET(%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,rFP)          # %ecx<- &StackSaveArea
+    movl        %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
+#endif
+    movl        rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
+    movl        rPC_SPILL(%ebp), %ecx
+    movl        %ecx, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
+    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 "glue" values for the new method
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
+    */
+
+    movl        offMethod_clazz(%eax), %edx # %edx<- method->clazz
+    GET_GLUE(%ecx)                      # %ecx<- pMterpGlue
+    movl        offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+    movl        %eax, offGlue_method(%ecx) # glue->method<- methodToCall
+    movl        %edx, offGlue_methodClassDex(%ecx) # glue->methodClassDex<- method->clazz->pDvmDex
+    movl        offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
+    movl        offGlue_self(%ecx), %eax # %eax<- glue->self
+    movl        LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
+    movl        rFP, offThread_curFrame(%eax) # glue->self->curFrame<- newFP
+    FETCH_INST()
+    GOTO_NEXT                           # jump to methodToCall->insns
+
+   /*
+    * Prep for the native call
+    * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea
+    */
+
+.LinvokeNative:
+    GET_GLUE(%ecx)                      # %ecx<- pMterpGlue
+    movl        %eax, OUT_ARG1(%esp)    # push parameter methodToCall
+    movl        offGlue_self(%ecx), %ecx        # %ecx<- glue->self
+    movl        offThread_jniLocal_topCookie(%ecx), %eax # %eax<- self->localRef->...
+    movl        %eax, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
+    movl        %edx, OUT_ARG4(%esp)    # save newSaveArea
+    movl        LOCAL1_OFFSET(%ebp), %edx # %edx<- newFP
+    movl        %edx, offThread_curFrame(%ecx)  # glue->self->curFrame<- newFP
+    movl        %ecx, OUT_ARG3(%esp)    # save glue->self
+    movl        %ecx, OUT_ARG2(%esp)    # push parameter glue->self
+    GET_GLUE(%ecx)                      # %ecx<- pMterpGlue
+    movl        OUT_ARG1(%esp), %eax    # %eax<- methodToCall
+    lea         offGlue_retval(%ecx), %ecx # %ecx<- &retval
+    movl        %ecx, OUT_ARG0(%esp)    # push parameter pMterpGlue
+    push        %edx                    # push parameter newFP
+
+    call        *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+    lea         4(%esp), %esp
+    movl        OUT_ARG4(%esp), %ecx    # %ecx<- newSaveArea
+    movl        OUT_ARG3(%esp), %eax    # %eax<- glue->self
+    movl        offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
+    cmp         $$0, offThread_exception(%eax) # check for exception
+    movl        rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
+    movl        %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
+    UNSPILL(rPC)
+    jne         common_exceptionThrown  # handle exception
+    FETCH_INST_WORD(3)
+    ADVANCE_PC(3)
+    GOTO_NEXT                           # jump to next instruction
+
+.LstackOverflow:    # eax=methodToCall
+    movl        %eax, OUT_ARG1(%esp)    # push parameter methodToCall
+    GET_GLUE(%eax)                      # %eax<- pMterpGlue
+    movl        offGlue_self(%eax), %eax # %eax<- glue->self
+    movl        %eax, OUT_ARG0(%esp)    # push parameter self
+    call        dvmHandleStackOverflow  # call: (Thread* self, Method* meth)
+    UNSPILL(rPC)                        # return: void
+    jmp         common_exceptionThrown  # handle exception
+
+
+/*
+ * Common invoke code (old-style).
+ * TUNING:  Rewrite along lines of new armv5 code?
+ *
+ * On entry:
+ *   eax = Method* methodToCall
+ *   ecx = bool methodCallRange
+ *   rINST trashed, must reload
+ */
+common_invokeOld:
+    movl     %ecx,OUT_ARG1(%esp)     # arg1<- methodCallRange
+    GET_GLUE(%ecx)
+    movzwl  (rPC),rINST_FULL         # recover rINST
+    movl     %eax,OUT_ARG2(%esp)     # arg2<- method
+    movzwl   4(rPC),%eax             # eax<- GFED or CCCC
+    SAVE_PC_TO_GLUE(%ecx)
+    SAVE_FP_TO_GLUE(%ecx)
+    movzbl   rINST_HI,rINST_FULL
+    movl     rINST_FULL,OUT_ARG3(%esp)# arg3<- AA
+    movl     %ecx,OUT_ARG0(%esp)     # arg0<- GLUE
+    movl     %eax,OUT_ARG4(%esp)     # arg4<- GFED/CCCC
+    call     dvmMterp_invokeMethod
+    jmp      common_resumeAfterGlueCall
+
+
+/*
+ * Do we need the thread to be suspended or have debugger/profiling activity?
+ *
+ * On entry:
+ *   ebx  -> PC adjustment in 16-bit words (must be preserved)
+ *   ecx  -> GLUE pointer
+ *   reentry type, e.g. kInterpEntryInstr stored in rGLUE->entryPoint
+ *
+ * Note: A call will normally kill %eax, rPC/%edx and %ecx.  To
+ *       streamline the normal case, this routine will preserve rPC and
+ *       %ecx in addition to the normal caller save regs.  The save/restore
+ *       is a bit ugly, but will happen in the relatively uncommon path.
+ * TODO: Basic-block style Jit will need a hook here as well.  Fold it into
+ *       the suspendCount check so we can get both in 1 shot.
+ */
+common_periodicChecks:
+    movl    offGlue_pSelfSuspendCount(%ecx),%eax    # eax <- &suspendCount
+    cmpl    $$0,(%eax)
+    jne     1f
+
+6:
+    movl   offGlue_pDebuggerActive(%ecx),%eax      # eax <- &DebuggerActive
+    movl   offGlue_pActiveProfilers(%ecx),%ecx     # ecx <- &ActiveProfilers
+    testl  %eax,%eax               # debugger enabled?
+    je     2f
+    movzbl (%eax),%eax             # get active count
+2:
+    orl    (%ecx),%eax             # eax <- debuggerActive | activeProfilers
+    GET_GLUE(%ecx)                 # restore rGLUE
+    jne    3f                      # one or both active - switch interp
+
+5:
+    ret
+
+    /* Check for suspend */
+1:
+    /*  At this point, the return pointer to the caller of
+     *  common_periodicChecks is on the top of stack.  We need to preserve
+     *  rPC(edx) and GLUE(ecx).  We'll spill rPC, and reload GLUE.
+     *  The outgoing profile is:
+     *      bool dvmCheckSuspendPending(Thread* self)
+     *  Because we reached here via a call, go ahead and build a new frame.
+     */
+    EXPORT_PC()                         # need for precise GC
+    movl    offGlue_self(%ecx),%eax      # eax<- glue->self
+    SPILL(rPC)                      # save edx
+    push    %ebp
+    movl    %esp,%ebp
+    subl    $$24,%esp
+    movl    %eax,OUT_ARG0(%esp)
+    call    dvmCheckSuspendPending
+    addl    $$24,%esp
+    pop     %ebp
+    UNSPILL(rPC)
+    GET_GLUE(%ecx)
+
+    /*
+     * Need to check to see if debugger or profiler flags got set
+     * while we were suspended.
+     */
+    jmp    6b
+
+    /* Switch interpreters */
+    /* Note: %ebx contains the 16-bit word offset to be applied to rPC to
+     * "complete" the interpretation of backwards branches.  In effect, we
+     * are completing the interpretation of the branch instruction here,
+     * and the new interpreter will resume interpretation at the branch
+     * target. However, a switch request recognized during the handling
+     * of a return from method instruction results in an immediate abort,
+     * and the new interpreter will resume by re-interpreting the return
+     * instruction.
+     */
+3:
+    leal    (rPC,%ebx,2),rPC       # adjust pc to show target
+    GET_GLUE(%ecx)                 # bail expect GLUE already loaded
+    movl    $$1,rINST_FULL         # set changeInterp to true
+    jmp     common_gotoBail
+
+
+/*
+ * Common code for handling a return instruction
+ */
+common_returnFromMethod:
+    GET_GLUE(%ecx)
+    /* Set entry mode in case we bail */
+    movb    $$kInterpEntryReturn,offGlue_entryPoint(%ecx)
+    xorl    rINST_FULL,rINST_FULL   # zero offset in case we switch interps
+    call    common_periodicChecks   # Note: expects %ecx to be preserved
+
+    SAVEAREA_FROM_FP(%eax,rFP)                    # eax<- saveArea (old)
+    movl    offStackSaveArea_prevFrame(%eax),rFP  # rFP<- prevFrame
+    movl    (offStackSaveArea_method-sizeofStackSaveArea)(rFP),rINST_FULL
+    cmpl    $$0,rINST_FULL                        # break?
+    je      common_gotoBail    # break frame, bail out completely
+
+    movl    offStackSaveArea_savedPc(%eax),rPC    # pc<- saveArea->savedPC
+    movl    offGlue_self(%ecx),%eax               # eax<- self
+    movl    rINST_FULL,offGlue_method(%ecx)  # glue->method = newSave->meethod
+    movl    rFP,offThread_curFrame(%eax)     # self->curFrame = fp
+    movl    offMethod_clazz(rINST_FULL),%eax # eax<- method->clazz
+    FETCH_INST_WORD(3)
+    movl    offClassObject_pDvmDex(%eax),%eax # eax<- method->clazz->pDvmDex
+    ADVANCE_PC(3)
+    movl    %eax,offGlue_methodClassDex(%ecx)
+    /* not bailing - restore entry mode to default */
+    movb    $$kInterpEntryInstr,offGlue_entryPoint(%ecx)
+    GOTO_NEXT
+
+/*
+ * Prepare to strip the current frame and "longjump" back to caller of
+ * dvmMterpStdRun.
+ *
+ * on entry:
+ *    rINST_FULL holds changeInterp
+ *    ecx holds glue pointer
+ *
+ * expected profile: dvmMterpStdBail(MterpGlue *glue, bool changeInterp)
+ */
+common_gotoBail:
+    SAVE_PC_TO_GLUE(%ecx)                # export state to glue
+    SAVE_FP_TO_GLUE(%ecx)
+    movl   %ecx,OUT_ARG0(%esp)           # glue in arg0
+    movl   rINST_FULL,OUT_ARG1(%esp)     # changeInterp in arg1
+    call    dvmMterpStdBail              # bail out....
+
+
+/*
+ * After returning from a "glued" function, pull out the updated values
+ * and start executing at the next instruction.
+ */
+ common_resumeAfterGlueCall:
+     GET_GLUE(%ecx)
+     LOAD_PC_FROM_GLUE(%ecx)
+     LOAD_FP_FROM_GLUE(%ecx)
+     FETCH_INST()
+     GOTO_NEXT
+
+/*
+ * Integer divide or mod by zero
+ */
+common_errDivideByZero:
+    EXPORT_PC()
+    movl    $$.LstrArithmeticException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    movl    $$.LstrDivideByZero,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+    EXPORT_PC()
+    movl    $$.LstrNegativeArraySizeException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNoSuchMethod:
+
+    EXPORT_PC()
+    movl    $$.LstrNoSuchMethodError,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    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()
+    movl    $$.LstrNullPointerException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+
+/*
+ * Array index exceeds max.
+ */
+common_errArrayIndex:
+    EXPORT_PC()
+    movl    $$.LstrArrayIndexException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    jmp     common_exceptionThrown
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+    EXPORT_PC()
+    movl    $$.LstrArrayStoreException,%eax
+    movl    %eax,OUT_ARG0(%esp)
+    xorl    %eax,%eax
+    movl    %eax,OUT_ARG1(%esp)
+    SPILL(rPC)
+    call    dvmThrowException
+    UNSPILL(rPC)
+    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.
+ *
+ * This does not return.
+ */
+common_exceptionThrown:
+    GET_GLUE(%ecx)
+    SAVE_PC_TO_GLUE(%ecx)
+    SAVE_FP_TO_GLUE(%ecx)
+    movl    %ecx,OUT_ARG0(%esp)
+    call    dvmMterp_exceptionThrown
+    jmp     common_resumeAfterGlueCall
+
+common_abort:
+    movl    $$0xdeadf00d,%eax
+    call     *%eax
+
+
+/*
+ * Strings
+ */
+
+    .section     .rodata
+.LstrNullPointerException:
+    .asciz    "Ljava/lang/NullPointerException;"
+.LstrArithmeticException:
+    .asciz  "Ljava/lang/ArithmeticException;"
+.LstrDivideByZero:
+    .asciz  "divide by zero"
+.LstrArrayIndexException:
+    .asciz  "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+    .asciz  "Ljava/lang/ArrayStoreException;"
+.LstrNegativeArraySizeException:
+    .asciz  "Ljava/lang/NegativeArraySizeException;"
+.LstrInstantiationError:
+    .asciz  "Ljava/lang/InstantiationError;"
+.LstrClassCastException:
+    .asciz  "Ljava/lang/ClassCastException;"
+.LstrNoSuchMethodError:
+    .asciz  "Ljava/lang/NoSuchMethodError;"
+.LstrInternalError:
+    .asciz  "Ljava/lang/InternalError;"
+.LstrFilledNewArrayNotImpl:
+    .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..4fffbe4
--- /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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $$12,rINST_FULL         # rINST_FULL<- B
+    $load    (rFP,rINST_FULL,4)      # %st0<- vB
+    andb     $$0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    $instr
+    $store  (rFP,%ecx,4)             # vA<- %st0
+    GOTO_NEXT
diff --git a/vm/mterp/x86/header.S b/vm/mterp/x86/header.S
new file mode 100644
index 0000000..bb043ba
--- /dev/null
+++ b/vm/mterp/x86/header.S
@@ -0,0 +1,208 @@
+/*
+ * 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)
+
+Alignment of stack not strictly required, but should be for performance.  We'll
+align frame sizes to 16-byte multiples.
+
+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 used useful for those assigned
+to callee save registers).
+
+  nick     reg   purpose
+  rPC      edx   interpreted program counter, used for fetching instructions
+  rFP      esi   interpreted frame pointer, used for accessing locals and args
+  rIBASE   edi   Base pointer for instruction dispatch computed goto
+  rINST    bx    first 16-bit code of current instruction
+  rOPCODE  bl    opcode portion of instruction word
+  rINST_HI bh    high byte of instruction word, usually contains src/tgt reg names
+
+Notes:
+   o High order 16 bits of ebx must be zero on entry to handler
+   o rPC, rFP, rIBASE, rINST/rOPCODE valid on handler entry and exit
+   o eax and ecx are scratch, rINST/ebx sometimes scratch
+   o rPC is in the caller save set, and will be killed across external calls. Don't
+     forget to SPILL/UNSPILL it around call points
+
+*/
+
+#define rPC      %edx
+#define rFP      %esi
+#define rIBASE   %edi
+#define rINST_FULL %ebx
+#define rINST    %bx
+#define rINST_HI %bh
+#define rINST_LO %bl
+#define rOPCODE  %bl
+
+
+/* Frame diagram while executing dvmMterpStdRun, high to low addresses */
+#define IN_ARG0        (  8)
+#define CALLER_RP      (  4)
+#define PREV_FP        (  0) /* <- dvmMterpStdRun ebp */
+/* Spill offsets relative to %ebp */
+#define EDI_SPILL      ( -4)
+#define ESI_SPILL      ( -8)
+#define EDX_SPILL      (-12) /* <- esp following dmMterpStdRun header */
+#define rPC_SPILL      (-16)
+#define rFP_SPILL      (-20)
+#define rGLUE_SPILL    (-24)
+#define rIBASE_SPILL   (-28)
+#define rINST_FULL_SPILL    (-32)
+#define TMP_SPILL      (-36)
+#define LOCAL0_OFFSET  (-40)
+#define LOCAL1_OFFSET  (-44)
+#define LOCAL2_OFFSET  (-48)
+#define LOCAL3_OFFSET  (-52)
+/* Out Arg offsets, relative to %sp */
+#define OUT_ARG4       ( 16)
+#define OUT_ARG3       ( 12)
+#define OUT_ARG2       (  8)
+#define OUT_ARG1       (  4)
+#define OUT_ARG0       (  0)  /* <- dvmMterpStdRun esp */
+
+#define SPILL(reg) movl reg##,reg##_SPILL(%ebp)
+#define UNSPILL(reg) movl reg##_SPILL(%ebp),reg
+#define SPILL_TMP(reg) movl reg,TMP_SPILL(%ebp)
+#define UNSPILL_TMP(reg) movl TMP_SPILL(%ebp),reg
+
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE(_glu)     movl    offGlue_pc(_glu),rPC
+#define SAVE_PC_TO_GLUE(_glu)       movl    rPC,offGlue_pc(_glu)
+#define LOAD_FP_FROM_GLUE(_glu)     movl    offGlue_fp(_glu),rFP
+#define SAVE_FP_TO_GLUE(_glu)       movl    rFP,offGlue_fp(_glu)
+
+#define GET_GLUE(_reg)     movl   rGLUE_SPILL(%ebp),_reg
+
+/* 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 calls dvmThrowException.
+ *
+ * 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() \
+    movl     rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+    leal    -sizeofStackSaveArea(_fpreg),_reg
+
+/*
+ * Fetch the next instruction from rPC into rINST.  Does not advance rPC.
+ */
+#define FETCH_INST()            movzwl    (rPC),rINST_FULL
+
+/*
+ * Fetch the nth instruction word from rPC into rINST.  Does not advance
+ * rPC, and _count is in words
+ */
+#define FETCH_INST_WORD(_count)  movzwl  _count*2(rPC),rINST_FULL
+
+/*
+ * Fetch instruction word indexed (used for branching).
+ * Index is in instruction word units.
+ */
+#define FETCH_INST_INDEXED(_reg) movzwl  (rPC,_reg,2),rINST_FULL
+
+/*
+ * Extract the opcode of the instruction in rINST
+ */
+#define EXTRACT_OPCODE(_reg)   movzx rOPCODE,_reg
+
+/*
+ * Advance rPC by instruction count
+ */
+#define ADVANCE_PC(_count)    leal  2*_count(rPC),rPC
+
+/*
+ * Advance rPC by branch offset in register
+ */
+#define ADVANCE_PC_INDEXED(_reg) leal (rPC,_reg,2),rPC
+
+/*
+ * Note: assumes opcode previously fetched and in rINST, and
+ *       %eax is killable at this point.
+ */
+#if 1
+.macro GOTO_NEXT
+    /* For computed next version */
+     movzx    rOPCODE,%eax
+     sall     $$$handler_size_bits,%eax
+     addl     rIBASE,%eax
+     jmp      *%eax
+.endm
+#else
+   /* For jump table version */
+.macro GOTO_NEXT
+     movzx   rOPCODE,%eax
+     jmp     *(rIBASE,%eax,4)
+.endm
+#endif
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg)   movl     (rFP,_vreg,4),_reg
+#define SET_VREG(_reg, _vreg)   movl     _reg,(rFP,_vreg,4)
+#define GET_VREG_WORD(_reg, _vreg, _offset)   movl     4*(_offset)(rFP,_vreg,4),_reg
+#define SET_VREG_WORD(_reg, _vreg, _offset)   movl     _reg,4*(_offset)(rFP,_vreg,4)
+
+/*
+ * 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"
diff --git a/vm/mterp/x86/shop2addr.S b/vm/mterp/x86/shop2addr.S
new file mode 100644
index 0000000..9e8bd56
--- /dev/null
+++ b/vm/mterp/x86/shop2addr.S
@@ -0,0 +1,16 @@
+%default {"result":"%eax"}
+    /*
+     * Generic 32-bit "shift/2addr" operation.
+     */
+    /* shift/2addr vA, vB */
+    movzx    rINST_HI,%ecx          # eax<- BA
+    sarl     $$4,%ecx               # ecx<- B
+    GET_VREG(%ecx,%ecx)             # eax<- vBB
+    movzbl   rINST_HI,rINST_FULL    # rINST_FULL<- BA
+    andb     $$0xf,rINST_LO         # rINST_FULL<- A
+    GET_VREG(%eax,rINST_FULL)       # eax<- vAA
+    $instr                          # ex: sarl %cl,%eax
+    SET_VREG($result,rINST_FULL)
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/stub.S b/vm/mterp/x86/stub.S
new file mode 100644
index 0000000..c75645e
--- /dev/null
+++ b/vm/mterp/x86/stub.S
@@ -0,0 +1,11 @@
+    /* (stub) */
+    GET_GLUE(%ecx)
+    SAVE_PC_TO_GLUE(%ecx)            # only need to export these two
+    SAVE_FP_TO_GLUE(%ecx)            # only need to export these two
+    movl %ecx,OUT_ARG0(%esp)         # glue is first arg to function
+    call      dvmMterp_${opcode}     # do the real work
+    GET_GLUE(%ecx)
+    LOAD_PC_FROM_GLUE(%ecx)          # retrieve updated values
+    LOAD_FP_FROM_GLUE(%ecx)          # retrieve updated values
+    FETCH_INST()
+    GOTO_NEXT
diff --git a/vm/mterp/x86/unop.S b/vm/mterp/x86/unop.S
new file mode 100644
index 0000000..7192d9a
--- /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   rINST_HI,%ecx           # ecx<- A+
+    sarl     $$12,rINST_FULL         # rINST_FULL<- B
+    GET_VREG(%eax,rINST_FULL)        # eax<- vB
+    andb     $$0xf,%cl               # ecx<- A
+    FETCH_INST_WORD(1)
+    ADVANCE_PC(1)
+    $pre0
+    $pre1
+    $instr
+    SET_VREG(%eax,%ecx)
+    GOTO_NEXT
diff --git a/vm/mterp/x86/unopWide.S b/vm/mterp/x86/unopWide.S
new file mode 100644
index 0000000..ad5b361
--- /dev/null
+++ b/vm/mterp/x86/unopWide.S
@@ -0,0 +1,22 @@
+%default {"instr1":"","instr2":"","instr3":""}
+    /*
+     * Generic 64-bit unary operation.
+     * Operand in %ecx:%eax
+     *
+     * For: neg-long, not-long
+     */
+    /* unop vA, vB */
+    movzbl    rINST_HI,%ecx            # ecx<- BA
+    sarl      $$4,%ecx                 # ecx<- B
+    movzbl    rINST_HI,rINST_FULL      # ecx<- BA
+    andb      $$0xf,rINST_LO           # rINST_FULL<- 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_FULL,0)   # v[A+0] <- eax
+    SET_VREG_WORD(%ecx,rINST_FULL,1)   # v[A+1] <- ecx
+    GET_INST_WORD(1)
+    ADVANCE_PC(1)
+    GOTO_NEXT
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..221822d
--- /dev/null
+++ b/vm/mterp/x86/zcmp.S
@@ -0,0 +1,22 @@
+%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 */
+    movzx    rINST_HI,%ecx             # ecx <- AA
+    cmpl     $$0,(rFP,%ecx,4)          # compare (vA, 0)
+    movswl   2(rPC),rINST_FULL         # fetch signed displacement
+    movl     $$2,%eax                  # assume branch not taken
+    j${revcmp}   1f
+    testl    rINST_FULL,rINST_FULL
+    js       common_backwardBranch
+    movl     rINST_FULL,%eax
+1:
+    FETCH_INST_INDEXED(%eax)
+    ADVANCE_PC_INDEXED(%eax)
+    GOTO_NEXT
diff --git a/vm/native/InternalNative.c b/vm/native/InternalNative.c
new file mode 100644
index 0000000..06ed665
--- /dev/null
+++ b/vm/native/InternalNative.c
@@ -0,0 +1,347 @@
+/*
+ * 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/Runtime;",              dvm_java_lang_Runtime, 0 },
+    { "Ljava/lang/String;",               dvm_java_lang_String, 0 },
+    { "Ljava/lang/System;",               dvm_java_lang_System, 0 },
+    { "Ljava/lang/SystemProperties;",     dvm_java_lang_SystemProperties, 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/security/AccessController;",
+            dvm_java_security_AccessController, 0 },
+    { "Ljava/util/concurrent/atomic/AtomicLong;",
+            dvm_java_util_concurrent_atomic_AtomicLong, 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/Zygote;",           dvm_dalvik_system_Zygote, 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(void)
+{
+    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(void)
+{
+    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 */
+                    //LOGV("+++  match on %s.%s %s at %p\n",
+                    //    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)
+{
+    LOGD("--- called into dvmAbstractMethodStub\n");
+    dvmThrowException("Ljava/lang/AbstractMethodError;",
+        "abstract method not implemented");
+}
+
+
+/*
+ * 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)
+{
+    if (obj == NULL) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        return false;
+    }
+    if (!dvmInstanceof(obj->clazz, clazz)) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "object is not an instance of the class");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Validate a "binary" class name, e.g. "java.lang.String" or "[I".
+ */
+static bool validateClassName(const char* name)
+{
+    int len = strlen(name);
+    int i = 0;
+
+    /* check for reasonable array types */
+    if (name[0] == '[') {
+        while (name[i] == '[')
+            i++;
+
+        if (name[i] == 'L') {
+            /* array of objects, make sure it ends well */
+            if (name[len-1] != ';')
+                return false;
+        } else if (strchr(PRIM_TYPE_TO_LETTER, name[i]) != NULL) {
+            if (i != len-1)
+                return false;
+        } else {
+            return false;
+        }
+    }
+
+    /* quick check for illegal chars */
+    for ( ; i < len; i++) {
+        if (name[i] == '/')
+            return false;
+    }
+
+    return true;
+}
+
+/*
+ * 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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", 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 (!validateClassName(name)) {
+        LOGW("dvmFindClassByName rejecting '%s'\n", name);
+        dvmThrowException("Ljava/lang/ClassNotFoundException;", 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)\n", descriptor, doInit);
+        Thread* self = dvmThreadSelf();
+        Object* oldExcep = dvmGetException(self);
+        dvmAddTrackedAlloc(oldExcep, self);     /* don't let this be GCed */
+        dvmClearException(self);
+        dvmThrowChainedException("Ljava/lang/ClassNotFoundException;",
+            name, oldExcep);
+        dvmReleaseTrackedAlloc(oldExcep, self);
+    } else {
+        LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n",
+            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;
+}
+
+
+#define NUM_DOPRIV_FUNCS    4
+
+/*
+ * Determine if "method" is a "privileged" invocation, i.e. is it one
+ * of the variations of AccessController.doPrivileged().
+ *
+ * Because the security stuff pulls in a pile of stuff that we may not
+ * want or need, we don't do the class/method lookups at init time, but
+ * instead on first use.
+ */
+bool dvmIsPrivilegedMethod(const Method* method)
+{
+    int i;
+
+    assert(method != NULL);
+
+    if (!gDvm.javaSecurityAccessControllerReady) {
+        /*
+         * Populate on first use.  No concurrency risk since we're just
+         * finding pointers to fixed structures.
+         */
+        static const char* kSignatures[NUM_DOPRIV_FUNCS] = {
+            "(Ljava/security/PrivilegedAction;)Ljava/lang/Object;",
+            "(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;",
+            "(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;",
+            "(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;",
+        };
+        ClassObject* clazz;
+
+        clazz = dvmFindClassNoInit("Ljava/security/AccessController;", NULL);
+        if (clazz == NULL) {
+            LOGW("Couldn't find java/security/AccessController\n");
+            return false;
+        }
+
+        assert(NELEM(gDvm.methJavaSecurityAccessController_doPrivileged) ==
+               NELEM(kSignatures));
+
+        /* verify init */
+        for (i = 0; i < NUM_DOPRIV_FUNCS; i++) {
+            gDvm.methJavaSecurityAccessController_doPrivileged[i] =
+                dvmFindDirectMethodByDescriptor(clazz, "doPrivileged", kSignatures[i]);
+            if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == NULL) {
+                LOGW("Warning: couldn't find java/security/AccessController"
+                    ".doPrivileged %s\n", kSignatures[i]);
+                return false;
+            }
+        }
+
+        /* all good, raise volatile readiness flag */
+        android_atomic_release_store(true,
+            &gDvm.javaSecurityAccessControllerReady);
+    }
+
+    for (i = 0; i < NUM_DOPRIV_FUNCS; i++) {
+        if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == method) {
+            //LOGI("+++ doPriv match\n");
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/vm/native/InternalNative.h b/vm/native/InternalNative.h
new file mode 100644
index 0000000..7c82dc0
--- /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
+#define _DALVIK_NATIVE_INTERNALNATIVE
+
+/*
+ * 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) */
+void dvmAbstractMethodStub(const u4* args, JValue* pResult);
+
+#endif /*_DALVIK_NATIVE_INTERNALNATIVE*/
diff --git a/vm/native/InternalNativePriv.h b/vm/native/InternalNativePriv.h
new file mode 100644
index 0000000..6f8d6c9
--- /dev/null
+++ b/vm/native/InternalNativePriv.h
@@ -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.
+ */
+
+/*
+ * Declarations and definitions common to internal native code.
+ */
+#ifndef _DALVIK_NATIVE_INTERNALNATIVEPRIV
+#define _DALVIK_NATIVE_INTERNALNATIVEPRIV
+
+/*
+ * 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 = (_val); return; } while(0)
+
+
+/*
+ * 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);
+
+/*
+ * Determine if "method" is a "privileged" invocation, i.e. is it one
+ * of the variations of AccessController.doPrivileged().
+ *
+ * Because the security stuff pulls in a pile of stuff that we may not
+ * want or need, we don't do the class/method lookups at init time, but
+ * instead on first use.
+ */
+bool dvmIsPrivilegedMethod(const Method* method);
+
+
+/*
+ * Tables of methods.
+ */
+extern const DalvikNativeMethod dvm_java_lang_Object[];
+extern const DalvikNativeMethod dvm_java_lang_Class[];
+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_SystemProperties[];
+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_security_AccessController[];
+extern const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[];
+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_Zygote[];
+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*/
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_system_DexFile.c b/vm/native/dalvik_system_DexFile.c
new file mode 100644
index 0000000..25c9dfb
--- /dev/null
+++ b/vm/native/dalvik_system_DexFile.c
@@ -0,0 +1,454 @@
+/*
+ * 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"
+
+
+/*
+ * Internal struct for managing DexFile.
+ */
+typedef struct DexOrJar {
+    char*       fileName;
+    bool        isDex;
+    bool        okayToFree;
+    RawDexFile* pRawDexFile;
+    JarFile*    pJarFile;
+} DexOrJar;
+
+/*
+ * (This is a dvmHashTableFree callback.)
+ */
+void dvmFreeDexOrJar(void* vptr)
+{
+    DexOrJar* pDexOrJar = (DexOrJar*) vptr;
+
+    LOGV("Freeing DexOrJar '%s'\n", pDexOrJar->fileName);
+
+    if (pDexOrJar->isDex)
+        dvmRawDexFileFree(pDexOrJar->pRawDexFile);
+    else
+        dvmJarFileFree(pDexOrJar->pJarFile);
+    free(pDexOrJar->fileName);
+    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\n", pDexOrJar);
+
+    if (pDexOrJar == NULL)
+        return false;
+
+    u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName);
+    dvmHashTableLock(gDvm.userDexFiles);
+    void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
+                hashcmpDexOrJar, false);
+    dvmHashTableUnlock(gDvm.userDexFiles);
+    if (result == NULL) {
+        dvmThrowException("Ljava/lang/RuntimeException;",
+            "invalid DexFile cookie");
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * private static int openDexFile(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_openDexFile(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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", 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)) {
+        LOGW("Refusing to reopen boot DEX '%s'\n", sourceName);
+        dvmThrowException("Ljava/io/IOException;",
+            "Re-opening BOOTCLASSPATH DEX files is not allowed");
+        free(sourceName);
+        RETURN_VOID();
+    }
+
+    /*
+     * Try to open it directly as a DEX.  If that fails, try it as a Zip
+     * with a "classes.dex" inside.
+     */
+    if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
+        LOGV("Opening DEX file '%s' (DEX)\n", sourceName);
+
+        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+        pDexOrJar->isDex = true;
+        pDexOrJar->pRawDexFile = pRawDexFile;
+    } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
+        LOGV("Opening DEX file '%s' (Jar)\n", sourceName);
+
+        pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+        pDexOrJar->isDex = false;
+        pDexOrJar->pJarFile = pJarFile;
+    } else {
+        LOGV("Unable to open DEX file '%s'\n", sourceName);
+        dvmThrowException("Ljava/io/IOException;", "unable to open DEX file");
+    }
+
+    if (pDexOrJar != NULL) {
+        pDexOrJar->fileName = sourceName;
+
+        /* add to hash table */
+        u4 hash = dvmComputeUtf8Hash(sourceName);
+        void* result;
+        dvmHashTableLock(gDvm.userDexFiles);
+        result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
+                    hashcmpDexOrJar, true);
+        dvmHashTableUnlock(gDvm.userDexFiles);
+        if (result != pDexOrJar) {
+            LOGE("Pointer has already been added?\n");
+            dvmAbort();
+        }
+
+        pDexOrJar->okayToFree = true;
+    } else
+        free(sourceName);
+
+    RETURN_PTR(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 = args[0];
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+
+    if (pDexOrJar == NULL)
+        RETURN_VOID();
+
+    LOGV("Closing DEX file %p (%s)\n", pDexOrJar, pDexOrJar->fileName);
+
+    if (!validateCookie(cookie))
+        RETURN_VOID();
+
+    /*
+     * 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 = dvmComputeUtf8Hash(pDexOrJar->fileName);
+        dvmHashTableLock(gDvm.userDexFiles);
+        if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) {
+            LOGW("WARNING: could not remove '%s' from DEX hash table\n",
+                pDexOrJar->fileName);
+        }
+        dvmHashTableUnlock(gDvm.userDexFiles);
+        LOGV("+++ freeing DexFile '%s' resources\n", pDexOrJar->fileName);
+        dvmFreeDexOrJar(pDexOrJar);
+    } else {
+        LOGV("+++ NOT freeing DexFile '%s' resources\n", pDexOrJar->fileName);
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * private static Class defineClass(String name, ClassLoader loader,
+ *      int cookie, ProtectionDomain pd)
+ *
+ * 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_defineClass(const u4* args,
+    JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    Object* loader = (Object*) args[1];
+    int cookie = args[2];
+    Object* pd = (Object*) args[3];
+    ClassObject* clazz = NULL;
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+    DvmDex* pDvmDex;
+    char* name;
+    char* descriptor;
+
+    name = dvmCreateCstrFromString(nameObj);
+    descriptor = dvmDotToDescriptor(name);
+    LOGV("--- Explicit class load '%s' 0x%08x\n", descriptor, 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;
+    }
+
+    /*
+     * Set the ProtectionDomain -- do we need this to happen before we
+     * link the class and make it available? If so, we need to pass it
+     * through dvmDefineClass (and figure out some other
+     * stuff, like where it comes from for bootstrap classes).
+     */
+    if (clazz != NULL) {
+        //LOGI("SETTING pd '%s' to %p\n", clazz->descriptor, pd);
+        dvmSetFieldObject((Object*) clazz, gDvm.offJavaLangClass_pd, pd);
+    }
+
+    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 = args[0];
+    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+    DvmDex* pDvmDex;
+    DexFile* pDexFile;
+    ArrayObject* stringArray;
+    Thread* self = dvmThreadSelf();
+
+    if (!validateCookie(cookie))
+        RETURN_VOID();
+
+    if (pDexOrJar->isDex)
+        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
+    else
+        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
+    assert(pDvmDex != NULL);
+    pDexFile = pDvmDex->pDexFile;
+
+    int count = pDexFile->pHeader->classDefsSize;
+    stringArray = dvmAllocObjectArray(gDvm.classJavaLangString, count,
+                    ALLOC_DEFAULT);
+    if (stringArray == NULL) {
+        /* probably OOM */
+        LOGD("Failed allocating array of %d strings\n", 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 apkName)
+ *         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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        RETURN_VOID();
+    }
+    if (access(name, R_OK) != 0) {
+        dvmThrowException("Ljava/io/FileNotFoundException;", name);
+        free(name);
+        RETURN_VOID();
+    }
+    status = dvmDexCacheStatus(name);
+    LOGV("dvmDexCacheStatus(%s) returned %d\n", name, status);
+
+    result = true;
+    switch (status) {
+    default: //FALLTHROUGH
+    case DEX_CACHE_BAD_ARCHIVE:
+        dvmThrowException("Ljava/io/IOException;", name);
+        result = -1;
+        break;
+    case DEX_CACHE_OK:
+        result = false;
+        break;
+    case DEX_CACHE_STALE:
+        result = true;
+        break;
+    case DEX_CACHE_STALE_ODEX:
+        dvmThrowException("Ldalvik/system/StaleDexCacheError;", name);
+        result = -1;
+        break;
+    }
+    free(name);
+
+    if (result >= 0) {
+        RETURN_BOOLEAN(result);
+    } else {
+        RETURN_VOID();
+    }
+}
+
+const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
+    { "openDexFile",        "(Ljava/lang/String;Ljava/lang/String;I)I",
+        Dalvik_dalvik_system_DexFile_openDexFile },
+    { "closeDexFile",       "(I)V",
+        Dalvik_dalvik_system_DexFile_closeDexFile },
+    { "defineClass",        "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;",
+        Dalvik_dalvik_system_DexFile_defineClass },
+    { "getClassNameList",   "(I)[Ljava/lang/String;",
+        Dalvik_dalvik_system_DexFile_getClassNameList },
+    { "isDexOptNeeded",     "(Ljava/lang/String;)Z",
+        Dalvik_dalvik_system_DexFile_isDexOptNeeded },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMDebug.c b/vm/native/dalvik_system_VMDebug.c
new file mode 100644
index 0000000..192a8f2
--- /dev/null
+++ b/vm/native/dalvik_system_VMDebug.c
@@ -0,0 +1,965 @@
+/*
+ * 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 "native/InternalNativePriv.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.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);
+
+    InstField* field = dvmFindInstanceField(obj->clazz, "descriptor", "I");
+    if (field == NULL) {
+        dvmThrowException("Ljava/lang/NoSuchFieldException;",
+            "No FileDescriptor.descriptor field");
+        return -1;
+    }
+
+    int fd = dvmGetFieldInt(obj, field->byteOffset);
+    if (fd < 0) {
+        dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
+            "Invalid file descriptor");
+        return -1;
+    }
+
+    return fd;
+}
+
+/*
+ * Convert an array of char* into a String[].
+ *
+ * Returns NULL on failure, with an exception raised.
+ */
+static ArrayObject* convertStringArray(char** strings, size_t count)
+{
+    Thread* self = dvmThreadSelf();
+
+    /*
+     * Allocate an array to hold the String objects.
+     */
+    ClassObject* stringArrayClass =
+        dvmFindArrayClass("[Ljava/lang/String;", NULL);
+    if (stringArrayClass == NULL) {
+        /* shouldn't happen */
+        LOGE("Unable to find [Ljava/lang/String;\n");
+        dvmAbort();
+    }
+
+    ArrayObject* stringArray =
+        dvmAllocArrayByClass(stringArrayClass, count, ALLOC_DEFAULT);
+    if (stringArray == NULL) {
+        /* probably OOM */
+        LOGD("Failed allocating array of %d strings\n", count);
+        assert(dvmCheckException(self));
+        return NULL;
+    }
+
+    /*
+     * Create the individual String objects and add them to the array.
+     */
+    size_t i;
+    for (i = 0; i < count; 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);
+    }
+
+    dvmReleaseTrackedAlloc((Object*)stringArray, self);
+    return stringArray;
+}
+
+/*
+ * static String[] getVmFeatureList()
+ *
+ * Return a set of strings describing available VM features (this is chiefly
+ * of interest to DDMS).  Some features may be controlled by compile-time
+ * or command-line flags.
+ */
+static void Dalvik_dalvik_system_VMDebug_getVmFeatureList(const u4* args,
+    JValue* pResult)
+{
+    static const int MAX_FEATURE_COUNT = 10;
+    char* features[MAX_FEATURE_COUNT];
+    int idx = 0;
+
+    /* VM responds to DDMS method profiling requests */
+    features[idx++] = "method-trace-profiling";
+    features[idx++] = "method-trace-profiling-streaming";
+#ifdef WITH_HPROF
+    /* VM responds to DDMS heap dump requests */
+    features[idx++] = "hprof-heap-dump";
+    features[idx++] = "hprof-heap-dump-streaming";
+#endif
+
+    assert(idx <= MAX_FEATURE_COUNT);
+
+    LOGV("+++ sending up %d features\n", idx);
+    ArrayObject* arrayObj = convertStringArray(features, idx);
+    RETURN_PTR(arrayObj);       /* will be null on OOM */
+}
+
+
+/* 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,
+#if PROFILE_EXTERNAL_ALLOCATIONS
+    KIND_EXT_ALLOCATED_OBJECTS = 1<<12,
+    KIND_EXT_ALLOCATED_BYTES   = 1<<13,
+    KIND_EXT_FREED_OBJECTS     = 1<<14,
+    KIND_EXT_FREED_BYTES       = 1<<15,
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+
+    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,
+#if PROFILE_EXTERNAL_ALLOCATIONS
+    KIND_GLOBAL_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS,
+    KIND_GLOBAL_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES,
+    KIND_GLOBAL_EXT_FREED_OBJECTS   = KIND_EXT_FREED_OBJECTS,
+    KIND_GLOBAL_EXT_FREED_BYTES     = KIND_EXT_FREED_BYTES,
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+
+    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,
+#if PROFILE_EXTERNAL_ALLOCATIONS
+    KIND_THREAD_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS << 16,
+    KIND_THREAD_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES << 16,
+    KIND_THREAD_EXT_FREED_OBJECTS   = KIND_EXT_FREED_OBJECTS << 16,
+    KIND_THREAD_EXT_FREED_BYTES     = KIND_EXT_FREED_BYTES << 16,
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+    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;
+    }
+#if PROFILE_EXTERNAL_ALLOCATIONS
+    if (kinds & KIND_EXT_ALLOCATED_OBJECTS) {
+        allocProf->externalAllocCount = 0;
+    }
+    if (kinds & KIND_EXT_ALLOCATED_BYTES) {
+        allocProf->externalAllocSize = 0;
+    }
+    if (kinds & KIND_EXT_FREED_OBJECTS) {
+        allocProf->externalFreeCount = 0;
+    }
+    if (kinds & KIND_EXT_FREED_BYTES) {
+        allocProf->externalFreeSize = 0;
+    }
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+}
+
+/*
+ * 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;
+#if PROFILE_EXTERNAL_ALLOCATIONS
+    case KIND_EXT_ALLOCATED_OBJECTS:
+        pResult->i = allocProf->externalAllocCount;
+        break;
+    case KIND_EXT_ALLOCATED_BYTES:
+        pResult->i = allocProf->externalAllocSize;
+        break;
+    case KIND_EXT_FREED_OBJECTS:
+        pResult->i = allocProf->externalFreeCount;
+        break;
+    case KIND_EXT_FREED_BYTES:
+        pResult->i = allocProf->externalFreeSize;
+        break;
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+    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 startMethodTracingNative(String traceFileName,
+ *     FileDescriptor fd, int bufferSize, int flags)
+ *
+ * Start method trace profiling.
+ *
+ * If both "traceFileName" and "fd" are null, the result will be sent
+ * directly to DDMS.  (The non-DDMS versions of the calls are expected
+ * to enforce non-NULL filenames.)
+ */
+static void Dalvik_dalvik_system_VMDebug_startMethodTracingNative(const u4* args,
+    JValue* pResult)
+{
+    StringObject* traceFileStr = (StringObject*) args[0];
+    Object* traceFd = (Object*) args[1];
+    int bufferSize = args[2];
+    int flags = args[3];
+
+    if (bufferSize == 0) {
+        // Default to 8MB per the documentation.
+        bufferSize = 8 * 1024 * 1024;
+    }
+
+    if (bufferSize < 1024) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;", NULL);
+        RETURN_VOID();
+    }
+
+    char* traceFileName = NULL;
+    if (traceFileStr != NULL)
+        traceFileName = dvmCreateCstrFromString(traceFileStr);
+
+    int fd = -1;
+    if (traceFd != NULL) {
+        int origFd = getFileDescriptor(traceFd);
+        if (origFd < 0)
+            RETURN_VOID();
+
+        fd = dup(origFd);
+        if (fd < 0) {
+            dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
+                "dup(%d) failed: %s", origFd, strerror(errno));
+            RETURN_VOID();
+        }
+    }
+
+    dvmMethodTraceStart(traceFileName != NULL ? traceFileName : "[DDMS]",
+        fd, bufferSize, flags, (traceFileName == NULL && fd == -1));
+    free(traceFileName);
+    RETURN_VOID();
+}
+
+/*
+ * static boolean isMethodTracingActive()
+ *
+ * Determine whether method tracing is currently active.
+ */
+static void Dalvik_dalvik_system_VMDebug_isMethodTracingActive(const u4* args,
+    JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    RETURN_BOOLEAN(dvmIsMethodTraceActive());
+}
+
+/*
+ * 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 int setAllocationLimit(int limit)
+ *
+ * Set the current allocation limit in this thread.  Return the previous
+ * value.
+ */
+static void Dalvik_dalvik_system_VMDebug_setAllocationLimit(const u4* args,
+    JValue* pResult)
+{
+#if defined(WITH_ALLOC_LIMITS)
+    gDvm.checkAllocLimits = true;
+
+    Thread* self = dvmThreadSelf();
+    int newLimit = args[0];
+    int oldLimit = self->allocLimit;
+
+    if (newLimit < -1) {
+        LOGE("WARNING: bad limit request (%d)\n", newLimit);
+        newLimit = -1;
+    }
+    self->allocLimit = newLimit;
+    RETURN_INT(oldLimit);
+#else
+    UNUSED_PARAMETER(args);
+    RETURN_INT(-1);
+#endif
+}
+
+/*
+ * static int setGlobalAllocationLimit(int limit)
+ *
+ * Set the allocation limit for this process.  Returns the previous value.
+ */
+static void Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit(const u4* args,
+    JValue* pResult)
+{
+#if defined(WITH_ALLOC_LIMITS)
+    gDvm.checkAllocLimits = true;
+
+    int newLimit = args[0];
+    int oldLimit = gDvm.allocationLimit;
+
+    if (newLimit < -1 || newLimit > 0) {
+        LOGE("WARNING: bad limit request (%d)\n", newLimit);
+        newLimit = -1;
+    }
+    // TODO: should use an atomic swap here
+    gDvm.allocationLimit = newLimit;
+    RETURN_INT(oldLimit);
+#else
+    UNUSED_PARAMETER(args);
+    RETURN_INT(-1);
+#endif
+}
+
+/*
+ * 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];
+    int* storage;
+
+    storage = (int*) countArray->contents;
+    sched_yield();
+    memcpy(storage, gDvm.executedInstrCounts,
+        kNumDalvikInstructions * 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, kNumDalvikInstructions * 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)
+{
+#ifdef WITH_HPROF
+    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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        RETURN_VOID();
+    }
+
+    if (fileNameStr != NULL) {
+        fileName = dvmCreateCstrFromString(fileNameStr);
+        if (fileName == NULL) {
+            /* unexpected -- malloc failure? */
+            dvmThrowException("Ljava/lang/RuntimeException;", "malloc failure?");
+            RETURN_VOID();
+        }
+    } else {
+        fileName = strdup("[fd]");
+    }
+
+    int fd = -1;
+    if (fileDescriptor != NULL) {
+        fd = getFileDescriptor(fileDescriptor);
+        if (fd < 0)
+            RETURN_VOID();
+    }
+
+    result = hprofDumpHeap(fileName, fd, false);
+    free(fileName);
+
+    if (result != 0) {
+        /* ideally we'd throw something more specific based on actual failure */
+        dvmThrowException("Ljava/lang/RuntimeException;",
+            "Failure during heap dump -- check log output for details");
+        RETURN_VOID();
+    }
+#else
+    dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL);
+#endif
+
+    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)
+{
+#ifdef WITH_HPROF
+    int result;
+
+    result = hprofDumpHeap("[DDMS]", -1, true);
+
+    if (result != 0) {
+        /* ideally we'd throw something more specific based on actual failure */
+        dvmThrowException("Ljava/lang/RuntimeException;",
+            "Failure during heap dump -- check log output for details");
+        RETURN_VOID();
+    }
+#else
+    dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL);
+#endif
+
+    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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", 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) {
+        dvmThrowException("Ljava/lang/RuntimeException;",
+            "method name not found in string");
+        RETURN_VOID();
+    }
+    *methodName++ = '\0';
+
+    char* methodDescr = strchr(methodName, ':');
+    if (methodDescr == NULL) {
+        dvmThrowException("Ljava/lang/RuntimeException;",
+            "method descriptor not found in string");
+        RETURN_VOID();
+    }
+    *methodDescr++ = '\0';
+
+    //LOGD("GOT: %s %s %s\n", classAndMethodDesc, methodName, methodDescr);
+
+    /*
+     * Find the class, but only if it's already loaded.
+     */
+    clazz = dvmLookupClass(classAndMethodDesc, NULL, false);
+    if (clazz == NULL) {
+        LOGD("Class %s not found in bootstrap loader\n", 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) {
+            LOGV("No map for %s.%s %s\n",
+                classAndMethodDesc, methodName, methodDescr);
+        } else {
+            LOGV("Found map %s.%s %s\n",
+                classAndMethodDesc, methodName, methodDescr);
+            result = true;
+        }
+    } else {
+        LOGV("Unable to find %s.%s %s\n",
+            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);
+
+    LOGI("--- reference table dump ---\n");
+    dvmDumpJniReferenceTables();
+    // could dump thread's internalLocalRefTable, probably not useful
+    // ditto for thread's jniMonitorRefTable
+    LOGI("---\n");
+    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);
+
+    LOGW("Crashing VM on request\n");
+    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;
+
+    LOGD("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);
+    }
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = {
+    { "getVmFeatureList",           "()[Ljava/lang/String;",
+        Dalvik_dalvik_system_VMDebug_getVmFeatureList },
+    { "getAllocCount",              "(I)I",
+        Dalvik_dalvik_system_VMDebug_getAllocCount },
+    { "resetAllocCount",            "(I)V",
+        Dalvik_dalvik_system_VMDebug_resetAllocCount },
+    { "startAllocCounting",         "()V",
+        Dalvik_dalvik_system_VMDebug_startAllocCounting },
+    { "stopAllocCounting",          "()V",
+        Dalvik_dalvik_system_VMDebug_stopAllocCounting },
+    { "startMethodTracingNative",   "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V",
+        Dalvik_dalvik_system_VMDebug_startMethodTracingNative },
+    { "isMethodTracingActive",      "()Z",
+        Dalvik_dalvik_system_VMDebug_isMethodTracingActive },
+    { "stopMethodTracing",          "()V",
+        Dalvik_dalvik_system_VMDebug_stopMethodTracing },
+    { "startEmulatorTracing",       "()V",
+        Dalvik_dalvik_system_VMDebug_startEmulatorTracing },
+    { "stopEmulatorTracing",        "()V",
+        Dalvik_dalvik_system_VMDebug_stopEmulatorTracing },
+    { "setAllocationLimit",         "(I)I",
+        Dalvik_dalvik_system_VMDebug_setAllocationLimit },
+    { "setGlobalAllocationLimit",   "(I)I",
+        Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit },
+    { "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.c b/vm/native/dalvik_system_VMRuntime.c
new file mode 100644
index 0000000..c020f8a
--- /dev/null
+++ b/vm/native/dalvik_system_VMRuntime.c
@@ -0,0 +1,242 @@
+/*
+ * 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 "native/InternalNativePriv.h"
+
+#include <limits.h>
+
+
+/*
+ * 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();
+}
+
+/*
+ * native long nativeMinimumHeapSize(long size, boolean set)
+ *
+ * If set is true, sets the new minimum heap size to size; always
+ * returns the current (or previous) size.  If size is negative or
+ * zero, removes the current minimum constraint (if present).
+ */
+static void Dalvik_dalvik_system_VMRuntime_nativeMinimumHeapSize(
+    const u4* args, JValue* pResult)
+{
+    s8 longSize = GET_ARG_LONG(args, 1);
+    size_t size;
+    bool set = (args[3] != 0);
+
+    /* Fit in 32 bits. */
+    if (longSize < 0) {
+        size = 0;
+    } else if (longSize > INT_MAX) {
+        size = INT_MAX;
+    } else {
+        size = (size_t)longSize;
+    }
+
+    size = dvmMinimumHeapSize(size, set);
+
+    RETURN_LONG(size);
+}
+
+/*
+ * public native void gcSoftReferences()
+ *
+ * Does a GC and forces collection of SoftReferences that are
+ * not strongly-reachable.
+ */
+static void Dalvik_dalvik_system_VMRuntime_gcSoftReferences(const u4* args,
+    JValue* pResult)
+{
+    dvmCollectGarbage(true);
+
+    RETURN_VOID();
+}
+
+/*
+ * public native void runFinalizationSync()
+ *
+ * Does not return until any pending finalizers have been called.
+ * This may or may not happen in the context of the calling thread.
+ * No exceptions will escape.
+ *
+ * Used by zygote, which doesn't have a HeapWorker thread.
+ */
+static void Dalvik_dalvik_system_VMRuntime_runFinalizationSync(const u4* args,
+    JValue* pResult)
+{
+    dvmRunFinalizationSync();
+
+    RETURN_VOID();
+}
+
+/*
+ * public native boolean trackExternalAllocation(long size)
+ *
+ * Asks the VM if <size> bytes can be allocated in an external heap.
+ * This information may be used to limit the amount of memory available
+ * to Dalvik threads.  Returns false if the VM would rather that the caller
+ * did not allocate that much memory.  If the call returns false, the VM
+ * will not update its internal counts.
+ */
+static void Dalvik_dalvik_system_VMRuntime_trackExternalAllocation(
+    const u4* args, JValue* pResult)
+{
+    s8 longSize = GET_ARG_LONG(args, 1);
+
+    /* Fit in 32 bits. */
+    if (longSize < 0) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "size must be positive");
+        RETURN_VOID();
+    } else if (longSize > INT_MAX) {
+        dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+            "size must fit in 32 bits");
+        RETURN_VOID();
+    }
+    RETURN_BOOLEAN(dvmTrackExternalAllocation((size_t)longSize));
+}
+
+/*
+ * public native void trackExternalFree(long size)
+ *
+ * Tells the VM that <size> bytes have been freed in an external
+ * heap.  This information may be used to control the amount of memory
+ * available to Dalvik threads.
+ */
+static void Dalvik_dalvik_system_VMRuntime_trackExternalFree(
+    const u4* args, JValue* pResult)
+{
+    s8 longSize = GET_ARG_LONG(args, 1);
+
+    /* Fit in 32 bits. */
+    if (longSize < 0) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "size must be positive");
+        RETURN_VOID();
+    } else if (longSize > INT_MAX) {
+        dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+            "size must fit in 32 bits");
+        RETURN_VOID();
+    }
+    dvmTrackExternalFree((size_t)longSize);
+
+    RETURN_VOID();
+}
+
+/*
+ * public native long getExternalBytesAllocated()
+ *
+ * Returns the number of externally-allocated bytes being tracked by
+ * trackExternalAllocation/Free().
+ */
+static void Dalvik_dalvik_system_VMRuntime_getExternalBytesAllocated(
+    const u4* args, JValue* pResult)
+{
+    RETURN_LONG((s8)dvmGetExternalBytesAllocated());
+}
+
+/*
+ * 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) {
+        dvmLockMutex(&gDvmJit.compilerLock);
+        gDvmJit.alreadyEnabledViaFramework = true;
+        pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+        dvmUnlockMutex(&gDvmJit.compilerLock);
+    }
+#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();
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
+    { "getTargetHeapUtilization", "()F",
+        Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization },
+    { "nativeSetTargetHeapUtilization", "(F)V",
+        Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization },
+    { "nativeMinimumHeapSize", "(JZ)J",
+        Dalvik_dalvik_system_VMRuntime_nativeMinimumHeapSize },
+    { "gcSoftReferences", "()V",
+        Dalvik_dalvik_system_VMRuntime_gcSoftReferences },
+    { "runFinalizationSync", "()V",
+        Dalvik_dalvik_system_VMRuntime_runFinalizationSync },
+    { "trackExternalAllocation", "(J)Z",
+        Dalvik_dalvik_system_VMRuntime_trackExternalAllocation },
+    { "trackExternalFree", "(J)V",
+        Dalvik_dalvik_system_VMRuntime_trackExternalFree },
+    { "getExternalBytesAllocated", "()J",
+        Dalvik_dalvik_system_VMRuntime_getExternalBytesAllocated },
+    { "startJitCompilation", "()V",
+        Dalvik_dalvik_system_VMRuntime_startJitCompilation },
+    { "disableJitCompilation", "()V",
+        Dalvik_dalvik_system_VMRuntime_disableJitCompilation },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMStack.c b/vm/native/dalvik_system_VMStack.c
new file mode 100644
index 0000000..8db4a6b
--- /dev/null
+++ b/vm/native/dalvik_system_VMStack.c
@@ -0,0 +1,235 @@
+/*
+ * 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 "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()->curFrame);
+
+    UNUSED_PARAMETER(args);
+
+    if (clazz == NULL)
+        RETURN_PTR(NULL);
+    RETURN_PTR(clazz->classLoader);
+}
+
+/*
+ * public static ClassLoader getCallingClassLoader2()
+ *
+ * Return the defining class loader of the caller's caller's caller.
+ */
+static void Dalvik_dalvik_system_VMStack_getCallingClassLoader2(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = dvmGetCaller3Class(dvmThreadSelf()->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()->curFrame);
+
+    UNUSED_PARAMETER(args);
+
+    RETURN_PTR(clazz);
+}
+
+/*
+ * public static Class<?>[] getClasses(int maxDepth, boolean stopAtPrivileged)
+ *
+ * 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 */
+    unsigned int maxSize = args[0];
+    bool stopAtPrivileged = args[1];
+    unsigned int size = 0;
+    const unsigned int kSkip = 2;
+    const Method** methods = NULL;
+    int methodCount;
+
+    /*
+     * Get an array with the stack trace in it.
+     */
+    if (!dvmCreateStackTraceArray(dvmThreadSelf()->curFrame, &methods,
+            &methodCount))
+    {
+        LOGE("Failed to create stack trace array\n");
+        dvmThrowException("Ljava/lang/InternalError;", NULL);
+        RETURN_VOID();
+    }
+
+    //int i;
+    //LOGI("dvmCreateStackTraceArray results:\n");
+    //for (i = 0; i < methodCount; i++) {
+    //    LOGI(" %2d: %s.%s\n",
+    //        i, methods[i]->clazz->descriptor, methods[i]->name);
+    //}
+
+    /*
+     * Run through the array and count up how many elements there are.
+     */
+    unsigned int idx;
+    for (idx = kSkip; (int) idx < methodCount && size < maxSize; idx++) {
+        const Method* meth = methods[idx];
+
+        if (dvmIsReflectionMethod(meth))
+            continue;
+
+        if (stopAtPrivileged && dvmIsPrivilegedMethod(meth)) {
+            /*
+             * We want the last element of the array to be the caller of
+             * the privileged method, so we want to include the privileged
+             * method and the next one.
+             */
+            if (maxSize > size + 2)
+                maxSize = size + 2;
+        }
+
+        size++;
+    }
+
+    /*
+     * Create an array object to hold the classes.
+     * TODO: can use gDvm.classJavaLangClassArray here?
+     */
+    ClassObject* classArrayClass = NULL;
+    ArrayObject* classes = NULL;
+    classArrayClass = dvmFindArrayClass("[Ljava/lang/Class;", NULL);
+    if (classArrayClass == NULL) {
+        LOGW("Unable to find java.lang.Class array class\n");
+        goto bail;
+    }
+    classes = dvmAllocArray(classArrayClass, size, kObjectArrayRefWidth,
+                ALLOC_DEFAULT);
+    if (classes == NULL) {
+        LOGW("Unable to allocate class array (%d elems)\n", size);
+        goto bail;
+    }
+
+    /*
+     * Fill in the array.
+     */
+    unsigned int objCount = 0;
+    for (idx = kSkip; (int) idx < methodCount; idx++) {
+        if (dvmIsReflectionMethod(methods[idx])) {
+            continue;
+        }
+        dvmSetObjectArrayElement(classes, objCount,
+                                 (Object *)methods[idx]->clazz);
+        objCount++;
+    }
+    assert(objCount == classes->length);
+
+bail:
+    free(methods);
+    dvmReleaseTrackedAlloc((Object*) classes, NULL);
+    RETURN_PTR(classes);
+}
+
+/*
+ * 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];
+    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) {
+        LOGI("VMStack.getThreadStackTrace: threadObj %p not active\n",
+            targetThreadObj);
+        dvmUnlockThreadList();
+        RETURN_PTR(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.
+     */
+    int stackDepth = -1;
+    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_PTR(trace);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMStack[] = {
+    { "getCallingClassLoader",  "()Ljava/lang/ClassLoader;",
+        Dalvik_dalvik_system_VMStack_getCallingClassLoader },
+    { "getCallingClassLoader2", "()Ljava/lang/ClassLoader;",
+        Dalvik_dalvik_system_VMStack_getCallingClassLoader2 },
+    { "getStackClass2", "()Ljava/lang/Class;",
+        Dalvik_dalvik_system_VMStack_getStackClass2 },
+    { "getClasses",             "(IZ)[Ljava/lang/Class;",
+        Dalvik_dalvik_system_VMStack_getClasses },
+    { "getThreadStackTrace",    "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;",
+        Dalvik_dalvik_system_VMStack_getThreadStackTrace },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_Zygote.c b/vm/native/dalvik_system_Zygote.c
new file mode 100644
index 0000000..bcc2313
--- /dev/null
+++ b/vm/native/dalvik_system_Zygote.c
@@ -0,0 +1,523 @@
+/*
+ * 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 "native/InternalNativePriv.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <grp.h>
+#include <errno.h>
+
+#if defined(HAVE_PRCTL)
+# include <sys/prctl.h>
+#endif
+
+#define ZYGOTE_LOG_TAG "Zygote"
+
+/* must match values in dalvik.system.Zygote */
+enum {
+    DEBUG_ENABLE_DEBUGGER           = 1,
+    DEBUG_ENABLE_CHECKJNI           = 1 << 1,
+    DEBUG_ENABLE_ASSERT             = 1 << 2,
+    DEBUG_ENABLE_SAFEMODE           = 1 << 3,
+};
+
+/*
+ * This signal handler is for zygote mode, since the zygote
+ * must reap its children
+ */
+static void sigchldHandler(int s)
+{
+    pid_t pid;
+    int status;
+
+    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+        /* Log process-death status that we care about.  In general it is not
+           safe to call LOG(...) from a signal handler because of possible
+           reentrancy.  However, we know a priori that the current implementation
+           of LOG() is safe to call from a SIGCHLD handler in the zygote process.
+           If the LOG() implementation changes its locking strategy or its use
+           of syscalls within the lazy-init critical section, its use here may
+           become unsafe. */
+        if (WIFEXITED(status)) {
+            if (WEXITSTATUS(status)) {
+                LOG(LOG_DEBUG, ZYGOTE_LOG_TAG, "Process %d exited cleanly (%d)\n",
+                    (int) pid, WEXITSTATUS(status));
+            } else {
+                IF_LOGV(/*should use ZYGOTE_LOG_TAG*/) {
+                    LOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
+                        "Process %d exited cleanly (%d)\n",
+                        (int) pid, WEXITSTATUS(status));
+                }
+            }
+        } else if (WIFSIGNALED(status)) {
+            if (WTERMSIG(status) != SIGKILL) {
+                LOG(LOG_DEBUG, ZYGOTE_LOG_TAG,
+                    "Process %d terminated by signal (%d)\n",
+                    (int) pid, WTERMSIG(status));
+            } else {
+                IF_LOGV(/*should use ZYGOTE_LOG_TAG*/) {
+                    LOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
+                        "Process %d terminated by signal (%d)\n",
+                        (int) pid, WTERMSIG(status));
+                }
+            }
+#ifdef WCOREDUMP
+            if (WCOREDUMP(status)) {
+                LOG(LOG_INFO, ZYGOTE_LOG_TAG, "Process %d dumped core\n",
+                    (int) pid);
+            }
+#endif /* ifdef WCOREDUMP */
+        }
+
+        /*
+         * If the just-crashed process is the system_server, bring down zygote
+         * so that it is restarted by init and system server will be restarted
+         * from there.
+         */
+        if (pid == gDvm.systemServerPid) {
+            LOG(LOG_INFO, ZYGOTE_LOG_TAG,
+                "Exit zygote because system server (%d) has terminated\n",
+                (int) pid);
+            kill(getpid(), SIGKILL);
+        }
+    }
+
+    if (pid < 0) {
+        LOG(LOG_WARN, ZYGOTE_LOG_TAG,
+            "Zygote SIGCHLD error in waitpid: %s\n",strerror(errno));
+    }
+}
+
+/*
+ * configure sigchld handler for the zygote process
+ * This is configured very late, because earlier in the dalvik lifecycle
+ * we can fork() and exec() for the verifier/optimizer, and we
+ * want to waitpid() for those rather than have them be harvested immediately.
+ *
+ * This ends up being called repeatedly before each fork(), but there's
+ * no real harm in that.
+ */
+static void setSignalHandler()
+{
+    int err;
+    struct sigaction sa;
+
+    memset(&sa, 0, sizeof(sa));
+
+    sa.sa_handler = sigchldHandler;
+
+    err = sigaction (SIGCHLD, &sa, NULL);
+
+    if (err < 0) {
+        LOGW("Error setting SIGCHLD handler: %s", strerror(errno));
+    }
+}
+
+/*
+ * Set the SIGCHLD handler back to default behavior in zygote children
+ */
+static void unsetSignalHandler()
+{
+    int err;
+    struct sigaction sa;
+
+    memset(&sa, 0, sizeof(sa));
+
+    sa.sa_handler = SIG_DFL;
+
+    err = sigaction (SIGCHLD, &sa, NULL);
+
+    if (err < 0) {
+        LOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
+    }
+}
+
+/*
+ * Calls POSIX setgroups() using the int[] object as an argument.
+ * A NULL argument is tolerated.
+ */
+
+static int setgroupsIntarray(ArrayObject* gidArray)
+{
+    gid_t *gids;
+    u4 i;
+    s4 *contents;
+
+    if (gidArray == NULL) {
+        return 0;
+    }
+
+    /* just in case gid_t and u4 are different... */
+    gids = alloca(sizeof(gid_t) * gidArray->length);
+    contents = (s4 *)gidArray->contents;
+
+    for (i = 0 ; i < gidArray->length ; i++) {
+        gids[i] = (gid_t) contents[i];
+    }
+
+    return setgroups((size_t) gidArray->length, gids);
+}
+
+/*
+ * Sets the resource limits via setrlimit(2) for the values in the
+ * two-dimensional array of integers that's passed in. The second dimension
+ * contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
+ * treated as an empty array.
+ *
+ * -1 is returned on error.
+ */
+static int setrlimitsFromArray(ArrayObject* rlimits)
+{
+    u4 i;
+    struct rlimit rlim;
+
+    if (rlimits == NULL) {
+        return 0;
+    }
+
+    memset (&rlim, 0, sizeof(rlim));
+
+    ArrayObject** tuples = (ArrayObject **)(rlimits->contents);
+
+    for (i = 0; i < rlimits->length; i++) {
+        ArrayObject * rlimit_tuple = tuples[i];
+        s4* contents = (s4 *)rlimit_tuple->contents;
+        int err;
+
+        if (rlimit_tuple->length != 3) {
+            LOGE("rlimits array must have a second dimension of size 3");
+            return -1;
+        }
+
+        rlim.rlim_cur = contents[1];
+        rlim.rlim_max = contents[2];
+
+        err = setrlimit(contents[0], &rlim);
+
+        if (err < 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+/* native public static int fork(); */
+static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult)
+{
+    pid_t pid;
+
+    if (!gDvm.zygote) {
+        dvmThrowException("Ljava/lang/IllegalStateException;",
+            "VM instance not started with -Xzygote");
+
+        RETURN_VOID();
+    }
+
+    if (!dvmGcPreZygoteFork()) {
+        LOGE("pre-fork heap failed\n");
+        dvmAbort();
+    }
+
+    setSignalHandler();
+
+    dvmDumpLoaderStats("zygote");
+    pid = fork();
+
+#ifdef HAVE_ANDROID_OS
+    if (pid == 0) {
+        /* child process */
+        extern int gMallocLeakZygoteChild;
+        gMallocLeakZygoteChild = 1;
+    }
+#endif
+
+    RETURN_INT(pid);
+}
+
+/*
+ * 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 eabled.  This is a little weird,
+ *   because we already have the JNIEnv for the main thread set up.  However,
+ *   since we only have one thread at this point, it's easy to patch up.
+ * 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)
+{
+    LOGV("debugFlags is 0x%02x\n", 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_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
+    }
+
+#if 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) {
+            LOGE("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) {
+                LOGE("could not disable core file generation for pid %d: %s",
+                    getpid(), strerror(errno));
+            }
+        }
+    }
+#endif
+}
+
+/*
+ * Set Linux capability flags.
+ *
+ * Returns 0 on success, errno on failure.
+ */
+static int setCapabilities(int64_t permitted, int64_t effective)
+{
+#ifdef HAVE_ANDROID_OS
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata;
+
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+
+    capheader.version = _LINUX_CAPABILITY_VERSION;
+    capheader.pid = 0;
+
+    capdata.effective = effective;
+    capdata.permitted = permitted;
+
+    LOGV("CAPSET perm=%llx eff=%llx\n", permitted, effective);
+    if (capset(&capheader, &capdata) != 0)
+        return errno;
+#endif /*HAVE_ANDROID_OS*/
+
+    return 0;
+}
+
+/*
+ * Utility routine to fork zygote and specialize the child process.
+ */
+static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
+{
+    pid_t pid;
+
+    uid_t uid = (uid_t) args[0];
+    gid_t gid = (gid_t) args[1];
+    ArrayObject* gids = (ArrayObject *)args[2];
+    u4 debugFlags = args[3];
+    ArrayObject *rlimits = (ArrayObject *)args[4];
+    int64_t permittedCapabilities, effectiveCapabilities;
+
+    if (isSystemServer) {
+        /*
+         * Don't use GET_ARG_LONG here for now.  gcc is generating code
+         * that uses register d8 as a temporary, and that's coming out
+         * scrambled in the child process.  b/3138621
+         */
+        //permittedCapabilities = GET_ARG_LONG(args, 5);
+        //effectiveCapabilities = GET_ARG_LONG(args, 7);
+        permittedCapabilities = args[5] | (int64_t) args[6] << 32;
+        effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
+    } else {
+        permittedCapabilities = effectiveCapabilities = 0;
+    }
+
+    if (!gDvm.zygote) {
+        dvmThrowException("Ljava/lang/IllegalStateException;",
+            "VM instance not started with -Xzygote");
+
+        return -1;
+    }
+
+    if (!dvmGcPreZygoteFork()) {
+        LOGE("pre-fork heap failed\n");
+        dvmAbort();
+    }
+
+    setSignalHandler();
+
+    dvmDumpLoaderStats("zygote");
+    pid = fork();
+
+    if (pid == 0) {
+        int err;
+        /* The child process */
+
+#ifdef HAVE_ANDROID_OS
+        extern int gMallocLeakZygoteChild;
+        gMallocLeakZygoteChild = 1;
+
+        /* keep caps across UID change, unless we're staying root */
+        if (uid != 0) {
+            err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+            if (err < 0) {
+                LOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno));
+                dvmAbort();
+            }
+        }
+
+#endif /* HAVE_ANDROID_OS */
+
+        err = setgroupsIntarray(gids);
+
+        if (err < 0) {
+            LOGE("cannot setgroups(): %s", strerror(errno));
+            dvmAbort();
+        }
+
+        err = setrlimitsFromArray(rlimits);
+
+        if (err < 0) {
+            LOGE("cannot setrlimit(): %s", strerror(errno));
+            dvmAbort();
+        }
+
+        err = setgid(gid);
+        if (err < 0) {
+            LOGE("cannot setgid(%d): %s", gid, strerror(errno));
+            dvmAbort();
+        }
+
+        err = setuid(uid);
+        if (err < 0) {
+            LOGE("cannot setuid(%d): %s", uid, strerror(errno));
+            dvmAbort();
+        }
+
+        err = setCapabilities(permittedCapabilities, effectiveCapabilities);
+        if (err != 0) {
+            LOGE("cannot set capabilities (%llx,%llx): %s\n",
+                permittedCapabilities, effectiveCapabilities, strerror(err));
+            dvmAbort();
+        }
+
+        /*
+         * Our system thread ID has changed.  Get the new one.
+         */
+        Thread* thread = dvmThreadSelf();
+        thread->systemTid = dvmGetSysThreadId();
+
+        /* configure additional debug options */
+        enableDebugFeatures(debugFlags);
+
+        unsetSignalHandler();
+        gDvm.zygote = false;
+        if (!dvmInitAfterZygote()) {
+            LOGE("error in post-zygote initialization\n");
+            dvmAbort();
+        }
+    } else if (pid > 0) {
+        /* the parent process */
+    }
+
+    return pid;
+}
+
+/* native public static int forkAndSpecialize(int uid, int gid,
+ *     int[] gids, int debugFlags);
+ */
+static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
+    JValue* pResult)
+{
+    pid_t pid;
+
+    pid = forkAndSpecializeCommon(args, false);
+
+    RETURN_INT(pid);
+}
+
+/* native public static int forkSystemServer(int uid, int gid,
+ *     int[] gids, int debugFlags, long permittedCapabilities,
+ *     long effectiveCapabilities);
+ */
+static void Dalvik_dalvik_system_Zygote_forkSystemServer(
+        const u4* args, JValue* pResult)
+{
+    pid_t pid;
+    pid = forkAndSpecializeCommon(args, true);
+
+    /* The zygote process checks whether the child process has died or not. */
+    if (pid > 0) {
+        int status;
+
+        LOGI("System server process %d has been created", pid);
+        gDvm.systemServerPid = pid;
+        /* There is a slight window that the system server process has crashed
+         * but it went unnoticed because we haven't published its pid yet. So
+         * we recheck here just to make sure that all is well.
+         */
+        if (waitpid(pid, &status, WNOHANG) == pid) {
+            LOGE("System server process %d has died. Restarting Zygote!", pid);
+            kill(getpid(), SIGKILL);
+        }
+    }
+    RETURN_INT(pid);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
+    { "fork",            "()I",
+        Dalvik_dalvik_system_Zygote_fork },
+    { "forkAndSpecialize",            "(II[II[[I)I",
+        Dalvik_dalvik_system_Zygote_forkAndSpecialize },
+    { "forkSystemServer",            "(II[II[[IJJ)I",
+        Dalvik_dalvik_system_Zygote_forkSystemServer },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Class.c b/vm/native/java_lang_Class.c
new file mode 100644
index 0000000..3c772d9
--- /dev/null
+++ b/vm/native/java_lang_Class.c
@@ -0,0 +1,814 @@
+/*
+ * 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"
+
+
+/*
+ * 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)
+            {
+                LOGV("ASRT: pkg no match: '%s'(%d) vs '%s'\n",
+                    className, pkgLen, pCtrl->pkgOrClass);
+            } else {
+                LOGV("ASRT: pkg match: '%s'(%d) vs '%s' --> %d\n",
+                    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) {
+                    LOGV("ASRT: sys no match: '%s'\n", className);
+                } else {
+                    LOGV("ASRT: sys match: '%s' --> %d\n",
+                        className, pCtrl->enable);
+                    enable = pCtrl->enable;
+                }
+            } else if (*pCtrl->pkgOrClass == '\0') {
+                LOGV("ASRT: class all: '%s' --> %d\n",
+                    className, pCtrl->enable);
+                enable = pCtrl->enable;
+            } else {
+                if (strcmp(pCtrl->pkgOrClass, className) != 0) {
+                    LOGV("ASRT: cls no match: '%s' vs '%s'\n",
+                        className, pCtrl->pkgOrClass);
+                } else {
+                    LOGV("ASRT: cls match: '%s' vs '%s' --> %d\n",
+                        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**) 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)
+ *     throws SecurityException
+ */
+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 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);
+}
+
+/*
+ * 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.
+ */
+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: {
+                LOGE("Unknown primitive type '%c'\n", 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);
+
+#if 0
+    /* doesn't work -- need "java.lang.String" not "java/lang/String" */
+    {
+        /*
+         * Find the string in the DEX file and use the copy in the intern
+         * table if it already exists (else put one there).  Only works
+         * for strings in the DEX file, e.g. not arrays.
+         *
+         * We have to do the class lookup by name in the DEX file because
+         * we don't have a DexClassDef pointer in the ClassObject, and it's
+         * not worth adding one there just for this.  Should be cheaper
+         * to do this than the string-creation above.
+         */
+        const DexFile* pDexFile = clazz->pDexFile;
+        const DexClassDef* pClassDef;
+        const DexClassId* pClassId;
+
+        pDexFile = clazz->pDexFile;
+        pClassDef = dvmDexFindClass(pDexFile, clazz->descriptor);
+        pClassId = dvmDexGetClassId(pDexFile, pClassDef->classIdx);
+        nameObj = dvmDexGetResolvedString(pDexFile, pClassId->nameIdx);
+        if (nameObj == NULL) {
+            nameObj = dvmResolveString(clazz, pClassId->nameIdx);
+            if (nameObj == NULL)
+                LOGW("WARNING: couldn't find string %u for '%s'\n",
+                    pClassId->nameIdx, clazz->name);
+        }
+    }
+#endif
+
+    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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", 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))
+    {
+        LOGD("newInstance failed: p%d i%d [%d a%d\n",
+            dvmIsPrimitiveClass(clazz), dvmIsInterfaceClass(clazz),
+            dvmIsArrayClass(clazz), dvmIsAbstractClass(clazz));
+        dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;",
+            clazz->descriptor);
+        RETURN_VOID();
+    }
+
+    /* initialize the class if it hasn't been already */
+    if (!dvmIsClassInitialized(clazz)) {
+        if (!dvmInitClass(clazz)) {
+            LOGW("Class init failed in newInstance call (%s)\n",
+                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 */
+        LOGD("newInstance failed: no <init>()\n");
+        dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;",
+            clazz->descriptor);
+        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->curFrame);
+
+    if (!dvmCheckClassAccess(callerClass, clazz)) {
+        LOGD("newInstance failed: %s not accessible to %s\n",
+            clazz->descriptor, callerClass->descriptor);
+        dvmThrowException("Ljava/lang/IllegalAccessException;",
+            "access to class not allowed");
+        RETURN_VOID();
+    }
+    if (!dvmCheckMethodAccess(callerClass, init)) {
+        LOGD("newInstance failed: %s.<init>() not accessible to %s\n",
+            clazz->descriptor, callerClass->descriptor);
+        dvmThrowException("Ljava/lang/IllegalAccessException;",
+            "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);
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation array.
+ */
+static void Dalvik_java_lang_Class_getSignatureAnnotation(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+    ArrayObject* arr = dvmGetClassSignatureAnnotation(clazz);
+
+    dvmReleaseTrackedAlloc((Object*) arr, NULL);
+    RETURN_PTR(arr);
+}
+
+/*
+ * public Class getDeclaringClass()
+ *
+ * Get the class that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getDeclaringClass(const u4* args,
+    JValue* pResult)
+{
+    ClassObject* clazz = (ClassObject*) args[0];
+
+    ClassObject* enclosing = dvmGetDeclaringClass(clazz);
+    dvmReleaseTrackedAlloc((Object*) enclosing, NULL);
+    RETURN_PTR(enclosing);
+}
+
+/*
+ * 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);
+}
+
+#if 0
+static void Dalvik_java_lang_Class_getGenericInterfaces(const u4* args,
+    JValue* pResult)
+{
+    dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+        "native method not implemented");
+
+    RETURN_PTR(NULL);
+}
+
+static void Dalvik_java_lang_Class_getGenericSuperclass(const u4* args,
+    JValue* pResult)
+{
+    dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+        "native method not implemented");
+
+    RETURN_PTR(NULL);
+}
+
+static void Dalvik_java_lang_Class_getTypeParameters(const u4* args,
+    JValue* pResult)
+{
+    dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+        "native method not implemented");
+
+    RETURN_PTR(NULL);
+}
+#endif
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+    }
+}
+
+/*
+ * static native void setAccessibleNoCheck(AccessibleObject ao, boolean flag);
+ */
+static void Dalvik_java_lang_Class_setAccessibleNoCheck(const u4* args,
+    JValue* pResult)
+{
+    Object* target = (Object*) args[0];
+    u4 flag = (u4) args[1];
+
+    dvmSetFieldBoolean(target, gDvm.offJavaLangReflectAccessibleObject_flag,
+            flag);
+}
+
+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 },
+    { "getSignatureAnnotation",  "()[Ljava/lang/Object;",
+        Dalvik_java_lang_Class_getSignatureAnnotation },
+    { "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 },
+    { "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 },
+    { "getInnerClassName",       "()Ljava/lang/String;",
+        Dalvik_java_lang_Class_getInnerClassName },
+    { "setAccessibleNoCheck",   "(Ljava/lang/reflect/AccessibleObject;Z)V",
+        Dalvik_java_lang_Class_setAccessibleNoCheck },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Object.c b/vm/native/java_lang_Object.c
new file mode 100644
index 0000000..f2adf52
--- /dev/null
+++ b/vm/native/java_lang_Object.c
@@ -0,0 +1,111 @@
+/*
+ * 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);
+
+    dvmReleaseTrackedAlloc(clone, NULL);
+    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.c b/vm/native/java_lang_Runtime.c
new file mode 100644
index 0000000..d28ff84
--- /dev/null
+++ b/vm/native/java_lang_Runtime.c
@@ -0,0 +1,197 @@
+/*
+ * 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 <unistd.h>
+#include <limits.h>
+
+/*
+ * public void gc()
+ *
+ * Initiate a gc.
+ */
+static void Dalvik_java_lang_Runtime_gc(const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    dvmCollectGarbage(false);
+    RETURN_VOID();
+}
+
+/*
+ * private static void nativeExit(int code, boolean isExit)
+ *
+ * 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];
+    bool isExit = (args[1] != 0);
+
+    if (isExit && gDvm.exitHook != NULL) {
+        dvmChangeStatus(NULL, THREAD_NATIVE);
+        (*gDvm.exitHook)(status);     // not expected to return
+        dvmChangeStatus(NULL, THREAD_RUNNING);
+        LOGW("JNI exit hook returned\n");
+    }
+    LOGD("Calling exit(%d)\n", status);
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+    dvmCompilerDumpStats();
+#endif
+    exit(status);
+}
+
+/*
+ * static String nativeLoad(String filename, ClassLoader loader)
+ *
+ * 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];
+    char* fileName = NULL;
+    StringObject* result = NULL;
+    char* reason = NULL;
+    bool success;
+
+    assert(fileNameObj != NULL);
+    fileName = dvmCreateCstrFromString(fileNameObj);
+
+    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 void runFinalization(boolean forced)
+ *
+ * Requests that the VM runs finalizers for objects on the heap. If the
+ * parameter forced is true, then the VM needs to ensure finalization.
+ * Otherwise this only inspires the VM to make a best-effort attempt to
+ * run finalizers before returning, but it's not guaranteed to actually
+ * do anything.
+ */
+static void Dalvik_java_lang_Runtime_runFinalization(const u4* args,
+    JValue* pResult)
+{
+    bool forced = (args[0] != 0);
+
+    dvmWaitForHeapWorkerIdle();
+    if (forced) {
+        // TODO(Google) Need to explicitly implement this,
+        //              although dvmWaitForHeapWorkerIdle()
+        //              should usually provide the "forced"
+        //              behavior already.
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * public int availableProcessors()
+ *
+ * Returns the number of online processors, at least one.
+ *
+ */
+static void Dalvik_java_lang_Runtime_availableProcessors(const u4* args,
+    JValue* pResult)
+{
+    long result = 1;
+#ifdef _SC_NPROCESSORS_ONLN
+    result = sysconf(_SC_NPROCESSORS_ONLN);
+    if (result > INT_MAX) {
+        result = INT_MAX;
+    } else if (result < 1 ) {
+        result = 1;
+    }
+#endif
+    RETURN_INT((int)result);
+}
+/*
+ * public void maxMemory()
+ *
+ * Returns GC heap max memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_maxMemory(const u4* args, JValue* pResult)
+{
+    unsigned int result = gDvm.heapSizeMax;
+    RETURN_LONG(result);
+}
+
+/*
+ * public void totalMemory()
+ *
+ * Returns GC heap total memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_totalMemory(const u4* args,
+    JValue* pResult)
+{
+    int result = dvmGetHeapDebugInfo(kVirtualHeapSize);
+    RETURN_LONG(result);
+}
+
+/*
+ * public void freeMemory()
+ *
+ * Returns GC heap free memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_freeMemory(const u4* args,
+    JValue* pResult)
+{
+    int result = dvmGetHeapDebugInfo(kVirtualHeapSize)
+                 - dvmGetHeapDebugInfo(kVirtualHeapAllocated);
+    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 },
+    { "availableProcessors", "()I",
+        Dalvik_java_lang_Runtime_availableProcessors },
+    { "maxMemory",          "()J",
+        Dalvik_java_lang_Runtime_maxMemory },
+    { "nativeExit",         "(IZ)V",
+        Dalvik_java_lang_Runtime_nativeExit },
+    { "nativeLoad",         "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;",
+        Dalvik_java_lang_Runtime_nativeLoad },
+    { "runFinalization",    "(Z)V",
+        Dalvik_java_lang_Runtime_runFinalization },
+    { "totalMemory",          "()J",
+        Dalvik_java_lang_Runtime_totalMemory },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_String.c b/vm/native/java_lang_String.c
new file mode 100644
index 0000000..b3cb7ec
--- /dev/null
+++ b/vm/native/java_lang_String.c
@@ -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.
+ */
+
+/*
+ * java.lang.String
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public String intern()
+ *
+ * Intern a string in the VM string table.
+ */
+static void Dalvik_java_lang_String_intern(const u4* args, JValue* pResult)
+{
+    StringObject* str = (StringObject*) args[0];
+    StringObject* interned;
+
+    interned = dvmLookupInternedString(str);
+    RETURN_PTR(interned);
+}
+
+const DalvikNativeMethod dvm_java_lang_String[] = {
+    { "intern",             "()Ljava/lang/String;",
+        Dalvik_java_lang_String_intern },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_System.c b/vm/native/java_lang_System.c
new file mode 100644
index 0000000..4af0dfa
--- /dev/null
+++ b/vm/native/java_lang_System.c
@@ -0,0 +1,329 @@
+/*
+ * 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"
+
+/*
+ * Call the appropriate copy function given the circumstances.
+ */
+static void copy(void *dest, const void *src, size_t n, bool sameArray,
+        size_t elemSize)
+{
+    if (sameArray) {
+        /* Might overlap. */
+        if (elemSize == sizeof(Object*)) {
+            /*
+             * In addition to handling overlap properly, bcopy()
+             * guarantees atomic treatment of words. This is needed so
+             * that concurrent threads never see half-formed pointers
+             * or ints. The former is required for proper gc behavior,
+             * and the latter is also required for proper high-level
+             * language support.
+             *
+             * Note: bcopy()'s argument order is different than memcpy().
+             */
+            bcopy(src, dest, n);
+        } else {
+            memmove(dest, src, n);
+        }
+    } else {
+        memcpy(dest, src, n); /* Can't overlap; use faster function. */
+    }
+}
+
+/*
+ * 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* dstArray;
+    ClassObject* srcClass;
+    ClassObject* dstClass;
+    int srcPos, dstPos, length;
+    char srcType, dstType;
+    bool srcPrim, dstPrim;
+    bool sameArray;
+
+    srcArray = (ArrayObject*) args[0];
+    srcPos = args[1];
+    dstArray = (ArrayObject*) args[2];
+    dstPos = args[3];
+    length = args[4];
+
+    sameArray = (srcArray == dstArray);
+
+    /* check for null or bad pointer */
+    if (!dvmValidateObject((Object*)srcArray) ||
+        !dvmValidateObject((Object*)dstArray))
+    {
+        assert(dvmCheckException(dvmThreadSelf()));
+        RETURN_VOID();
+    }
+    /* make sure it's an array */
+    if (!dvmIsArray(srcArray) || !dvmIsArray(dstArray)) {
+        dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;",
+            "source and destination must be arrays, but were %s and %s",
+            ((Object*)srcArray)->clazz->descriptor,
+            ((Object*)dstArray)->clazz->descriptor);
+        RETURN_VOID();
+    }
+
+    // avoid int overflow
+    if (srcPos < 0 || dstPos < 0 || length < 0 ||
+        srcPos > (int) srcArray->length - length ||
+        dstPos > (int) dstArray->length - length)
+    {
+        dvmThrowExceptionFmt("Ljava/lang/ArrayIndexOutOfBoundsException;",
+            "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
+            srcArray->length, srcPos, dstArray->length, dstPos, length);
+        RETURN_VOID();
+    }
+
+    srcClass = srcArray->obj.clazz;
+    dstClass = dstArray->obj.clazz;
+    srcType = srcClass->descriptor[1];
+    dstType = dstClass->descriptor[1];
+
+    /*
+     * If one of the arrays holds a primitive type, the other array must
+     * hold the same type.
+     */
+    srcPrim = (srcType != '[' && srcType != 'L');
+    dstPrim = (dstType != '[' && dstType != 'L');
+    if (srcPrim || dstPrim) {
+        int width;
+
+        if (srcPrim != dstPrim || srcType != dstType) {
+            dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;",
+                "source and destination arrays are incompatible: %s and %s",
+                srcClass->descriptor, dstClass->descriptor);
+            RETURN_VOID();
+        }
+
+        switch (srcClass->descriptor[1]) {
+        case 'B':
+        case 'Z':
+            width = 1;
+            break;
+        case 'C':
+        case 'S':
+            width = 2;
+            break;
+        case 'F':
+        case 'I':
+            width = 4;
+            break;
+        case 'D':
+        case 'J':
+            width = 8;
+            break;
+        default:        /* 'V' or something weird */
+            LOGE("Weird array type '%s'\n", srcClass->descriptor);
+            assert(false);
+            width = 0;
+            break;
+        }
+
+        if (false) LOGVV("arraycopy prim dst=%p %d src=%p %d len=%d\n",
+                dstArray->contents, dstPos * width,
+                srcArray->contents, srcPos * width,
+                length * width);
+        copy((u1*)dstArray->contents + dstPos * width,
+                (const u1*)srcArray->contents + srcPos * width,
+                length * width,
+                sameArray, width);
+    } 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).
+         */
+        int width = sizeof(Object*);
+
+        if (srcClass->arrayDim == dstClass->arrayDim &&
+            dvmInstanceof(srcClass, dstClass))
+        {
+            /*
+             * "dst" can hold "src"; copy the whole thing.
+             */
+            if (false) LOGVV("arraycopy ref dst=%p %d src=%p %d len=%d\n",
+                dstArray->contents, dstPos * width,
+                srcArray->contents, srcPos * width,
+                length * width);
+            copy((u1*)dstArray->contents + dstPos * width,
+                    (const u1*)srcArray->contents + srcPos * width,
+                    length * width,
+                    sameArray, 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
+             * memmove() to shift the actual data.  If we just start from the
+             * front we could do a smear rather than a move.
+             */
+            Object** srcObj;
+            Object** dstObj;
+            int copyCount;
+            ClassObject*   clazz = NULL;
+
+            srcObj = ((Object**) srcArray->contents) + srcPos;
+            dstObj = ((Object**) dstArray->contents) + dstPos;
+
+            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) LOGVV("arraycopy iref dst=%p %d src=%p %d count=%d of %d\n",
+                dstArray->contents, dstPos * width,
+                srcArray->contents, srcPos * width,
+                copyCount, length);
+            copy((u1*)dstArray->contents + dstPos * width,
+                    (const u1*)srcArray->contents + srcPos * width,
+                    copyCount * width,
+                    sameArray, width);
+            dvmWriteBarrierArray(dstArray, 0, copyCount);
+            if (copyCount != length) {
+                dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;",
+                    "source[%d] of type %s cannot be stored in destination array of type %s",
+                    copyCount, srcObj[copyCount]->clazz->descriptor,
+                    dstClass->descriptor);
+                RETURN_VOID();
+            }
+        }
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * static long currentTimeMillis()
+ *
+ * Current time, in miliseconds.  This doesn't need to be internal to the
+ * VM, but we're already handling java.lang.System here.
+ */
+static void Dalvik_java_lang_System_currentTimeMillis(const u4* args,
+    JValue* pResult)
+{
+    struct timeval tv;
+
+    UNUSED_PARAMETER(args);
+
+    gettimeofday(&tv, (struct timezone *) NULL);
+    long long when = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+
+    RETURN_LONG(when);
+}
+
+/*
+ * static long nanoTime()
+ *
+ * Current monotonically-increasing time, in nanoseconds.  This doesn't
+ * need to be internal to the VM, but we're already handling
+ * java.lang.System here.
+ */
+static void Dalvik_java_lang_System_nanoTime(const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    u8 when = dvmGetRelativeTimeNsec();
+    RETURN_LONG(when);
+}
+
+/*
+ * 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));
+}
+
+/*
+ * public static String mapLibraryName(String libname)
+ */
+static void Dalvik_java_lang_System_mapLibraryName(const u4* args,
+    JValue* pResult)
+{
+    StringObject* nameObj = (StringObject*) args[0];
+    StringObject* result = NULL;
+    char* name;
+    char* mappedName;
+
+    if (nameObj == NULL) {
+        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+        RETURN_VOID();
+    }
+
+    name = dvmCreateCstrFromString(nameObj);
+    mappedName = dvmCreateSystemLibraryName(name);
+    if (mappedName != NULL) {
+        result = dvmCreateStringFromCstr(mappedName);
+        dvmReleaseTrackedAlloc((Object*) result, NULL);
+    }
+
+    free(name);
+    free(mappedName);
+    RETURN_PTR(result);
+}
+
+const DalvikNativeMethod dvm_java_lang_System[] = {
+    { "arraycopy",          "(Ljava/lang/Object;ILjava/lang/Object;II)V",
+        Dalvik_java_lang_System_arraycopy },
+    { "currentTimeMillis",  "()J",
+        Dalvik_java_lang_System_currentTimeMillis },
+    { "nanoTime",  "()J",
+        Dalvik_java_lang_System_nanoTime },
+    { "identityHashCode",  "(Ljava/lang/Object;)I",
+        Dalvik_java_lang_System_identityHashCode },
+    { "mapLibraryName",     "(Ljava/lang/String;)Ljava/lang/String;",
+        Dalvik_java_lang_System_mapLibraryName },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_SystemProperties.c b/vm/native/java_lang_SystemProperties.c
new file mode 100644
index 0000000..bbcf25e
--- /dev/null
+++ b/vm/native/java_lang_SystemProperties.c
@@ -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.
+ */
+
+/*
+ * java.lang.SystemProperties
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * Expected call sequence:
+ *  (1) call SystemProperties.preInit() to get VM defaults
+ *  (2) set any higher-level defaults
+ *  (3) call SystemProperties.postInit() to get command-line overrides
+ * This currently happens the first time somebody tries to access a property.
+ *
+ * SystemProperties is a Dalvik-specific package-scope class.
+ */
+
+/*
+ * void preInit()
+ *
+ * Tells the VM to populate the properties table with VM defaults.
+ */
+static void Dalvik_java_lang_SystemProperties_preInit(const u4* args,
+    JValue* pResult)
+{
+    dvmCreateDefaultProperties((Object*) args[0]);
+    RETURN_VOID();
+}
+
+/*
+ * void postInit()
+ *
+ * Tells the VM to update properties with values from the command line.
+ */
+static void Dalvik_java_lang_SystemProperties_postInit(const u4* args,
+    JValue* pResult)
+{
+    dvmSetCommandLineProperties((Object*) args[0]);
+    RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_java_lang_SystemProperties[] = {
+    { "preInit",            "()V",
+        Dalvik_java_lang_SystemProperties_preInit },
+    { "postInit",           "()V",
+        Dalvik_java_lang_SystemProperties_postInit },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Throwable.c b/vm/native/java_lang_Throwable.c
new file mode 100644
index 0000000..f96ee6d
--- /dev/null
+++ b/vm/native/java_lang_Throwable.c
@@ -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) {
+        LOGW("getStackTrace() called but no trace available\n");
+        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.c b/vm/native/java_lang_VMClassLoader.c
new file mode 100644
index 0000000..e8dbc6e
--- /dev/null
+++ b/vm/native/java_lang_VMClassLoader.c
@@ -0,0 +1,201 @@
+/*
+ * 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, ProtectionDomain pd)
+ *     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];
+    Object* pd = (Object*) args[5];
+    char* name = NULL;
+
+    name = dvmCreateCstrFromString(nameObj);
+    LOGE("ERROR: defineClass(%p, %s, %p, %d, %d, %p)\n",
+        loader, name, data, offset, len, pd);
+    dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+        "can't load this type of class file");
+
+    free(name);
+    RETURN_VOID();
+}
+
+/*
+ * static Class defineClass(ClassLoader cl, byte[] data, int offset,
+ *     int len, ProtectionDomain pd)
+ *     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];
+    Object* pd = (Object*) args[4];
+
+    LOGE("ERROR: defineClass(%p, %p, %d, %d, %p)\n",
+        loader, data, offset, len, pd);
+    dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+        "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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", 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\n", 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 = 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;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;",
+        Dalvik_java_lang_VMClassLoader_defineClass },
+    { "defineClass",        "(Ljava/lang/ClassLoader;[BIILjava/security/ProtectionDomain;)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.c b/vm/native/java_lang_VMThread.c
new file mode 100644
index 0000000..3b7331b
--- /dev/null
+++ b/vm/native/java_lang_VMThread.c
@@ -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) {
+        dvmThrowException("Ljava/lang/NullPointerException;", 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);
+    //LOGI("UPDATE: threadid=%d now '%s'\n", 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.c b/vm/native/java_lang_reflect_AccessibleObject.c
new file mode 100644
index 0000000..46a1357
--- /dev/null
+++ b/vm/native/java_lang_reflect_AccessibleObject.c
@@ -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.c b/vm/native/java_lang_reflect_Array.c
new file mode 100644
index 0000000..e7713f6
--- /dev/null
+++ b/vm/native/java_lang_reflect_Array.c
@@ -0,0 +1,146 @@
+/*
+ * 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];
+    ArrayObject* newArray;
+
+    assert(elementClass != NULL);       // tested by caller
+    if (length < 0) {
+        dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+        RETURN_VOID();
+    }
+
+    newArray = dvmAllocObjectArray(elementClass, 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)
+{
+    static const char kPrimLetter[] = PRIM_TYPE_TO_LETTER;
+    ClassObject* elementClass = (ClassObject*) args[0];
+    ArrayObject* dimArray = (ArrayObject*) args[1];
+    ClassObject* arrayClass;
+    ArrayObject* newArray;
+    char* acDescriptor;
+    int numDim, i;
+    int* dimensions;
+
+    LOGV("createMultiArray: '%s' [%d]\n",
+        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*) dimArray->contents;
+    for (i = 0; i < numDim; i++) {
+        if (dimensions[i] < 0) {
+            dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+            RETURN_VOID();
+        }
+        LOGVV("DIM %d: %d\n", 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'\n", elementClass->descriptor);
+    if (dvmIsPrimitiveClass(elementClass)) {
+        assert(elementClass->primitiveType >= 0);
+        acDescriptor[numDim] = kPrimLetter[elementClass->primitiveType];
+        acDescriptor[numDim+1] = '\0';
+    } else {
+        strcpy(acDescriptor+numDim, elementClass->descriptor);
+    }
+    LOGVV("#### array name = '%s'\n", acDescriptor);
+
+    /*
+     * Find/generate the array class.
+     */
+    arrayClass = dvmFindArrayClass(acDescriptor, elementClass->classLoader);
+    if (arrayClass == NULL) {
+        LOGW("Unable to find or generate array class '%s'\n", 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.c b/vm/native/java_lang_reflect_Constructor.c
new file mode 100644
index 0000000..76f69a9
--- /dev/null
+++ b/vm/native/java_lang_reflect_Constructor.c
@@ -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.
+ */
+
+/*
+ * java.lang.reflect.Constructor
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public int getConstructorModifiers(Class declaringClass, int slot)
+ */
+static void Dalvik_java_lang_reflect_Constructor_getConstructorModifiers(
+    const u4* args, JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    RETURN_INT(dvmFixMethodFlags(meth->accessFlags));
+}
+
+/*
+ * 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)) {
+        dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;",
+            declaringClass->descriptor);
+        RETURN_VOID();
+    }
+
+    /* initialize the class if it hasn't been already */
+    if (!dvmIsClassInitialized(declaringClass)) {
+        if (!dvmInitClass(declaringClass)) {
+            LOGW("Class init failed in Constructor.constructNative (%s)\n",
+                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);
+}
+
+/*
+ * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this constructor.
+ */
+static void Dalvik_java_lang_reflect_Constructor_getDeclaredAnnotations(
+    const u4* args, JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* annos = dvmGetMethodAnnotations(meth);
+    dvmReleaseTrackedAlloc((Object*)annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * public Annotation[][] getParameterAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this constructor's parameters.
+ */
+static void Dalvik_java_lang_reflect_Constructor_getParameterAnnotations(
+    const u4* args, JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* annos = dvmGetParameterAnnotations(meth);
+    dvmReleaseTrackedAlloc((Object*)annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Constructor_getSignatureAnnotation(
+    const u4* args, JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    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_Constructor[] = {
+    { "constructNative",    "([Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;IZ)Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Constructor_constructNative },
+    { "getConstructorModifiers", "(Ljava/lang/Class;I)I",
+        Dalvik_java_lang_reflect_Constructor_getConstructorModifiers },
+    { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Constructor_getDeclaredAnnotations },
+    { "getParameterAnnotations", "(Ljava/lang/Class;I)[[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Constructor_getParameterAnnotations },
+    { "getSignatureAnnotation",  "(Ljava/lang/Class;I)[Ljava/lang/Object;",
+        Dalvik_java_lang_reflect_Constructor_getSignatureAnnotation },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Field.c b/vm/native/java_lang_reflect_Field.c
new file mode 100644
index 0000000..cb9f2bf
--- /dev/null
+++ b/vm/native/java_lang_reflect_Field.c
@@ -0,0 +1,458 @@
+/*
+ * 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"
+
+
+/*
+ * Get the address of a field from an object.  This can be used with "get"
+ * or "set".
+ *
+ * "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 previals 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 othe 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 JValue* getFieldDataAddr(Object* obj, ClassObject* declaringClass,
+    int slot, bool isSetOperation, bool noAccessCheck)
+{
+    Field* field;
+    JValue* result;
+
+    field = dvmSlotToField(declaringClass, slot);
+    assert(field != NULL);
+
+    /* verify access */
+    if (!noAccessCheck) {
+        if (isSetOperation && dvmIsFinalField(field)) {
+            dvmThrowException("Ljava/lang/IllegalAccessException;",
+                "field is marked 'final'");
+            return NULL;
+        }
+
+        ClassObject* callerClass =
+            dvmGetCaller2Class(dvmThreadSelf()->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)) {
+            dvmThrowException("Ljava/lang/IllegalAccessException;",
+                "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) {
+                dvmThrowException("Ljava/lang/IllegalAccessException;",
+                    "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;
+            }
+        }
+
+        result = dvmStaticFieldPtr((StaticField*) field);
+    } 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()));
+            if (obj != NULL) {
+                LOGD("Wrong type of object for field lookup: %s %s\n",
+                    obj->clazz->descriptor, declaringClass->descriptor);
+            }
+            return NULL;
+        }
+        result = dvmFieldPtr(obj, ((InstField*) field)->byteOffset);
+    }
+
+    return result;
+}
+
+/*
+ * 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);
+    JValue value;
+    const JValue* fieldPtr;
+    DataObject* result;
+
+    //dvmDumpClass(obj->clazz, kDumpClassFullDetail);
+
+    /* get a pointer to the field's data; performs access checks */
+    fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck);
+    if (fieldPtr == NULL)
+        RETURN_VOID();
+
+    /* copy 4 or 8 bytes out */
+    if (fieldType->primitiveType == PRIM_LONG ||
+        fieldType->primitiveType == PRIM_DOUBLE)
+    {
+        value.j = fieldPtr->j;
+    } else {
+        value.i = fieldPtr->i;
+    }
+
+    result = dvmWrapPrimitive(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];
+    JValue* fieldPtr;
+    JValue value;
+
+    /* unwrap primitive, or verify object type */
+    if (!dvmUnwrapPrimitive(valueObj, fieldType, &value)) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "invalid value for field");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the field's data; performs access checks */
+    fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck);
+    if (fieldPtr == NULL)
+        RETURN_VOID();
+
+    /* store 4 or 8 bytes */
+    if (fieldType->primitiveType == PRIM_LONG ||
+        fieldType->primitiveType == PRIM_DOUBLE)
+    {
+        fieldPtr->j = value.j;
+    } else if (fieldType->primitiveType == PRIM_NOT) {
+        if (slot < 0) {
+            StaticField *sfield;
+            sfield = (StaticField *)dvmSlotToField(declaringClass, slot);
+            assert(fieldPtr == &sfield->value);
+            dvmSetStaticFieldObject(sfield, value.l);
+        } else {
+            int offset = declaringClass->ifields[slot].byteOffset;
+            assert(fieldPtr == (JValue *)BYTE_OFFSET(obj, offset));
+            dvmSetFieldObject(obj, offset, value.l);
+        }
+    } else {
+        fieldPtr->i = value.i;
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * Convert a reflection primitive type ordinal (inherited from the previous
+ * VM's reflection classes) to our value.
+ */
+static PrimitiveType convPrimType(int typeNum)
+{
+    static const PrimitiveType conv[PRIM_MAX] = {
+        PRIM_NOT, PRIM_BOOLEAN, PRIM_BYTE, PRIM_CHAR, PRIM_SHORT,
+        PRIM_INT, PRIM_FLOAT, PRIM_LONG, PRIM_DOUBLE
+    };
+    if (typeNum <= 0 || typeNum > 8)
+        return PRIM_NOT;
+    return conv[typeNum];
+}
+
+/*
+ * Primitive field getters, e.g.:
+ * private double getIField(Object o, Class declaringClass,
+ *     Class type, int slot, boolean noAccessCheck, int type_no)
+ *
+ * The "type_no" is defined by the java.lang.reflect.Field class.
+ */
+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);
+    int typeNum = args[6];
+    PrimitiveType targetType = convPrimType(typeNum);
+    const JValue* fieldPtr;
+    JValue value;
+
+    if (!dvmIsPrimitiveClass(fieldType)) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "not a primitive field");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the field's data; performs access checks */
+    fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck);
+    if (fieldPtr == NULL)
+        RETURN_VOID();
+
+    /* copy 4 or 8 bytes out */
+    if (fieldType->primitiveType == PRIM_LONG ||
+        fieldType->primitiveType == PRIM_DOUBLE)
+    {
+        value.j = fieldPtr->j;
+    } else {
+        value.i = fieldPtr->i;
+    }
+
+    /* retrieve value, performing a widening conversion if necessary */
+    if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType,
+        &(value.i), &(pResult->i)) < 0)
+    {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "invalid primitive conversion");
+        RETURN_VOID();
+    }
+}
+
+/*
+ * Primitive field setters, e.g.:
+ * private void setIField(Object o, Class declaringClass,
+ *     Class type, int slot, boolean noAccessCheck, int type_no, int value)
+ *
+ * The "type_no" is defined by the java.lang.reflect.Field class.
+ */
+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);
+    int typeNum = args[6];
+    const s4* valuePtr = (s4*) &args[7];
+    PrimitiveType srcType = convPrimType(typeNum);
+    JValue* fieldPtr;
+    JValue value;
+
+    if (!dvmIsPrimitiveClass(fieldType)) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "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)
+    {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "invalid primitive conversion");
+        RETURN_VOID();
+    }
+
+    /* get a pointer to the field's data; performs access checks */
+    fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck);
+    if (fieldPtr == NULL)
+        RETURN_VOID();
+
+    /* store 4 or 8 bytes */
+    if (fieldType->primitiveType == PRIM_LONG ||
+        fieldType->primitiveType == PRIM_DOUBLE)
+    {
+        fieldPtr->j = value.j;
+    } else {
+        fieldPtr->i = value.i;
+    }
+
+    RETURN_VOID();
+}
+
+/*
+ * public 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)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Field* field;
+
+    field = dvmSlotToField(declaringClass, slot);
+    assert(field != NULL);
+
+    ArrayObject* annos = dvmGetFieldAnnotations(field);
+    dvmReleaseTrackedAlloc((Object*) annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * 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;IZI)B",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getCField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)C",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getDField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)D",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getFField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)F",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getIField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)I",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getJField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)J",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getSField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)S",
+        Dalvik_java_lang_reflect_Field_getPrimitiveField },
+    { "getZField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)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;IZIB)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setCField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIC)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setDField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZID)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setFField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIF)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setIField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZII)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setJField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIJ)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setSField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIS)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "setZField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIZ)V",
+        Dalvik_java_lang_reflect_Field_setPrimitiveField },
+    { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+        Dalvik_java_lang_reflect_Field_getDeclaredAnnotations },
+    { "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.c b/vm/native/java_lang_reflect_Method.c
new file mode 100644
index 0000000..f73c8d0
--- /dev/null
+++ b/vm/native/java_lang_reflect_Method.c
@@ -0,0 +1,219 @@
+/*
+ * 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"
+
+
+/*
+ * private 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)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    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.
+     */
+    LOGD("Method.invoke() on bad class %s failed\n",
+        declaringClass->descriptor);
+    assert(dvmCheckException(dvmThreadSelf()));
+    RETURN_VOID();
+}
+
+/*
+ * public 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)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    Method* meth;
+
+    meth = dvmSlotToMethod(declaringClass, slot);
+    assert(meth != NULL);
+
+    ArrayObject* annos = dvmGetMethodAnnotations(meth);
+    dvmReleaseTrackedAlloc((Object*)annos, NULL);
+    RETURN_PTR(annos);
+}
+
+/*
+ * public 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)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    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);
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Method_getSignatureAnnotation(
+    const u4* args, JValue* pResult)
+{
+    // ignore thisPtr in args[0]
+    ClassObject* declaringClass = (ClassObject*) args[1];
+    int slot = args[2];
+    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 },
+    { "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.c b/vm/native/java_lang_reflect_Proxy.c
new file mode 100644
index 0000000..da1232c
--- /dev/null
+++ b/vm/native/java_lang_reflect_Proxy.c
@@ -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_security_AccessController.c b/vm/native/java_security_AccessController.c
new file mode 100644
index 0000000..378fb94
--- /dev/null
+++ b/vm/native/java_security_AccessController.c
@@ -0,0 +1,138 @@
+/*
+ * 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.security.AccessController
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static ProtectionDomain[] getStackDomains()
+ *
+ * Return an array of ProtectionDomain objects from the classes of the
+ * methods on the stack.  Ignore reflection frames.  Stop at the first
+ * privileged frame we see.
+ */
+static void Dalvik_java_security_AccessController_getStackDomains(
+    const u4* args, JValue* pResult)
+{
+    UNUSED_PARAMETER(args);
+
+    const Method** methods = NULL;
+    int length;
+
+    /*
+     * Get an array with the stack trace in it.
+     */
+    if (!dvmCreateStackTraceArray(dvmThreadSelf()->curFrame, &methods, &length))
+    {
+        LOGE("Failed to create stack trace array\n");
+        dvmThrowException("Ljava/lang/InternalError;", NULL);
+        RETURN_VOID();
+    }
+
+    //int i;
+    //LOGI("dvmCreateStackTraceArray results:\n");
+    //for (i = 0; i < length; i++)
+    //    LOGI(" %2d: %s.%s\n", i, methods[i]->clazz->name, methods[i]->name);
+
+    /*
+     * Generate a list of ProtectionDomain objects from the frames that
+     * we're interested in.  Skip the first two methods (this method, and
+     * the one that called us), and ignore reflection frames.  Stop on the
+     * frame *after* the first privileged frame we see as we walk up.
+     *
+     * We create a new array, probably over-allocated, and fill in the
+     * stuff we want.  We could also just run the list twice, but the
+     * costs of the per-frame tests could be more expensive than the
+     * second alloc.  (We could also allocate it on the stack using C99
+     * array creation, but it's not guaranteed to fit.)
+     *
+     * The array we return doesn't include null ProtectionDomain objects,
+     * so we skip those here.
+     */
+    Object** subSet = (Object**) malloc((length-2) * sizeof(Object*));
+    if (subSet == NULL) {
+        LOGE("Failed to allocate subSet (length=%d)\n", length);
+        free(methods);
+        dvmThrowException("Ljava/lang/InternalError;", NULL);
+        RETURN_VOID();
+    }
+    int idx, subIdx = 0;
+    for (idx = 2; idx < length; idx++) {
+        const Method* meth = methods[idx];
+        Object* pd;
+
+        if (dvmIsReflectionMethod(meth))
+            continue;
+
+        if (dvmIsPrivilegedMethod(meth)) {
+            /* find nearest non-reflection frame; note we skip priv frame */
+            //LOGI("GSD priv frame at %s.%s\n", meth->clazz->name, meth->name);
+            while (++idx < length && dvmIsReflectionMethod(methods[idx]))
+                ;
+            length = idx;       // stomp length to end loop
+            meth = methods[idx];
+        }
+
+        /* get the pd object from the method's class */
+        assert(gDvm.offJavaLangClass_pd != 0);
+        pd = dvmGetFieldObject((Object*) meth->clazz,
+                gDvm.offJavaLangClass_pd);
+        //LOGI("FOUND '%s' pd=%p\n", meth->clazz->name, pd);
+        if (pd != NULL)
+            subSet[subIdx++] = pd;
+    }
+
+    //LOGI("subSet:\n");
+    //for (i = 0; i < subIdx; i++)
+    //    LOGI("  %2d: %s\n", i, subSet[i]->clazz->name);
+
+    /*
+     * Create an array object to contain "subSet".
+     */
+    ClassObject* pdArrayClass = NULL;
+    ArrayObject* domains = NULL;
+    pdArrayClass = dvmFindArrayClass("[Ljava/security/ProtectionDomain;", NULL);
+    if (pdArrayClass == NULL) {
+        LOGW("Unable to find ProtectionDomain class for array\n");
+        goto bail;
+    }
+    domains = dvmAllocArray(pdArrayClass, subIdx, kObjectArrayRefWidth,
+                ALLOC_DEFAULT);
+    if (domains == NULL) {
+        LOGW("Unable to allocate pd array (%d elems)\n", subIdx);
+        goto bail;
+    }
+
+    /* copy the ProtectionDomain objects out */
+    memcpy(domains->contents, subSet, subIdx * sizeof(Object *));
+    dvmWriteBarrierArray(domains, 0, subIdx);
+
+bail:
+    free(subSet);
+    free(methods);
+    dvmReleaseTrackedAlloc((Object*) domains, NULL);
+    RETURN_PTR(domains);
+}
+
+const DalvikNativeMethod dvm_java_security_AccessController[] = {
+    { "getStackDomains",    "()[Ljava/security/ProtectionDomain;",
+        Dalvik_java_security_AccessController_getStackDomains },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_util_concurrent_atomic_AtomicLong.c b/vm/native/java_util_concurrent_atomic_AtomicLong.c
new file mode 100644
index 0000000..eb1d0de
--- /dev/null
+++ b/vm/native/java_util_concurrent_atomic_AtomicLong.c
@@ -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.c b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.c
new file mode 100644
index 0000000..ccc9467
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.c
@@ -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.c b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c
new file mode 100644
index 0000000..570d469
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c
@@ -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.c b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c
new file mode 100644
index 0000000..caa280b
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c
@@ -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);
+
+    //LOGI("ddmThreadNotification: %d\n", 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.c b/vm/native/sun_misc_Unsafe.c
new file mode 100644
index 0000000..9b9caba
--- /dev/null
+++ b/vm/native/sun_misc_Unsafe.c
@@ -0,0 +1,339 @@
+/*
+ * 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(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 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 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();
+}
+
+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 },
+    { "getLong", "(Ljava/lang/Object;J)J",
+      Dalvik_sun_misc_Unsafe_getLong },
+    { "putLong", "(Ljava/lang/Object;JJ)V",
+      Dalvik_sun_misc_Unsafe_putLong },
+    { "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 },
+    { NULL, NULL, NULL },
+};
diff --git a/vm/oo/AccessCheck.c b/vm/oo/AccessCheck.c
new file mode 100644
index 0000000..95b7f3e
--- /dev/null
+++ b/vm/oo/AccessCheck.c
@@ -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)
+{
+    //LOGI("CHECK ACCESS from '%s' to field '%s' (in %s) flags=0x%x\n",
+    //    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..105c9e1
--- /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
+#define _DALVIK_OO_ACCESSCHECK
+
+/*
+ * 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*/
diff --git a/vm/oo/Array.c b/vm/oo/Array.c
new file mode 100644
index 0000000..4166a85
--- /dev/null
+++ b/vm/oo/Array.c
@@ -0,0 +1,814 @@
+/*
+ * 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>
+
+#if WITH_HPROF && WITH_HPROF_STACK
+#include "hprof/Hprof.h"
+#endif
+
+static ClassObject* createArrayClass(const char* descriptor, Object* loader);
+static ClassObject* createPrimitiveClass(int idx);
+
+static const char gPrimLetter[] = PRIM_TYPE_TO_LETTER;
+
+/*
+ * 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.
+ */
+ArrayObject* dvmAllocArray(ClassObject* arrayClass, size_t length,
+    size_t elemWidth, int allocFlags)
+{
+    ArrayObject* newArray;
+    size_t size;
+
+    assert(arrayClass->descriptor[0] == '[');
+
+    if (length > 0x0fffffff) {
+        /* too large and (length * elemWidth) will overflow 32 bits */
+        LOGE("Rejecting allocation of %u-element array\n", length);
+        dvmThrowBadAllocException("array size too large");
+        return NULL;
+    }
+
+    size = offsetof(ArrayObject, contents);
+    size += length * elemWidth;
+
+    /* Note that we assume that the Array class does not
+     * override finalize().
+     */
+    newArray = dvmMalloc(size, allocFlags);
+    if (newArray != NULL) {
+        DVM_OBJECT_INIT(&newArray->obj, arrayClass);
+        newArray->length = length;
+        LOGVV("AllocArray: %s [%d] (%d)\n",
+            arrayClass->descriptor, (int) length, (int) size);
+#if WITH_HPROF && WITH_HPROF_STACK
+        hprofFillInStackTrace(&newArray->obj);
+#endif
+        dvmTrackAllocation(arrayClass, size);
+    }
+    /* the caller must call dvmReleaseTrackedAlloc */
+    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 dvmAllocArray(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 references to members of the specified class.
+ *
+ * "elemClassObj" is the element type, and may itself be an array class.  It
+ * may not be a primitive class.
+ *
+ * "allocFlags" determines whether the new object will be added to the
+ * "tracked alloc" table.
+ *
+ * This is less efficient than dvmAllocArray(), but occasionally convenient.
+ */
+ArrayObject* dvmAllocObjectArray(ClassObject* elemClassObj, size_t length,
+    int allocFlags)
+{
+    ClassObject* arrayClass;
+    ArrayObject* newArray = NULL;
+
+    LOGVV("dvmAllocObjectArray: '%s' len=%d\n",
+        elemClassObj->descriptor, (int)length);
+
+    arrayClass = dvmFindArrayClassForElement(elemClassObj);
+    if (arrayClass != NULL) {
+        newArray = dvmAllocArray(arrayClass, length, kObjectArrayRefWidth,
+            allocFlags);
+    }
+
+    /* the caller must call dvmReleaseTrackedAlloc */
+    return newArray;
+}
+
+/*
+ * Create a new array that holds primitive types.
+ *
+ * "type" is the primitive type letter, e.g. 'I' for int or 'J' for long.
+ * If the array class doesn't exist, it will be created.
+ */
+ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags)
+{
+    ArrayObject* newArray;
+    ClassObject** pTypeClass;
+    int width;
+
+    switch (type) {
+    case 'I':
+        pTypeClass = &gDvm.classArrayInt;
+        width = 4;
+        break;
+    case 'C':
+        pTypeClass = &gDvm.classArrayChar;
+        width = 2;
+        break;
+    case 'B':
+        pTypeClass = &gDvm.classArrayByte;
+        width = 1;
+        break;
+    case 'Z':
+        pTypeClass = &gDvm.classArrayBoolean;
+        width = 1; /* special-case this? */
+        break;
+    case 'F':
+        pTypeClass = &gDvm.classArrayFloat;
+        width = 4;
+        break;
+    case 'D':
+        pTypeClass = &gDvm.classArrayDouble;
+        width = 8;
+        break;
+    case 'S':
+        pTypeClass = &gDvm.classArrayShort;
+        width = 2;
+        break;
+    case 'J':
+        pTypeClass = &gDvm.classArrayLong;
+        width = 8;
+        break;
+    default:
+        LOGE("Unknown type '%c'\n", type);
+        assert(false);
+        return NULL;
+    }
+
+    if (*pTypeClass == NULL) {
+        char typeClassName[3] = "[x";
+
+        typeClassName[1] = type;
+
+        *pTypeClass = dvmFindArrayClass(typeClassName, NULL);
+        if (*pTypeClass == NULL) {
+            LOGE("ERROR: failed to generate array class for '%s'\n",
+                typeClassName);
+            return NULL;
+        }
+    }
+
+    newArray = dvmAllocArray(*pTypeClass, 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\n",
+        arrayClass->descriptor, curDim, *dimensions);
+
+    if (curDim == 0) {
+        if (*elemName == 'L' || *elemName == '[') {
+            LOGVV("  end: array class (obj) is '%s'\n",
+                arrayClass->descriptor);
+            newArray = dvmAllocArray(arrayClass, *dimensions,
+                        kObjectArrayRefWidth, ALLOC_DEFAULT);
+        } else {
+            LOGVV("  end: array class (prim) is '%s'\n",
+                arrayClass->descriptor);
+            newArray = dvmAllocPrimitiveArray(
+                    gPrimLetter[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 = dvmAllocArray(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] == '[');
+    //LOGV("dvmFindArrayClass: '%s' %p\n", descriptor, loader);
+
+    clazz = dvmLookupClass(descriptor, loader, false);
+    if (clazz == NULL) {
+        LOGV("Array class '%s' %p not found; creating\n", 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'\n", 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\n",
+            descriptor, loader, elementClass->classLoader);
+        newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
+        if (newClass != NULL) {
+            LOGV("--- we already have %s in %p, don't need in %p\n",
+                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_DEFAULT);
+    if (newClass == NULL)
+        return NULL;
+    DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptorAlloc = strdup(descriptor);
+    newClass->descriptor = newClass->descriptorAlloc;
+    dvmSetFieldObject((Object *)newClass,
+                      offsetof(ClassObject, super),
+                      (Object *)gDvm.classJavaLangObject);
+    newClass->vtableCount = gDvm.classJavaLangObject->vtableCount;
+    newClass->vtable = gDvm.classJavaLangObject->vtable;
+    newClass->primitiveType = PRIM_NOT;
+    dvmSetFieldObject((Object *)newClass,
+                      offsetof(ClassObject, elementClass),
+                      (Object *)elementClass);
+    dvmSetFieldObject((Object *)newClass,
+                      offsetof(ClassObject, classLoader),
+                      (Object *)elementClass->classLoader);
+    newClass->arrayDim = arrayDim;
+    newClass->status = CLASS_INITIALIZED;
+#if WITH_HPROF && WITH_HPROF_STACK
+    hprofFillInStackTrace(newClass);
+#endif
+
+    /* 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) {
+        LOGE("Unable to create array class '%s': missing interfaces\n",
+            descriptor);
+        dvmFreeClassInnards(newClass);
+        dvmThrowException("Ljava/lang/InternalError;", "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 "final" and remove
+     * "interface".
+     *
+     * Don't inherit any non-standard flags (e.g., CLASS_FINALIZABLE)
+     * from elementClass.  We assume that the array class does not
+     * override finalize().
+     */
+    newClass->accessFlags = ((newClass->elementClass->accessFlags &
+                             ~ACC_INTERFACE) | ACC_FINAL) & JAVA_FLAGS_MASK;
+
+    /* Set the flags we determined above.
+     * This must happen after accessFlags is set.
+     */
+    SET_CLASS_FLAG(newClass, 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.
+         */
+        LOGI("WOW: somebody generated %s simultaneously\n",
+            newClass->descriptor);
+
+        /* 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);
+
+    LOGV("Created array class '%s' %p (access=0x%04x.%04x)\n",
+        descriptor, newClass->classLoader,
+        newClass->accessFlags >> 16,
+        newClass->accessFlags & JAVA_FLAGS_MASK);
+
+    return newClass;
+}
+
+/*
+ * Get a class we generated for the primitive types.
+ *
+ * These correspond to e.g. Integer.TYPE, and are used as the element
+ * class in arrays of primitives.
+ *
+ * "type" should be 'I', 'J', 'Z', etc.
+ *
+ * Returns NULL if the type doesn't correspond to a known primitive type.
+ */
+ClassObject* dvmFindPrimitiveClass(char type)
+{
+    int idx;
+
+    switch (type) {
+    case 'Z':
+        idx = PRIM_BOOLEAN;
+        break;
+    case 'C':
+        idx = PRIM_CHAR;
+        break;
+    case 'F':
+        idx = PRIM_FLOAT;
+        break;
+    case 'D':
+        idx = PRIM_DOUBLE;
+        break;
+    case 'B':
+        idx = PRIM_BYTE;
+        break;
+    case 'S':
+        idx = PRIM_SHORT;
+        break;
+    case 'I':
+        idx = PRIM_INT;
+        break;
+    case 'J':
+        idx = PRIM_LONG;
+        break;
+    case 'V':
+        idx = PRIM_VOID;
+        break;
+    default:
+        LOGW("Unknown primitive type '%c'\n", type);
+        return NULL;
+    }
+
+    /*
+     * Create the primitive class if it hasn't already been, and add it
+     * to the table.
+     */
+    if (gDvm.primitiveClass[idx] == NULL) {
+        ClassObject* primClass = createPrimitiveClass(idx);
+        dvmReleaseTrackedAlloc((Object*) primClass, NULL);
+
+        if (android_atomic_release_cas(0, (int) primClass,
+                (int*) &gDvm.primitiveClass[idx]) != 0)
+        {
+            /*
+             * Looks like somebody beat us to it.  Free up the one we
+             * just created and use the other one.
+             */
+            dvmFreeClassInnards(primClass);
+        }
+    }
+
+    return gDvm.primitiveClass[idx];
+}
+
+/*
+ * Synthesize a primitive class.
+ *
+ * Just creates the class and returns it (does not add it to the class list).
+ */
+static ClassObject* createPrimitiveClass(int idx)
+{
+    ClassObject* newClass;
+    static const char* kClassDescriptors[PRIM_MAX] = {
+        "Z", "C", "F", "D", "B", "S", "I", "J", "V"
+    };
+
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(idx >= 0 && idx < PRIM_MAX);
+
+    /*
+     * Fill out a few fields in the ClassObject.
+     *
+     * Note that primitive classes do not sub-class java/lang/Object.  This
+     * matters for "instanceof" checks.  Also, we assume that the primitive
+     * class does not override finalize().
+     */
+    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT);
+    if (newClass == NULL)
+        return NULL;
+    DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->accessFlags = ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT;
+    newClass->primitiveType = idx;
+    newClass->descriptorAlloc = NULL;
+    newClass->descriptor = kClassDescriptors[idx];
+    //newClass->super = gDvm.classJavaLangObject;
+    newClass->status = CLASS_INITIALIZED;
+#if WITH_HPROF && WITH_HPROF_STACK
+    hprofFillInStackTrace(newClass);
+#endif
+
+    /* don't need to set newClass->objectSize */
+
+    LOGVV("Created primitive class '%s'\n", kClassDescriptors[idx]);
+
+    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**)srcArray->contents;
+    u4 length, count;
+
+    assert(srcArray->length == dstArray->length);
+    assert(dstArray->obj.clazz->elementClass == dstElemClass ||
+        (dstArray->obj.clazz->elementClass == dstElemClass->elementClass &&
+         dstArray->obj.clazz->arrayDim == dstElemClass->arrayDim+1));
+
+    length = dstArray->length;
+    for (count = 0; count < length; count++) {
+        if (!dvmInstanceof(src[count]->clazz, dstElemClass)) {
+            LOGW("dvmCopyObjectArray: can't store %s in %s\n",
+                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**)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 (!dvmUnwrapPrimitive(*src, dstElemClass, &result)) {
+            LOGW("dvmCopyObjectArray: can't store %s in %s\n",
+                (*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 = dst;
+                *tmp++ = result.b;
+                dst = tmp;
+            }
+            break;
+        case PRIM_CHAR:
+        case PRIM_SHORT:
+            {
+                u2* tmp = dst;
+                *tmp++ = result.s;
+                dst = tmp;
+            }
+            break;
+        case PRIM_FLOAT:
+        case PRIM_INT:
+            {
+                u4* tmp = dst;
+                *tmp++ = result.i;
+                dst = tmp;
+            }
+            break;
+        case PRIM_DOUBLE:
+        case PRIM_LONG:
+            {
+                u8* tmp = 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 */
+        }
+    }
+    LOGE("class %p has an unhandled descriptor '%s'", arrayClass, descriptor);
+    dvmDumpThread(dvmThreadSelf(), false);
+    dvmAbort();
+    return 0;  /* Quiet the compiler. */
+}
+
+size_t dvmArrayObjectSize(const ArrayObject *array)
+{
+    size_t size;
+
+    assert(array != NULL);
+    size = offsetof(ArrayObject, contents);
+    size += array->length * dvmArrayClassElementWidth(array->obj.clazz);
+    return size;
+}
+
+/*
+ * Add all primitive classes to the root set of objects.
+TODO: do these belong to the root class loader?
+ */
+void dvmGcScanPrimitiveClasses()
+{
+    int i;
+
+    for (i = 0; i < PRIM_MAX; i++) {
+        dvmMarkObject((Object *)gDvm.primitiveClass[i]);    // may be NULL
+    }
+}
diff --git a/vm/oo/Array.h b/vm/oo/Array.h
new file mode 100644
index 0000000..9cd7996
--- /dev/null
+++ b/vm/oo/Array.h
@@ -0,0 +1,176 @@
+/*
+ * 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
+#define _DALVIK_OO_ARRAY
+
+/* width of an object reference, for arrays of objects */
+#define kObjectArrayRefWidth    sizeof(Object*)
+
+/*
+ * 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);
+
+/*
+ * Allocate space for a new array object.
+ *
+ * "allocFlags" determines whether the new object will be added to the
+ * "tracked alloc" table.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocArray(ClassObject* arrayClass, size_t length,
+    size_t elemWidth, int allocFlags);
+
+/*
+ * 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.
+ */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
+    size_t length, int allocFlags);
+
+/*
+ * Create a new array that holds references to members of the specified class.
+ *
+ * "elemClassObj" is the element type, and may itself be an array class.  It
+ * may not be a primitive class.
+ *
+ * "allocFlags" determines whether the new object will be added to the
+ * "tracked alloc" table.
+ *
+ * This is less efficient than dvmAllocArray(), but occasionally convenient.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocObjectArray(ClassObject* elemClassObj, 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);
+
+/*
+ * Find the synthesized object for the primitive class, generating it
+ * if this is the first reference.
+ */
+ClassObject* dvmFindPrimitiveClass(char type);
+
+/*
+ * 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->obj.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*/
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
new file mode 100644
index 0000000..8f55815
--- /dev/null
+++ b/vm/oo/Class.c
@@ -0,0 +1,4967 @@
+/*
+ * 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 "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();
+
+    LOG(LOG_INFO, "PRELOAD", "%c%d:%d:%d:%s:%d:%s:%lld\n", 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 try = 1;
+
+    switch (try) {
+    case 0:
+        fiddle = dvmLinearAlloc(NULL, 3200-28);
+        dvmLinearReadOnly(NULL, fiddle);
+        break;
+    case 1:
+        fiddle = dvmLinearAlloc(NULL, 3200-24);
+        dvmLinearReadOnly(NULL, fiddle);
+        break;
+    case 2:
+        fiddle = dvmLinearAlloc(NULL, 3200-20);
+        dvmLinearReadOnly(NULL, fiddle);
+        break;
+    case 3:
+        fiddle = dvmLinearAlloc(NULL, 3200-16);
+        dvmLinearReadOnly(NULL, fiddle);
+        break;
+    case 4:
+        fiddle = dvmLinearAlloc(NULL, 3200-12);
+        dvmLinearReadOnly(NULL, fiddle);
+        break;
+    }
+    fiddle = dvmLinearAlloc(NULL, 896);
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = dvmLinearAlloc(NULL, 20);      // watch addr of this alloc
+    dvmLinearReadOnly(NULL, fiddle);
+
+    fiddle = dvmLinearAlloc(NULL, 1);
+    fiddle[0] = 'q';
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = dvmLinearAlloc(NULL, 4096);
+    fiddle[0] = 'x';
+    fiddle[4095] = 'y';
+    dvmLinearReadOnly(NULL, fiddle);
+    dvmLinearFree(NULL, fiddle);
+    fiddle = dvmLinearAlloc(NULL, 0);
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = dvmLinearRealloc(NULL, fiddle, 12);
+    fiddle[11] = 'z';
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = dvmLinearRealloc(NULL, fiddle, 5);
+    dvmLinearReadOnly(NULL, fiddle);
+    fiddle = dvmLinearAlloc(NULL, 17001);
+    fiddle[0] = 'x';
+    fiddle[17000] = 'y';
+    dvmLinearReadOnly(NULL, fiddle);
+
+    char* str = dvmLinearStrdup(NULL, "This is a test!");
+    LOGI("GOT: '%s'\n", str);
+
+    /* try to check the bounds; allocator may round allocation size up */
+    fiddle = dvmLinearAlloc(NULL, 12);
+    LOGI("Should be 1: %d\n", dvmLinearAllocContains(fiddle, 12));
+    LOGI("Should be 0: %d\n", dvmLinearAllocContains(fiddle, 13));
+    LOGI("Should be 0: %d\n", dvmLinearAllocContains(fiddle - 128*1024, 1));
+
+    dvmLinearAllocDump(NULL);
+    dvmLinearFree(NULL, str);
+}
+
+static size_t classObjectSize(size_t sfieldCount)
+{
+    size_t size;
+
+    size = offsetof(ClassObject, sfields);
+    size += sizeof(StaticField) * sfieldCount;
+    return size;
+}
+
+size_t dvmClassObjectSize(const ClassObject *clazz)
+{
+    assert(clazz != NULL);
+    return classObjectSize(clazz->sfieldCount);
+}
+
+/*
+ * Initialize the bootstrap class loader.
+ *
+ * Call this after the bootclasspath string has been finalized.
+ */
+bool dvmClassStartup(void)
+{
+    /* make this a requirement -- don't currently support dirs in path */
+    if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
+        LOGE("ERROR: must specify non-'.' bootclasspath\n");
+        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 =
+        calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
+
+    gDvm.classJavaLangClass = (ClassObject*) dvmMalloc(
+        classObjectSize(CLASS_SFIELD_SLOTS), ALLOC_DEFAULT);
+    DVM_OBJECT_INIT(&gDvm.classJavaLangClass->obj, gDvm.classJavaLangClass);
+    gDvm.classJavaLangClass->descriptor = "Ljava/lang/Class;";
+    /*
+     * 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(void)
+{
+    int i;
+
+    /* discard all system-loaded classes */
+    dvmHashTableFree(gDvm.loadedClasses);
+    gDvm.loadedClasses = NULL;
+
+    /* discard primitive classes created for arrays */
+    for (i = 0; i < PRIM_MAX; i++)
+        dvmFreeClassInnards(gDvm.primitiveClass[i]);
+
+    /* 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 kCpeDir:       kindStr = "dir";    break;
+        case kCpeJar:       kindStr = "jar";    break;
+        case kCpeDex:       kindStr = "dex";    break;
+        default:            kindStr = "???";    break;
+        }
+
+        LOGI("  %2d: type=%s %s %p\n", 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(void)
+{
+    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:
+            /* e.g. kCpeDir */
+            assert(cpe->ptr == NULL);
+            break;
+        }
+
+        free(cpe->fileName);
+        cpe++;
+    }
+
+    free(cpeStart);
+}
+
+/*
+ * 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)
+{
+    JarFile* pJarFile = NULL;
+    RawDexFile* pRawDexFile = NULL;
+    struct stat sb;
+    int cc;
+
+    cc = stat(cpe->fileName, &sb);
+    if (cc < 0) {
+        LOGD("Unable to stat classpath element '%s'\n", cpe->fileName);
+        return false;
+    }
+    if (S_ISDIR(sb.st_mode)) {
+        /*
+         * The directory will usually have .class files in subdirectories,
+         * which may be a few levels down.  Doing a recursive scan and
+         * caching the results would help us avoid hitting the filesystem
+         * on misses.  Whether or not this is of measureable benefit
+         * depends on a number of factors, but most likely it is not
+         * worth the effort (especially since most of our stuff will be
+         * in DEX or JAR).
+         */
+        cpe->kind = kCpeDir;
+        assert(cpe->ptr == NULL);
+        return true;
+    }
+
+    if (dvmJarFileOpen(cpe->fileName, NULL, &pJarFile, isBootstrap) == 0) {
+        cpe->kind = kCpeJar;
+        cpe->ptr = pJarFile;
+        return true;
+    }
+
+    // TODO: do we still want to support "raw" DEX files in the classpath?
+    if (dvmRawDexFileOpen(cpe->fileName, NULL, &pRawDexFile, isBootstrap) == 0)
+    {
+        cpe->kind = kCpeDex;
+        cpe->ptr = pRawDexFile;
+        return true;
+    }
+
+    LOGD("Unable to process classpath element '%s'\n", 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) {
+                LOGE("Non-absolute bootclasspath entry '%s'\n", 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) {
+        LOGE("No valid entries found in bootclasspath '%s'\n", pathStr);
+        free(cpe);
+        cpe = NULL;
+        goto bail;
+    }
+
+    LOGVV("  (filled %d of %d slots)\n", 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...\n",
+        descriptor);
+
+    while (cpe->kind != kCpeLastEntry) {
+        //LOGV("+++  checking '%s' (%d)\n", cpe->fileName, cpe->kind);
+
+        switch (cpe->kind) {
+        case kCpeDir:
+            LOGW("Directory entries ('%s') not supported in bootclasspath\n",
+                cpe->fileName);
+            break;
+        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:
+            LOGE("Unknown kind %d\n", 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(void)
+{
+    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;
+
+    LOGV("+++ searching for resource '%s' in %d(%s)\n",
+        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 kCpeDir:
+        sprintf(urlBuf, "file://%s/%s", cpe->fileName, name);
+        if (access(urlBuf+7, F_OK) != 0)
+            goto bail;
+        break;
+    case kCpeJar:
+        {
+            JarFile* pJarFile = (JarFile*) cpe->ptr;
+            if (dexZipFindEntry(&pJarFile->archive, name) == NULL)
+                goto bail;
+            sprintf(urlBuf, "jar:file://%s!/%s", cpe->fileName, name);
+        }
+        break;
+    case kCpeDex:
+        LOGV("No resources in DEX files\n");
+        goto bail;
+    default:
+        assert(false);
+        goto bail;
+    }
+
+    LOGV("+++ using URL='%s'\n", urlBuf);
+    urlObj = dvmCreateStringFromCstr(urlBuf);
+
+bail:
+    return urlObj;
+}
+
+
+/*
+ * ===========================================================================
+ *      Class list management
+ * ===========================================================================
+ */
+
+/* search for these criteria in the Class hash table */
+typedef struct ClassMatchCriteria {
+    const char* descriptor;
+    Object*     loader;
+} ClassMatchCriteria;
+
+#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) {
+            //LOGI("+++ found initiating match %p in %s\n",
+            //    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\n", 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)) {
+        //    LOGW("WOW: simultaneous add of initiating class loader\n");
+        //    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;
+
+            //LOGI("Expanded init list to %d (%s)\n",
+            //    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)
+    //    LOGI("+++ %s %p matches existing %s %p\n",
+    //        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\n",
+        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 != NULL && !unprepOkay && !dvmIsClassLinked(found)) {
+        LOGV("Ignoring not-yet-ready %s, using slow path\n",
+            ((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);
+
+    LOGV("+++ dvmAddClassToHash '%s' %p (isnew=%d) --> %p\n",
+        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(void)
+{
+    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)
+{
+    LOGV("+++ removeClassFromHash '%s'\n", clazz->descriptor);
+
+    u4 hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+    dvmHashTableLock(gDvm.loadedClasses);
+    if (!dvmHashTableRemove(gDvm.loadedClasses, hash, clazz))
+        LOGW("Hash table remove failed on class '%s'\n", 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\n", 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)
+{
+    //LOGI("##### findClassFromLoaderNoInit (%s,%p)\n",
+    //        descriptor, loader);
+
+    Thread* self = dvmThreadSelf();
+    ClassObject* clazz;
+
+    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.
+     */
+    clazz = dvmLookupClass(descriptor, loader, false);
+    if (clazz != NULL) {
+        LOGVV("Already loaded: %s %p\n", descriptor, loader);
+        return clazz;
+    } else {
+        LOGVV("Not already loaded: %s %p\n", descriptor, loader);
+    }
+
+    char* dotName = NULL;
+    StringObject* nameObj = NULL;
+    Object* excep;
+    Method* loadClass;
+
+    /* convert "Landroid/debug/Stuff;" to "android.debug.Stuff" */
+    dotName = dvmDescriptorToDot(descriptor);
+    if (dotName == NULL) {
+        dvmThrowException("Ljava/lang/OutOfMemoryError;", NULL);
+        goto bail;
+    }
+    nameObj = dvmCreateStringFromCstr(dotName);
+    if (nameObj == NULL) {
+        assert(dvmCheckException(self));
+        goto bail;
+    }
+
+    // TODO: cache the vtable offset
+    loadClass = dvmFindVirtualMethodHierByDescriptor(loader->clazz, "loadClass",
+                 "(Ljava/lang/String;)Ljava/lang/Class;");
+    if (loadClass == NULL) {
+        LOGW("Couldn't find loadClass in ClassLoader\n");
+        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)\n", dotName, loader);
+    JValue result;
+    dvmCallMethod(self, loadClass, loader, &result, nameObj);
+    clazz = (ClassObject*) result.l;
+
+    dvmMethodTraceClassPrepEnd();
+
+    excep = dvmGetException(self);
+    if (excep != NULL) {
+#if DVM_SHOW_EXCEPTION >= 2
+        LOGD("NOTE: loadClass '%s' %p threw exception %s\n",
+            dotName, loader, excep->clazz->descriptor);
+#endif
+        dvmAddTrackedAlloc(excep, self);
+        dvmClearException(self);
+        dvmThrowChainedExceptionWithClassMessage(
+            "Ljava/lang/NoClassDefFoundError;", descriptor, excep);
+        dvmReleaseTrackedAlloc(excep, self);
+        clazz = NULL;
+        goto bail;
+    } else if (clazz == NULL) {
+        LOGW("ClassLoader returned NULL w/o exception pending\n");
+        dvmThrowException("Ljava/lang/NullPointerException;",
+            "ClassLoader returned null");
+        goto bail;
+    }
+
+    dvmAddInitiatingLoader(clazz, loader);
+
+    LOGVV("--- Successfully loaded %s %p (thisldr=%p clazz=%p)\n",
+        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)\n", 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)) {
+        LOGE("Class lookup %s attempted while exception %s pending\n",
+            descriptor, dvmGetException(self)->clazz->descriptor);
+        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) */
+                dvmThrowExceptionWithClassMessage(
+                    "Ljava/lang/NoClassDefFoundError;", 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.
+         */
+        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.)
+             */
+            //LOGW("WOW: somebody loaded %s simultaneously\n", 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
+            LOG(LOG_INFO, "DVMLINK FAILED FOR CLASS ", "%s in %s\n",
+                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 */
+                LOGV("Link of class '%s' failed\n", descriptor);
+            } else {
+                LOGW("Link of class '%s' failed\n", 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)
+            {
+                LOGW("Recursive link on class %s\n", clazz->descriptor);
+                dvmUnlockObject(self, (Object*) clazz);
+                dvmThrowExceptionWithClassMessage(
+                    "Ljava/lang/ClassCircularityError;", clazz->descriptor);
+                clazz = NULL;
+                goto bail;
+            }
+            //LOGI("WAITING  for '%s' (owner=%d)\n",
+            //    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->obj.clazz == gDvm.classJavaLangClass);
+    if (clazz != gDvm.classJavaLangObject) {
+        if (clazz->super == NULL) {
+            LOGE("Non-Object has no superclass (gDvm.classJavaLangObject=%p)\n",
+                gDvm.classJavaLangObject);
+            dvmAbort();
+        }
+    }
+    if (!dvmIsInterfaceClass(clazz)) {
+        //LOGI("class=%s vtableCount=%d, virtualMeth=%d\n",
+        //    clazz->descriptor, clazz->vtableCount,
+        //    clazz->virtualMethodCount);
+        assert(clazz->vtableCount >= clazz->virtualMethodCount);
+    }
+
+    /*
+     * Normally class objects are initialized before we instantiate them,
+     * but we can't do that with java.lang.Class (chicken, meet egg).  We
+     * do it explicitly here.
+     *
+     * The verifier could call here to find Class while verifying Class,
+     * so we need to check for CLASS_VERIFYING as well as !initialized.
+     */
+    if (clazz == gDvm.classJavaLangClass && !dvmIsClassInitialized(clazz) &&
+        !(clazz->status == CLASS_VERIFYING))
+    {
+        LOGV("+++ explicitly initializing %s\n", clazz->descriptor);
+        dvmInitClass(clazz);
+    }
+
+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.
+     */
+    if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {
+        LOGW("Invalid file flags in class %s: %04x\n",
+            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_DEFAULT);
+    }
+    if (newClass == NULL)
+        return NULL;
+
+    DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptor = descriptor;
+    assert(newClass->descriptorAlloc == NULL);
+    newClass->accessFlags = pClassDef->accessFlags;
+    dvmSetFieldObject((Object *)newClass,
+                      offsetof(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)
+        {
+            LOGE("ERROR: in %s, direct=%d virtual=%d, maps have %d\n",
+                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) {
+        LOGV("CLASS: loading '%s'...\n",
+            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)) {
+        LOGI("[Loaded %s from DEX %p (cl=%p)]\n",
+            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->obj.clazz == gDvm.classJavaLangClass);
+
+    /* 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) {
+        LOGE("GLITCH: only expected abstract methods here\n");
+        LOGE("        cloning %s.%s\n", 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) {
+        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);
+    LOGD("Making a copy of %s.%s code (%d bytes)\n",
+        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);
+    LOGV("+++ marking %p read-only\n", 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->field.clazz = clazz;
+    sfield->field.name = dexStringById(pDexFile, pFieldId->nameIdx);
+    sfield->field.signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    sfield->field.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
+
+#ifdef PROFILE_FIELD_ACCESS
+    sfield->field.gets = sfield->field.puts = 0;
+#endif
+}
+
+/*
+ * 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->field.clazz = clazz;
+    ifield->field.name = dexStringById(pDexFile, pFieldId->nameIdx);
+    ifield->field.signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+    ifield->field.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
+
+#ifdef PROFILE_FIELD_ACCESS
+    ifield->field.gets = ifield->field.puts = 0;
+#endif
+}
+
+/*
+ * Cache java.lang.ref.Reference fields and methods.
+ */
+static bool precacheReferenceOffsets(ClassObject* clazz)
+{
+    Method *meth;
+    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->field.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) {
+        LOGE("Unable to reorder 'referent' in %s\n", clazz->descriptor);
+        return false;
+    }
+
+    /* Cache pretty much everything about Reference so that
+     * we don't need to call interpreted code when clearing/enqueueing
+     * references.  This is fragile, so we'll be paranoid.
+     */
+    gDvm.classJavaLangRefReference = clazz;
+
+    gDvm.offJavaLangRefReference_referent =
+        dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+                "referent", "Ljava/lang/Object;");
+    assert(gDvm.offJavaLangRefReference_referent >= 0);
+
+    gDvm.offJavaLangRefReference_queue =
+        dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+                "queue", "Ljava/lang/ref/ReferenceQueue;");
+    assert(gDvm.offJavaLangRefReference_queue >= 0);
+
+    gDvm.offJavaLangRefReference_queueNext =
+        dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+                "queueNext", "Ljava/lang/ref/Reference;");
+    assert(gDvm.offJavaLangRefReference_queueNext >= 0);
+
+    gDvm.offJavaLangRefReference_pendingNext =
+        dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+                "pendingNext", "Ljava/lang/ref/Reference;");
+    assert(gDvm.offJavaLangRefReference_pendingNext >= 0);
+
+    /* enqueueInternal() is private and thus a direct method. */
+    meth = dvmFindDirectMethodByDescriptor(clazz, "enqueueInternal", "()Z");
+    assert(meth != NULL);
+    gDvm.methJavaLangRefReference_enqueueInternal = meth;
+
+    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)
+        LOGV("CLASS: linking '%s'...\n", clazz->descriptor);
+
+    assert(gDvm.classJavaLangClass != NULL);
+    assert(clazz->obj.clazz == gDvm.classJavaLangClass);
+    if (clazz->classLoader == NULL &&
+        (strcmp(clazz->descriptor, "Ljava/lang/Class;") == 0))
+    {
+        if (gDvm.classJavaLangClass->ifieldCount > CLASS_FIELD_SLOTS) {
+            LOGE("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) {
+            LOGE("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 = malloc(len);
+            if (interfaceIdxArray == NULL) {
+                LOGW("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 */
+                    LOGV("Unable to resolve superclass of %s (%d)\n",
+                         clazz->descriptor, superclassIdx);
+                } else {
+                    LOGW("Unable to resolve superclass of %s (%d)\n",
+                         clazz->descriptor, superclassIdx);
+                }
+                goto bail;
+            }
+            dvmSetFieldObject((Object *)clazz,
+                              offsetof(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 */
+                        LOGV("Failed resolving %s interface %d '%s'\n",
+                             clazz->descriptor, interfaceIdxArray[i],
+                             classDescriptor);
+                    } else {
+                        LOGI("Failed resolving %s interface %d '%s'\n",
+                             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);
+                    LOGW("Interface '%s' is not accessible to '%s'\n",
+                         clazz->interfaces[i]->descriptor, clazz->descriptor);
+                    dvmThrowException("Ljava/lang/IllegalAccessError;",
+                                      "interface not accessible");
+                    goto bail;
+                }
+                LOGVV("+++  found interface '%s'\n",
+                      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.
+             */
+            dvmThrowException("Ljava/lang/ClassFormatError;",
+                "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) {
+            dvmThrowException("Ljava/lang/LinkageError;",
+                              "no superclass defined");
+            goto bail;
+        }
+        /* verify */
+        if (dvmIsFinalClass(clazz->super)) {
+            LOGW("Superclass of '%s' is final '%s'\n",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+                "superclass is final");
+            goto bail;
+        } else if (dvmIsInterfaceClass(clazz->super)) {
+            LOGW("Superclass of '%s' is interface '%s'\n",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+                "superclass is an interface");
+            goto bail;
+        } else if (!dvmCheckClassAccess(clazz, clazz->super)) {
+            LOGW("Superclass of '%s' (%s) is not accessible\n",
+                clazz->descriptor, clazz->super->descriptor);
+            dvmThrowException("Ljava/lang/IllegalAccessError;",
+                "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_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/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.
+                dvmThrowException("Ljava/lang/LinkageError;",
+                    "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_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) {
+            LOGE("Too many methods (%d) in interface '%s'\n", 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)) {
+            LOGW("failed creating vtable\n");
+            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 fields and methods from java/lang/ref/Reference and
+     * java/lang/Class.  This has to happen after computeFieldOffsets().
+     */
+    if (clazz->classLoader == NULL) {
+        if (strcmp(clazz->descriptor, "Ljava/lang/ref/Reference;") == 0) {
+            if (!precacheReferenceOffsets(clazz)) {
+                LOGE("failed pre-caching Reference offsets\n");
+                dvmThrowException("Ljava/lang/InternalError;", NULL);
+                goto bail;
+            }
+        } else if (clazz == gDvm.classJavaLangClass) {
+            gDvm.offJavaLangClass_pd = dvmFindFieldOffset(clazz, "pd",
+                "Ljava/security/ProtectionDomain;");
+            if (gDvm.offJavaLangClass_pd <= 0) {
+                LOGE("ERROR: unable to find 'pd' field in Class\n");
+                dvmAbort();     /* we're not going to get much farther */
+            }
+        }
+    }
+
+    /*
+     * 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)
+        LOGV("CLASS: linked '%s'\n", 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())) {
+            dvmThrowException("Ljava/lang/VirtualMachineError;", 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) {
+        //LOGI("SUPER METHODS %d %s->%s\n", 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);
+    }
+    //LOGD("+++ max vmethods for '%s' is %d\n", 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)
+                {
+                    /* verify */
+                    if (dvmIsFinalMethod(superMeth)) {
+                        LOGW("Method %s.%s overrides final %s.%s\n",
+                            localMeth->clazz->descriptor, localMeth->name,
+                            superMeth->clazz->descriptor, superMeth->name);
+                        goto bail;
+                    }
+                    clazz->vtable[si] = localMeth;
+                    localMeth->methodIndex = (u2) si;
+                    //LOGV("+++   override %s.%s (slot %d)\n",
+                    //    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++;
+
+                //LOGV("+++   add method %s.%s\n",
+                //    clazz->descriptor, localMeth->name);
+            }
+        }
+
+        if (actualCount != (u2) actualCount) {
+            LOGE("Too many methods (%d) in class '%s'\n", actualCount,
+                 clazz->descriptor);
+            goto bail;
+        }
+
+        assert(actualCount <= maxCount);
+
+        if (actualCount < maxCount) {
+            assert(clazz->vtable != NULL);
+            dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+            clazz->vtable = dvmLinearRealloc(clazz->classLoader, clazz->vtable,
+                sizeof(*(clazz->vtable)) * actualCount);
+            if (clazz->vtable == NULL) {
+                LOGE("vtable realloc failed\n");
+                goto bail;
+            } else {
+                LOGVV("+++  reduced vtable from %d to %d\n",
+                    maxCount, actualCount);
+            }
+        }
+
+        clazz->vtableCount = actualCount;
+    } else {
+        /* java/lang/Object case */
+        int count = clazz->virtualMethodCount;
+        if (count != (u2) count) {
+            LOGE("Too many methods (%d) in base class '%s'\n", 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 ifCount, superIfCount, idx;
+    int i;
+
+    if (clazz->super != NULL)
+        superIfCount = clazz->super->iftableCount;
+    else
+        superIfCount = 0;
+
+    ifCount = superIfCount;
+    ifCount += clazz->interfaceCount;
+    for (i = 0; i < clazz->interfaceCount; i++)
+        ifCount += clazz->interfaces[i]->iftableCount;
+
+    LOGVV("INTF: class '%s' direct w/supra=%d super=%d total=%d\n",
+        clazz->descriptor, ifCount - superIfCount, superIfCount, ifCount);
+
+    if (ifCount == 0) {
+        assert(clazz->iftableCount == 0);
+        assert(clazz->iftable == NULL);
+        result = true;
+        goto bail;
+    }
+
+    /*
+     * 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.
+     */
+    idx = superIfCount;
+
+    for (i = 0; i < clazz->interfaceCount; i++) {
+        ClassObject* interf;
+        int j;
+
+        interf = clazz->interfaces[i];
+        assert(interf != NULL);
+
+        /* make sure this is still an interface class */
+        if (!dvmIsInterfaceClass(interf)) {
+            LOGW("Class '%s' implements non-interface '%s'\n",
+                clazz->descriptor, interf->descriptor);
+            dvmThrowExceptionWithClassMessage(
+                "Ljava/lang/IncompatibleClassChangeError;",
+                clazz->descriptor);
+            goto bail;
+        }
+
+        /* add entry for this interface */
+        clazz->iftable[idx++].clazz = interf;
+
+        /* add entries for the interface's superinterfaces */
+        for (j = 0; j < interf->iftableCount; j++) {
+            clazz->iftable[idx++].clazz = interf->iftable[j].clazz;
+        }
+    }
+
+    assert(idx == ifCount);
+
+    if (false) {
+        /*
+         * Remove anything redundant from our recent additions.  Note we have
+         * to traverse the recent adds when looking for duplicates, because
+         * it's possible the recent additions are self-redundant.  This
+         * reduces the memory footprint of classes with lots of inherited
+         * interfaces.
+         *
+         * (I don't know if this will cause problems later on when we're trying
+         * to find a static field.  It looks like the proper search order is
+         * (1) current class, (2) interfaces implemented by current class,
+         * (3) repeat with superclass.  A field implemented by an interface
+         * and by a superclass might come out wrong if the superclass also
+         * implements the interface.  The javac compiler will reject the
+         * situation as ambiguous, so the concern is somewhat artificial.)
+         *
+         * UPDATE: this makes ReferenceType.Interfaces difficult to implement,
+         * because it wants to return just the interfaces declared to be
+         * implemented directly by the class.  I'm excluding this code for now.
+         */
+        for (i = superIfCount; i < ifCount; i++) {
+            int j;
+
+            for (j = 0; j < ifCount; j++) {
+                if (i == j)
+                    continue;
+                if (clazz->iftable[i].clazz == clazz->iftable[j].clazz) {
+                    LOGVV("INTF: redundant interface %s in %s\n",
+                        clazz->iftable[i].clazz->descriptor,
+                        clazz->descriptor);
+
+                    if (i != ifCount-1)
+                        memmove(&clazz->iftable[i], &clazz->iftable[i+1],
+                            (ifCount - i -1) * sizeof(InterfaceEntry));
+                    ifCount--;
+                    i--;        // adjust for i++ above
+                    break;
+                }
+            }
+        }
+        LOGVV("INTF: class '%s' nodupes=%d\n", clazz->descriptor, ifCount);
+    } // if (false)
+
+    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.
+     */
+    int poolSize = 0;
+    for (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\n",
+            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\n");
+        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.
+     */
+    int poolOffset = 0;
+    Method** mirandaList = NULL;
+    int mirandaCount = 0, mirandaAlloc = 0;
+
+    for (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'\n", 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\n", j);
+                    if (!dvmIsPublicMethod(clazz->vtable[j])) {
+                        LOGW("Implementation of %s.%s is not public\n",
+                            clazz->descriptor, clazz->vtable[j]->name);
+                        dvmThrowException("Ljava/lang/IllegalAccessError;",
+                            "interface implementation not public");
+                        goto bail;
+                    }
+                    clazz->iftable[i].methodIndexArray[methIdx] = j;
+                    break;
+                }
+            }
+            if (j < 0) {
+                IF_LOGV() {
+                    char* desc =
+                        dexProtoCopyMethodDescriptor(&imeth->prototype);
+                    LOGV("No match for '%s' '%s' in '%s' (creating miranda)\n",
+                            imeth->name, desc, clazz->descriptor);
+                    free(desc);
+                }
+                //dvmThrowException("Ljava/lang/RuntimeException;", "Miranda!");
+                //return false;
+
+                if (mirandaCount == mirandaAlloc) {
+                    mirandaAlloc += 8;
+                    if (mirandaList == NULL) {
+                        mirandaList = dvmLinearAlloc(clazz->classLoader,
+                                        mirandaAlloc * sizeof(Method*));
+                    } else {
+                        dvmLinearReadOnly(clazz->classLoader, mirandaList);
+                        mirandaList = 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\n",
+                                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\n",
+                    imeth->name, clazz->vtableCount + mir);
+
+                /* if non-duplicate among Mirandas, add to Miranda list */
+                if (mir == mirandaCount) {
+                    //LOGV("MIRANDA: holding '%s' in slot %d\n",
+                    //    imeth->name, mir);
+                    mirandaList[mirandaCount++] = imeth;
+                }
+            }
+        }
+    }
+
+    if (mirandaCount != 0) {
+        static const int kManyMirandas = 150;   /* arbitrary */
+        Method* newVirtualMethods;
+        Method* meth;
+        int oldMethodCount, oldVtableCount;
+
+        for (i = 0; i < mirandaCount; i++) {
+            LOGVV("MIRANDA %d: %s.%s\n", 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.
+             */
+            LOGD("Note: class %s has %d unimplemented (abstract) methods\n",
+                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\n");
+            dvmLinearReadWrite(clazz->classLoader, clazz->vtable);
+            Method* meth = newVirtualMethods;
+            for (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 (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'\n", pOne->field.name, pTwo->field.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(DataObject, instanceData);
+
+    LOGVV("--- computeFieldOffsets '%s'\n", clazz->descriptor);
+
+    //LOGI("OFFSETS fieldCount=%d\n", clazz->ifieldCount);
+    //LOGI("dataobj, instance: %d\n", offsetof(DataObject, instanceData));
+    //LOGI("classobj, access: %d\n", offsetof(ClassObject, accessFlags));
+    //LOGI("super=%p, fieldOffset=%d\n", 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->field.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->field.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\n", pField->field.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\n");
+
+        InstField* pField = &clazz->ifields[i];
+        char c = pField->field.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\n",
+                pField->field.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->field.signature[0];
+
+                if (rc != 'J' && rc != 'D') {
+                    swapField(pField, singleField);
+                    //c = rc;
+                    LOGVV("  +++ swapped '%s' for alignment\n",
+                        pField->field.name);
+                    pField->byteOffset = fieldOffset;
+                    fieldOffset += sizeof(u4);
+                    LOGVV("  --- offset3 '%s'=%d\n",
+                        pField->field.name, pField->byteOffset);
+                    found = true;
+                    i++;
+                    break;
+                }
+            }
+            if (!found) {
+                LOGV("  +++ inserting pad field in '%s'\n", 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->field.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->field.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\n", pField->field.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->field.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(clazz != gDvm.classJavaLangClass || (size_t)fieldOffset <
+        offsetof(ClassObject, instanceData) + sizeof(clazz->instanceData));
+
+    clazz->objectSize = fieldOffset;
+
+    dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+    return true;
+}
+
+/*
+ * Throw the VM-spec-mandated error when an exception is thrown during
+ * class initialization.
+ *
+ * The safest way to do this is to call the ExceptionInInitializerError
+ * constructor that takes a Throwable.
+ *
+ * [Do we want to wrap it if the original is an Error rather than
+ * an Exception?]
+ */
+static void throwClinitError(void)
+{
+    Thread* self = dvmThreadSelf();
+    Object* exception;
+    Object* eiie;
+
+    exception = dvmGetException(self);
+    dvmAddTrackedAlloc(exception, self);
+    dvmClearException(self);
+
+    if (gDvm.classJavaLangExceptionInInitializerError == NULL) {
+        /*
+         * Always resolves to same thing -- no race condition.
+         */
+        gDvm.classJavaLangExceptionInInitializerError =
+            dvmFindSystemClass(
+                    "Ljava/lang/ExceptionInInitializerError;");
+        if (gDvm.classJavaLangExceptionInInitializerError == NULL) {
+            LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
+            goto fail;
+        }
+
+        gDvm.methJavaLangExceptionInInitializerError_init =
+            dvmFindDirectMethodByDescriptor(gDvm.classJavaLangExceptionInInitializerError,
+            "<init>", "(Ljava/lang/Throwable;)V");
+        if (gDvm.methJavaLangExceptionInInitializerError_init == NULL) {
+            LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
+            goto fail;
+        }
+    }
+
+    eiie = dvmAllocObject(gDvm.classJavaLangExceptionInInitializerError,
+                ALLOC_DEFAULT);
+    if (eiie == NULL)
+        goto fail;
+
+    /*
+     * Construct the new object, and replace the exception with it.
+     */
+    JValue unused;
+    dvmCallMethod(self, gDvm.methJavaLangExceptionInInitializerError_init,
+        eiie, &unused, exception);
+    dvmSetException(self, eiie);
+    dvmReleaseTrackedAlloc(eiie, NULL);
+    dvmReleaseTrackedAlloc(exception, self);
+    return;
+
+fail:       /* restore original exception */
+    dvmSetException(self, exception);
+    dvmReleaseTrackedAlloc(exception, self);
+    return;
+}
+
+/*
+ * 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)
+{
+    LOGI("Rejecting re-init on previously-failed class %s v=%p\n",
+        clazz->descriptor, clazz->verifyErrorClass);
+
+    if (clazz->verifyErrorClass == NULL) {
+        dvmThrowExceptionWithClassMessage("Ljava/lang/NoClassDefFoundError;",
+            clazz->descriptor);
+    } else {
+        dvmThrowExceptionByClassWithClassMessage(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 */
+        LOGV("Not initializing static fields in %s\n", 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->field.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.
+             */
+            LOGE("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, value.value.l);
+                dvmReleaseTrackedAlloc(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.
+             */
+            LOGE("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))
+            {
+                LOGW("Method mismatch: %s in %s (cl=%p) and super %s (cl=%p)\n",
+                    meth->name, clazz->descriptor, clazz->classLoader,
+                    clazz->super->descriptor, clazz->super->classLoader);
+                dvmThrowException("Ljava/lang/LinkageError;",
+                    "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)) {
+                    LOGW("Method mismatch: %s in %s (cl=%p) and "
+                            "iface %s (cl=%p)\n",
+                        meth->name, clazz->descriptor, clazz->classLoader,
+                        iface->descriptor, iface->classLoader);
+                    dvmThrowException("Ljava/lang/LinkageError;",
+                        "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).
+ *
+ * There isn't a race here, because either clazz->initThreadId won't match
+ * us, or it will and it was set in the same thread.
+ */
+bool dvmIsClassInitializing(const ClassObject* clazz)
+{
+    return (clazz->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)
+{
+#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 */
+            LOGV("+++ not verifying class %s (cl=%p)\n",
+                clazz->descriptor, clazz->classLoader);
+            clazz->status = CLASS_VERIFIED;
+            goto noverify;
+        }
+
+        if (!gDvm.optimizing)
+            LOGV("+++ late verify on %s\n", 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)) {
+            LOGW("Class '%s' was optimized without verification; "
+                 "not verifying now\n",
+                clazz->descriptor);
+            LOGW("  ('rm /data/dalvik-cache/*' and restart to fix this)");
+            goto verify_failed;
+        }
+
+        clazz->status = CLASS_VERIFYING;
+        if (!dvmVerifyClass(clazz)) {
+verify_failed:
+            dvmThrowExceptionWithClassMessage("Ljava/lang/VerifyError;",
+                clazz->descriptor);
+            dvmSetFieldObject((Object*) clazz,
+                offsetof(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.
+     */
+    if (!IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED)) {
+        LOGV("+++ late optimize on %s (pv=%d)\n",
+            clazz->descriptor, IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+        dvmOptimizeClass(clazz, true);
+        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) {
+            //LOGV("HEY: found a recursive <clinit>\n");
+            goto bail_unlock;
+        }
+
+        if (dvmCheckException(self)) {
+            LOGW("GLITCH: exception pending at start of class init\n");
+            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)) {
+            LOGI("Class init of '%s' failing with wait() exception\n",
+                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);
+            throwClinitError();
+            clazz->status = CLASS_ERROR;
+            goto bail_unlock;
+        }
+        if (clazz->status == CLASS_INITIALIZING) {
+            LOGI("Waiting again for class init\n");
+            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.
+             */
+            dvmThrowException("Ljava/lang/UnsatisfiedLinkError;",
+                "(<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;
+    }
+
+    u8 startWhen = 0;
+    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
+
+    clazz->status = CLASS_INITIALIZING;
+    clazz->initThreadId = self->threadId;
+    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\n", clazz->descriptor);
+    } else {
+        LOGVV("Invoking %s.<clinit>\n", 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.
+         */
+        LOGW("Exception %s thrown while initializing %s\n",
+            (dvmGetException(self)->clazz)->descriptor, clazz->descriptor);
+        throwClinitError();
+        //LOGW("+++ replaced\n");
+
+        dvmLockObject(self, (Object*) clazz);
+        clazz->status = CLASS_ERROR;
+    } else {
+        /* success! */
+        dvmLockObject(self, (Object*) clazz);
+        clazz->status = CLASS_INITIALIZED;
+        LOGVV("Initialized class: %s\n", 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,
+            (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) */
+        LOGV("NOTE: registerMap already set for %s.%s\n",
+            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 = 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.
+ */
+Object* dvmGetSystemClassLoader(void)
+{
+    ClassObject* clazz;
+    Method* getSysMeth;
+    Object* loader;
+
+    clazz = dvmFindSystemClass("Ljava/lang/ClassLoader;");
+    if (clazz == NULL)
+        return NULL;
+
+    getSysMeth = dvmFindDirectMethodByDescriptor(clazz, "getSystemClassLoader",
+        "()Ljava/lang/ClassLoader;");
+    if (getSysMeth == NULL)
+        return NULL;
+
+    JValue result;
+    dvmCallMethod(dvmThreadSelf(), getSysMeth, NULL, &result);
+    loader = (Object*)result.l;
+    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) {
+        LOGI("dumpClass: ignoring request to dump null class\n");
+        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)
+            LOGI("%s %p %s\n", clazz->descriptor, clazz->classLoader, initStr);
+        else if (showInit)
+            LOGI("%s %s\n", clazz->descriptor, initStr);
+        else if (showLoader)
+            LOGI("%s %p\n", clazz->descriptor, clazz->classLoader);
+        else
+            LOGI("%s\n", 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;
+
+    LOGI("----- %s '%s' cl=%p ser=0x%08x -----\n",
+        dvmIsInterfaceClass(clazz) ? "interface" : "class",
+        clazz->descriptor, clazz->classLoader, clazz->serialNumber);
+    LOGI("  objectSize=%d (%d from super)\n", (int) clazz->objectSize,
+        super != NULL ? (int) super->objectSize : -1);
+    LOGI("  access=0x%04x.%04x\n", clazz->accessFlags >> 16,
+        clazz->accessFlags & JAVA_FLAGS_MASK);
+    if (super != NULL)
+        LOGI("  super='%s' (cl=%p)\n", super->descriptor, super->classLoader);
+    if (dvmIsArrayClass(clazz)) {
+        LOGI("  dimensions=%d elementClass=%s\n",
+            clazz->arrayDim, clazz->elementClass->descriptor);
+    }
+    if (clazz->iftableCount > 0) {
+        LOGI("  interfaces (%d):\n", clazz->iftableCount);
+        for (i = 0; i < clazz->iftableCount; i++) {
+            InterfaceEntry* ent = &clazz->iftable[i];
+            int j;
+
+            LOGI("    %2d: %s (cl=%p)\n",
+                i, ent->clazz->descriptor, ent->clazz->classLoader);
+
+            /* enable when needed */
+            if (false && ent->methodIndexArray != NULL) {
+                for (j = 0; j < ent->clazz->virtualMethodCount; j++)
+                    LOGI("      %2d: %d %s %s\n",
+                        j, ent->methodIndexArray[j],
+                        ent->clazz->virtualMethods[j].name,
+                        clazz->vtable[ent->methodIndexArray[j]]->name);
+            }
+        }
+    }
+    if (!dvmIsInterfaceClass(clazz)) {
+        LOGI("  vtable (%d entries, %d in super):\n", clazz->vtableCount,
+            super != NULL ? super->vtableCount : 0);
+        for (i = 0; i < clazz->vtableCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(&clazz->vtable[i]->prototype);
+            LOGI("    %s%2d: %p %20s %s\n",
+                (i != clazz->vtable[i]->methodIndex) ? "*** " : "",
+                (u4) clazz->vtable[i]->methodIndex, clazz->vtable[i],
+                clazz->vtable[i]->name, desc);
+            free(desc);
+        }
+        LOGI("  direct methods (%d entries):\n", clazz->directMethodCount);
+        for (i = 0; i < clazz->directMethodCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(
+                    &clazz->directMethods[i].prototype);
+            LOGI("    %2d: %20s %s\n", i, clazz->directMethods[i].name,
+                desc);
+            free(desc);
+        }
+    } else {
+        LOGI("  interface methods (%d):\n", clazz->virtualMethodCount);
+        for (i = 0; i < clazz->virtualMethodCount; i++) {
+            desc = dexProtoCopyMethodDescriptor(
+                    &clazz->virtualMethods[i].prototype);
+            LOGI("    %2d: %2d %20s %s\n", i,
+                (u4) clazz->virtualMethods[i].methodIndex,
+                clazz->virtualMethods[i].name,
+                desc);
+            free(desc);
+        }
+    }
+    if (clazz->sfieldCount > 0) {
+        LOGI("  static fields (%d entries):\n", clazz->sfieldCount);
+        for (i = 0; i < clazz->sfieldCount; i++) {
+            LOGI("    %2d: %20s %s\n", i, clazz->sfields[i].field.name,
+                clazz->sfields[i].field.signature);
+        }
+    }
+    if (clazz->ifieldCount > 0) {
+        LOGI("  instance fields (%d entries):\n", clazz->ifieldCount);
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            LOGI("    %2d: %20s %s\n", i, clazz->ifields[i].field.name,
+                clazz->ifields[i].field.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)
+{
+    LOGV("VM stats (%s): cls=%d/%d meth=%d ifld=%d sfld=%d linear=%d\n",
+        msg, gDvm.numLoadedClasses, dvmHashTableNumEntries(gDvm.loadedClasses),
+        gDvm.numDeclaredMethods, gDvm.numDeclaredInstFields,
+        gDvm.numDeclaredStaticFields, gDvm.pBootLoaderAlloc->curOffset);
+#ifdef COUNT_PRECISE_METHODS
+    LOGI("GC precise methods: %d\n",
+        dvmPointerSetGetCount(gDvm.preciseMethods));
+#endif
+}
+
+#ifdef PROFILE_FIELD_ACCESS
+/*
+ * Dump the field access counts for all fields in this method.
+ */
+static int dumpAccessCounts(void* vclazz, void* varg)
+{
+    const ClassObject* clazz = (const ClassObject*) vclazz;
+    int i;
+
+    for (i = 0; i < clazz->ifieldCount; i++) {
+        Field* field = &clazz->ifields[i].field;
+
+        if (field->gets != 0)
+            printf("GI %d %s.%s\n", field->gets,
+                field->clazz->descriptor, field->name);
+        if (field->puts != 0)
+            printf("PI %d %s.%s\n", field->puts,
+                field->clazz->descriptor, field->name);
+    }
+    for (i = 0; i < clazz->sfieldCount; i++) {
+        Field* field = &clazz->sfields[i].field;
+
+        if (field->gets != 0)
+            printf("GS %d %s.%s\n", field->gets,
+                field->clazz->descriptor, field->name);
+        if (field->puts != 0)
+            printf("PS %d %s.%s\n", field->puts,
+                field->clazz->descriptor, field->name);
+    }
+
+    return 0;
+}
+
+/*
+ * Dump the field access counts for all loaded classes.
+ */
+void dvmDumpFieldAccessCounts(void)
+{
+    dvmHashTableLock(gDvm.loadedClasses);
+    dvmHashForeach(gDvm.loadedClasses, dumpAccessCounts, NULL);
+    dvmHashTableUnlock(gDvm.loadedClasses);
+}
+#endif
+
+
+/*
+ * Mark all classes associated with the built-in loader.
+ */
+static int markClassObject(void *clazz, void *arg)
+{
+    UNUSED_PARAMETER(arg);
+
+    dvmMarkObjectNonNull((Object *)clazz);
+    return 0;
+}
+
+/*
+ * The garbage collector calls this to mark the class objects for all
+ * loaded classes.
+ */
+void dvmGcScanRootClassLoader()
+{
+    /* dvmClassStartup() may not have been called before the first GC.
+     */
+    if (gDvm.loadedClasses != NULL) {
+        dvmHashTableLock(gDvm.loadedClasses);
+        dvmHashForeach(gDvm.loadedClasses, markClassObject, NULL);
+        dvmHashTableUnlock(gDvm.loadedClasses);
+    }
+}
+
+
+/*
+ * ===========================================================================
+ *      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..e27ef79
--- /dev/null
+++ b/vm/oo/Class.h
@@ -0,0 +1,281 @@
+/*
+ * 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
+#define _DALVIK_OO_CLASS
+
+/*
+ * 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 three ways:
+ *  - as a "loose" .class file in a directory
+ *  - as a .class file held in a JAR archive
+ *  - in a .dex file
+ *
+ * These three may be freely intermixed in a classpath specification.
+ * Ordering is significant.  (Currently only ".dex" is supported directly
+ * by the VM.)
+ */
+typedef struct ClassPathEntry {
+    enum {
+        kCpeUnknown = 0,
+        kCpeDir,
+        kCpeJar,
+        kCpeDex,
+        kCpeLastEntry       /* used as sentinel at end of array */
+    }       kind;
+    char*   fileName;
+    void*   ptr;            /* JarFile* or DexFile* */
+} ClassPathEntry;
+
+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 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.
+ */
+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();
+
+#ifdef PROFILE_FIELD_ACCESS
+void dvmDumpFieldAccessCounts(void);
+#endif
+
+/* 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*/
diff --git a/vm/oo/Object.c b/vm/oo/Object.c
new file mode 100644
index 0000000..32ed679
--- /dev/null
+++ b/vm/oo/Object.c
@@ -0,0 +1,776 @@
+/*
+ * 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->field.name) == 0 &&
+            strcmp(signature, pField->field.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->field.name) == 0 &&
+            strcmp(signature, pField->field.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) {
+        LOGW("Bogus method descriptor: %s\n", 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.
+ *
+ * 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.
+ *
+ * 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 "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.
+ *
+ * (This is used for reflection and JNI "call method" calls.)
+ */
+const Method* dvmGetVirtualizedMethod(const ClassObject* clazz,
+    const Method* meth)
+{
+    Method* actualMeth;
+    int methodIndex;
+
+    assert(!dvmIsStaticMethod(meth));
+
+    if (dvmIsPrivateMethod(meth))   // no vtable entry for these
+        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) {
+            dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+                "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)) {
+        dvmThrowException("Ljava/lang/AbstractMethodError;", 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) {
+        LOGW("Null or malformed object not dumped");
+        return;
+    }
+
+    clazz = obj->clazz;
+    LOGD("----- Object dump: %p (%s, %d bytes) -----",
+        obj, clazz->descriptor, (int) clazz->objectSize);
+    //printHexDump(obj, clazz->objectSize);
+    LOGD("  Fields:");
+    while (clazz != NULL) {
+        LOGD("    -- %s", clazz->descriptor);
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            const InstField* pField = &clazz->ifields[i];
+            char type = pField->field.signature[0];
+
+            if (type == 'F' || type == 'D') {
+                double dval;
+
+                if (type == 'F')
+                    dval = dvmGetFieldFloat(obj, pField->byteOffset);
+                else
+                    dval = dvmGetFieldDouble(obj, pField->byteOffset);
+
+                LOGD("    %2d: '%s' '%s' af=%04x off=%d %.3f", i,
+                    pField->field.name, pField->field.signature,
+                    pField->field.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);
+
+                LOGD("    %2d: '%s' '%s' af=%04x off=%d 0x%08llx", i,
+                    pField->field.name, pField->field.signature,
+                    pField->field.accessFlags, pField->byteOffset, lval);
+            }
+        }
+
+        clazz = clazz->super;
+    }
+    if (obj->clazz == gDvm.classJavaLangClass) {
+        LOGD("  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->field.signature[0];
+
+            if (type == 'F' || type == 'D') {
+                double dval;
+
+                if (type == 'F')
+                    dval = pField->value.f;
+                else
+                    dval = pField->value.d;
+
+                LOGD("    %2d: '%s' '%s' af=%04x off=%zd %.3f", i,
+                     pField->field.name, pField->field.signature,
+                     pField->field.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;
+
+                LOGD("    %2d: '%s' '%s' af=%04x off=%zd 0x%08llx", i,
+                     pField->field.name, pField->field.signature,
+                     pField->field.accessFlags, byteOffset, lval);
+            }
+        }
+    }
+}
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
new file mode 100644
index 0000000..903450f
--- /dev/null
+++ b/vm/oo/Object.h
@@ -0,0 +1,794 @@
+/*
+ * 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
+#define _DALVIK_OO_OBJECT
+
+#include <Atomic.h>
+
+#include <stddef.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;
+typedef struct DataObject DataObject;
+typedef struct InitiatingLoaderList InitiatingLoaderList;
+typedef struct ClassObject ClassObject;
+typedef struct StringObject StringObject;
+typedef struct ArrayObject ArrayObject;
+typedef struct Method Method;
+typedef struct ExceptionEntry ExceptionEntry;
+typedef struct LineNumEntry LineNumEntry;
+typedef struct StaticField StaticField;
+typedef struct InstField InstField;
+typedef struct Field Field;
+typedef struct RegisterMap 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 */
+typedef enum AccessFlags {
+    ACC_MIRANDA         = 0x8000,       // method (internal to VM)
+    JAVA_FLAGS_MASK     = 0xffff,       // bits set from Java sources (low 16)
+} AccessFlags;
+
+/* 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.
+ */
+typedef 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_ISREFERENCE       = (1<<28),  // class is a soft/weak/phantom ref
+                                        // only ISREFERENCE is set --> soft
+    CLASS_ISWEAKREFERENCE   = (1<<27),  // class is a weak reference
+    CLASS_ISPHANTOMREFERENCE = (1<<26), // class is a phantom reference
+
+    CLASS_MULTIPLE_DEFS     = (1<<25),  // 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
+} ClassFlags;
+
+/* bits we can reasonably expect to see set in a DEX access flags field */
+#define EXPECTED_FILE_FLAGS \
+    (ACC_CLASS_MASK | CLASS_ISPREVERIFIED | CLASS_ISOPTIMIZED)
+
+/*
+ * 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.
+ */
+typedef enum MethodFlags {
+    METHOD_ISWRITABLE       = (1<<31),  // the method's code is writable
+} MethodFlags;
+
+/*
+ * 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 */
+typedef 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 */
+} ClassStatus;
+
+/*
+ * Primitive type identifiers.  We use these values as indexes into an
+ * array of synthesized classes, so these start at zero and count up.
+ * The order is arbitrary (mimics table in doc for newarray opcode),
+ * but can't be changed without shuffling some reflection tables.
+ *
+ * PRIM_VOID can't be used as an array type, but we include it here for
+ * other uses (e.g. Void.TYPE).
+ */
+typedef enum PrimitiveType {
+    PRIM_NOT        = -1,       /* value is not a primitive type */
+    PRIM_BOOLEAN    = 0,
+    PRIM_CHAR       = 1,
+    PRIM_FLOAT      = 2,
+    PRIM_DOUBLE     = 3,
+    PRIM_BYTE       = 4,
+    PRIM_SHORT      = 5,
+    PRIM_INT        = 6,
+    PRIM_LONG       = 7,
+    PRIM_VOID       = 8,
+
+    PRIM_MAX
+} PrimitiveType;
+#define PRIM_TYPE_TO_LETTER "ZCFDBSIJV"     /* must match order in enum */
+
+/*
+ * 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.
+ */
+typedef 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;
+} InterfaceEntry;
+
+
+
+/*
+ * 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.
+ */
+typedef 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;
+} Object;
+
+/*
+ * Properly initialize an Object.
+ * void DVM_OBJECT_INIT(Object *obj, ClassObject *clazz_)
+ */
+#define DVM_OBJECT_INIT(obj, clazz_)                                    \
+    do {                                                                \
+        dvmSetFieldObject((Object *)obj, offsetof(Object, clazz),       \
+                          (Object *)clazz_);                            \
+        DVM_LOCK_INIT(&(obj)->lock);                                    \
+    } while (0)
+
+/*
+ * Data objects have an Object header followed by their instance data.
+ */
+struct DataObject {
+    Object          obj;                /* MUST be first item */
+
+    /* 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          obj;                /* MUST be first item */
+
+    /* variable #of u4 slots; u8 uses 2 slots */
+    u4              instanceData[1];
+};
+
+
+/*
+ * 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          obj;                /* MUST be first item */
+
+    /* 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;
+#ifdef PROFILE_FIELD_ACCESS
+    u4              gets;
+    u4              puts;
+#endif
+};
+
+/*
+ * Static field.
+ */
+struct StaticField {
+    Field           field;          /* MUST be first item */
+    JValue          value;          /* initially set from DEX for primitives */
+};
+
+/*
+ * Instance field.
+ */
+struct InstField {
+    Field           field;          /* MUST be first item */
+
+    /*
+     * 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          obj;                /* MUST be first item */
+
+    /* 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[]; /* 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 */
+
+    /* cached JNI argument and return-type hints */
+    int             jniArgInfo;
+
+    /*
+     * 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;
+
+    /*
+     * 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;
+};
+
+
+/*
+ * 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 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.
+ */
+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 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;
+}
+
+/*
+ * 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*/
diff --git a/vm/oo/ObjectInlines.h b/vm/oo/ObjectInlines.h
new file mode 100644
index 0000000..23a72b2
--- /dev/null
+++ b/vm/oo/ObjectInlines.h
@@ -0,0 +1,342 @@
+/*
+ * 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
+#define _DALVIK_OO_OBJECTINLINES
+
+/*
+ * 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 **)(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 = 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 = BYTE_OFFSET(obj, offset);
+    alias.lval = dvmQuasiAtomicRead64(addr);
+    ANDROID_MEMBAR_FULL();
+    return alias.dval;
+}
+INLINE Object* dvmGetFieldObjectVolatile(const Object* obj, int offset) {
+    void** 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 = 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;
+    android_atomic_release_store(val, ptr);
+}
+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 = BYTE_OFFSET(obj, offset);
+    ANDROID_MEMBAR_FULL();
+    dvmQuasiAtomicSwap64(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) {
+    void** ptr = &((JValue*)BYTE_OFFSET(obj, offset))->l;
+    android_atomic_release_store((int32_t)val, (int32_t*)ptr);
+    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) {
+    void* 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((Object *)sfield->field.clazz, &sfield->value.l);
+    }
+}
+INLINE void dvmSetStaticFieldIntVolatile(StaticField* sfield, s4 val) {
+    s4* ptr = &sfield->value.i;
+    android_atomic_release_store(val, ptr);
+}
+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;
+    ANDROID_MEMBAR_FULL();
+    dvmQuasiAtomicSwap64(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) {
+    void** ptr = &(sfield->value.l);
+    android_atomic_release_store((int32_t)val, (int32_t*)ptr);
+    if (val != NULL) {
+        dvmWriteBarrierField((Object *)sfield->field.clazz, &sfield->value.l);
+    }
+}
+
+#endif /*_DALVIK_OO_OBJECTINLINES*/
diff --git a/vm/oo/Resolve.c b/vm/oo/Resolve.c
new file mode 100644
index 0000000..c4bda8b
--- /dev/null
+++ b/vm/oo/Resolve.c
@@ -0,0 +1,588 @@
+/*
+ * 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)\n",
+        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)
+            {
+                LOGW("Class resolved by unexpected DEX:"
+                     " %s(%p):%p ref [%s] %s(%p):%p\n",
+                    referrer->descriptor, referrer->classLoader,
+                    referrer->pDvmDex,
+                    resClass->descriptor, resClassCheck->descriptor,
+                    resClassCheck->classLoader, resClassCheck->pDvmDex);
+                LOGW("(%s had used a different %s during pre-verification)\n",
+                    referrer->descriptor, resClass->descriptor);
+                dvmThrowException("Ljava/lang/IllegalAccessError;",
+                    "Class ref in pre-verified class resolved to unexpected "
+                    "implementation");
+                return NULL;
+            }
+        }
+
+        LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d\n",
+            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\n",
+            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)\n", 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 */
+        dvmThrowExceptionWithClassMessage(
+            "Ljava/lang/IncompatibleClassChangeError;",
+            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) {
+        dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
+        return NULL;
+    }
+
+    LOGVV("--- found method %d (%s.%s)\n",
+        methodIdx, resClass->descriptor, resMethod->name);
+
+    /* see if this is a pure-abstract method */
+    if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+        dvmThrowException("Ljava/lang/AbstractMethodError;", 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)\n",
+            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;
+    int i;
+
+    LOGVV("--- resolving interface method %d (referrer=%s)\n",
+        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 */
+        dvmThrowExceptionWithClassMessage(
+            "Ljava/lang/IncompatibleClassChangeError;",
+            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' '%s' in resClass='%s'\n",
+        methodName, methodSig, resClass->descriptor);
+    resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
+    if (resMethod == NULL) {
+        LOGVV("+++ did not resolve immediately\n");
+        for (i = 0; i < resClass->iftableCount; i++) {
+            resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
+                            methodName, &proto);
+            if (resMethod != NULL)
+                break;
+        }
+
+        if (resMethod == NULL) {
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", methodName);
+            return NULL;
+        }
+    } else {
+        LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
+            resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
+    }
+
+    LOGVV("--- found interface method %d (%s.%s)\n",
+        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)\n",
+        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) {
+        dvmThrowException("Ljava/lang/NoSuchFieldError;",
+            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->field.clazz) ||
+           dvmIsClassInitializing(resField->field.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\n",
+        ifieldIdx, resField->field.clazz->descriptor, resField->field.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) {
+        dvmThrowException("Ljava/lang/NoSuchFieldError;",
+            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->field.clazz) &&
+        !dvmInitClass(resField->field.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->field.clazz)) {
+        dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+    } else {
+        LOGVV("--- not caching resolved field %s.%s (class init=%d/%d)\n",
+            resField->field.clazz->descriptor, resField->field.name,
+            dvmIsClassInitializing(resField->field.clazz),
+            dvmIsClassInitialized(resField->field.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\n", 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..70b2294
--- /dev/null
+++ b/vm/oo/Resolve.h
@@ -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.
+ */
+/*
+ * Resolve "constant pool" references into pointers to VM structs.
+ */
+#ifndef _DALVIK_OO_RESOLVE
+#define _DALVIK_OO_RESOLVE
+
+/*
+ * "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.)
+ */
+typedef enum MethodType {
+    METHOD_UNKNOWN  = 0,
+    METHOD_DIRECT,      // <init>, private
+    METHOD_STATIC,      // static
+    METHOD_VIRTUAL,     // virtual, super
+    METHOD_INTERFACE    // interface
+} MethodType;
+
+/*
+ * 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.
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx);
+
+/*
+ * Resolve a "const-string" reference.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx);
+
+/*
+ * Return debug string constant for enum.
+ */
+const char* dvmMethodTypeStr(MethodType methodType);
+
+#endif /*_DALVIK_OO_RESOLVE*/
diff --git a/vm/oo/TypeCheck.c b/vm/oo/TypeCheck.c
new file mode 100644
index 0000000..fdd38ea
--- /dev/null
+++ b/vm/oo/TypeCheck.c
@@ -0,0 +1,248 @@
+/*
+ * 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(void)
+{
+    gDvm.instanceofCache = dvmAllocAtomicCache(INSTANCEOF_CACHE_SIZE);
+    if (gDvm.instanceofCache == NULL)
+        return false;
+    return true;
+}
+
+/*
+ * Discard the cache.
+ */
+void dvmInstanceofShutdown(void)
+{
+    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)
+    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..1397998
--- /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
+#define _DALVIK_OO_TYPECHECK
+
+/* VM startup/shutdown */
+bool dvmInstanceofStartup(void);
+void dvmInstanceofShutdown(void);
+
+
+/* used by dvmInstanceof; don't call */
+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.
+ */
+bool dvmCanPutArrayElement(const ClassObject* elemClass,
+    const ClassObject* arrayClass);
+
+#endif /*_DALVIK_OO_TYPECHECK*/
diff --git a/vm/reflect/Annotation.c b/vm/reflect/Annotation.c
new file mode 100644
index 0000000..a5007ba
--- /dev/null
+++ b/vm/reflect/Annotation.c
@@ -0,0 +1,2181 @@
+/*
+ * 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;";
+
+
+/*
+ * Perform Annotation setup.
+ */
+bool dvmReflectAnnotationStartup(void)
+{
+    Method* meth;
+
+    /*
+     * Find some standard Annotation classes.
+     */
+    gDvm.classJavaLangAnnotationAnnotationArray =
+        dvmFindArrayClass("[Ljava/lang/annotation/Annotation;", NULL);
+    gDvm.classJavaLangAnnotationAnnotationArrayArray =
+        dvmFindArrayClass("[[Ljava/lang/annotation/Annotation;", NULL);
+    if (gDvm.classJavaLangAnnotationAnnotationArray == NULL ||
+        gDvm.classJavaLangAnnotationAnnotationArrayArray == NULL)
+    {
+        LOGE("Could not find Annotation-array classes\n");
+        return false;
+    }
+
+    /*
+     * VM-specific annotation classes.
+     */
+    gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory =
+        dvmFindSystemClassNoInit("Lorg/apache/harmony/lang/annotation/AnnotationFactory;");
+    gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember =
+        dvmFindSystemClassNoInit("Lorg/apache/harmony/lang/annotation/AnnotationMember;");
+    gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray =
+        dvmFindArrayClass("[Lorg/apache/harmony/lang/annotation/AnnotationMember;", NULL);
+    if (gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory == NULL ||
+        gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember == NULL ||
+        gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray == NULL)
+    {
+        LOGE("Could not find android.lang annotation classes\n");
+        return false;
+    }
+
+    meth = dvmFindDirectMethodByDescriptor(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory,
+            "createAnnotation",
+            "(Ljava/lang/Class;[Lorg/apache/harmony/lang/annotation/AnnotationMember;)Ljava/lang/annotation/Annotation;");
+    if (meth == NULL) {
+        LOGE("Unable to find createAnnotation() in android AnnotationFactory\n");
+        return false;
+    }
+    gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation = meth;
+
+    meth = dvmFindDirectMethodByDescriptor(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember,
+            "<init>",
+            "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V");
+    if (meth == NULL) {
+        LOGE("Unable to find 4-arg constructor in android AnnotationMember\n");
+        return false;
+    }
+
+    gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init = meth;
+
+    return true;
+}
+
+/*
+ * 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(void)
+{
+    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**) 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 */
+        LOGD("resolveAmbiguousMethod: unable to find class %d\n", methodIdx);
+        return NULL;
+    }
+    if (dvmIsInterfaceClass(resClass)) {
+        /* method is part of an interface -- not expecting that */
+        LOGD("resolveAmbiguousMethod: method in interface?\n");
+        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
+ */
+typedef enum {
+    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 */
+} AnnotationResultStyle;
+
+/*
+ * 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 */
+
+    LOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]\n",
+        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*) dvmWrapPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('B'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationShort:
+        pValue->value.i = (s2) readSignedInt(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('S'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationChar:
+        pValue->value.i = (u2) readUnsignedInt(ptr, valueArg, false);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('C'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationInt:
+        pValue->value.i = readSignedInt(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('I'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationLong:
+        pValue->value.j = readSignedLong(ptr, valueArg);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('J'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationFloat:
+        pValue->value.i = readUnsignedInt(ptr, valueArg, true);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('F'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationDouble:
+        pValue->value.j = readUnsignedLong(ptr, valueArg, true);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+                        dvmFindPrimitiveClass('D'));
+            setObject = true;
+        }
+        break;
+    case kDexAnnotationBoolean:
+        pValue->value.i = (valueArg != 0);
+        if (resultStyle == kAllObjects) {
+            elemObj = (Object*) dvmWrapPrimitive(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);
+                dvmThrowExceptionWithClassMessage(
+                        "Ljava/lang/TypeNotPresentException;", 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->field.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\n", size, ptr);
+            newArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
+                size, ALLOC_DEFAULT);
+            if (newArray == NULL) {
+                LOGE("annotation element array alloc failed (%d)\n", 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 = 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:
+        LOGE("Bad annotation element value byte 0x%02x (0x%02x)\n",
+            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) {
+        LOGE("Unexpected src type class (%s)\n", 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);
+    }
+    LOGV("HEY: converting valueObj from [%s to [%s\n",
+        srcElemClass->descriptor, dstElemClass->descriptor);
+
+    ArrayObject* srcArray = (ArrayObject*) valueObj;
+    u4 length = srcArray->length;
+    ArrayObject* newArray;
+
+    newArray = dvmAllocArrayByClass(methodReturn, length, ALLOC_DEFAULT);
+    if (newArray == NULL) {
+        LOGE("Failed creating duplicate annotation class (%s %d)\n",
+            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) {
+        LOGE("Annotation array copy failed\n");
+        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 result;
+    bool failed = true;
+
+    elementNameIdx = readUleb128(pPtr);
+
+    if (!processAnnotationValue(clazz, pPtr, &avalue, kAllObjects)) {
+        LOGW("Failed processing annotation value\n");
+        goto bail;
+    }
+    valueObj = avalue.value.l;
+
+    /* new member to hold the element */
+    newMember =
+        dvmAllocObject(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember,
+        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) {
+            LOGW("WARNING: could not find annotation member %s in %s\n",
+                name, annoClass->descriptor);
+        } else {
+            methodObj = dvmCreateReflectMethodObject(annoMeth);
+            methodReturn = dvmGetBoxedReturnType(annoMeth);
+        }
+    }
+    if (newMember == NULL || nameObj == NULL || methodObj == NULL ||
+        methodReturn == NULL)
+    {
+        LOGE("Failed creating annotation element (m=%p n=%p a=%p r=%p)\n",
+            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, &result, nameObj, valueObj, methodReturn, methodObj);
+    if (dvmCheckException(self)) {
+        LOGD("Failed constructing annotation element\n");
+        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\n", ptr, typeIdx, size);
+
+    annoClass = dvmDexGetResolvedClass(clazz->pDvmDex, typeIdx);
+    if (annoClass == NULL) {
+        annoClass = dvmResolveClass(clazz, typeIdx, true);
+        if (annoClass == NULL) {
+            LOGE("Unable to resolve %s annotation class %d\n",
+                clazz->descriptor, typeIdx);
+            assert(dvmCheckException(self));
+            return NULL;
+        }
+    }
+
+    LOGV("----- processEnc ptr=%p [0x%06x]  typeIdx=%d size=%d class=%s\n",
+        *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.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray,
+            size, ALLOC_DEFAULT);
+        if (elementArray == NULL) {
+            LOGE("failed to allocate annotation member array (%d elements)\n",
+                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)) {
+        LOGD("Failed creating an annotation\n");
+        //dvmLogExceptionStackTrace();
+        goto bail;
+    }
+
+    newAnno = result.l;
+
+bail:
+    dvmReleaseTrackedAlloc((Object*) elementArray, NULL);
+    *pPtr = ptr;
+    if (newAnno == NULL && !dvmCheckException(self)) {
+        /* make sure an exception is raised */
+        dvmThrowException("Ljava/lang/RuntimeException;",
+            "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;
+    ArrayObject* annoArray;
+    int i, count;
+    u4 dstIndex;
+
+    /* we need these later; make sure they're initialized */
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory))
+        dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory);
+    if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember))
+        dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember);
+
+    /* count up the number of visible elements */
+    for (i = count = 0; i < (int) pAnnoSet->size; i++) {
+        pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+        if (pAnnoItem->visibility == visibility)
+            count++;
+    }
+
+    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).
+     */
+    dstIndex = 0;
+    for (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) {
+            dvmReleaseTrackedAlloc((Object*) annoArray, NULL);
+            return NULL;
+        }
+        dvmSetObjectArrayElement(annoArray, dstIndex, anno);
+        ++dstIndex;
+    }
+
+    return annoArray;
+}
+
+
+/*
+ * ===========================================================================
+ *      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 */
+
+    LOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]\n",
+        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:
+        LOGE("Bad annotation element value byte 0x%02x\n", 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) {
+        LOGW("%s annotation lacks '%s' member\n", 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) {
+        LOGW("%s %s has wrong type (0x%02x, expected 0x%02x)\n",
+            debugAnnoName, annoName, avalue.type, expectedType);
+        return GAV_FAILED;
+    }
+
+    return 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;
+}
+
+/*
+ * 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(obj->clazz == gDvm.classJavaLangClass);
+    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(obj->clazz == gDvm.classJavaLangClass);
+            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) {
+        LOGW("EnclosingMethod annotation lacks 'value' member\n");
+        return NULL;
+    }
+
+    /* parse it, verify the type */
+    AnnotationValue avalue;
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
+        LOGW("EnclosingMethod parse failed\n");
+        return NULL;
+    }
+    if (avalue.type != kDexAnnotationMethod) {
+        LOGW("EnclosingMethod value has wrong type (0x%02x, expected 0x%02x)\n",
+            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) {
+        LOGW("InnerClass annotation lacks 'name' member\n");
+        return false;
+    }
+
+    /* parse it into an Object */
+    AnnotationValue avalue;
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
+        LOGD("processAnnotationValue failed on InnerClass member 'name'\n");
+        return false;
+    }
+
+    /* make sure it has the expected format */
+    if (avalue.type != kDexAnnotationNull &&
+        avalue.type != kDexAnnotationString)
+    {
+        LOGW("InnerClass name has bad type (0x%02x, expected STRING or NULL)\n",
+            avalue.type);
+        return false;
+    }
+
+    *pName = (StringObject*) avalue.value.l;
+    assert(*pName == NULL || (*pName)->obj.clazz == gDvm.classJavaLangString);
+
+    ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "accessFlags");
+    if (ptr == NULL) {
+        LOGW("InnerClass annotation lacks 'accessFlags' member\n");
+        return false;
+    }
+
+    /* parse it, verify the type */
+    if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
+        LOGW("InnerClass accessFlags parse failed\n");
+        return false;
+    }
+    if (avalue.type != kDexAnnotationInt) {
+        LOGW("InnerClass value has wrong type (0x%02x, expected 0x%02x)\n",
+            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.
+ */
+static u4 getMethodIdx(const Method* method)
+{
+    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);
+        LOGE("Unable to find method %s.%s %s in DEX file!\n",
+            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 = getMethodIdx(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;
+}
+
+/*
+ * 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) {
+        LOGW("AnnotationDefault annotation lacks 'value'\n");
+        return NULL;
+    }
+    if ((*ptr & kDexAnnotationValueTypeMask) != kDexAnnotationAnnotation) {
+        LOGW("AnnotationDefault value has wrong type (0x%02x)\n",
+            *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)) {
+        LOGD("processAnnotationValue failed on default for '%s'\n",
+            method->name);
+        return NULL;
+    }
+
+    /* convert the return type, if necessary */
+    ClassObject* methodReturn = dvmGetBoxedReturnType(method);
+    Object* obj = 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 getMethodIdx.
+ */
+static u4 getFieldIdx(const Field* field)
+{
+    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 */
+        LOGE("Unable to find field %s.%s %s in DEX file!\n",
+            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;
+    DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+    const DexAnnotationsDirectoryItem* pAnnoDir;
+    const DexFieldAnnotationsItem* pFieldList;
+
+    pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+    if (pAnnoDir == NULL)
+        return NULL;
+
+    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 = getFieldIdx(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;
+}
+
+/*
+ * 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) {
+        LOGW("annotation set ref array alloc failed\n");
+        goto bail;
+    }
+
+    for (idx = 0; idx < count; idx++) {
+        Thread* self = dvmThreadSelf();
+        const DexAnnotationSetRefItem* pItem;
+        const DexAnnotationSetItem* pAnnoSet;
+        Object *annoSet;
+
+        pItem = dexGetParameterAnnotationSetRef(pAnnoSetList, idx);
+        pAnnoSet = dexGetSetRefItemItem(pDexFile, pItem);
+        annoSet = (Object *)processAnnotationSet(clazz,
+                                                 pAnnoSet,
+                                                 kDexVisibilityRuntime);
+        if (annoSet == NULL) {
+            LOGW("processAnnotationSet failed\n");
+            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 = getMethodIdx(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) {
+        LOGE("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.c b/vm/reflect/Proxy.c
new file mode 100644
index 0000000..eef658d
--- /dev/null
+++ b/vm/reflect/Proxy.c
@@ -0,0 +1,1103 @@
+/*
+ * 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
+
+
+/*
+ * Perform Proxy setup.
+ */
+bool dvmReflectProxyStartup()
+{
+    /*
+     * Standard methods we must provide in our proxy.
+     */
+    Method* methE;
+    Method* methH;
+    Method* methT;
+    Method* methF;
+    methE = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject,
+                "equals", "(Ljava/lang/Object;)Z");
+    methH = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject,
+                "hashCode", "()I");
+    methT = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject,
+                "toString", "()Ljava/lang/String;");
+    methF = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject,
+                "finalize", "()V");
+    if (methE == NULL || methH == NULL || methT == NULL || methF == NULL) {
+        LOGE("Could not find equals/hashCode/toString/finalize in Object\n");
+        return false;
+    }
+    gDvm.voffJavaLangObject_equals = methE->methodIndex;
+    gDvm.voffJavaLangObject_hashCode = methH->methodIndex;
+    gDvm.voffJavaLangObject_toString = methT->methodIndex;
+    gDvm.voffJavaLangObject_finalize = methF->methodIndex;
+
+    /*
+     * The prototype signature needs to be cloned from a method in a
+     * "real" DEX file.  We declared this otherwise unused method just
+     * for this purpose.
+     */
+    ClassObject* proxyClass;
+    Method* meth;
+    proxyClass = dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;");
+    if (proxyClass == NULL) {
+        LOGE("No java.lang.reflect.Proxy\n");
+        return false;
+    }
+    meth = dvmFindDirectMethodByDescriptor(proxyClass, "constructorPrototype",
+                "(Ljava/lang/reflect/InvocationHandler;)V");
+    if (meth == NULL) {
+        LOGE("Could not find java.lang.Proxy.constructorPrototype()\n");
+        return false;
+    }
+    gDvm.methJavaLangReflectProxy_constructorPrototype = meth;
+
+    /*
+     * Get the offset of the "h" field in Proxy.
+     */
+    gDvm.offJavaLangReflectProxy_h = dvmFindFieldOffset(proxyClass, "h",
+        "Ljava/lang/reflect/InvocationHandler;");
+    if (gDvm.offJavaLangReflectProxy_h < 0) {
+        LOGE("Unable to find 'h' field in java.lang.Proxy\n");
+        return false;
+    }
+
+    return true;
+}
+
+
+/*
+ * 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)
+{
+    int result = -1;
+    char* nameStr = NULL;
+    Method** methods = NULL;
+    ArrayObject* throws = NULL;
+    ClassObject* newClass = NULL;
+    int i;
+
+    nameStr = dvmCreateCstrFromString(str);
+    if (nameStr == NULL) {
+        dvmThrowException("Ljava/lang/IllegalArgumentException;",
+            "missing name");
+        goto bail;
+    }
+
+    LOGV("+++ Generate proxy class '%s' %p from %d interface classes\n",
+        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).
+     */
+
+    /*
+     * Generate a temporary list of virtual methods.
+     */
+    int methodCount = -1;
+    if (!gatherMethods(interfaces, &methods, &throws, &methodCount))
+        goto bail;
+
+    /*
+     * Allocate storage for the class object and set some basic fields.
+     */
+    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass) +
+                                        kProxySFieldCount * sizeof(StaticField),
+                                        ALLOC_DEFAULT);
+    if (newClass == NULL)
+        goto bail;
+    DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
+    dvmSetClassSerialNumber(newClass);
+    newClass->descriptorAlloc = dvmNameToDescriptor(nameStr);
+    newClass->descriptor = newClass->descriptorAlloc;
+    newClass->accessFlags = ACC_PUBLIC | ACC_FINAL;
+    dvmSetFieldObject((Object *)newClass,
+                      offsetof(ClassObject, super),
+                      (Object *)gDvm.classJavaLangReflectProxy);
+    newClass->primitiveType = PRIM_NOT;
+    dvmSetFieldObject((Object *)newClass,
+                      offsetof(ClassObject, classLoader),
+                      (Object *)loader);
+#if WITH_HPROF && WITH_HPROF_STACK
+    hprofFillInStackTrace(newClass);
+#endif
+
+    /*
+     * 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.
+     */
+    newClass->virtualMethodCount = methodCount;
+    newClass->virtualMethods = (Method*) dvmLinearAlloc(newClass->classLoader,
+            newClass->virtualMethodCount * sizeof(Method));
+    for (i = 0; i < newClass->virtualMethodCount; i++) {
+        createHandlerMethod(newClass, &newClass->virtualMethods[i],methods[i]);
+    }
+    dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods);
+
+    /*
+     * Add interface list.
+     */
+    int interfaceCount = interfaces->length;
+    ClassObject** ifArray = (ClassObject**) interfaces->contents;
+    newClass->interfaceCount = interfaceCount;
+    newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader,
+                                sizeof(ClassObject*) * interfaceCount);
+    for (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->field.clazz = newClass;
+    sfield->field.name = "throws";
+    sfield->field.signature = "[[Ljava/lang/Throwable;";
+    sfield->field.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)) {
+        LOGD("Proxy class link failed\n");
+        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)) {
+        LOGE("ERROR: attempted to generate %s more than once\n",
+            newClass->descriptor);
+        goto bail;
+    }
+
+    result = 0;
+
+bail:
+    free(nameStr);
+    free(methods);
+    if (result != 0) {
+        /* must free innards explicitly if we didn't finish linking */
+        dvmFreeClassInnards(newClass);
+        newClass = NULL;
+        if (!dvmCheckException(dvmThreadSelf())) {
+            /* throw something */
+            dvmThrowException("Ljava/lang/RuntimeException;", NULL);
+        }
+    }
+
+    /* allow the GC to free these when nothing else has a reference */
+    dvmReleaseTrackedAlloc((Object*) throws, NULL);
+    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+    return newClass;
+}
+
+
+/*
+ * 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**) interfaces->contents;
+
+    for (i = 0; i < numInterfaces; i++, classes++) {
+        ClassObject* clazz = *classes;
+
+        LOGVV("---  %s virtualMethodCount=%d\n",
+            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\n",
+                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**) 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;
+
+    //LOGI("gathered methods:\n");
+    //for (i = 0; i < actualCount; i++) {
+    //    LOGI(" %d: %s.%s\n",
+    //        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.
+                 */
+                LOGV("MATCH on %s.%s and %s.%s\n",
+                    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) {
+                LOGV("BEST %d %s.%s -> %d\n", 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)
+                    {
+                        LOGV("DEL %d %s.%s\n", 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) {
+                        LOGE("common-throw array alloc failed\n");
+                        return -1;
+                    }
+
+                    contents = (Object**) throwArray->contents;
+                    for (ent = 0; ent < commonCount; ent++) {
+                        contents[ent] = (Object*)
+                            dvmPointerSetGetEntry(commonThrows, ent);
+                    }
+
+                    /* add it to the array of arrays */
+                    contents = (Object**) 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 {
+                LOGV("BEST not %d\n", i);
+            }
+        } else {
+            /*
+             * Singleton.  Copy the entry and NULL it out.
+             */
+            LOGV("COPY singleton %d %s.%s -> %d\n", 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**) 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) {
+            LOGV("BAD DUPE: %d %s.%s\n", i,
+                allMethods[i]->clazz->descriptor, allMethods[i]->name);
+            dvmThrowException("Ljava/lang/IllegalArgumentException;",
+                "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**)exceptionArray->contents;
+    int len = exceptionArray->length;
+    int i, j;
+
+    /*
+     * Consider all pairs of classes.  If one is the subclass of the other,
+     * null out the subclass.
+     */
+    for (i = 0; i < len-1; i++) {
+        if (classes[i] == NULL)
+            continue;
+        for (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;
+        int i;
+
+        contents = (const ClassObject**) exceptionArray->contents;
+        for (i = 0; i < (int) 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);
+
+    int mixLen = dvmPointerSetGetCount(throws);
+    const ClassObject* mixSet[mixLen];
+
+    int declLen = exceptionArray->length;
+    const ClassObject** declSet = (const ClassObject**)exceptionArray->contents;
+
+    int i, j;
+
+    /* grab a local copy to work on */
+    for (i = 0; i < mixLen; i++) {
+        mixSet[i] = dvmPointerSetGetEntry(throws, i);
+    }
+
+    for (i = 0; i < mixLen; i++) {
+        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 (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)
+{
+    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.
+    ArrayObject* argArray = NULL;
+    int argCount;
+    Object** argObjects;
+    bool failed = true;
+
+    /* count args */
+    argCount = dexProtoGetParameterCount(&method->prototype);
+
+    /* allocate storage */
+    argArray = dvmAllocArray(gDvm.classJavaLangObjectArray, argCount,
+        kObjectArrayRefWidth, ALLOC_DEFAULT);
+    if (argArray == NULL)
+        goto bail;
+    argObjects = (Object**) argArray->contents;
+
+    /*
+     * Fill in the array.
+     */
+
+    int srcIndex = 0;
+
+    argCount = 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[argCount] = (Object*) dvmWrapPrimitive(value,
+                dvmFindPrimitiveClass(descChar));
+            /* argObjects is tracked, don't need to hold this too */
+            dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
+            argCount++;
+            break;
+        case 'D':
+        case 'J':
+            value.j = dvmGetArgLong(args, srcIndex);
+            srcIndex += 2;
+            argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
+                dvmFindPrimitiveClass(descChar));
+            dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
+            argCount++;
+            break;
+        case '[':
+        case 'L':
+            argObjects[argCount++] = (Object*) args[srcIndex++];
+            break;
+        }
+    }
+
+    failed = false;
+
+bail:
+    if (failed) {
+        dvmReleaseTrackedAlloc((Object*)argArray, NULL);
+        argArray = NULL;
+    }
+    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) {
+        LOGE("Unable to find invoke()\n");
+        dvmAbort();
+    }
+
+    LOGV("invoke: %s.%s, this=%p, handler=%s\n",
+        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);
+        LOGE("Could not determine return type for '%s'\n", desc);
+        free(desc);
+        assert(dvmCheckException(self));
+        goto bail;
+    }
+    LOGV("  return type will be %s\n", 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.
+     */
+    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\n");
+    } else if (invokeResult.l == NULL) {
+        if (dvmIsPrimitiveClass(returnType)) {
+            dvmThrowException("Ljava/lang/NullPointerException;",
+                "null result when primitive expected");
+            goto bail;
+        }
+        pResult->l = NULL;
+    } else {
+        if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) {
+            dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;",
+                ((Object*)invokeResult.l)->clazz->descriptor);
+            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)
+{
+    const ArrayObject* throws;
+    const ArrayObject* methodThrows;
+    const Object** contents;
+    const ClassObject** classes;
+
+    if (!dvmIsCheckedException(throwable))
+        return false;
+
+    const StaticField* sfield = &method->clazz->sfields[kThrowsField];
+    throws = (ArrayObject*) dvmGetStaticFieldObject(sfield);
+
+    int methodIndex = method - method->clazz->virtualMethods;
+    assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount);
+
+    contents = (const Object**) throws->contents;
+    methodThrows = (ArrayObject*) contents[methodIndex];
+
+    if (methodThrows == NULL) {
+        /* no throws declared, must wrap all checked exceptions */
+        //printf("+++ methodThrows[%d] is null, wrapping all\n", methodIndex);
+        return true;
+    }
+
+    int throwCount = methodThrows->length;
+    classes = (const ClassObject**) methodThrows->contents;
+    int i;
+
+    //printf("%s.%s list:\n", method->clazz->descriptor, method->name);
+    //for (i = 0; i < throwCount; i++)
+    //    printf(" %d: %s\n", i, classes[i]->descriptor);
+
+    for (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.c b/vm/reflect/Reflect.c
new file mode 100644
index 0000000..7e93f19
--- /dev/null
+++ b/vm/reflect/Reflect.c
@@ -0,0 +1,1253 @@
+/*
+ * 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>
+
+/*
+ * Cache some classes.
+ */
+bool dvmReflectStartup(void)
+{
+    gDvm.classJavaLangReflectAccessibleObject =
+        dvmFindSystemClassNoInit("Ljava/lang/reflect/AccessibleObject;");
+    gDvm.classJavaLangReflectConstructor =
+        dvmFindSystemClassNoInit("Ljava/lang/reflect/Constructor;");
+    gDvm.classJavaLangReflectConstructorArray =
+        dvmFindArrayClass("[Ljava/lang/reflect/Constructor;", NULL);
+    gDvm.classJavaLangReflectField =
+        dvmFindSystemClassNoInit("Ljava/lang/reflect/Field;");
+    gDvm.classJavaLangReflectFieldArray =
+        dvmFindArrayClass("[Ljava/lang/reflect/Field;", NULL);
+    gDvm.classJavaLangReflectMethod =
+        dvmFindSystemClassNoInit("Ljava/lang/reflect/Method;");
+    gDvm.classJavaLangReflectMethodArray =
+        dvmFindArrayClass("[Ljava/lang/reflect/Method;", NULL);
+    gDvm.classJavaLangReflectProxy =
+        dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;");
+    if (gDvm.classJavaLangReflectAccessibleObject == NULL ||
+        gDvm.classJavaLangReflectConstructor == NULL ||
+        gDvm.classJavaLangReflectConstructorArray == NULL ||
+        gDvm.classJavaLangReflectField == NULL ||
+        gDvm.classJavaLangReflectFieldArray == NULL ||
+        gDvm.classJavaLangReflectMethod == NULL ||
+        gDvm.classJavaLangReflectMethodArray == NULL ||
+        gDvm.classJavaLangReflectProxy == NULL)
+    {
+        LOGE("Could not find one or more reflection classes\n");
+        return false;
+    }
+
+    gDvm.methJavaLangReflectConstructor_init =
+        dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectConstructor, "<init>",
+        "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;I)V");
+    gDvm.methJavaLangReflectField_init =
+        dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectField, "<init>",
+        "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;I)V");
+    gDvm.methJavaLangReflectMethod_init =
+        dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectMethod, "<init>",
+        "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;I)V");
+    if (gDvm.methJavaLangReflectConstructor_init == NULL ||
+        gDvm.methJavaLangReflectField_init == NULL ||
+        gDvm.methJavaLangReflectMethod_init == NULL)
+    {
+        LOGE("Could not find reflection constructors\n");
+        return false;
+    }
+
+    gDvm.classJavaLangClassArray =
+        dvmFindArrayClass("[Ljava/lang/Class;", NULL);
+    gDvm.classJavaLangObjectArray =
+        dvmFindArrayClass("[Ljava/lang/Object;", NULL);
+    if (gDvm.classJavaLangClassArray == NULL ||
+        gDvm.classJavaLangObjectArray == NULL)
+    {
+        LOGE("Could not find class-array or object-array class\n");
+        return false;
+    }
+
+    gDvm.offJavaLangReflectAccessibleObject_flag =
+        dvmFindFieldOffset(gDvm.classJavaLangReflectAccessibleObject, "flag",
+            "Z");
+
+    gDvm.offJavaLangReflectConstructor_slot =
+        dvmFindFieldOffset(gDvm.classJavaLangReflectConstructor, "slot", "I");
+    gDvm.offJavaLangReflectConstructor_declClass =
+        dvmFindFieldOffset(gDvm.classJavaLangReflectConstructor,
+            "declaringClass", "Ljava/lang/Class;");
+
+    gDvm.offJavaLangReflectField_slot =
+        dvmFindFieldOffset(gDvm.classJavaLangReflectField, "slot", "I");
+    gDvm.offJavaLangReflectField_declClass =
+        dvmFindFieldOffset(gDvm.classJavaLangReflectField,
+            "declaringClass", "Ljava/lang/Class;");
+
+    gDvm.offJavaLangReflectMethod_slot =
+        dvmFindFieldOffset(gDvm.classJavaLangReflectMethod, "slot", "I");
+    gDvm.offJavaLangReflectMethod_declClass =
+        dvmFindFieldOffset(gDvm.classJavaLangReflectMethod,
+            "declaringClass", "Ljava/lang/Class;");
+
+    if (gDvm.offJavaLangReflectAccessibleObject_flag < 0 ||
+        gDvm.offJavaLangReflectConstructor_slot < 0 ||
+        gDvm.offJavaLangReflectConstructor_declClass < 0 ||
+        gDvm.offJavaLangReflectField_slot < 0 ||
+        gDvm.offJavaLangReflectField_declClass < 0 ||
+        gDvm.offJavaLangReflectMethod_slot < 0 ||
+        gDvm.offJavaLangReflectMethod_declClass < 0)
+    {
+        LOGE("Could not find reflection fields\n");
+        return false;
+    }
+
+    if (!dvmReflectProxyStartup())
+        return false;
+    if (!dvmReflectAnnotationStartup())
+        return false;
+
+    return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmReflectShutdown(void)
+{
+    // nothing to do
+}
+
+/*
+ * 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) {
+            LOGE("Couldn't find '%s'\n", *ccp);
+            return false;
+        }
+
+        if (clazz->ifieldCount != 1) {
+            LOGE("Found %d instance fields in '%s'\n",
+                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) {
+        LOGW("Unable to match class for part: '%s'\n", *pSignature);
+        dvmClearException(dvmThreadSelf());
+        dvmThrowException("Ljava/lang/NoSuchMethodException;", NULL);
+    }
+    *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)
+{
+    ArrayObject* classArray;
+    char* signature = *pSignature;
+    char* cp;
+    int i, count;
+
+    assert(*signature == '(');
+    signature++;
+
+    /* count up the number of parameters */
+    count = 0;
+    cp = signature;
+    while (*cp != ')') {
+        count++;
+
+        if (*cp == '[') {
+            while (*++cp == '[')
+                ;
+        }
+        if (*cp == 'L') {
+            while (*++cp != ';')
+                ;
+        }
+        cp++;
+    }
+    LOGVV("REFLECT found %d parameters in '%s'\n", count, *pSignature);
+
+    /* create an array to hold them */
+    classArray = dvmAllocArray(gDvm.classJavaLangClassArray, count,
+                    kObjectArrayRefWidth, ALLOC_DEFAULT);
+    if (classArray == NULL)
+        return NULL;
+
+    /* fill it in */
+    cp = signature;
+    for (i = 0; i < count; i++) {
+        ClassObject* clazz;
+
+        clazz = convertSignaturePartToClass(&cp, defClass);
+        if (clazz == NULL) {
+            assert(dvmCheckException(dvmThreadSelf()));
+            return NULL;
+        }
+        LOGVV("REFLECT  %d: '%s'\n", 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*) &clazz->sfields[slot];
+    } else {
+        assert(slot < clazz->ifieldCount);
+        return (Field*) &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;
+
+    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);
+
+    JValue unused;
+    dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectField_init,
+        fieldObj, &unused, clazz, type, nameObj, slot);
+    if (dvmCheckException(dvmThreadSelf())) {
+        LOGD("Field class init threw exception\n");
+        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)
+{
+    ArrayObject* fieldArray = NULL;
+    int i, count;
+
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+        dvmInitClass(gDvm.classJavaLangReflectField);
+
+    /* count #of fields */
+    if (!publicOnly)
+        count = clazz->sfieldCount + clazz->ifieldCount;
+    else {
+        count = 0;
+        for (i = 0; i < clazz->sfieldCount; i++) {
+            if ((clazz->sfields[i].field.accessFlags & ACC_PUBLIC) != 0)
+                count++;
+        }
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            if ((clazz->ifields[i].field.accessFlags & ACC_PUBLIC) != 0)
+                count++;
+        }
+    }
+
+    /* create the Field[] array */
+    fieldArray = dvmAllocArray(gDvm.classJavaLangReflectFieldArray, count,
+                    kObjectArrayRefWidth, ALLOC_DEFAULT);
+    if (fieldArray == NULL)
+        return NULL;
+
+    /* populate */
+    size_t fieldCount = 0;
+    for (i = 0; i < clazz->sfieldCount; i++) {
+        if (!publicOnly ||
+            (clazz->sfields[i].field.accessFlags & ACC_PUBLIC) != 0)
+        {
+            Object* field = createFieldObject(&clazz->sfields[i].field, clazz);
+            if (field == NULL) {
+                goto fail;
+            }
+            dvmSetObjectArrayElement(fieldArray, fieldCount, field);
+            dvmReleaseTrackedAlloc(field, NULL);
+            ++fieldCount;
+        }
+    }
+    for (i = 0; i < clazz->ifieldCount; i++) {
+        if (!publicOnly ||
+            (clazz->ifields[i].field.accessFlags & ACC_PUBLIC) != 0)
+        {
+            Object* field = createFieldObject(&clazz->ifields[i].field, 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;
+
+    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);
+
+    JValue unused;
+    dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectConstructor_init,
+        consObj, &unused, meth->clazz, params, exceptions, slot);
+    if (dvmCheckException(dvmThreadSelf())) {
+        LOGD("Constructor class init threw exception\n");
+        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)
+{
+    ArrayObject* consArray;
+    Method* meth;
+    int i, count;
+
+    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.
+     */
+    count = 0;
+    meth = clazz->directMethods;
+    for (i = 0; i < clazz->directMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth))
+        {
+            count++;
+        }
+    }
+
+    /*
+     * Create an array of Constructor objects.
+     */
+    consArray = dvmAllocArray(gDvm.classJavaLangReflectConstructorArray, count,
+                kObjectArrayRefWidth, ALLOC_DEFAULT);
+    if (consArray == NULL)
+        return NULL;
+
+    /*
+     * Fill out the array.
+     */
+    meth = clazz->directMethods;
+    size_t consObjCount = 0;
+    for (i = 0; i < clazz->directMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth))
+        {
+            Object* consObj = createConstructorObject(meth);
+            if (consObj == NULL)
+                goto fail;
+            dvmSetObjectArrayElement(consArray, consObjCount, consObj);
+            ++consObjCount;
+            dvmReleaseTrackedAlloc(consObj, NULL);
+        }
+    }
+
+    assert(consObjCount == consArray->length);
+
+    /* caller must call dvmReleaseTrackedAlloc */
+    return consArray;
+
+fail:
+    dvmReleaseTrackedAlloc((Object*) consArray, NULL);
+    return NULL;
+}
+
+/*
+ * 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;
+
+    if (dvmCheckException(dvmThreadSelf())) {
+        LOGW("WARNING: dvmCreateReflectMethodObject called with "
+             "exception pending\n");
+        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);
+
+    JValue unused;
+    dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectMethod_init,
+        methObj, &unused, meth->clazz, params, exceptions, returnType,
+        nameObj, slot);
+    if (dvmCheckException(dvmThreadSelf())) {
+        LOGD("Method class init threw exception\n");
+        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)
+{
+    ArrayObject* methodArray;
+    Method* meth;
+    int i, count;
+
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+        dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+    /*
+     * Count up the #of relevant methods.
+     *
+     * Ignore virtual Miranda methods and direct class/object constructors.
+     */
+    count = 0;
+    meth = clazz->virtualMethods;
+    for (i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            !dvmIsMirandaMethod(meth))
+        {
+            count++;
+        }
+    }
+    meth = clazz->directMethods;
+    for (i = 0; i < clazz->directMethodCount; i++, meth++) {
+        if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+            meth->name[0] != '<')
+        {
+            count++;
+        }
+    }
+
+    /*
+     * Create an array of Method objects.
+     */
+    methodArray = dvmAllocArray(gDvm.classJavaLangReflectMethodArray, count,
+                kObjectArrayRefWidth, ALLOC_DEFAULT);
+    if (methodArray == NULL)
+        return NULL;
+
+
+    /*
+     * Fill out the array.
+     */
+    meth = clazz->virtualMethods;
+    size_t methObjCount = 0;
+    for (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 (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;
+}
+
+/*
+ * 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)
+{
+    ArrayObject* interfaceArray;
+
+    if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+        dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+    /*
+     * Create an array of Class objects.
+     */
+    int count = clazz->interfaceCount;
+    interfaceArray = dvmAllocArray(gDvm.classJavaLangClassArray, count,
+                kObjectArrayRefWidth, 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/")
+    const char* name;
+
+    if (arg == NULL)
+        return PRIM_NOT;
+
+    name = arg->obj.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 bytes 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 {
+        OK4, OK8, ItoJ,
+        ItoD, JtoD, FtoD,
+        ItoF, JtoF,
+        bad, kMax
+    };
+    /* [src][dst] */
+    static const int kConvMode[kMax][kMax] = {
+    /*FROM *TO: bool    char    float   double  byte    short   int     long */
+    /*bool */ { OK4,    bad,    bad,    bad,    bad,    bad,    bad,    bad  },
+    /*char */ { bad,    OK4,    ItoF,   ItoD,   bad,    bad,    OK4,    ItoJ },
+    /*float*/ { bad,    bad,    OK4,    FtoD,   bad,    bad,    bad,    bad  },
+    /*doubl*/ { bad,    bad,    bad,    OK8,    bad,    bad,    bad,    bad  },
+    /*byte */ { bad,    bad,    ItoF,   ItoD,   OK4,    OK4,    OK4,    ItoJ },
+    /*short*/ { bad,    bad,    ItoF,   ItoD,   bad,    OK4,    OK4,    ItoJ },
+    /*int  */ { bad,    bad,    ItoF,   ItoD,   bad,    bad,    OK4,    ItoJ },
+    /*long */ { bad,    bad,    JtoF,   JtoD,   bad,    bad,    bad,    OK8  },
+    };
+    int result;
+
+    assert(srcType != PRIM_NOT && dstType != PRIM_NOT &&
+           srcType != PRIM_VOID && dstType != PRIM_VOID);
+    result = kConvMode[srcType][dstType];
+
+    //LOGV("+++ convprim: src=%d dst=%d result=%d\n", srcType, dstType, result);
+
+    switch (result) {
+    case OK4:
+        *dstPtr = *srcPtr;
+        return 1;
+    case OK8:
+        *(s8*)dstPtr = *(s8*)srcPtr;
+        return 2;
+    case ItoJ:
+        *(s8*)dstPtr = (s8) (*(s4*) srcPtr);
+        return 2;
+    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;
+    case ItoF:
+        *(float*)dstPtr = (float) (*(int*) srcPtr);
+        return 1;
+    case JtoF:
+        *(float*)dstPtr = (float) (*(long long*) srcPtr);
+        return 1;
+    case bad:
+        LOGV("convert primitive: prim %d to %d not allowed\n",
+            srcType, dstType);
+        return -1;
+    default:
+        assert(false);
+        return -1;
+    }
+}
+
+/*
+ * 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 < 0) {     // didn't pass a boxed primitive in
+            LOGVV("conv arg: type '%s' not boxed primitive\n",
+                arg->obj.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->obj.clazz, type)) {
+            *destPtr = (s4) arg;
+            retVal = 1;
+        } else {
+            LOGVV("Arg %p (%s) not compatible with %s\n",
+                arg, arg->obj.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* dvmWrapPrimitive(JValue value, ClassObject* returnType)
+{
+    static const char* boxTypes[] = {       // order from enum PrimitiveType
+        "Ljava/lang/Boolean;",
+        "Ljava/lang/Character;",
+        "Ljava/lang/Float;",
+        "Ljava/lang/Double;",
+        "Ljava/lang/Byte;",
+        "Ljava/lang/Short;",
+        "Ljava/lang/Integer;",
+        "Ljava/lang/Long;"
+    };
+    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(value.l, NULL);
+        return (DataObject*) value.l;
+    }
+
+    assert(typeIndex >= 0 && typeIndex < PRIM_MAX);
+    if (typeIndex == PRIM_VOID)
+        return NULL;
+
+    classDescriptor = boxTypes[typeIndex];
+
+    wrapperClass = dvmFindSystemClass(classDescriptor);
+    if (wrapperClass == NULL) {
+        LOGW("Unable to find '%s'\n", 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 dvmUnwrapPrimitive(Object* value, ClassObject* returnType,
+    JValue* pResult)
+{
+    PrimitiveType typeIndex = returnType->primitiveType;
+    PrimitiveType valueIndex;
+
+    if (typeIndex == PRIM_NOT) {
+        if (value != NULL && !dvmInstanceof(value->clazz, returnType)) {
+            LOGD("wrong object type: %s %s\n",
+                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)
+    {
+        LOGV("Prim conversion failed\n");
+        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);
+        LOGE("Bad return type in signature '%s'\n", desc);
+        free(desc);
+        dvmThrowException("Ljava/lang/InternalError;", 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..16e7148
--- /dev/null
+++ b/vm/reflect/Reflect.h
@@ -0,0 +1,239 @@
+/*
+ * 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
+#define _DALVIK_REFLECT_REFLECT
+
+bool dvmReflectStartup(void);
+bool dvmReflectProxyStartup(void);
+bool dvmReflectAnnotationStartup(void);
+void dvmReflectShutdown(void);
+
+/*
+ * 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 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 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);
+
+/*
+ * Create a wrapper object for a primitive data type.  If "returnType" is
+ * not primitive, this just returns "value" cast to an object.
+ */
+DataObject* dvmWrapPrimitive(JValue value, ClassObject* returnType);
+
+/*
+ * Unwrap a boxed primitive.  If "returnType" is not primitive, this just
+ * returns "value" cast into a JValue.
+ */
+bool dvmUnwrapPrimitive(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);
+
+/*
+ * 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.
+ */
+typedef struct AnnotationValue {
+    JValue  value;
+    u1      type;
+} AnnotationValue;
+
+
+/**
+ * Iterator structure for iterating over DexEncodedArray instances. The
+ * structure should be treated as opaque.
+ */
+typedef struct {
+    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 */
+} EncodedArrayIterator;
+
+/**
+ * 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*/
diff --git a/vm/test/AtomicTest.c b/vm/test/AtomicTest.c
new file mode 100644
index 0000000..62333d0
--- /dev/null
+++ b/vm/test/AtomicTest.c
@@ -0,0 +1,383 @@
+/*
+ * 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 provides a handful of correctness and speed tests on our atomic
+ * operations.
+ *
+ * This doesn't really belong here, but we currently lack a better place
+ * for it, so this will do for now.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <cutils/atomic.h>
+#ifdef __arm__
+# include <machine/cpu-features.h>
+#endif
+
+#define USE_ATOMIC      1
+#define THREAD_COUNT    10
+#define ITERATION_COUNT 500000
+
+#ifdef HAVE_ANDROID_OS
+/*#define TEST_BIONIC 1*/
+#endif
+
+
+#ifdef TEST_BIONIC
+extern int __atomic_cmpxchg(int old, int _new, volatile int *ptr);
+extern int __atomic_swap(int _new, volatile int *ptr);
+extern int __atomic_dec(volatile int *ptr);
+extern int __atomic_inc(volatile int *ptr);
+#endif
+
+static pthread_mutex_t waitLock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t waitCond = PTHREAD_COND_INITIALIZER;
+
+static volatile int threadsStarted = 0;
+
+/* results */
+static int incTest = 0;
+static int decTest = 0;
+static int addTest = 0;
+static int andTest = 0;
+static int orTest = 0;
+static int casTest = 0;
+static int failingCasTest = 0;
+static int swapTest = 0;
+static int64_t wideCasTest = 0x6600000077000000LL;
+
+/*
+ * Get a relative time value.
+ */
+static int64_t getRelativeTimeNsec(void)
+{
+#define HAVE_POSIX_CLOCKS
+#ifdef HAVE_POSIX_CLOCKS
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return (int64_t) now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    return (int64_t) now.tv_sec*1000000000LL + now.tv_usec * 1000LL;
+#endif
+}
+
+
+/*
+ * Non-atomic implementations, for comparison.
+ *
+ * If these get inlined the compiler may figure out what we're up to and
+ * completely elide the operations.
+ */
+static void incr(void) __attribute__((noinline));
+static void decr(void) __attribute__((noinline));
+static void add(int addVal) __attribute__((noinline));
+static int compareAndSwap(int oldVal, int newVal, int* addr) __attribute__((noinline));
+static int compareAndSwapWide(int64_t oldVal, int64_t newVal, int64_t* addr) __attribute__((noinline));
+
+static void incr(void)
+{
+    incTest++;
+}
+static void decr(void)
+{
+    decTest--;
+}
+static void add(int32_t addVal)
+{
+    addTest += addVal;
+}
+static int compareAndSwap(int32_t oldVal, int32_t newVal, int32_t* addr)
+{
+    if (*addr == oldVal) {
+        *addr = newVal;
+        return 0;
+    }
+    return 1;
+}
+static int compareAndSwapWide(int64_t oldVal, int64_t newVal, int64_t* addr)
+{
+    if (*addr == oldVal) {
+        *addr = newVal;
+        return 0;
+    }
+    return 1;
+}
+
+/*
+ * Exercise several of the atomic ops.
+ */
+static void doAtomicTest(int num)
+{
+    int addVal = (num & 0x01) + 1;
+
+    int i;
+    for (i = 0; i < ITERATION_COUNT; i++) {
+        if (USE_ATOMIC) {
+            android_atomic_inc(&incTest);
+            android_atomic_dec(&decTest);
+            android_atomic_add(addVal, &addTest);
+
+            int val;
+            do {
+                val = casTest;
+            } while (android_atomic_release_cas(val, val+3, &casTest) != 0);
+            do {
+                val = casTest;
+            } while (android_atomic_acquire_cas(val, val-1, &casTest) != 0);
+
+            int64_t wval;
+            do {
+                wval = dvmQuasiAtomicRead64(&wideCasTest);
+            } while (dvmQuasiAtomicCas64(wval,
+                        wval + 0x0000002000000001LL, &wideCasTest) != 0);
+            do {
+                wval = dvmQuasiAtomicRead64(&wideCasTest);
+            } while (dvmQuasiAtomicCas64(wval,
+                        wval - 0x0000002000000001LL, &wideCasTest) != 0);
+        } else {
+            incr();
+            decr();
+            add(addVal);
+
+            int val;
+            do {
+                val = casTest;
+            } while (compareAndSwap(val, val+3, &casTest) != 0);
+            do {
+                val = casTest;
+            } while (compareAndSwap(val, val-1, &casTest) != 0);
+
+            int64_t wval;
+            do {
+                wval = wideCasTest;
+            } while (compareAndSwapWide(wval,
+                        wval + 0x0000002000000001LL, &wideCasTest) != 0);
+            do {
+                wval = wideCasTest;
+            } while (compareAndSwapWide(wval,
+                        wval - 0x0000002000000001LL, &wideCasTest) != 0);
+        }
+    }
+}
+
+/*
+ * Entry point for multi-thread test.
+ */
+static void* atomicTest(void* arg)
+{
+    pthread_mutex_lock(&waitLock);
+    threadsStarted++;
+    pthread_cond_wait(&waitCond, &waitLock);
+    pthread_mutex_unlock(&waitLock);
+
+    doAtomicTest((int) arg);
+
+    return NULL;
+}
+
+/* lifted from a VM test */
+static int64_t testAtomicSpeedSub(int repeatCount)
+{
+    static int value = 7;
+    int* valuePtr = &value;
+    int64_t start, end;
+    int i;
+
+    start = getRelativeTimeNsec();
+
+    for (i = repeatCount / 10; i != 0; i--) {
+        if (USE_ATOMIC) {
+            // succeed 10x
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+            (void) android_atomic_release_cas(7, 7, valuePtr);
+        } else {
+            // succeed 10x
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+            compareAndSwap(7, 7, valuePtr);
+        }
+    }
+
+    end = getRelativeTimeNsec();
+
+    dvmFprintf(stdout, ".");
+    fflush(stdout);
+    return end - start;
+}
+
+static void testAtomicSpeed(void)
+{
+    static const int kIterations = 10;
+    static const int kRepeatCount = 5 * 1000 * 1000;
+    static const int kDelay = 50 * 1000;
+    int64_t results[kIterations];
+    int i;
+
+    for (i = 0; i < kIterations; i++) {
+        results[i] = testAtomicSpeedSub(kRepeatCount);
+        usleep(kDelay);
+    }
+
+    dvmFprintf(stdout, "\n");
+    dvmFprintf(stdout, "%s speed test results (%d per iteration):\n",
+        USE_ATOMIC ? "Atomic" : "Non-atomic", kRepeatCount);
+    for (i = 0; i < kIterations; i++) {
+        dvmFprintf(stdout,
+            " %2d: %.3fns\n", i, (double) results[i] / kRepeatCount);
+    }
+}
+
+/*
+ * Start tests, show results.
+ */
+bool dvmTestAtomicSpeed(void)
+{
+    pthread_t threads[THREAD_COUNT];
+    void *(*startRoutine)(void*) = atomicTest;
+    int64_t startWhen, endWhen;
+
+#if defined(__ARM_ARCH__)
+    dvmFprintf(stdout, "__ARM_ARCH__ is %d\n", __ARM_ARCH__);
+#endif
+#if defined(ANDROID_SMP)
+    dvmFprintf(stdout, "ANDROID_SMP is %d\n", ANDROID_SMP);
+#endif
+    dvmFprintf(stdout, "Creating threads\n");
+
+    int i;
+    for (i = 0; i < THREAD_COUNT; i++) {
+        void* arg = (void*) i;
+        if (pthread_create(&threads[i], NULL, startRoutine, arg) != 0) {
+            dvmFprintf(stderr, "thread create failed\n");
+        }
+    }
+
+    /* wait for all the threads to reach the starting line */
+    while (1) {
+        pthread_mutex_lock(&waitLock);
+        if (threadsStarted == THREAD_COUNT) {
+            dvmFprintf(stdout, "Starting test\n");
+            startWhen = getRelativeTimeNsec();
+            pthread_cond_broadcast(&waitCond);
+            pthread_mutex_unlock(&waitLock);
+            break;
+        }
+        pthread_mutex_unlock(&waitLock);
+        usleep(100000);
+    }
+
+    for (i = 0; i < THREAD_COUNT; i++) {
+        void* retval;
+        if (pthread_join(threads[i], &retval) != 0) {
+            dvmFprintf(stderr, "thread join (%d) failed\n", i);
+        }
+    }
+
+    endWhen = getRelativeTimeNsec();
+    dvmFprintf(stdout, "All threads stopped, time is %.6fms\n",
+        (endWhen - startWhen) / 1000000.0);
+
+    /*
+     * Show results; expecting:
+     *
+     * incTest = 5000000
+     * decTest = -5000000
+     * addTest = 7500000
+     * casTest = 10000000
+     * wideCasTest = 0x6600000077000000
+     */
+    dvmFprintf(stdout, "incTest = %d\n", incTest);
+    dvmFprintf(stdout, "decTest = %d\n", decTest);
+    dvmFprintf(stdout, "addTest = %d\n", addTest);
+    dvmFprintf(stdout, "casTest = %d\n", casTest);
+    dvmFprintf(stdout, "wideCasTest = 0x%llx\n", wideCasTest);
+
+    /* do again, serially (SMP check) */
+    startWhen = getRelativeTimeNsec();
+    for (i = 0; i < THREAD_COUNT; i++) {
+        doAtomicTest(i);
+    }
+    endWhen = getRelativeTimeNsec();
+    dvmFprintf(stdout, "Same iterations done serially: time is %.6fms\n",
+        (endWhen - startWhen) / 1000000.0);
+
+    /*
+     * Hard to do a meaningful thrash test on these, so just do a simple
+     * function test.
+     */
+    andTest = 0xffd7fa96;
+    orTest = 0x122221ff;
+    swapTest = 0x11111111;
+    android_atomic_and(0xfffdaf96, &andTest);
+    android_atomic_or(0xdeaaeb00, &orTest);
+    int oldSwap = android_atomic_swap(0x22222222, &swapTest);
+    int oldSwap2 = android_atomic_swap(0x33333333, &swapTest);
+    if (android_atomic_release_cas(failingCasTest+1, failingCasTest-1,
+            &failingCasTest) == 0)
+        dvmFprintf(stdout, "failing test did not fail!\n");
+
+    dvmFprintf(stdout, "andTest = 0x%x\n", andTest);
+    dvmFprintf(stdout, "orTest = 0x%x\n", orTest);
+    dvmFprintf(stdout, "swapTest = 0x%x -> 0x%x\n", oldSwap, oldSwap2);
+    dvmFprintf(stdout, "swapTest = 0x%x -> 0x%x\n", oldSwap2, swapTest);
+    dvmFprintf(stdout, "failingCasTest = %d\n", failingCasTest);
+
+#ifdef TEST_BIONIC
+    /*
+     * Quick function test on the bionic ops.
+     */
+    int prev;
+    int tester = 7;
+    prev = __atomic_inc(&tester);
+    __atomic_inc(&tester);
+    __atomic_inc(&tester);
+    dvmFprintf(stdout, "bionic 3 inc: %d -> %d\n", prev, tester);
+    prev = __atomic_dec(&tester);
+    __atomic_dec(&tester);
+    __atomic_dec(&tester);
+    dvmFprintf(stdout, "bionic 3 dec: %d -> %d\n", prev, tester);
+    prev = __atomic_swap(27, &tester);
+    dvmFprintf(stdout, "bionic swap: %d -> %d\n", prev, tester);
+    int swapok = __atomic_cmpxchg(27, 72, &tester);
+    dvmFprintf(stdout, "bionic cmpxchg: %d (%d)\n", tester, swapok);
+#endif
+
+    testAtomicSpeed();
+
+    return 0;
+}
diff --git a/vm/test/Test.h b/vm/test/Test.h
new file mode 100644
index 0000000..f17526f
--- /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
+#define _DALVIK_TEST_TEST
+
+bool dvmTestHash(void);
+bool dvmTestAtomicSpeed(void);
+bool dvmTestIndirectRefTable(void);
+
+#endif /*_DALVIK_TEST_TEST*/
diff --git a/vm/test/TestHash.c b/vm/test/TestHash.c
new file mode 100644
index 0000000..26de141
--- /dev/null
+++ b/vm/test/TestHash.c
@@ -0,0 +1,191 @@
+/*
+ * 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) {
+        LOGE("TestHash foreach test failed\n");
+        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) {
+        LOGE("TestHash iterator test failed\n");
+        assert(false);
+    }
+}
+
+/*
+ * Some quick hash table tests.
+ */
+bool dvmTestHash(void)
+{
+    HashTable* pTab;
+    char tmpStr[64];
+    const char* str;
+    u4 hash;
+    int i;
+
+    LOGV("TestHash BEGIN\n");
+
+    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) {
+            LOGE("TestHash: failure: could not find '%s'\n", 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 {
+        LOGE("TestHash found nonexistent string (improper add?)\n");
+    }
+
+    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 */
+    char* str1;
+    str1 = dvmHashTableLookup(pTab, hash, strdup("one"),
+            (HashCompareFunc) strcmp, true);
+    assert(str1 != NULL);
+    str = dvmHashTableLookup(pTab, hash, strdup("two"),
+            (HashCompareFunc) strcmp, true);
+
+    /* remove the first one */
+    if (!dvmHashTableRemove(pTab, hash, str1))
+        LOGE("TestHash failed to delete item\n");
+    else
+        free(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) {
+        LOGE("TestHash wrong number of entries (%d)\n", count);
+    }
+
+    /* see if we can find them */
+    str = dvmHashTableLookup(pTab, hash, "one", (HashCompareFunc) strcmp,false);
+    if (str != NULL)
+        LOGE("TestHash deleted entry has returned!");
+    str = dvmHashTableLookup(pTab, hash, "two", (HashCompareFunc) strcmp,false);
+    if (str == NULL)
+        LOGE("TestHash entry vanished\n");
+
+    /* 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);
+    LOGV("TestHash END\n");
+
+    return true;
+}
+
+#endif /*NDEBUG*/
diff --git a/vm/test/TestIndirectRefTable.c b/vm/test/TestIndirectRefTable.c
new file mode 100644
index 0000000..25f1dd1
--- /dev/null
+++ b/vm/test/TestIndirectRefTable.c
@@ -0,0 +1,488 @@
+/*
+ * 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>
+
+#ifndef NDEBUG
+
+#define DBUG_MSG    LOGV
+
+/*
+ * Basic add/get/delete tests in an unsegmented table.
+ */
+static bool basicTest(void)
+{
+    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 (!dvmInitIndirectRefTable(&irt, kTableMax/2, kTableMax,
+            kIndirectKindGlobal))
+    {
+        return false;
+    }
+
+    iref0 = (IndirectRef) 0x11110;
+    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+        LOGE("unexpectedly successful removal\n");
+        goto bail;
+    }
+
+    /*
+     * Add three, check, remove in the order in which they were added.
+     */
+    DBUG_MSG("+++ START fifo\n");
+    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        LOGE("trivial add1 failed\n");
+        goto bail;
+    }
+
+    if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 ||
+        dvmGetFromIndirectRefTable(&irt, iref1) != obj1 ||
+        dvmGetFromIndirectRefTable(&irt, iref2) != obj2)
+    {
+        LOGE("objects don't match expected values %p %p %p vs. %p %p %p\n",
+            dvmGetFromIndirectRefTable(&irt, iref0),
+            dvmGetFromIndirectRefTable(&irt, iref1),
+            dvmGetFromIndirectRefTable(&irt, iref2),
+            obj0, obj1, obj2);
+        goto bail;
+    } else {
+        DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1);
+    }
+
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref2))
+    {
+        LOGE("fifo deletion failed\n");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("fifo del not empty\n");
+        goto bail;
+    }
+
+    /* get invalid entry (off the end of the list) */
+    if (dvmGetFromIndirectRefTable(&irt, iref0) != NULL) {
+        LOGE("stale entry get succeeded unexpectedly\n");
+        goto bail;
+    }
+
+    /*
+     * Add three, remove in the opposite order.
+     */
+    DBUG_MSG("+++ START lifo\n");
+    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        LOGE("trivial add2 failed\n");
+        goto bail;
+    }
+
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
+    {
+        LOGE("lifo deletion failed\n");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("lifo del not empty\n");
+        goto bail;
+    }
+
+    /*
+     * Add three, remove middle / middle / bottom / top.  (Second attempt
+     * to remove middle should fail.)
+     */
+    DBUG_MSG("+++ START unorder\n");
+    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+        LOGE("trivial add3 failed\n");
+        goto bail;
+    }
+
+    if (dvmIndirectRefTableEntries(&irt) != 3) {
+        LOGE("expected 3 entries, found %d\n",
+            dvmIndirectRefTableEntries(&irt));
+        goto bail;
+    }
+
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
+        dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
+    {
+        LOGE("unorder deletion1 failed\n");
+        goto bail;
+    }
+
+    /* get invalid entry (from hole) */
+    if (dvmGetFromIndirectRefTable(&irt, iref1) != NULL) {
+        LOGE("hole get succeeded unexpectedly\n");
+        goto bail;
+    }
+
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
+    {
+        LOGE("unorder deletion2 failed\n");
+        goto bail;
+    }
+
+    /* table should be empty now */
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("unorder del not empty\n");
+        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 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
+    if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) {
+        LOGE("trivial add4 failed\n");
+        goto bail;
+    }
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+        LOGE("remove 1 of 4 failed\n");
+        goto bail;
+    }
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+    if (dvmIndirectRefTableEntries(&irt) != 4) {
+        LOGE("hole not filled\n");
+        goto bail;
+    }
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3))
+    {
+        LOGE("remove 1/3 failed\n");
+        goto bail;
+    }
+    if (dvmIndirectRefTableEntries(&irt) != 3) {
+        LOGE("should be 3 after two deletions\n");
+        goto bail;
+    }
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
+    {
+        LOGE("remove 2/0 failed\n");
+        goto bail;
+    }
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("not empty after split remove\n");
+        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 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+        LOGE("mismatched del succeeded (%p vs %p)\n", iref0, iref1);
+        goto bail;
+    }
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+        LOGE("switched del failed\n");
+        goto bail;
+    }
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("switching del not empty\n");
+        goto bail;
+    }
+
+    /*
+     * Same as above, but with the same object.  A more rigorous checker
+     * (e.g. with slot serialization) will catch this.
+     */
+    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    if (iref0 != iref1) {
+        /* try 0, should not work */
+        if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+            LOGE("temporal del succeeded (%p vs %p)\n", iref0, iref1);
+            goto bail;
+        }
+    }
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+        LOGE("temporal cleanup failed\n");
+        goto bail;
+    }
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("temporal del not empty\n");
+        goto bail;
+    }
+
+    /*
+     * Test table overflow.
+     */
+    DBUG_MSG("+++ START overflow\n");
+    int i;
+    for (i = 0; i < kTableMax; i++) {
+        manyRefs[i] = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+        if (manyRefs[i] == NULL) {
+            LOGE("Failed adding %d of %d\n", i, kTableMax);
+            goto bail;
+        }
+    }
+    if (dvmAddToIndirectRefTable(&irt, cookie, obj0) != NULL) {
+        LOGE("Table overflow succeeded\n");
+        goto bail;
+    }
+    if (dvmIndirectRefTableEntries(&irt) != (size_t)kTableMax) {
+        LOGE("Expected %d entries, found %d\n",
+            kTableMax, dvmIndirectRefTableEntries(&irt));
+        goto bail;
+    }
+    for (i = 0; i < kTableMax-1; i++) {
+        if (!dvmRemoveFromIndirectRefTable(&irt, cookie, manyRefs[i])) {
+            LOGE("multi-remove failed at %d\n", i);
+            goto bail;
+        }
+    }
+    /* because of removal order, should have 20 entries, 19 of them holes */
+    if (dvmIndirectRefTableEntries(&irt) != (size_t)kTableMax) {
+        LOGE("Expected %d entries (with holes), found %d\n",
+            kTableMax, dvmIndirectRefTableEntries(&irt));
+        goto bail;
+    }
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, manyRefs[kTableMax-1])) {
+        LOGE("multi-remove final failed\n");
+        goto bail;
+    }
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("multi-del not empty\n");
+        goto bail;
+    }
+
+    DBUG_MSG("+++ basic test complete\n");
+    result = true;
+
+bail:
+    dvmClearIndirectRefTable(&irt);
+    return result;
+}
+
+/*
+ * Test operations on a segmented table.
+ */
+static bool segmentTest(void)
+{
+    static const int kTableMax = 20;
+    IndirectRefTable irt;
+    IndirectRef iref0, iref1, iref2, iref3;
+    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);
+    u4 cookie;
+    u4 segmentState[4];
+    bool result = false;
+
+    if (!dvmInitIndirectRefTable(&irt, kTableMax, kTableMax,
+            kIndirectKindLocal))
+    {
+        return false;
+    }
+    cookie = segmentState[0] = IRT_FIRST_SEGMENT;
+    DBUG_MSG("+++ objs %p %p %p %p\n", obj0, obj1, obj2, obj3);
+
+    /*
+     * Push two, create new segment, push two more, try to get all four,
+     * try to delete all 4.  All four should be accessible, but only the
+     * last two should be deletable.
+     */
+    DBUG_MSG("+++ START basic segment\n");
+    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
+    DBUG_MSG("+++ pushed, cookie is 0x%08x\n", cookie);
+    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
+
+    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
+        dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
+    {
+        LOGE("removed values from earlier segment\n");
+        goto bail;
+    }
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3))
+    {
+        LOGE("unable to remove values from current segment\n");
+        goto bail;
+    }
+    if (dvmIndirectRefTableEntries(&irt) != 2) {
+        LOGE("wrong total entries\n");
+        goto bail;
+    }
+    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
+    cookie = segmentState[0];
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
+        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
+    {
+        LOGE("unable to remove values from first segment\n");
+        goto bail;
+    }
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("basic push/pop not empty\n");
+        goto bail;
+    }
+
+    /*
+     * Push two, delete first, segment, push two more, pop segment, verify
+     * the last two are no longer present and hole count is right.  The
+     * adds after the segment pop should not be filling in the hole.
+     */
+    DBUG_MSG("+++ START segment pop\n");
+    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
+    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
+    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+    iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
+    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
+    cookie = segmentState[0];
+    if (dvmIndirectRefTableEntries(&irt) != 2) {
+        LOGE("wrong total entries after pop\n");
+        goto bail;
+    }
+    dvmRemoveFromIndirectRefTable(&irt, cookie, iref1);
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("not back to zero after pop + del\n");
+        goto bail;
+    }
+
+    /*
+     * Multiple segments, some empty.
+     */
+    DBUG_MSG("+++ START multiseg\n");
+    iref0 = dvmAppendToIndirectRefTable(&irt, cookie, obj0);
+    iref1 = dvmAppendToIndirectRefTable(&irt, cookie, obj1);
+    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
+    cookie = segmentState[2] = dvmPushIndirectRefTableSegment(&irt);
+    iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3);
+    iref2 = dvmAppendToIndirectRefTable(&irt, cookie, obj2);
+    dvmRemoveFromIndirectRefTable(&irt, cookie, iref3);
+    cookie = segmentState[3] = dvmPushIndirectRefTableSegment(&irt);
+    iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3);
+
+    if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 ||
+        dvmGetFromIndirectRefTable(&irt, iref1) != obj1 ||
+        dvmGetFromIndirectRefTable(&irt, iref2) != obj2 ||
+        dvmGetFromIndirectRefTable(&irt, iref3) != obj3)
+    {
+        LOGE("Unable to retrieve all multiseg objects\n");
+        goto bail;
+    }
+
+    dvmDumpIndirectRefTable(&irt, "test");
+
+    //int i;
+    //for (i = 0; i < sizeof(segmentState) / sizeof(segmentState[0]); i++) {
+    //    DBUG_MSG("+++  segment %d = 0x%08x\n", i, segmentState[i]);
+    //}
+
+    dvmRemoveFromIndirectRefTable(&irt, cookie, iref3);
+    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) {
+        LOGE("multiseg del2 worked\n");
+        goto bail;
+    }
+    dvmPopIndirectRefTableSegment(&irt, segmentState[3]);
+    cookie = segmentState[2];
+    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) {
+        LOGE("multiseg del2b failed (cookie=0x%08x ref=%p)\n", cookie, iref2);
+        goto bail;
+    }
+    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+
+    /* pop two off at once */
+    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
+    cookie = segmentState[0];
+
+    if (dvmIndirectRefTableEntries(&irt) != 2) {
+        LOGE("Unexpected entry count in multiseg\n");
+        goto bail;
+    }
+    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
+    dvmRemoveFromIndirectRefTable(&irt, cookie, iref1);
+    if (dvmIndirectRefTableEntries(&irt) != 0) {
+        LOGE("Unexpected entry count at multiseg end\n");
+        goto bail;
+    }
+
+    DBUG_MSG("+++ segment test complete\n");
+    result = true;
+
+bail:
+    dvmClearIndirectRefTable(&irt);
+    return result;
+}
+
+
+/*
+ * Some quick tests.
+ */
+bool dvmTestIndirectRefTable(void)
+{
+    if (!basicTest()) {
+        LOGE("IRT basic test failed\n");
+        return false;
+    }
+    if (!segmentTest()) {
+        LOGE("IRT segment test failed\n");
+        return false;
+    }
+
+    return true;
+}
+
+#endif /*NDEBUG*/